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 |