Chromium Code Reviews| 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 440 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 *did_call = true; | 460 *did_call = true; |
| 461 APIBindingHooks::RequestResult result( | |
| 462 APIBindingHooks::RequestResult::HANDLED); | |
| 461 if (arguments->size() != 1u) { | 463 if (arguments->size() != 1u) { |
| 462 EXPECT_EQ(1u, arguments->size()); | 464 EXPECT_EQ(1u, arguments->size()); |
| 463 return APIBindingHooks::RequestResult::HANDLED; | 465 return result; |
| 464 } | 466 } |
| 465 EXPECT_EQ("foo", gin::V8ToString(arguments->at(0))); | 467 EXPECT_EQ("foo", gin::V8ToString(arguments->at(0))); |
| 466 return APIBindingHooks::RequestResult::HANDLED; | 468 return result; |
| 467 }; | 469 }; |
| 468 hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call)); | 470 hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call)); |
| 469 | 471 |
| 470 APIBinding binding( | 472 APIBinding binding( |
| 471 "test", *functions, nullptr, nullptr, | 473 "test", *functions, nullptr, nullptr, |
| 472 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)), | 474 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)), |
| 473 std::move(hooks), &refs); | 475 std::move(hooks), &refs); |
| 474 EXPECT_TRUE(refs.empty()); | 476 EXPECT_TRUE(refs.empty()); |
| 475 | 477 |
| 476 v8::HandleScope handle_scope(isolate()); | 478 v8::HandleScope handle_scope(isolate()); |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 664 "(function(obj) { return obj.oneString('ping'); })"); | 666 "(function(obj) { return obj.oneString('ping'); })"); |
| 665 v8::Local<v8::Value> args[] = {binding_object}; | 667 v8::Local<v8::Value> args[] = {binding_object}; |
| 666 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()), | 668 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()), |
| 667 arraysize(args), args, | 669 arraysize(args), args, |
| 668 "Uncaught Error: Custom Hook Error"); | 670 "Uncaught Error: Custom Hook Error"); |
| 669 | 671 |
| 670 // Other methods, like stringAndInt(), should behave normally. | 672 // Other methods, like stringAndInt(), should behave normally. |
| 671 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]"); | 673 ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]"); |
| 672 } | 674 } |
| 673 | 675 |
| 676 // Tests that custom JS hooks can return results synchronously. | |
| 677 TEST_F(APIBindingUnittest, TestReturningResultFromCustomJSHook) { | |
| 678 // Register a hook for the test.oneString method. | |
| 679 auto hooks = base::MakeUnique<APIBindingHooks>( | |
| 680 base::Bind(&RunFunctionOnGlobalAndReturnHandle)); | |
| 681 | |
| 682 v8::HandleScope handle_scope(isolate()); | |
| 683 v8::Local<v8::Context> context = ContextLocal(); | |
| 684 const char kRegisterHook[] = | |
| 685 "(function(hooks) {\n" | |
| 686 " hooks.setHandleRequest('oneString', str => {\n" | |
| 687 " return str + ' pong';\n" | |
| 688 " });\n" | |
| 689 "})"; | |
| 690 v8::Local<v8::String> source_string = | |
| 691 gin::StringToV8(isolate(), kRegisterHook); | |
| 692 v8::Local<v8::String> source_name = | |
| 693 gin::StringToV8(isolate(), "custom_hook"); | |
| 694 hooks->RegisterJsSource( | |
| 695 v8::Global<v8::String>(isolate(), source_string), | |
| 696 v8::Global<v8::String>(isolate(), source_name)); | |
| 697 | |
| 698 std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions); | |
| 699 ASSERT_TRUE(functions); | |
| 700 ArgumentSpec::RefMap refs; | |
| 701 | |
| 702 APIBinding binding( | |
| 703 "test", *functions, nullptr, nullptr, | |
| 704 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)), | |
| 705 std::move(hooks), &refs); | |
| 706 EXPECT_TRUE(refs.empty()); | |
| 707 | |
| 708 APIEventHandler event_handler( | |
| 709 base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); | |
| 710 v8::Local<v8::Object> binding_object = binding.CreateInstance( | |
| 711 context, isolate(), &event_handler, base::Bind(&AllowAllAPIs)); | |
| 712 | |
| 713 v8::Local<v8::Function> function = | |
| 714 FunctionFromString(context, | |
| 715 "(function(obj) { return obj.oneString('ping'); })"); | |
| 716 v8::Local<v8::Value> args[] = {binding_object}; | |
| 717 v8::Local<v8::Value> result = | |
| 718 RunFunction(function, context, arraysize(args), args); | |
| 719 ASSERT_FALSE(result.IsEmpty()); | |
| 720 std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context); | |
| 721 ASSERT_TRUE(json_result); | |
| 722 EXPECT_EQ("\"ping pong\"", ValueToString(*json_result)); | |
| 723 } | |
| 724 | |
| 725 // Tests that JS custom hooks can throw exceptions for bad invocations. | |
| 726 TEST_F(APIBindingUnittest, TestThrowingFromCustomJSHook) { | |
| 727 // Our testing handlers for running functions expect a pre-determined success | |
| 728 // or failure. Since we're testing throwing exceptions here, we need a way of | |
| 729 // running that allows exceptions to be thrown, but we still expect most JS | |
| 730 // calls to succeed. | |
| 731 // TODO(devlin): This is a bit clunky. If we need to do this enough, we could | |
| 732 // figure out a different solution, like having a stack object for allowing | |
| 733 // errors/exceptions. But given this is the only place we need it so far, this | |
| 734 // is sufficient. | |
| 735 auto run_js_and_expect_error = [](v8::Local<v8::Function> function, | |
| 736 v8::Local<v8::Context> context, | |
| 737 int argc, | |
| 738 v8::Local<v8::Value> argv[]) { | |
| 739 v8::MaybeLocal<v8::Value> maybe_result = | |
| 740 function->Call(context, context->Global(), argc, argv); | |
| 741 v8::Global<v8::Value> result; | |
| 742 v8::Local<v8::Value> local; | |
| 743 if (maybe_result.ToLocal(&local)) | |
| 744 result.Reset(context->GetIsolate(), local); | |
| 745 return result; | |
| 746 }; | |
| 747 // Register a hook for the test.oneString method. | |
| 748 auto hooks = base::MakeUnique<APIBindingHooks>( | |
| 749 base::Bind(run_js_and_expect_error)); | |
| 750 | |
| 751 v8::HandleScope handle_scope(isolate()); | |
| 752 v8::Local<v8::Context> context = ContextLocal(); | |
| 753 const char kRegisterHook[] = | |
| 754 "(function(hooks) {\n" | |
| 755 " hooks.setHandleRequest('oneString', str => {\n" | |
| 756 " throw new Error('Custom Hook Error');\n" | |
| 757 " });\n" | |
| 758 "})"; | |
| 759 v8::Local<v8::String> source_string = | |
| 760 gin::StringToV8(isolate(), kRegisterHook); | |
| 761 v8::Local<v8::String> source_name = | |
| 762 gin::StringToV8(isolate(), "custom_hook"); | |
| 763 hooks->RegisterJsSource( | |
| 764 v8::Global<v8::String>(isolate(), source_string), | |
| 765 v8::Global<v8::String>(isolate(), source_name)); | |
| 766 | |
| 767 std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions); | |
| 768 ASSERT_TRUE(functions); | |
| 769 ArgumentSpec::RefMap refs; | |
| 770 | |
| 771 APIBinding binding( | |
| 772 "test", *functions, nullptr, nullptr, | |
| 773 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)), | |
| 774 std::move(hooks), &refs); | |
| 775 EXPECT_TRUE(refs.empty()); | |
| 776 | |
| 777 APIEventHandler event_handler( | |
| 778 base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); | |
| 779 v8::Local<v8::Object> binding_object = binding.CreateInstance( | |
| 780 context, isolate(), &event_handler, base::Bind(&AllowAllAPIs)); | |
| 781 | |
| 782 v8::Local<v8::Function> function = | |
| 783 FunctionFromString(context, | |
| 784 "(function(obj) { return obj.oneString('ping'); })"); | |
| 785 v8::Local<v8::Value> args[] = {binding_object}; | |
| 786 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()), | |
| 787 arraysize(args), args, | |
| 788 "Uncaught Error: Custom Hook Error"); | |
| 789 } | |
| 790 | |
| 791 // Tests that native custom hooks can return results synchronously, or throw | |
| 792 // exceptions for bad invocations. | |
| 793 TEST_F(APIBindingUnittest, | |
| 794 TestReturningResultAndThrowingExceptionFromCustomNativeHook) { | |
| 795 v8::HandleScope handle_scope(isolate()); | |
| 796 v8::Local<v8::Context> context = ContextLocal(); | |
| 797 | |
| 798 // Register a hook for the test.oneString method. | |
| 799 auto hooks = base::MakeUnique<APIBindingHooks>( | |
| 800 base::Bind(&RunFunctionOnGlobalAndReturnHandle)); | |
| 801 bool did_call = false; | |
| 802 auto hook = [](bool* did_call, const APISignature* signature, | |
| 803 v8::Local<v8::Context> context, | |
| 804 std::vector<v8::Local<v8::Value>>* arguments, | |
| 805 const ArgumentSpec::RefMap& ref_map) { | |
| 806 APIBindingHooks::RequestResult result( | |
| 807 APIBindingHooks::RequestResult::HANDLED); | |
| 808 if (arguments->size() != 1u) { | |
| 809 EXPECT_EQ(1u, arguments->size()); | |
|
lazyboy
2017/01/04 23:22:45
Is this line correct given the condition on line a
Devlin
2017/01/04 23:29:29
It is correct-ish - these should be ASSERTs since
| |
| 810 return result; | |
| 811 } | |
| 812 v8::Isolate* isolate = context->GetIsolate(); | |
| 813 std::string arg_value = gin::V8ToString(arguments->at(0)); | |
| 814 if (arg_value == "throw") { | |
| 815 isolate->ThrowException(v8::Exception::Error( | |
| 816 gin::StringToV8(isolate, "Custom Hook Error"))); | |
| 817 result.code = APIBindingHooks::RequestResult::THROWN; | |
| 818 return result; | |
| 819 } | |
| 820 result.return_value = | |
| 821 gin::StringToV8(context->GetIsolate(), arg_value + " pong"); | |
| 822 return result; | |
| 823 }; | |
| 824 hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call)); | |
| 825 | |
| 826 std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions); | |
| 827 ASSERT_TRUE(functions); | |
| 828 ArgumentSpec::RefMap refs; | |
| 829 | |
| 830 APIBinding binding( | |
| 831 "test", *functions, nullptr, nullptr, | |
| 832 base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)), | |
| 833 std::move(hooks), &refs); | |
| 834 EXPECT_TRUE(refs.empty()); | |
| 835 | |
| 836 APIEventHandler event_handler( | |
| 837 base::Bind(&RunFunctionOnGlobalAndIgnoreResult)); | |
| 838 v8::Local<v8::Object> binding_object = binding.CreateInstance( | |
| 839 context, isolate(), &event_handler, base::Bind(&AllowAllAPIs)); | |
| 840 | |
| 841 { | |
| 842 // Test an invocation that we expect to throw an exception. | |
| 843 v8::Local<v8::Function> function = | |
| 844 FunctionFromString( | |
| 845 context, "(function(obj) { return obj.oneString('throw'); })"); | |
| 846 v8::Local<v8::Value> args[] = {binding_object}; | |
| 847 RunFunctionAndExpectError(function, context, v8::Undefined(isolate()), | |
| 848 arraysize(args), args, | |
| 849 "Uncaught Error: Custom Hook Error"); | |
| 850 } | |
| 851 | |
| 852 { | |
| 853 // Test an invocation we expect to succeed. | |
| 854 v8::Local<v8::Function> function = | |
| 855 FunctionFromString(context, | |
| 856 "(function(obj) { return obj.oneString('ping'); })"); | |
| 857 v8::Local<v8::Value> args[] = {binding_object}; | |
| 858 v8::Local<v8::Value> result = | |
| 859 RunFunction(function, context, arraysize(args), args); | |
| 860 ASSERT_FALSE(result.IsEmpty()); | |
| 861 std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context); | |
| 862 ASSERT_TRUE(json_result); | |
| 863 EXPECT_EQ("\"ping pong\"", ValueToString(*json_result)); | |
| 864 } | |
| 865 } | |
| 866 | |
| 674 } // namespace extensions | 867 } // namespace extensions |
| OLD | NEW |