Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: extensions/renderer/api_binding_unittest.cc

Issue 2609553003: [Extensions Bindings] Allow custom hooks to return values, throw (Closed)
Patch Set: jbroman Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698