| Index: extensions/renderer/bindings/api_binding_unittest.cc
 | 
| diff --git a/extensions/renderer/bindings/api_binding_unittest.cc b/extensions/renderer/bindings/api_binding_unittest.cc
 | 
| index 73e1ab3d84ed74d0e83b90fbac2068531be5b8cb..b8e52c2228cc88fa70b773414a8a174151156456 100644
 | 
| --- a/extensions/renderer/bindings/api_binding_unittest.cc
 | 
| +++ b/extensions/renderer/bindings/api_binding_unittest.cc
 | 
| @@ -20,6 +20,7 @@
 | 
|  #include "gin/arguments.h"
 | 
|  #include "gin/converter.h"
 | 
|  #include "gin/public/context_holder.h"
 | 
| +#include "testing/gmock/include/gmock/gmock.h"
 | 
|  #include "testing/gtest/include/gtest/gtest.h"
 | 
|  #include "third_party/WebKit/public/web/WebScopedUserGesture.h"
 | 
|  #include "v8/include/v8.h"
 | 
| @@ -80,6 +81,11 @@ void OnEventListenersChanged(const std::string& event_name,
 | 
|                               bool was_manual,
 | 
|                               v8::Local<v8::Context> context) {}
 | 
|  
 | 
| +void DoNothingWithSilentRequest(
 | 
| +    v8::Local<v8::Context> context,
 | 
| +    const std::string& call_name,
 | 
| +    const std::vector<v8::Local<v8::Value>>& arguments) {}
 | 
| +
 | 
|  }  // namespace
 | 
|  
 | 
