| Index: extensions/renderer/api_binding_unittest.cc
|
| diff --git a/extensions/renderer/api_binding_unittest.cc b/extensions/renderer/api_binding_unittest.cc
|
| index a333a9264e7cf029e965c69ce678ff5025143c67..711805b069abfa7354b0a9d97e96022a83556dff 100644
|
| --- a/extensions/renderer/api_binding_unittest.cc
|
| +++ b/extensions/renderer/api_binding_unittest.cc
|
| @@ -456,7 +456,8 @@ TEST_F(APIBindingUnittest, TestCustomHooks) {
|
| auto hook = [](bool* did_call, const APISignature* signature,
|
| v8::Local<v8::Context> context,
|
| std::vector<v8::Local<v8::Value>>* arguments,
|
| - const ArgumentSpec::RefMap& ref_map) {
|
| + const ArgumentSpec::RefMap& ref_map,
|
| + v8::Local<v8::Value>* return_value) {
|
| *did_call = true;
|
| if (arguments->size() != 1u) {
|
| EXPECT_EQ(1u, arguments->size());
|
| @@ -617,4 +618,192 @@ TEST_F(APIBindingUnittest, TestUpdateArgumentsPreValidate) {
|
| ExpectPass(binding_object, "obj.stringAndInt('foo', 42);", "['foo',42]");
|
| }
|
|
|
| +// Tests that custom JS hooks can return results synchronously.
|
| +TEST_F(APIBindingUnittest, TestReturningResultFromCustomJSHook) {
|
| + // Register a hook for the test.oneString method.
|
| + auto hooks = base::MakeUnique<APIBindingHooks>(
|
| + base::Bind(&RunFunctionOnGlobalAndReturnHandle));
|
| +
|
| + v8::HandleScope handle_scope(isolate());
|
| + v8::Local<v8::Context> context = ContextLocal();
|
| + const char kRegisterHook[] =
|
| + "(function(hooks) {\n"
|
| + " hooks.setHandleRequest('oneString', str => {\n"
|
| + " return str + ' pong';\n"
|
| + " });\n"
|
| + "})";
|
| + v8::Local<v8::String> source_string =
|
| + gin::StringToV8(isolate(), kRegisterHook);
|
| + v8::Local<v8::String> source_name =
|
| + gin::StringToV8(isolate(), "custom_hook");
|
| + hooks->RegisterJsSource(
|
| + v8::Global<v8::String>(isolate(), source_string),
|
| + v8::Global<v8::String>(isolate(), source_name));
|
| +
|
| + std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions);
|
| + ASSERT_TRUE(functions);
|
| + ArgumentSpec::RefMap refs;
|
| +
|
| + APIBinding binding(
|
| + "test", *functions, nullptr, nullptr,
|
| + base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)),
|
| + std::move(hooks), &refs);
|
| + EXPECT_TRUE(refs.empty());
|
| +
|
| + APIEventHandler event_handler(
|
| + base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
|
| + v8::Local<v8::Object> binding_object = binding.CreateInstance(
|
| + context, isolate(), &event_handler, base::Bind(&AllowAllAPIs));
|
| +
|
| + v8::Local<v8::Function> function =
|
| + FunctionFromString(context,
|
| + "(function(obj) { return obj.oneString('ping'); })");
|
| + v8::Local<v8::Value> args[] = {binding_object};
|
| + v8::Local<v8::Value> result =
|
| + RunFunction(function, context, arraysize(args), args);
|
| + ASSERT_FALSE(result.IsEmpty());
|
| + std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context);
|
| + ASSERT_TRUE(json_result);
|
| + EXPECT_EQ("\"ping pong\"", ValueToString(*json_result));
|
| +}
|
| +
|
| +// Tests that JS custom hooks can throw exceptions for bad invocations.
|
| +TEST_F(APIBindingUnittest, TestThrowingFromCustomJSHook) {
|
| + // Our testing handlers for running functions expect a pre-determined success
|
| + // or failure. Since we're testing throwing exceptions here, we need a way of
|
| + // running that allows exceptions to be thrown, but we still expect most JS
|
| + // calls to succeed.
|
| + // TODO(devlin): This is a bit clunky. If we need to do this enough, we could
|
| + // figure out a different solution, like having a stack object for allowing
|
| + // errors/exceptions. But given this is the only place we need it so far, this
|
| + // is sufficient.
|
| + auto run_js_and_expect_error = [](v8::Local<v8::Function> function,
|
| + v8::Local<v8::Context> context,
|
| + int argc,
|
| + v8::Local<v8::Value> argv[]) {
|
| + v8::MaybeLocal<v8::Value> maybe_result =
|
| + function->Call(context, context->Global(), argc, argv);
|
| + v8::Global<v8::Value> result;
|
| + v8::Local<v8::Value> local;
|
| + if (maybe_result.ToLocal(&local))
|
| + result.Reset(context->GetIsolate(), local);
|
| + return result;
|
| + };
|
| + // Register a hook for the test.oneString method.
|
| + auto hooks = base::MakeUnique<APIBindingHooks>(
|
| + base::Bind(run_js_and_expect_error));
|
| +
|
| + v8::HandleScope handle_scope(isolate());
|
| + v8::Local<v8::Context> context = ContextLocal();
|
| + const char kRegisterHook[] =
|
| + "(function(hooks) {\n"
|
| + " hooks.setHandleRequest('oneString', str => {\n"
|
| + " throw new Error('Custom Hook Error');\n"
|
| + " });\n"
|
| + "})";
|
| + v8::Local<v8::String> source_string =
|
| + gin::StringToV8(isolate(), kRegisterHook);
|
| + v8::Local<v8::String> source_name =
|
| + gin::StringToV8(isolate(), "custom_hook");
|
| + hooks->RegisterJsSource(
|
| + v8::Global<v8::String>(isolate(), source_string),
|
| + v8::Global<v8::String>(isolate(), source_name));
|
| +
|
| + std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions);
|
| + ASSERT_TRUE(functions);
|
| + ArgumentSpec::RefMap refs;
|
| +
|
| + APIBinding binding(
|
| + "test", *functions, nullptr, nullptr,
|
| + base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)),
|
| + std::move(hooks), &refs);
|
| + EXPECT_TRUE(refs.empty());
|
| +
|
| + APIEventHandler event_handler(
|
| + base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
|
| + v8::Local<v8::Object> binding_object = binding.CreateInstance(
|
| + context, isolate(), &event_handler, base::Bind(&AllowAllAPIs));
|
| +
|
| + v8::Local<v8::Function> function =
|
| + FunctionFromString(context,
|
| + "(function(obj) { return obj.oneString('ping'); })");
|
| + v8::Local<v8::Value> args[] = {binding_object};
|
| + RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
|
| + arraysize(args), args,
|
| + "Uncaught Error: Custom Hook Error");
|
| +}
|
| +
|
| +// Tests that native custom hooks can return results synchronously, or throw
|
| +// exceptions for bad invocations.
|
| +TEST_F(APIBindingUnittest,
|
| + TestReturningResultAndThrowingExceptionFromCustomNativeHook) {
|
| + v8::HandleScope handle_scope(isolate());
|
| + v8::Local<v8::Context> context = ContextLocal();
|
| +
|
| + // Register a hook for the test.oneString method.
|
| + auto hooks = base::MakeUnique<APIBindingHooks>(
|
| + base::Bind(&RunFunctionOnGlobalAndReturnHandle));
|
| + bool did_call = false;
|
| + auto hook = [](bool* did_call, const APISignature* signature,
|
| + v8::Local<v8::Context> context,
|
| + std::vector<v8::Local<v8::Value>>* arguments,
|
| + const ArgumentSpec::RefMap& ref_map,
|
| + v8::Local<v8::Value>* return_value) {
|
| + if (arguments->size() != 1u) {
|
| + EXPECT_EQ(1u, arguments->size());
|
| + return APIBindingHooks::RequestResult::HANDLED;
|
| + }
|
| + v8::Isolate* isolate = context->GetIsolate();
|
| + std::string arg_value = gin::V8ToString(arguments->at(0));
|
| + if (arg_value == "throw") {
|
| + isolate->ThrowException(v8::Exception::Error(
|
| + gin::StringToV8(isolate, "Custom Hook Error")));
|
| + return APIBindingHooks::RequestResult::THROWN;
|
| + }
|
| + *return_value = gin::StringToV8(context->GetIsolate(), arg_value + " pong");
|
| + return APIBindingHooks::RequestResult::HANDLED;
|
| + };
|
| + hooks->RegisterHandleRequest("test.oneString", base::Bind(hook, &did_call));
|
| +
|
| + std::unique_ptr<base::ListValue> functions = ListValueFromString(kFunctions);
|
| + ASSERT_TRUE(functions);
|
| + ArgumentSpec::RefMap refs;
|
| +
|
| + APIBinding binding(
|
| + "test", *functions, nullptr, nullptr,
|
| + base::Bind(&APIBindingUnittest::OnFunctionCall, base::Unretained(this)),
|
| + std::move(hooks), &refs);
|
| + EXPECT_TRUE(refs.empty());
|
| +
|
| + APIEventHandler event_handler(
|
| + base::Bind(&RunFunctionOnGlobalAndIgnoreResult));
|
| + v8::Local<v8::Object> binding_object = binding.CreateInstance(
|
| + context, isolate(), &event_handler, base::Bind(&AllowAllAPIs));
|
| +
|
| + {
|
| + // Test an invocation that we expect to throw an exception.
|
| + v8::Local<v8::Function> function =
|
| + FunctionFromString(
|
| + context, "(function(obj) { return obj.oneString('throw'); })");
|
| + v8::Local<v8::Value> args[] = {binding_object};
|
| + RunFunctionAndExpectError(function, context, v8::Undefined(isolate()),
|
| + arraysize(args), args,
|
| + "Uncaught Error: Custom Hook Error");
|
| + }
|
| +
|
| + {
|
| + // Test an invocation we expect to succeed.
|
| + v8::Local<v8::Function> function =
|
| + FunctionFromString(context,
|
| + "(function(obj) { return obj.oneString('ping'); })");
|
| + v8::Local<v8::Value> args[] = {binding_object};
|
| + v8::Local<v8::Value> result =
|
| + RunFunction(function, context, arraysize(args), args);
|
| + ASSERT_FALSE(result.IsEmpty());
|
| + std::unique_ptr<base::Value> json_result = V8ToBaseValue(result, context);
|
| + ASSERT_TRUE(json_result);
|
| + EXPECT_EQ("\"ping pong\"", ValueToString(*json_result));
|
| + }
|
| +}
|
| +
|
| } // namespace extensions
|
|
|