| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/pepper/v8_var_converter.h" | 5 #include "content/renderer/pepper/v8_var_converter.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <stack> | 8 #include <stack> |
| 9 #include <string> | 9 #include <string> |
| 10 | 10 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 } | 57 } |
| 58 }; | 58 }; |
| 59 #elif defined(COMPILER_MSVC) | 59 #elif defined(COMPILER_MSVC) |
| 60 inline size_t hash_value(const HashedHandle& handle) { | 60 inline size_t hash_value(const HashedHandle& handle) { |
| 61 return handle.hash(); | 61 return handle.hash(); |
| 62 } | 62 } |
| 63 #endif | 63 #endif |
| 64 } // namespace BASE_HASH_NAMESPACE | 64 } // namespace BASE_HASH_NAMESPACE |
| 65 | 65 |
| 66 namespace content { | 66 namespace content { |
| 67 namespace V8VarConverter { | |
| 68 | 67 |
| 69 namespace { | 68 namespace { |
| 70 | 69 |
| 71 // Maps PP_Var IDs to the V8 value handle they correspond to. | 70 // Maps PP_Var IDs to the V8 value handle they correspond to. |
| 72 typedef base::hash_map<int64_t, v8::Handle<v8::Value> > VarHandleMap; | 71 typedef base::hash_map<int64_t, v8::Handle<v8::Value> > VarHandleMap; |
| 73 typedef base::hash_set<int64_t> ParentVarSet; | 72 typedef base::hash_set<int64_t> ParentVarSet; |
| 74 | 73 |
| 75 // Maps V8 value handles to the PP_Var they correspond to. | 74 // Maps V8 value handles to the PP_Var they correspond to. |
| 76 typedef base::hash_map<HashedHandle, ScopedPPVar> HandleVarMap; | 75 typedef base::hash_map<HashedHandle, ScopedPPVar> HandleVarMap; |
| 77 typedef base::hash_set<HashedHandle> ParentHandleSet; | 76 typedef base::hash_set<HashedHandle> ParentHandleSet; |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 228 } | 227 } |
| 229 return true; | 228 return true; |
| 230 } | 229 } |
| 231 | 230 |
| 232 bool CanHaveChildren(PP_Var var) { | 231 bool CanHaveChildren(PP_Var var) { |
| 233 return var.type == PP_VARTYPE_ARRAY || var.type == PP_VARTYPE_DICTIONARY; | 232 return var.type == PP_VARTYPE_ARRAY || var.type == PP_VARTYPE_DICTIONARY; |
| 234 } | 233 } |
| 235 | 234 |
| 236 } // namespace | 235 } // namespace |
| 237 | 236 |
| 237 V8VarConverter::V8VarConverter() |
| 238 : message_loop_proxy_(base::MessageLoopProxy::current()), |
| 239 resource_converter_(new ResourceConverterImpl()) { |
| 240 } |
| 241 |
| 242 V8VarConverter::V8VarConverter( |
| 243 const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy, |
| 244 scoped_ptr<ResourceConverter> resource_converter) |
| 245 : message_loop_proxy_(message_loop_proxy), |
| 246 resource_converter_(resource_converter.release()) { |
| 247 } |
| 248 |
| 249 V8VarConverter::~V8VarConverter() { |
| 250 } |
| 251 |
| 238 // To/FromV8Value use a stack-based DFS search to traverse V8/Var graph. Each | 252 // To/FromV8Value use a stack-based DFS search to traverse V8/Var graph. Each |
| 239 // iteration, the top node on the stack examined. If the node has not been | 253 // iteration, the top node on the stack examined. If the node has not been |
| 240 // visited yet (i.e. sentinel == false) then it is added to the list of parents | 254 // visited yet (i.e. sentinel == false) then it is added to the list of parents |
| 241 // which contains all of the nodes on the path from the start node to the | 255 // which contains all of the nodes on the path from the start node to the |
| 242 // current node. Each of the current nodes children are examined. If they appear | 256 // current node. Each of the current nodes children are examined. If they appear |
| 243 // in the list of parents it means we have a cycle and we return NULL. | 257 // in the list of parents it means we have a cycle and we return NULL. |
| 244 // Otherwise, if they can have children, we add them to the stack. If the | 258 // Otherwise, if they can have children, we add them to the stack. If the |
| 245 // node at the top of the stack has already been visited, then we pop it off the | 259 // node at the top of the stack has already been visited, then we pop it off the |
| 246 // stack and erase it from the list of parents. | 260 // stack and erase it from the list of parents. |
| 247 // static | 261 // static |
| 248 bool ToV8Value(const PP_Var& var, | 262 bool V8VarConverter::ToV8Value(const PP_Var& var, |
| 249 v8::Handle<v8::Context> context, | 263 v8::Handle<v8::Context> context, |
| 250 v8::Handle<v8::Value>* result) { | 264 v8::Handle<v8::Value>* result) { |
| 251 v8::Context::Scope context_scope(context); | 265 v8::Context::Scope context_scope(context); |
| 252 v8::HandleScope handle_scope; | 266 v8::HandleScope handle_scope; |
| 253 | 267 |
| 254 VarHandleMap visited_ids; | 268 VarHandleMap visited_ids; |
| 255 ParentVarSet parent_ids; | 269 ParentVarSet parent_ids; |
| 256 | 270 |
| 257 std::stack<StackEntry<PP_Var> > stack; | 271 std::stack<StackEntry<PP_Var> > stack; |
| 258 stack.push(StackEntry<PP_Var>(var)); | 272 stack.push(StackEntry<PP_Var>(var)); |
| 259 v8::Handle<v8::Value> root; | 273 v8::Handle<v8::Value> root; |
| 260 bool is_root = true; | 274 bool is_root = true; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 return false; | 355 return false; |
| 342 } | 356 } |
| 343 } | 357 } |
| 344 } | 358 } |
| 345 } | 359 } |
| 346 | 360 |
| 347 *result = handle_scope.Close(root); | 361 *result = handle_scope.Close(root); |
| 348 return true; | 362 return true; |
| 349 } | 363 } |
| 350 | 364 |
| 351 void FromV8Value( | 365 void V8VarConverter::FromV8Value( |
| 352 v8::Handle<v8::Value> val, | 366 v8::Handle<v8::Value> val, |
| 353 v8::Handle<v8::Context> context, | 367 v8::Handle<v8::Context> context, |
| 354 const base::Callback<void(const ScopedPPVar&, bool)>& callback, | 368 const base::Callback<void(const ScopedPPVar&, bool)>& callback) { |
| 355 const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy) { | |
| 356 v8::Context::Scope context_scope(context); | 369 v8::Context::Scope context_scope(context); |
| 357 v8::HandleScope handle_scope; | 370 v8::HandleScope handle_scope; |
| 358 | 371 |
| 359 HandleVarMap visited_handles; | 372 HandleVarMap visited_handles; |
| 360 ParentHandleSet parent_handles; | 373 ParentHandleSet parent_handles; |
| 361 | 374 |
| 362 ResourceConverter resource_converter; | |
| 363 | |
| 364 std::stack<StackEntry<v8::Handle<v8::Value> > > stack; | 375 std::stack<StackEntry<v8::Handle<v8::Value> > > stack; |
| 365 stack.push(StackEntry<v8::Handle<v8::Value> >(val)); | 376 stack.push(StackEntry<v8::Handle<v8::Value> >(val)); |
| 366 ScopedPPVar root; | 377 ScopedPPVar root; |
| 367 bool is_root = true; | 378 bool is_root = true; |
| 368 | 379 |
| 369 while (!stack.empty()) { | 380 while (!stack.empty()) { |
| 370 v8::Handle<v8::Value> current_v8 = stack.top().val; | 381 v8::Handle<v8::Value> current_v8 = stack.top().val; |
| 371 PP_Var current_var; | 382 PP_Var current_var; |
| 372 | 383 |
| 373 if (stack.top().sentinel) { | 384 if (stack.top().sentinel) { |
| 374 stack.pop(); | 385 stack.pop(); |
| 375 if (current_v8->IsObject()) | 386 if (current_v8->IsObject()) |
| 376 parent_handles.erase(HashedHandle(current_v8->ToObject())); | 387 parent_handles.erase(HashedHandle(current_v8->ToObject())); |
| 377 continue; | 388 continue; |
| 378 } else { | 389 } else { |
| 379 stack.top().sentinel = true; | 390 stack.top().sentinel = true; |
| 380 } | 391 } |
| 381 | 392 |
| 382 bool did_create = false; | 393 bool did_create = false; |
| 383 if (!GetOrCreateVar(current_v8, ¤t_var, &did_create, | 394 if (!GetOrCreateVar(current_v8, ¤t_var, &did_create, |
| 384 &visited_handles, &parent_handles, | 395 &visited_handles, &parent_handles, |
| 385 &resource_converter)) { | 396 resource_converter_.get())) { |
| 386 message_loop_proxy->PostTask(FROM_HERE, | 397 message_loop_proxy_->PostTask(FROM_HERE, |
| 387 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); | 398 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); |
| 388 return; | 399 return; |
| 389 } | 400 } |
| 390 | 401 |
| 391 if (is_root) { | 402 if (is_root) { |
| 392 is_root = false; | 403 is_root = false; |
| 393 root = current_var; | 404 root = current_var; |
| 394 } | 405 } |
| 395 | 406 |
| 396 // Add child nodes to the stack. | 407 // Add child nodes to the stack. |
| 397 if (current_var.type == PP_VARTYPE_ARRAY) { | 408 if (current_var.type == PP_VARTYPE_ARRAY) { |
| 398 DCHECK(current_v8->IsArray()); | 409 DCHECK(current_v8->IsArray()); |
| 399 v8::Handle<v8::Array> v8_array = current_v8.As<v8::Array>(); | 410 v8::Handle<v8::Array> v8_array = current_v8.As<v8::Array>(); |
| 400 parent_handles.insert(HashedHandle(v8_array)); | 411 parent_handles.insert(HashedHandle(v8_array)); |
| 401 | 412 |
| 402 ArrayVar* array_var = ArrayVar::FromPPVar(current_var); | 413 ArrayVar* array_var = ArrayVar::FromPPVar(current_var); |
| 403 if (!array_var) { | 414 if (!array_var) { |
| 404 NOTREACHED(); | 415 NOTREACHED(); |
| 405 message_loop_proxy->PostTask(FROM_HERE, | 416 message_loop_proxy_->PostTask(FROM_HERE, |
| 406 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); | 417 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); |
| 407 return; | 418 return; |
| 408 } | 419 } |
| 409 | 420 |
| 410 for (uint32 i = 0; i < v8_array->Length(); ++i) { | 421 for (uint32 i = 0; i < v8_array->Length(); ++i) { |
| 411 v8::TryCatch try_catch; | 422 v8::TryCatch try_catch; |
| 412 v8::Handle<v8::Value> child_v8 = v8_array->Get(i); | 423 v8::Handle<v8::Value> child_v8 = v8_array->Get(i); |
| 413 if (try_catch.HasCaught()) { | 424 if (try_catch.HasCaught()) { |
| 414 message_loop_proxy->PostTask(FROM_HERE, | 425 message_loop_proxy_->PostTask(FROM_HERE, |
| 415 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); | 426 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); |
| 416 return; | 427 return; |
| 417 } | 428 } |
| 418 | 429 |
| 419 if (!v8_array->HasRealIndexedProperty(i)) | 430 if (!v8_array->HasRealIndexedProperty(i)) |
| 420 continue; | 431 continue; |
| 421 | 432 |
| 422 PP_Var child_var; | 433 PP_Var child_var; |
| 423 if (!GetOrCreateVar(child_v8, &child_var, &did_create, | 434 if (!GetOrCreateVar(child_v8, &child_var, &did_create, |
| 424 &visited_handles, &parent_handles, | 435 &visited_handles, &parent_handles, |
| 425 &resource_converter)) { | 436 resource_converter_.get())) { |
| 426 message_loop_proxy->PostTask(FROM_HERE, | 437 message_loop_proxy_->PostTask(FROM_HERE, |
| 427 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); | 438 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); |
| 428 return; | 439 return; |
| 429 } | 440 } |
| 430 if (did_create && child_v8->IsObject()) | 441 if (did_create && child_v8->IsObject()) |
| 431 stack.push(child_v8); | 442 stack.push(child_v8); |
| 432 | 443 |
| 433 array_var->Set(i, child_var); | 444 array_var->Set(i, child_var); |
| 434 } | 445 } |
| 435 } else if (current_var.type == PP_VARTYPE_DICTIONARY) { | 446 } else if (current_var.type == PP_VARTYPE_DICTIONARY) { |
| 436 DCHECK(current_v8->IsObject()); | 447 DCHECK(current_v8->IsObject()); |
| 437 v8::Handle<v8::Object> v8_object = current_v8->ToObject(); | 448 v8::Handle<v8::Object> v8_object = current_v8->ToObject(); |
| 438 parent_handles.insert(HashedHandle(v8_object)); | 449 parent_handles.insert(HashedHandle(v8_object)); |
| 439 | 450 |
| 440 DictionaryVar* dict_var = DictionaryVar::FromPPVar(current_var); | 451 DictionaryVar* dict_var = DictionaryVar::FromPPVar(current_var); |
| 441 if (!dict_var) { | 452 if (!dict_var) { |
| 442 NOTREACHED(); | 453 NOTREACHED(); |
| 443 message_loop_proxy->PostTask(FROM_HERE, | 454 message_loop_proxy_->PostTask(FROM_HERE, |
| 444 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); | 455 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); |
| 445 return; | 456 return; |
| 446 } | 457 } |
| 447 | 458 |
| 448 v8::Handle<v8::Array> property_names(v8_object->GetOwnPropertyNames()); | 459 v8::Handle<v8::Array> property_names(v8_object->GetOwnPropertyNames()); |
| 449 for (uint32 i = 0; i < property_names->Length(); ++i) { | 460 for (uint32 i = 0; i < property_names->Length(); ++i) { |
| 450 v8::Handle<v8::Value> key(property_names->Get(i)); | 461 v8::Handle<v8::Value> key(property_names->Get(i)); |
| 451 | 462 |
| 452 // Extend this test to cover more types as necessary and if sensible. | 463 // Extend this test to cover more types as necessary and if sensible. |
| 453 if (!key->IsString() && !key->IsNumber()) { | 464 if (!key->IsString() && !key->IsNumber()) { |
| 454 NOTREACHED() << "Key \"" << *v8::String::AsciiValue(key) << "\" " | 465 NOTREACHED() << "Key \"" << *v8::String::AsciiValue(key) << "\" " |
| 455 "is neither a string nor a number"; | 466 "is neither a string nor a number"; |
| 456 message_loop_proxy->PostTask(FROM_HERE, | 467 message_loop_proxy_->PostTask(FROM_HERE, |
| 457 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); | 468 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); |
| 458 return; | 469 return; |
| 459 } | 470 } |
| 460 | 471 |
| 461 // Skip all callbacks: crbug.com/139933 | 472 // Skip all callbacks: crbug.com/139933 |
| 462 if (v8_object->HasRealNamedCallbackProperty(key->ToString())) | 473 if (v8_object->HasRealNamedCallbackProperty(key->ToString())) |
| 463 continue; | 474 continue; |
| 464 | 475 |
| 465 v8::String::Utf8Value name_utf8(key->ToString()); | 476 v8::String::Utf8Value name_utf8(key->ToString()); |
| 466 | 477 |
| 467 v8::TryCatch try_catch; | 478 v8::TryCatch try_catch; |
| 468 v8::Handle<v8::Value> child_v8 = v8_object->Get(key); | 479 v8::Handle<v8::Value> child_v8 = v8_object->Get(key); |
| 469 if (try_catch.HasCaught()) { | 480 if (try_catch.HasCaught()) { |
| 470 message_loop_proxy->PostTask(FROM_HERE, | 481 message_loop_proxy_->PostTask(FROM_HERE, |
| 471 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); | 482 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); |
| 472 return; | 483 return; |
| 473 } | 484 } |
| 474 | 485 |
| 475 PP_Var child_var; | 486 PP_Var child_var; |
| 476 if (!GetOrCreateVar(child_v8, &child_var, &did_create, | 487 if (!GetOrCreateVar(child_v8, &child_var, &did_create, |
| 477 &visited_handles, &parent_handles, | 488 &visited_handles, &parent_handles, |
| 478 &resource_converter)) { | 489 resource_converter_.get())) { |
| 479 message_loop_proxy->PostTask(FROM_HERE, | 490 message_loop_proxy_->PostTask(FROM_HERE, |
| 480 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); | 491 base::Bind(callback, ScopedPPVar(PP_MakeUndefined()), false)); |
| 481 return; | 492 return; |
| 482 } | 493 } |
| 483 if (did_create && child_v8->IsObject()) | 494 if (did_create && child_v8->IsObject()) |
| 484 stack.push(child_v8); | 495 stack.push(child_v8); |
| 485 | 496 |
| 486 bool success = dict_var->SetWithStringKey( | 497 bool success = dict_var->SetWithStringKey( |
| 487 std::string(*name_utf8, name_utf8.length()), child_var); | 498 std::string(*name_utf8, name_utf8.length()), child_var); |
| 488 DCHECK(success); | 499 DCHECK(success); |
| 489 } | 500 } |
| 490 } | 501 } |
| 491 } | 502 } |
| 492 resource_converter.ShutDown(base::Bind(callback, root), message_loop_proxy); | 503 resource_converter_->ShutDown(base::Bind(callback, root)); |
| 493 } | 504 } |
| 494 | 505 |
| 495 void FromV8Value( | |
| 496 v8::Handle<v8::Value> val, | |
| 497 v8::Handle<v8::Context> context, | |
| 498 const base::Callback<void(const ScopedPPVar&, bool)>& callback) { | |
| 499 FromV8Value(val, context, callback, base::MessageLoopProxy::current()); | |
| 500 } | |
| 501 | |
| 502 } // namespace V8VarConverter | |
| 503 } // namespace content | 506 } // namespace content |
| OLD | NEW |