|  class APIBindingUnittest : public APIBindingTest {
 | 
| @@ -150,6 +156,10 @@ class APIBindingUnittest : public APIBindingTest {
 | 
|      create_custom_type_ = callback;
 | 
|    }
 | 
|  
 | 
| +  void SetOnSilentRequest(const APIBinding::OnSilentRequest& callback) {
 | 
| +    on_silent_request_ = callback;
 | 
| +  }
 | 
| +
 | 
|    void SetAvailabilityCallback(
 | 
|        const BindingAccessChecker::AvailabilityCallback& callback) {
 | 
|      availability_callback_ = callback;
 | 
| @@ -162,6 +172,8 @@ class APIBindingUnittest : public APIBindingTest {
 | 
|      }
 | 
|      if (binding_hooks_delegate_)
 | 
|        binding_hooks_->SetDelegate(std::move(binding_hooks_delegate_));
 | 
| +    if (!on_silent_request_)
 | 
| +      on_silent_request_ = base::Bind(&DoNothingWithSilentRequest);
 | 
|      if (!availability_callback_)
 | 
|        availability_callback_ = base::Bind(&AllowAllFeatures);
 | 
|      event_handler_ = base::MakeUnique<APIEventHandler>(
 | 
| @@ -173,8 +185,8 @@ class APIBindingUnittest : public APIBindingTest {
 | 
|      binding_ = base::MakeUnique<APIBinding>(
 | 
|          kBindingName, binding_functions_.get(), binding_types_.get(),
 | 
|          binding_events_.get(), binding_properties_.get(), create_custom_type_,
 | 
| -        std::move(binding_hooks_), &type_refs_, request_handler_.get(),
 | 
| -        event_handler_.get(), access_checker_.get());
 | 
| +        on_silent_request_, std::move(binding_hooks_), &type_refs_,
 | 
| +        request_handler_.get(), event_handler_.get(), access_checker_.get());
 | 
|      EXPECT_EQ(!binding_types_.get(), type_refs_.empty());
 | 
|    }
 | 
|  
 | 
| @@ -243,6 +255,7 @@ class APIBindingUnittest : public APIBindingTest {
 | 
|    std::unique_ptr<APIBindingHooks> binding_hooks_;
 | 
|    std::unique_ptr<APIBindingHooksDelegate> binding_hooks_delegate_;
 | 
|    APIBinding::CreateCustomType create_custom_type_;
 | 
| +  APIBinding::OnSilentRequest on_silent_request_;
 | 
|    BindingAccessChecker::AvailabilityCallback availability_callback_;
 | 
|  
 | 
|    DISALLOW_COPY_AND_ASSIGN(APIBindingUnittest);
 | 
| @@ -1301,4 +1314,188 @@ TEST_F(APIBindingUnittest, HooksTemplateInitializer) {
 | 
|              GetStringPropertyFromObject(binding_object, context, "oneString"));
 | 
|  }
 | 
|  
 | 
| +// Test that running hooks returning different results correctly sends requests
 | 
| +// or notifies of silent requests.
 | 
| +TEST_F(APIBindingUnittest, TestSendingRequestsAndSilentRequestsWithHooks) {
 | 
| +  SetFunctions(
 | 
| +      "[{"
 | 
| +      "  'name': 'modifyArgs',"
 | 
| +      "  'parameters': []"
 | 
| +      "}, {"
 | 
| +      "  'name': 'invalidInvocation',"
 | 
| +      "  'parameters': []"
 | 
| +      "}, {"
 | 
| +      "  'name': 'throwException',"
 | 
| +      "  'parameters': []"
 | 
| +      "}, {"
 | 
| +      "  'name': 'dontHandle',"
 | 
| +      "  'parameters': []"
 | 
| +      "}, {"
 | 
| +      "  'name': 'handle',"
 | 
| +      "  'parameters': []"
 | 
| +      "}, {"
 | 
| +      "  'name': 'handleAndSendRequest',"
 | 
| +      "  'parameters': []"
 | 
| +      "}, {"
 | 
| +      "  'name': 'handleWithArgs',"
 | 
| +      "  'parameters': [{"
 | 
| +      "    'name': 'first',"
 | 
| +      "    'type': 'string'"
 | 
| +      "  }, {"
 | 
| +      "    'name': 'second',"
 | 
| +      "    'type': 'integer'"
 | 
| +      "  }]"
 | 
| +      "}]");
 | 
| +
 | 
| +  using RequestResult = APIBindingHooks::RequestResult;
 | 
| +
 | 
| +  auto basic_handler = [](RequestResult::ResultCode code, const APISignature*,
 | 
| +                          v8::Local<v8::Context> context,
 | 
| +                          std::vector<v8::Local<v8::Value>>* arguments,
 | 
| +                          const APITypeReferenceMap& map) {
 | 
| +    return RequestResult(code);
 | 
| +  };
 | 
| +
 | 
| +  auto hooks = base::MakeUnique<APIBindingHooksTestDelegate>();
 | 
| +  hooks->AddHandler(
 | 
| +      "test.modifyArgs",
 | 
| +      base::Bind(basic_handler, RequestResult::ARGUMENTS_UPDATED));
 | 
| +  hooks->AddHandler(
 | 
| +      "test.invalidInvocation",
 | 
| +      base::Bind(basic_handler, RequestResult::INVALID_INVOCATION));
 | 
| +  hooks->AddHandler("test.dontHandle",
 | 
| +                    base::Bind(basic_handler, RequestResult::NOT_HANDLED));
 | 
| +  hooks->AddHandler("test.handle",
 | 
| +                    base::Bind(basic_handler, RequestResult::HANDLED));
 | 
| +  hooks->AddHandler(
 | 
| +      "test.throwException",
 | 
| +      base::Bind([](const APISignature*, v8::Local<v8::Context> context,
 | 
| +                    std::vector<v8::Local<v8::Value>>* arguments,
 | 
| +                    const APITypeReferenceMap& map) {
 | 
| +        context->GetIsolate()->ThrowException(
 | 
| +            gin::StringToV8(context->GetIsolate(), "some error"));
 | 
| +        return RequestResult(RequestResult::THROWN);
 | 
| +      }));
 | 
| +  hooks->AddHandler(
 | 
| +      "test.handleWithArgs",
 | 
| +      base::Bind([](const APISignature*, v8::Local<v8::Context> context,
 | 
| +                    std::vector<v8::Local<v8::Value>>* arguments,
 | 
| +                    const APITypeReferenceMap& map) {
 | 
| +        arguments->push_back(v8::Integer::New(context->GetIsolate(), 42));
 | 
| +        return RequestResult(RequestResult::HANDLED);
 | 
| +      }));
 | 
| +
 | 
| +  auto handle_and_send_request =
 | 
| +      [](APIRequestHandler* handler, const APISignature*,
 | 
| +         v8::Local<v8::Context> context,
 | 
| +         std::vector<v8::Local<v8::Value>>* arguments,
 | 
| +         const APITypeReferenceMap& map) {
 | 
| +        handler->StartRequest(
 | 
| +            context, "test.handleAndSendRequest",
 | 
| +            base::MakeUnique<base::ListValue>(), v8::Local<v8::Function>(),
 | 
| +            v8::Local<v8::Function>(), binding::RequestThread::UI);
 | 
| +        return RequestResult(RequestResult::HANDLED);
 | 
| +      };
 | 
| +  hooks->AddHandler("test.handleAndSendRequest",
 | 
| +                    base::Bind(handle_and_send_request, request_handler()));
 | 
| +
 | 
| +  SetHooksDelegate(std::move(hooks));
 | 
| +
 | 
| +  auto on_silent_request =
 | 
| +      [](base::Optional<std::string>* name_out,
 | 
| +         base::Optional<std::vector<std::string>>* args_out,
 | 
| +         v8::Local<v8::Context> context, const std::string& call_name,
 | 
| +         const std::vector<v8::Local<v8::Value>>& arguments) {
 | 
| +        *name_out = call_name;
 | 
| +        *args_out = std::vector<std::string>();
 | 
| +        (*args_out)->reserve(arguments.size());
 | 
| +        for (const auto& arg : arguments)
 | 
| +          (*args_out)->push_back(V8ToString(arg, context));
 | 
| +      };
 | 
| +  base::Optional<std::string> silent_request;
 | 
| +  base::Optional<std::vector<std::string>> request_arguments;
 | 
| +  SetOnSilentRequest(
 | 
| +      base::Bind(on_silent_request, &silent_request, &request_arguments));
 | 
| +
 | 
| +  InitializeBinding();
 | 
| +
 | 
| +  v8::HandleScope handle_scope(isolate());
 | 
| +  v8::Local<v8::Context> context = MainContext();
 | 
| +
 | 
| +  v8::Local<v8::Object> binding_object = binding()->CreateInstance(context);
 | 
| +
 | 
| +  auto call_api_method = [binding_object, context](
 | 
| +                             base::StringPiece name,
 | 
| +                             base::StringPiece string_args) {
 | 
| +    v8::Local<v8::Function> call = FunctionFromString(
 | 
| +        context, base::StringPrintf("(function(binding) { binding.%s(%s); })",
 | 
| +                                    name.data(), string_args.data()));
 | 
| +    v8::Local<v8::Value> args[] = {binding_object};
 | 
| +    v8::TryCatch try_catch(context->GetIsolate());
 | 
| +    // The throwException call will throw an exception; ignore it.
 | 
| +    ignore_result(call->Call(context, v8::Undefined(context->GetIsolate()),
 | 
| +                             arraysize(args), args));
 | 
| +  };
 | 
| +
 | 
| +  call_api_method("modifyArgs", "");
 | 
| +  ASSERT_TRUE(last_request());
 | 
| +  EXPECT_EQ("test.modifyArgs", last_request()->method_name);
 | 
| +  EXPECT_FALSE(silent_request);
 | 
| +  reset_last_request();
 | 
| +  silent_request.reset();
 | 
| +  request_arguments.reset();
 | 
| +
 | 
| +  call_api_method("invalidInvocation", "");
 | 
| +  EXPECT_FALSE(last_request());
 | 
| +  EXPECT_FALSE(silent_request);
 | 
| +  reset_last_request();
 | 
| +  silent_request.reset();
 | 
| +  request_arguments.reset();
 | 
| +
 | 
| +  call_api_method("throwException", "");
 | 
| +  EXPECT_FALSE(last_request());
 | 
| +  EXPECT_FALSE(silent_request);
 | 
| +  reset_last_request();
 | 
| +  silent_request.reset();
 | 
| +  request_arguments.reset();
 | 
| +
 | 
| +  call_api_method("dontHandle", "");
 | 
| +  ASSERT_TRUE(last_request());
 | 
| +  EXPECT_EQ("test.dontHandle", last_request()->method_name);
 | 
| +  EXPECT_FALSE(silent_request);
 | 
| +  reset_last_request();
 | 
| +  silent_request.reset();
 | 
| +  request_arguments.reset();
 | 
| +
 | 
| +  call_api_method("handle", "");
 | 
| +  EXPECT_FALSE(last_request());
 | 
| +  ASSERT_TRUE(silent_request);
 | 
| +  EXPECT_EQ("test.handle", *silent_request);
 | 
| +  ASSERT_TRUE(request_arguments);
 | 
| +  EXPECT_TRUE(request_arguments->empty());
 | 
| +  reset_last_request();
 | 
| +  silent_request.reset();
 | 
| +  request_arguments.reset();
 | 
| +
 | 
| +  call_api_method("handleAndSendRequest", "");
 | 
| +  ASSERT_TRUE(last_request());
 | 
| +  EXPECT_EQ("test.handleAndSendRequest", last_request()->method_name);
 | 
| +  EXPECT_FALSE(silent_request);
 | 
| +  reset_last_request();
 | 
| +  silent_request.reset();
 | 
| +  request_arguments.reset();
 | 
| +
 | 
| +  call_api_method("handleWithArgs", "'str'");
 | 
| +  EXPECT_FALSE(last_request());
 | 
| +  ASSERT_TRUE(silent_request);
 | 
| +  ASSERT_EQ("test.handleWithArgs", *silent_request);
 | 
| +  ASSERT_TRUE(request_arguments);
 | 
| +  EXPECT_THAT(
 | 
| +      *request_arguments,
 | 
| +      testing::ElementsAre("\"str\"", "42"));  // 42 was added by the handler.
 | 
| +  reset_last_request();
 | 
| +  silent_request.reset();
 | 
| +  request_arguments.reset();
 | 
| +}
 | 
| +
 | 
|  }  // namespace extensions
 | 
| 
 |