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