Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: webkit/plugins/ppapi/v8_var_converter.cc

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

Powered by Google App Engine
This is Rietveld 408576698