OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "base/bind.h" | 5 #include "base/bind.h" |
6 #include "base/memory/ptr_util.h" | 6 #include "base/memory/ptr_util.h" |
7 #include "base/stl_util.h" | 7 #include "base/stl_util.h" |
8 #include "base/strings/stringprintf.h" | 8 #include "base/strings/stringprintf.h" |
9 #include "base/values.h" | 9 #include "base/values.h" |
10 #include "extensions/renderer/api_binding.h" | 10 #include "extensions/renderer/api_binding.h" |
(...skipping 438 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
449 ASSERT_TRUE(functions); | 449 ASSERT_TRUE(functions); |
450 ArgumentSpec::RefMap refs; | 450 ArgumentSpec::RefMap refs; |
451 | 451 |
452 // Register a hook for the test.oneString method. | 452 // Register a hook for the test.oneString method. |
453 auto hooks = base::MakeUnique<APIBindingHooks>( | 453 auto hooks = base::MakeUnique<APIBindingHooks>( |
454 base::Bind(&RunFunctionOnGlobalAndReturnHandle)); | 454 base::Bind(&RunFunctionOnGlobalAndReturnHandle)); |
455 bool did_call = false; | 455 bool did_call = false; |
456 auto hook = [](bool* did_call, const APISignature* signature, | 456 auto hook = [](bool* did_call, const APISignature* signature, |
457 v8::Local<v8::Context> context, | 457 v8::Local<v8::Context> context, |
458 std::vector<v8::Local<v8::Value>>* arguments, | 458 std::vector<v8::Local<v8::Value>>* arguments, |
459 const ArgumentSpec::RefMap& ref_map) { | 459 const ArgumentSpec::RefMap& ref_map, |
| 460 v8::Local<v8::Value>* return_value) { |
460 *did_call = true; | 461 *did_call = true; |
461 if (arguments->size() != 1u) { | 462 if (arguments->size() != 1u) { |
462 EXPECT_EQ(1u, arguments->size()); | 463 EXPECT_EQ(1u, arguments->size()); |
463 return APIBindingHooks::RequestResult::HANDLED; | 464 return APIBindingHooks::RequestResult::HANDLED; |
464 } | 465 } |
465 EXPECT_EQ("foo", gin::V8ToString(arguments->at(0))); | 466 EXPECT_EQ("foo", gin::V8ToString(arguments->at(0))); |
466 return APIBindingHooks::RequestResult::HANDLED; | 467 return APIBindingHooks::RequestResult::HANDLED; |
467 }; | 468 }; |
468 hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call)); | 469 hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call)); |
469 | 470 |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
610 response_args = | 611 response_args = |
611 GetBaseValuePropertyFromObject(context->Global(), context, | 612 GetBaseValuePropertyFromObject(context->Global(), context, |
612 "requestArguments"); | 613 "requestArguments"); |
613 ASSERT_TRUE(response_args); | 614 ASSERT_TRUE(response_args); |
614 EXPECT_EQ("[true]", ValueToString(*response_args)); | 615 EXPECT_EQ("[true]", ValueToString(*response_args)); |
615 | 616 |
616 // Other methods, like stringAndInt(), should behave normally. | 617 // Other methods, like stringAndInt(), should behave normally. |
617 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]"); | 618 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]"); |
618 } | 619 } |
619 | 620 |
| 621 // Tests that custom JS hooks can return results synchronously. |
| 622 TEST_F(APIBindingUnittest, TestReturningResultFromCustomJSHook) { |
| 623 // Register a hook for the test.oneString method. |
| 624 auto hooks = base::MakeUnique<APIBindingHooks>( |
| 625 base::Bind(&RunFunctionOnGlobalAndReturnHandle)); |
| 626 |
| 627 v8::HandleScope handle_scope(isolate()); |
| 628 v8::Local<v8::Context> context = ContextLocal(); |
| 629 const char kRegisterHook[] = |
| 630 "(function(hooks) {\n" |
| 631 " hooks.setHandleRequest('oneString', str => {\n" |
| 632 " return str + ' pong';\n" |
| 633 " });\n" |
| 634 "})"; |
| 635 v8::Local<v8::String> source_string = |
| 636 gin::StringToV8(isolate(), kRegisterHook); |
| 637 v8::Local<v8::String> source_name = |
| 638 gin::StringToV8(isolate(), "custom_hook"); |
| 639 hooks->RegisterJsSource( |
| 640 v8::Global<v8::String>(isolate(), source_string), |
| 641 v8::Global<v8::String>(isolate(), source_name)); |
| 642 |
| 643 std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions); |
| 644 ASSERT_TRUE(functions); |
| 645 ArgumentSpec::RefMap refs; |
| 646 |
| 647 APIBinding binding( |
| 648 "test", *functions, nullptr, nullptr, |
| 649 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)), |
| 650 std::move(hooks), &refs); |
| 651 EXPECT_TRUE(refs.empty()); |
| 652 |
| 653 APIEventHandler event_handler( |
| 654 base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); |
| 655 v8::Local<v8::Object> binding_object = binding.CreateInstance( |
| 656 context, isolate(), &event_handler, base::Bind(&AllowAllAPIs)); |
| 657 |
| 658 v8::Local<v8::Function> function = |
| 659 FunctionFromString(context, |
| 660 "(function(obj) { return obj.oneString('ping'); })"); |
| 661 v8::Local<v8::Value> args[] = {binding_object}; |
| 662 v8::Local<v8::Value> result = |
| 663 RunFunction(function, context, arraysize(args), args); |
| 664 ASSERT_FALSE(result.IsEmpty()); |
| 665 std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context); |
| 666 ASSERT_TRUE(json_result); |
| 667 EXPECT_EQ("\"ping pong\"", ValueToString(*json_result)); |
| 668 } |
| 669 |
| 670 // Tests that JS custom hooks can throw exceptions for bad invocations. |
| 671 TEST_F(APIBindingUnittest, TestThrowingFromCustomJSHook) { |
| 672 // Our testing handlers for running functions expect a pre-determined success |
| 673 // or failure. Since we're testing throwing exceptions here, we need a way of |
| 674 // running that allows exceptions to be thrown, but we still expect most JS |
| 675 // calls to succeed. |
| 676 // TODO(devlin): This is a bit clunky. If we need to do this enough, we could |
| 677 // figure out a different solution, like having a stack object for allowing |
| 678 // errors/exceptions. But given this is the only place we need it so far, this |
| 679 // is sufficient. |
| 680 auto run_js_and_expect_error = [](v8::Local<v8::Function> function, |
| 681 v8::Local<v8::Context> context, |
| 682 int argc, |
| 683 v8::Local<v8::Value> argv[]) { |
| 684 v8::MaybeLocal<v8::Value> maybe_result = |
| 685 function->Call(context, context->Global(), argc, argv); |
| 686 v8::Global<v8::Value> result; |
| 687 v8::Local<v8::Value> local; |
| 688 if (maybe_result.ToLocal(&local)) |
| 689 result.Reset(context->GetIsolate(), local); |
| 690 return result; |
| 691 }; |
| 692 // Register a hook for the test.oneString method. |
| 693 auto hooks = base::MakeUnique<APIBindingHooks>( |
| 694 base::Bind(run_js_and_expect_error)); |
| 695 |
| 696 v8::HandleScope handle_scope(isolate()); |
| 697 v8::Local<v8::Context> context = ContextLocal(); |
| 698 const char kRegisterHook[] = |
| 699 "(function(hooks) {\n" |
| 700 " hooks.setHandleRequest('oneString', str => {\n" |
| 701 " throw new Error('Custom Hook Error');\n" |
| 702 " });\n" |
| 703 "})"; |
| 704 v8::Local<v8::String> source_string = |
| 705 gin::StringToV8(isolate(), kRegisterHook); |
| 706 v8::Local<v8::String> source_name = |
| 707 gin::StringToV8(isolate(), "custom_hook"); |
| 708 hooks->RegisterJsSource( |
| 709 v8::Global<v8::String>(isolate(), source_string), |
| 710 v8::Global<v8::String>(isolate(), source_name)); |
| 711 |
| 712 std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions); |
| 713 ASSERT_TRUE(functions); |
| 714 ArgumentSpec::RefMap refs; |
| 715 |
| 716 APIBinding binding( |
| 717 "test", *functions, nullptr, nullptr, |
| 718 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)), |
| 719 std::move(hooks), &refs); |
| 720 EXPECT_TRUE(refs.empty()); |
| 721 |
| 722 APIEventHandler event_handler( |
| 723 base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); |
| 724 v8::Local<v8::Object> binding_object = binding.CreateInstance( |
| 725 context, isolate(), &event_handler, base::Bind(&AllowAllAPIs)); |
| 726 |
| 727 v8::Local<v8::Function> function = |
| 728 FunctionFromString(context, |
| 729 "(function(obj) { return obj.oneString('ping'); })"); |
| 730 v8::Local<v8::Value> args[] = {binding_object}; |
| 731 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()), |
| 732 arraysize(args), args, |
| 733 "Uncaught Error: Custom Hook Error"); |
| 734 } |
| 735 |
| 736 // Tests that native custom hooks can return results synchronously, or throw |
| 737 // exceptions for bad invocations. |
| 738 TEST_F(APIBindingUnittest, |
| 739 TestReturningResultAndThrowingExceptionFromCustomNativeHook) { |
| 740 v8::HandleScope handle_scope(isolate()); |
| 741 v8::Local<v8::Context> context = ContextLocal(); |
| 742 |
| 743 // Register a hook for the test.oneString method. |
| 744 auto hooks = base::MakeUnique<APIBindingHooks>( |
| 745 base::Bind(&RunFunctionOnGlobalAndReturnHandle)); |
| 746 bool did_call = false; |
| 747 auto hook = [](bool* did_call, const APISignature* signature, |
| 748 v8::Local<v8::Context> context, |
| 749 std::vector<v8::Local<v8::Value>>* arguments, |
| 750 const ArgumentSpec::RefMap& ref_map, |
| 751 v8::Local<v8::Value>* return_value) { |
| 752 if (arguments->size() != 1u) { |
| 753 EXPECT_EQ(1u, arguments->size()); |
| 754 return APIBindingHooks::RequestResult::HANDLED; |
| 755 } |
| 756 v8::Isolate* isolate = context->GetIsolate(); |
| 757 std::string arg_value = gin::V8ToString(arguments->at(0)); |
| 758 if (arg_value == "throw") { |
| 759 isolate->ThrowException(v8::Exception::Error( |
| 760 gin::StringToV8(isolate, "Custom Hook Error"))); |
| 761 return APIBindingHooks::RequestResult::THROWN; |
| 762 } |
| 763 *return_value = gin::StringToV8(context->GetIsolate(), arg_value + " pong"); |
| 764 return APIBindingHooks::RequestResult::HANDLED; |
| 765 }; |
| 766 hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call)); |
| 767 |
| 768 std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions); |
| 769 ASSERT_TRUE(functions); |
| 770 ArgumentSpec::RefMap refs; |
| 771 |
| 772 APIBinding binding( |
| 773 "test", *functions, nullptr, nullptr, |
| 774 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)), |
| 775 std::move(hooks), &refs); |
| 776 EXPECT_TRUE(refs.empty()); |
| 777 |
| 778 APIEventHandler event_handler( |
| 779 base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); |
| 780 v8::Local<v8::Object> binding_object = binding.CreateInstance( |
| 781 context, isolate(), &event_handler, base::Bind(&AllowAllAPIs)); |
| 782 |
| 783 { |
| 784 // Test an invocation that we expect to throw an exception. |
| 785 v8::Local<v8::Function> function = |
| 786 FunctionFromString( |
| 787 context, "(function(obj) { return obj.oneString('throw'); })"); |
| 788 v8::Local<v8::Value> args[] = {binding_object}; |
| 789 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()), |
| 790 arraysize(args), args, |
| 791 "Uncaught Error: Custom Hook Error"); |
| 792 } |
| 793 |
| 794 { |
| 795 // Test an invocation we expect to succeed. |
| 796 v8::Local<v8::Function> function = |
| 797 FunctionFromString(context, |
| 798 "(function(obj) { return obj.oneString('ping'); })"); |
| 799 v8::Local<v8::Value> args[] = {binding_object}; |
| 800 v8::Local<v8::Value> result = |
| 801 RunFunction(function, context, arraysize(args), args); |
| 802 ASSERT_FALSE(result.IsEmpty()); |
| 803 std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context); |
| 804 ASSERT_TRUE(json_result); |
| 805 EXPECT_EQ("\"ping pong\"", ValueToString(*json_result)); |
| 806 } |
| 807 } |
| 808 |
620 } // namespace extensions | 809 } // namespace extensions |
OLD | NEW |