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

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

Issue 2609553003: [Extensions Bindings] Allow custom hooks to return values, throw (Closed)
Patch Set: woot 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 438 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698