1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/shared_impl/var_value_conversions.h"
6
7#include <limits>
8#include <set>
9#include <stack>
10
11#include "base/logging.h"
12#include "base/memory/ref_counted.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/stl_util.h"
15#include "base/values.h"
16#include "ppapi/c/pp_bool.h"
17#include "ppapi/c/pp_stdint.h"
18#include "ppapi/shared_impl/array_var.h"
19#include "ppapi/shared_impl/dictionary_var.h"
20#include "ppapi/shared_impl/ppapi_globals.h"
21#include "ppapi/shared_impl/scoped_pp_var.h"
22#include "ppapi/shared_impl/var.h"
23#include "ppapi/shared_impl/var_tracker.h"
24
25namespace ppapi {
26
27namespace {
28
29// In CreateValueFromVar(), a stack is used to keep track of conversion progress
30// of array and dictionary vars. VarNode represents elements of that stack.
31struct VarNode {
32  VarNode(const PP_Var& in_var, base::Value* in_value)
33      : var(in_var),
34        value(in_value),
35        sentinel(false) {
36  }
37
38  // This object doesn't hold a reference to it.
39  PP_Var var;
40  // It is not owned by this object.
41  base::Value* value;
42  // When this is set to true for a node in the stack, it means that we have
43  // finished processing the node itself. However, we keep it in the stack as
44  // a sentinel. When it becomes the top element of the stack again, we know
45  // that we have processed all the descendants of this node.
46  bool sentinel;
47};
48
49// In CreateVarFromValue(), a stack is used to keep track of conversion progress
50// of list and dictionary values. ValueNode represents elements of that stack.
51struct ValueNode {
52  ValueNode(const PP_Var& in_var, const base::Value* in_value)
53      : var(in_var),
54        value(in_value) {
55  }
56
57  // This object doesn't hold a reference to it.
58  PP_Var var;
59  // It is not owned by this object.
60  const base::Value* value;
61};
62
63// Helper function for CreateValueFromVar(). It only looks at |var| but not its
64// descendants. The conversion result is stored in |value|. If |var| is array or
65// dictionary, a new node is pushed onto |state|.
66//
67// Returns false on failure.
68bool CreateValueFromVarHelper(const std::set<int64_t>& parent_ids,
69                              const PP_Var& var,
70                              scoped_ptr<base::Value>* value,
71                              std::stack<VarNode>* state) {
72  switch (var.type) {
73    case PP_VARTYPE_UNDEFINED:
74    case PP_VARTYPE_NULL: {
75      value->reset(base::Value::CreateNullValue());
76      return true;
77    }
78    case PP_VARTYPE_BOOL: {
79      value->reset(new base::FundamentalValue(PP_ToBool(var.value.as_bool)));
80      return true;
81    }
82    case PP_VARTYPE_INT32: {
83      value->reset(new base::FundamentalValue(var.value.as_int));
84      return true;
85    }
86    case PP_VARTYPE_DOUBLE: {
87      value->reset(new base::FundamentalValue(var.value.as_double));
88      return true;
89    }
90    case PP_VARTYPE_STRING: {
91      StringVar* string_var = StringVar::FromPPVar(var);
92      if (!string_var)
93        return false;
94
95      value->reset(new base::StringValue(string_var->value()));
96      return true;
97    }
98    case PP_VARTYPE_OBJECT: {
99      return false;
100    }
101    case PP_VARTYPE_ARRAY: {
102      if (ContainsKey(parent_ids, var.value.as_id)) {
103        // A circular reference is found.
104        return false;
105      }
106
107      value->reset(new base::ListValue());
108      state->push(VarNode(var, value->get()));
109      return true;
110    }
111    case PP_VARTYPE_DICTIONARY: {
112      if (ContainsKey(parent_ids, var.value.as_id)) {
113        // A circular reference is found.
114        return false;
115      }
116
117      value->reset(new base::DictionaryValue());
118      state->push(VarNode(var, value->get()));
119      return true;
120    }
121    case PP_VARTYPE_ARRAY_BUFFER: {
122      ArrayBufferVar* array_buffer = ArrayBufferVar::FromPPVar(var);
123      if (!array_buffer)
124        return false;
125
126      base::BinaryValue* binary_value =
127          base::BinaryValue::CreateWithCopiedBuffer(
128              static_cast<const char*>(array_buffer->Map()),
129              array_buffer->ByteLength());
130      array_buffer->Unmap();
131      value->reset(binary_value);
132      return true;
133    }
134    case PP_VARTYPE_RESOURCE: {
135      return false;
136    }
137  }
138  NOTREACHED();
139  return false;
140}
141
142// Helper function for CreateVarFromValue(). It only looks at |value| but not
143// its descendants. The conversion result is stored in |var|. If |value| is list
144// or dictionary, a new node is pushed onto |state|.
145//
146// Returns false on failure.
147bool CreateVarFromValueHelper(const base::Value& value,
148                              ScopedPPVar* var,
149                              std::stack<ValueNode>* state) {
150  switch (value.GetType()) {
151    case base::Value::TYPE_NULL: {
152      *var = PP_MakeNull();
153      return true;
154    }
155    case base::Value::TYPE_BOOLEAN: {
156      bool result = false;
157      if (value.GetAsBoolean(&result)) {
158        *var = PP_MakeBool(PP_FromBool(result));
159        return true;
160      }
161      return false;
162    }
163    case base::Value::TYPE_INTEGER: {
164      int result = 0;
165      if (value.GetAsInteger(&result)) {
166        *var = PP_MakeInt32(result);
167        return true;
168      }
169      return false;
170    }
171    case base::Value::TYPE_DOUBLE: {
172      double result = 0;
173      if (value.GetAsDouble(&result)) {
174        *var = PP_MakeDouble(result);
175        return true;
176      }
177      return false;
178    }
179    case base::Value::TYPE_STRING: {
180      std::string result;
181      if (value.GetAsString(&result)) {
182        *var = ScopedPPVar(ScopedPPVar::PassRef(),
183                           StringVar::StringToPPVar(result));
184        return true;
185      }
186      return false;
187    }
188    case base::Value::TYPE_BINARY: {
189      const base::BinaryValue& binary_value =
190          static_cast<const base::BinaryValue&>(value);
191
192      size_t size = binary_value.GetSize();
193      if (size > std::numeric_limits<uint32>::max())
194        return false;
195
196      ScopedPPVar temp(
197          ScopedPPVar::PassRef(),
198          PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
199              static_cast<uint32>(size), binary_value.GetBuffer()));
200      if (temp.get().type == PP_VARTYPE_ARRAY_BUFFER) {
201        *var = temp;
202        return true;
203      }
204      return false;
205    }
206    case base::Value::TYPE_DICTIONARY: {
207      scoped_refptr<DictionaryVar> dict_var(new DictionaryVar());
208      *var = ScopedPPVar(ScopedPPVar::PassRef(), dict_var->GetPPVar());
209      state->push(ValueNode(var->get(), &value));
210      return true;
211    }
212    case base::Value::TYPE_LIST: {
213      scoped_refptr<ArrayVar> array_var(new ArrayVar());
214      *var = ScopedPPVar(ScopedPPVar::PassRef(), array_var->GetPPVar());
215      state->push(ValueNode(var->get(), &value));
216      return true;
217    }
218  }
219  NOTREACHED();
220  return false;
221}
222
223}  // namespace
224
225base::Value* CreateValueFromVar(const PP_Var& var) {
226  // Used to detect circular references.
227  std::set<int64_t> parent_ids;
228  std::stack<VarNode> state;
229  scoped_ptr<base::Value> root_value;
230
231  if (!CreateValueFromVarHelper(parent_ids, var, &root_value, &state))
232    return NULL;
233
234  while (!state.empty()) {
235    VarNode& top = state.top();
236    if (top.sentinel) {
237      parent_ids.erase(top.var.value.as_id);
238      state.pop();
239    } else if (top.var.type == PP_VARTYPE_DICTIONARY) {
240      parent_ids.insert(top.var.value.as_id);
241      top.sentinel = true;
242
243      DictionaryVar* dict_var = DictionaryVar::FromPPVar(top.var);
244      if (!dict_var)
245        return NULL;
246
247      DCHECK(top.value->GetType() == base::Value::TYPE_DICTIONARY);
248      base::DictionaryValue* dict_value =
249          static_cast<base::DictionaryValue*>(top.value);
250
251      for (DictionaryVar::KeyValueMap::const_iterator iter =
252               dict_var->key_value_map().begin();
253           iter != dict_var->key_value_map().end();
254           ++iter) {
255        // Skip the key-value pair if the value is undefined or null.
256        if (iter->second.get().type == PP_VARTYPE_UNDEFINED ||
257            iter->second.get().type == PP_VARTYPE_NULL) {
258          continue;
259        }
260
261        scoped_ptr<base::Value> child_value;
262        if (!CreateValueFromVarHelper(parent_ids, iter->second.get(),
263                                      &child_value, &state)) {
264          return NULL;
265        }
266
267        dict_value->SetWithoutPathExpansion(iter->first, child_value.release());
268      }
269    } else if (top.var.type == PP_VARTYPE_ARRAY) {
270      parent_ids.insert(top.var.value.as_id);
271      top.sentinel = true;
272
273      ArrayVar* array_var = ArrayVar::FromPPVar(top.var);
274      if (!array_var)
275        return NULL;
276
277      DCHECK(top.value->GetType() == base::Value::TYPE_LIST);
278      base::ListValue* list_value = static_cast<base::ListValue*>(top.value);
279
280      for (ArrayVar::ElementVector::const_iterator iter =
281               array_var->elements().begin();
282           iter != array_var->elements().end();
283           ++iter) {
284        scoped_ptr<base::Value> child_value;
285        if (!CreateValueFromVarHelper(parent_ids, iter->get(), &child_value,
286                                      &state)) {
287          return NULL;
288        }
289
290        list_value->Append(child_value.release());
291      }
292    } else {
293      NOTREACHED();
294      return NULL;
295    }
296  }
297  DCHECK(parent_ids.empty());
298  return root_value.release();
299}
300
301PP_Var CreateVarFromValue(const base::Value& value) {
302  std::stack<ValueNode> state;
303  ScopedPPVar root_var;
304
305  if (!CreateVarFromValueHelper(value, &root_var, &state))
306    return PP_MakeUndefined();
307
308  while (!state.empty()) {
309    ValueNode top = state.top();
310    state.pop();
311
312    if (top.value->GetType() == base::Value::TYPE_DICTIONARY) {
313      const base::DictionaryValue* dict_value =
314          static_cast<const base::DictionaryValue*>(top.value);
315      DictionaryVar* dict_var = DictionaryVar::FromPPVar(top.var);
316      DCHECK(dict_var);
317      for (base::DictionaryValue::Iterator iter(*dict_value);
318           !iter.IsAtEnd();
319           iter.Advance()) {
320        ScopedPPVar child_var;
321        if (!CreateVarFromValueHelper(iter.value(), &child_var, &state) ||
322            !dict_var->SetWithStringKey(iter.key(), child_var.get())) {
323          return PP_MakeUndefined();
324        }
325      }
326    } else if (top.value->GetType() == base::Value::TYPE_LIST) {
327      const base::ListValue* list_value =
328          static_cast<const base::ListValue*>(top.value);
329      ArrayVar* array_var = ArrayVar::FromPPVar(top.var);
330      DCHECK(array_var);
331      for (base::ListValue::const_iterator iter = list_value->begin();
332           iter != list_value->end();
333           ++iter) {
334        ScopedPPVar child_var;
335        if (!CreateVarFromValueHelper(**iter, &child_var, &state))
336          return PP_MakeUndefined();
337
338        array_var->elements().push_back(child_var);
339      }
340    } else {
341      NOTREACHED();
342      return PP_MakeUndefined();
343    }
344  }
345
346  return root_var.Release();
347}
348
349base::ListValue* CreateListValueFromVarVector(
350    const std::vector<PP_Var>& vars) {
351  scoped_ptr<base::ListValue> list_value(new base::ListValue());
352
353  for (std::vector<PP_Var>::const_iterator iter = vars.begin();
354       iter != vars.end();
355       ++iter) {
356    base::Value* value = CreateValueFromVar(*iter);
357    if (!value)
358      return NULL;
359    list_value->Append(value);
360  }
361  return list_value.release();
362}
363
364bool CreateVarVectorFromListValue(const base::ListValue& list_value,
365                                  std::vector<PP_Var>* vars) {
366  if (!vars)
367    return false;
368
369  std::vector<ScopedPPVar> result;
370  result.reserve(list_value.GetSize());
371  for (base::ListValue::const_iterator iter = list_value.begin();
372       iter != list_value.end();
373       ++iter) {
374    ScopedPPVar child_var(ScopedPPVar::PassRef(),
375                          CreateVarFromValue(**iter));
376    if (child_var.get().type == PP_VARTYPE_UNDEFINED)
377      return false;
378
379    result.push_back(child_var);
380  }
381
382  vars->clear();
383  vars->reserve(result.size());
384  for (std::vector<ScopedPPVar>::iterator iter = result.begin();
385       iter != result.end();
386       ++iter) {
387    vars->push_back(iter->Release());
388  }
389
390  return true;
391}
392
393}  // namespace ppapi
394
395