Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / v8_var_converter.cc
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 "content/renderer/pepper/v8_var_converter.h"
6
7 #include <map>
8 #include <stack>
9 #include <string>
10
11 #include "base/bind.h"
12 #include "base/containers/hash_tables.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "content/public/renderer/renderer_ppapi_host.h"
17 #include "content/renderer/pepper/host_array_buffer_var.h"
18 #include "content/renderer/pepper/resource_converter.h"
19 #include "ppapi/shared_impl/array_var.h"
20 #include "ppapi/shared_impl/dictionary_var.h"
21 #include "ppapi/shared_impl/var.h"
22 #include "ppapi/shared_impl/var_tracker.h"
23 #include "third_party/WebKit/public/platform/WebArrayBuffer.h"
24 #include "third_party/WebKit/public/web/WebArrayBufferConverter.h"
25
26 using ppapi::ArrayBufferVar;
27 using ppapi::ArrayVar;
28 using ppapi::DictionaryVar;
29 using ppapi::ScopedPPVar;
30 using ppapi::StringVar;
31 using std::make_pair;
32
33 namespace {
34
35 template <class T>
36 struct StackEntry {
37   StackEntry(T v) : val(v), sentinel(false) {}
38   T val;
39   // Used to track parent nodes on the stack while traversing the graph.
40   bool sentinel;
41 };
42
43 struct HashedHandle {
44   HashedHandle(v8::Handle<v8::Object> h) : handle(h) {}
45   size_t hash() const { return handle->GetIdentityHash(); }
46   bool operator==(const HashedHandle& h) const { return handle == h.handle; }
47   bool operator<(const HashedHandle& h) const { return hash() < h.hash(); }
48   v8::Handle<v8::Object> handle;
49 };
50
51 }  // namespace
52
53 namespace BASE_HASH_NAMESPACE {
54 #if defined(COMPILER_GCC)
55 template <>
56 struct hash<HashedHandle> {
57   size_t operator()(const HashedHandle& handle) const {
58     return handle.hash();
59   }
60 };
61 #elif defined(COMPILER_MSVC)
62 inline size_t hash_value(const HashedHandle& handle) {
63   return handle.hash();
64 }
65 #endif
66 }  // namespace BASE_HASH_NAMESPACE
67
68 namespace content {
69
70 namespace {
71
72 // Maps PP_Var IDs to the V8 value handle they correspond to.
73 typedef base::hash_map<int64_t, v8::Handle<v8::Value> > VarHandleMap;
74 typedef base::hash_set<int64_t> ParentVarSet;
75
76 // Maps V8 value handles to the PP_Var they correspond to.
77 typedef base::hash_map<HashedHandle, ScopedPPVar> HandleVarMap;
78 typedef base::hash_set<HashedHandle> ParentHandleSet;
79
80 // Returns a V8 value which corresponds to a given PP_Var. If |var| is a
81 // reference counted PP_Var type, and it exists in |visited_ids|, the V8 value
82 // associated with it in the map will be returned, otherwise a new V8 value will
83 // be created and added to the map. |did_create| indicates whether a new v8
84 // value was created as a result of calling the function.
85 bool GetOrCreateV8Value(v8::Isolate* isolate,
86                         const PP_Var& var,
87                         v8::Handle<v8::Value>* result,
88                         bool* did_create,
89                         VarHandleMap* visited_ids,
90                         ParentVarSet* parent_ids) {
91   *did_create = false;
92
93   if (ppapi::VarTracker::IsVarTypeRefcounted(var.type)) {
94     if (parent_ids->count(var.value.as_id) != 0)
95       return false;
96     VarHandleMap::iterator it = visited_ids->find(var.value.as_id);
97     if (it != visited_ids->end()) {
98       *result = it->second;
99       return true;
100     }
101   }
102
103   switch (var.type) {
104     case PP_VARTYPE_UNDEFINED:
105       *result = v8::Undefined(isolate);
106       break;
107     case PP_VARTYPE_NULL:
108       *result = v8::Null(isolate);
109       break;
110     case PP_VARTYPE_BOOL:
111       *result = (var.value.as_bool == PP_TRUE)
112           ? v8::True(isolate)
113           : v8::False(isolate);
114       break;
115     case PP_VARTYPE_INT32:
116       *result = v8::Integer::New(isolate, var.value.as_int);
117       break;
118     case PP_VARTYPE_DOUBLE:
119       *result = v8::Number::New(isolate, var.value.as_double);
120       break;
121     case PP_VARTYPE_STRING: {
122       StringVar* string = StringVar::FromPPVar(var);
123       if (!string) {
124         NOTREACHED();
125         result->Clear();
126         return false;
127       }
128       const std::string& value = string->value();
129       // Create a string object rather than a string primitive. This allows us
130       // to have multiple references to the same string in javascript, which
131       // matches the reference behavior of PP_Vars.
132       *result = v8::String::NewFromUtf8(isolate,
133                                         value.c_str(),
134                                         v8::String::kNormalString,
135                                         value.size())->ToObject();
136       break;
137     }
138     case PP_VARTYPE_ARRAY_BUFFER: {
139       ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var);
140       if (!buffer) {
141         NOTREACHED();
142         result->Clear();
143         return false;
144       }
145       HostArrayBufferVar* host_buffer =
146           static_cast<HostArrayBufferVar*>(buffer);
147       *result = blink::WebArrayBufferConverter::toV8Value(
148           &host_buffer->webkit_buffer());
149       break;
150     }
151     case PP_VARTYPE_ARRAY:
152       *result = v8::Array::New(isolate);
153       break;
154     case PP_VARTYPE_DICTIONARY:
155       *result = v8::Object::New(isolate);
156       break;
157     case PP_VARTYPE_OBJECT:
158     case PP_VARTYPE_RESOURCE:
159       // TODO(mgiuca): Convert PP_VARTYPE_RESOURCE vars into the correct V8
160       // type. (http://crbug.com/177017)
161       result->Clear();
162       return false;
163   }
164
165   *did_create = true;
166   if (ppapi::VarTracker::IsVarTypeRefcounted(var.type))
167     (*visited_ids)[var.value.as_id] = *result;
168   return true;
169 }
170
171 // For a given V8 value handle, this returns a PP_Var which corresponds to it.
172 // If the handle already exists in |visited_handles|, the PP_Var associated with
173 // it will be returned, otherwise a new V8 value will be created and added to
174 // the map. |did_create| indicates if a new PP_Var was created as a result of
175 // calling the function.
176 bool GetOrCreateVar(v8::Handle<v8::Value> val,
177                     v8::Handle<v8::Context> context,
178                     PP_Var* result,
179                     bool* did_create,
180                     HandleVarMap* visited_handles,
181                     ParentHandleSet* parent_handles,
182                     ResourceConverter* resource_converter) {
183   CHECK(!val.IsEmpty());
184   *did_create = false;
185
186   // Even though every v8 string primitive encountered will be a unique object,
187   // we still add them to |visited_handles| so that the corresponding string
188   // PP_Var created will be properly refcounted.
189   if (val->IsObject() || val->IsString()) {
190     if (parent_handles->count(HashedHandle(val->ToObject())) != 0)
191       return false;
192
193     HandleVarMap::const_iterator it = visited_handles->find(
194         HashedHandle(val->ToObject()));
195     if (it != visited_handles->end()) {
196       *result = it->second.get();
197       return true;
198     }
199   }
200
201   if (val->IsUndefined()) {
202     *result = PP_MakeUndefined();
203   } else if (val->IsNull()) {
204     *result = PP_MakeNull();
205   } else if (val->IsBoolean() || val->IsBooleanObject()) {
206     *result = PP_MakeBool(PP_FromBool(val->ToBoolean()->Value()));
207   } else if (val->IsInt32()) {
208     *result = PP_MakeInt32(val->ToInt32()->Value());
209   } else if (val->IsNumber() || val->IsNumberObject()) {
210     *result = PP_MakeDouble(val->ToNumber()->Value());
211   } else if (val->IsString() || val->IsStringObject()) {
212     v8::String::Utf8Value utf8(val->ToString());
213     *result = StringVar::StringToPPVar(std::string(*utf8, utf8.length()));
214   } else if (val->IsArray()) {
215     *result = (new ArrayVar())->GetPPVar();
216   } else if (val->IsObject()) {
217     scoped_ptr<blink::WebArrayBuffer> web_array_buffer(
218         blink::WebArrayBufferConverter::createFromV8Value(val));
219     if (web_array_buffer.get()) {
220       scoped_refptr<HostArrayBufferVar> buffer_var(new HostArrayBufferVar(
221           *web_array_buffer));
222       *result = buffer_var->GetPPVar();
223     } else {
224       bool was_resource;
225       if (!resource_converter->FromV8Value(val->ToObject(), context, result,
226                                            &was_resource))
227         return false;
228       if (!was_resource) {
229         *result = (new DictionaryVar())->GetPPVar();
230       }
231     }
232   } else {
233     // Silently ignore the case where we can't convert to a Var as we may
234     // be trying to convert a type that doesn't have a corresponding
235     // PP_Var type.
236     return true;
237   }
238
239   *did_create = true;
240   if (val->IsObject() || val->IsString()) {
241     visited_handles->insert(make_pair(
242         HashedHandle(val->ToObject()),
243         ScopedPPVar(ScopedPPVar::PassRef(), *result)));
244   }
245   return true;
246 }
247
248 bool CanHaveChildren(PP_Var var) {
249   return var.type == PP_VARTYPE_ARRAY || var.type == PP_VARTYPE_DICTIONARY;
250 }
251
252 }  // namespace
253
254 V8VarConverter::V8VarConverter(PP_Instance instance)
255     : message_loop_proxy_(base::MessageLoopProxy::current()) {
256   resource_converter_.reset(new ResourceConverterImpl(
257       instance, RendererPpapiHost::GetForPPInstance(instance)));
258 }
259
260 V8VarConverter::V8VarConverter(
261     PP_Instance instance,
262     scoped_ptr<ResourceConverter> resource_converter)
263     : message_loop_proxy_(base::MessageLoopProxy::current()),
264       resource_converter_(resource_converter.release()) {
265 }
266
267 V8VarConverter::~V8VarConverter() {
268 }
269
270 // To/FromV8Value use a stack-based DFS search to traverse V8/Var graph. Each
271 // iteration, the top node on the stack examined. If the node has not been
272 // visited yet (i.e. sentinel == false) then it is added to the list of parents
273 // which contains all of the nodes on the path from the start node to the
274 // current node. Each of the current nodes children are examined. If they appear
275 // in the list of parents it means we have a cycle and we return NULL.
276 // Otherwise, if they can have children, we add them to the stack. If the
277 // node at the top of the stack has already been visited, then we pop it off the
278 // stack and erase it from the list of parents.
279 // static
280 bool V8VarConverter::ToV8Value(const PP_Var& var,
281                                v8::Handle<v8::Context> context,
282                                v8::Handle<v8::Value>* result) {
283   v8::Context::Scope context_scope(context);
284   v8::Isolate* isolate = context->GetIsolate();
285   v8::EscapableHandleScope handle_scope(isolate);
286
287   VarHandleMap visited_ids;
288   ParentVarSet parent_ids;
289
290   std::stack<StackEntry<PP_Var> > stack;
291   stack.push(StackEntry<PP_Var>(var));
292   v8::Local<v8::Value> root;
293   bool is_root = true;
294
295   while (!stack.empty()) {
296     const PP_Var& current_var = stack.top().val;
297     v8::Handle<v8::Value> current_v8;
298
299     if (stack.top().sentinel) {
300       stack.pop();
301       if (CanHaveChildren(current_var))
302         parent_ids.erase(current_var.value.as_id);
303       continue;
304     } else {
305       stack.top().sentinel = true;
306     }
307
308     bool did_create = false;
309     if (!GetOrCreateV8Value(isolate, current_var, &current_v8, &did_create,
310                             &visited_ids, &parent_ids)) {
311       return false;
312     }
313
314     if (is_root) {
315       is_root = false;
316       root = current_v8;
317     }
318
319     // Add child nodes to the stack.
320     if (current_var.type == PP_VARTYPE_ARRAY) {
321       parent_ids.insert(current_var.value.as_id);
322       ArrayVar* array_var = ArrayVar::FromPPVar(current_var);
323       if (!array_var) {
324         NOTREACHED();
325         return false;
326       }
327       DCHECK(current_v8->IsArray());
328       v8::Handle<v8::Array> v8_array = current_v8.As<v8::Array>();
329
330       for (size_t i = 0; i < array_var->elements().size(); ++i) {
331         const PP_Var& child_var = array_var->elements()[i].get();
332         v8::Handle<v8::Value> child_v8;
333         if (!GetOrCreateV8Value(isolate, child_var, &child_v8, &did_create,
334                                 &visited_ids, &parent_ids)) {
335           return false;
336         }
337         if (did_create && CanHaveChildren(child_var))
338           stack.push(child_var);
339         v8::TryCatch try_catch;
340         v8_array->Set(static_cast<uint32>(i), child_v8);
341         if (try_catch.HasCaught()) {
342           LOG(ERROR) << "Setter for index " << i << " threw an exception.";
343           return false;
344         }
345       }
346     } else if (current_var.type == PP_VARTYPE_DICTIONARY) {
347       parent_ids.insert(current_var.value.as_id);
348       DictionaryVar* dict_var = DictionaryVar::FromPPVar(current_var);
349       if (!dict_var) {
350         NOTREACHED();
351         return false;
352       }
353       DCHECK(current_v8->IsObject());
354       v8::Handle<v8::Object> v8_object = current_v8->ToObject();
355
356       for (DictionaryVar::KeyValueMap::const_iterator iter =
357                dict_var->key_value_map().begin();
358            iter != dict_var->key_value_map().end();
359            ++iter) {
360         const std::string& key = iter->first;
361         const PP_Var& child_var = iter->second.get();
362         v8::Handle<v8::Value> child_v8;
363         if (!GetOrCreateV8Value(isolate, child_var, &child_v8, &did_create,
364                                 &visited_ids, &parent_ids)) {
365           return false;
366         }
367         if (did_create && CanHaveChildren(child_var))
368           stack.push(child_var);
369         v8::TryCatch try_catch;
370         v8_object->Set(v8::String::NewFromUtf8(isolate,
371                                                key.c_str(),
372                                                v8::String::kNormalString,
373                                                key.length()),
374                        child_v8);
375         if (try_catch.HasCaught()) {
376           LOG(ERROR) << "Setter for property " << key.c_str() << " threw an "
377               << "exception.";
378           return false;
379         }
380       }
381     }
382   }
383
384   *result = handle_scope.Escape(root);
385   return true;
386 }
387
388 void V8VarConverter::FromV8Value(
389     v8::Handle<v8::Value> val,
390     v8::Handle<v8::Context> context,
391     const base::Callback<void(const ScopedPPVar&, bool)>& callback) {
392   v8::Context::Scope context_scope(context);
393   v8::HandleScope handle_scope(context->GetIsolate());
394
395   HandleVarMap visited_handles;
396   ParentHandleSet parent_handles;
397
398   std::stack<StackEntry<v8::Handle<v8::Value> > > stack;
399   stack.push(StackEntry<v8::Handle<v8::Value> >(val));
400   ScopedPPVar root;
401   bool is_root = true;
402
403   while (!stack.empty()) {
404     v8::Handle<v8::Value> current_v8 = stack.top().val;
405     PP_Var current_var;
406
407     if (stack.top().sentinel) {
408       stack.pop();
409       if (current_v8->IsObject())
410         parent_handles.erase(HashedHandle(current_v8->ToObject()));
411       continue;
412     } else {
413       stack.top().sentinel = true;
414     }
415
416     bool did_create = false;
417     if (!GetOrCreateVar(current_v8, context, &current_var, &did_create,
418                         &visited_handles, &parent_handles,
419                         resource_converter_.get())) {
420       message_loop_proxy_->PostTask(FROM_HERE,
421           base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false));
422       return;
423     }
424
425     if (is_root) {
426       is_root = false;
427       root = current_var;
428     }
429
430     // Add child nodes to the stack.
431     if (current_var.type == PP_VARTYPE_ARRAY) {
432       DCHECK(current_v8->IsArray());
433       v8::Handle<v8::Array> v8_array = current_v8.As<v8::Array>();
434       parent_handles.insert(HashedHandle(v8_array));
435
436       ArrayVar* array_var = ArrayVar::FromPPVar(current_var);
437       if (!array_var) {
438         NOTREACHED();
439         message_loop_proxy_->PostTask(FROM_HERE,
440             base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false));
441         return;
442       }
443
444       for (uint32 i = 0; i < v8_array->Length(); ++i) {
445         v8::TryCatch try_catch;
446         v8::Handle<v8::Value> child_v8 = v8_array->Get(i);
447         if (try_catch.HasCaught()) {
448           message_loop_proxy_->PostTask(FROM_HERE,
449               base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false));
450           return;
451         }
452
453         if (!v8_array->HasRealIndexedProperty(i))
454           continue;
455
456         PP_Var child_var;
457         if (!GetOrCreateVar(child_v8, context, &child_var, &did_create,
458                             &visited_handles, &parent_handles,
459                             resource_converter_.get())) {
460           message_loop_proxy_->PostTask(FROM_HERE,
461               base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false));
462           return;
463         }
464         if (did_create && child_v8->IsObject())
465           stack.push(child_v8);
466
467         array_var->Set(i, child_var);
468       }
469     } else if (current_var.type == PP_VARTYPE_DICTIONARY) {
470       DCHECK(current_v8->IsObject());
471       v8::Handle<v8::Object> v8_object = current_v8->ToObject();
472       parent_handles.insert(HashedHandle(v8_object));
473
474       DictionaryVar* dict_var = DictionaryVar::FromPPVar(current_var);
475       if (!dict_var) {
476         NOTREACHED();
477         message_loop_proxy_->PostTask(FROM_HERE,
478             base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false));
479         return;
480       }
481
482       v8::Handle<v8::Array> property_names(v8_object->GetOwnPropertyNames());
483       for (uint32 i = 0; i < property_names->Length(); ++i) {
484         v8::Handle<v8::Value> key(property_names->Get(i));
485
486         // Extend this test to cover more types as necessary and if sensible.
487         if (!key->IsString() && !key->IsNumber()) {
488           NOTREACHED() << "Key \"" << *v8::String::Utf8Value(key) << "\" "
489                           "is neither a string nor a number";
490           message_loop_proxy_->PostTask(FROM_HERE,
491               base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false));
492           return;
493         }
494
495         // Skip all callbacks: crbug.com/139933
496         if (v8_object->HasRealNamedCallbackProperty(key->ToString()))
497           continue;
498
499         v8::String::Utf8Value name_utf8(key->ToString());
500
501         v8::TryCatch try_catch;
502         v8::Handle<v8::Value> child_v8 = v8_object->Get(key);
503         if (try_catch.HasCaught()) {
504           message_loop_proxy_->PostTask(FROM_HERE,
505               base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false));
506           return;
507         }
508
509         PP_Var child_var;
510         if (!GetOrCreateVar(child_v8, context, &child_var, &did_create,
511                             &visited_handles, &parent_handles,
512                             resource_converter_.get())) {
513           message_loop_proxy_->PostTask(FROM_HERE,
514               base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false));
515           return;
516         }
517         if (did_create && child_v8->IsObject())
518           stack.push(child_v8);
519
520         bool success = dict_var->SetWithStringKey(
521             std::string(*name_utf8, name_utf8.length()), child_var);
522         DCHECK(success);
523       }
524     }
525   }
526   resource_converter_->Flush(base::Bind(callback, root));
527 }
528
529 }  // namespace content