diff options
author | kalman <kalman@chromium.org> | 2015-08-21 12:30:13 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-21 19:31:00 +0000 |
commit | 12033f1290753504a3c324293689313b890e4ef2 (patch) | |
tree | 84493e36fd5ef8f215bcde3cd82aa638dac52d06 /extensions/renderer | |
parent | b3ad146d98e8e67187e4f072676afbe210e73042 (diff) | |
download | chromium_src-12033f1290753504a3c324293689313b890e4ef2.zip chromium_src-12033f1290753504a3c324293689313b890e4ef2.tar.gz chromium_src-12033f1290753504a3c324293689313b890e4ef2.tar.bz2 |
Implement the wake-event-page internal extension API.
This is implemented purely using IPC because it will be used from worker
contexts, which don't have access to extension bindings. The next step will be
to add worker thread support so the renderer side is structured to easily
accommodate for that, once the public content API is chaned to allow it.
It will be used in the public chrome.runtime.getBackgroundClient() exposed to
service workers.
BUG=501569
R=rdevlin.cronin@chromium.org, palmer@chromium.org
Review URL: https://codereview.chromium.org/1294993004
Cr-Commit-Position: refs/heads/master@{#344834}
Diffstat (limited to 'extensions/renderer')
-rw-r--r-- | extensions/renderer/dispatcher.cc | 6 | ||||
-rw-r--r-- | extensions/renderer/resources/test_custom_bindings.js | 5 | ||||
-rw-r--r-- | extensions/renderer/test_native_handler.cc | 24 | ||||
-rw-r--r-- | extensions/renderer/test_native_handler.h | 28 | ||||
-rw-r--r-- | extensions/renderer/wake_event_page.cc | 173 | ||||
-rw-r--r-- | extensions/renderer/wake_event_page.h | 108 |
6 files changed, 344 insertions, 0 deletions
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc index ed64dfd..636a2bf 100644 --- a/extensions/renderer/dispatcher.cc +++ b/extensions/renderer/dispatcher.cc @@ -81,10 +81,12 @@ #include "extensions/renderer/send_request_natives.h" #include "extensions/renderer/set_icon_natives.h" #include "extensions/renderer/test_features_native_handler.h" +#include "extensions/renderer/test_native_handler.h" #include "extensions/renderer/user_gestures_native_handler.h" #include "extensions/renderer/utils_native_handler.h" #include "extensions/renderer/v8_context_native_handler.h" #include "extensions/renderer/v8_helpers.h" +#include "extensions/renderer/wake_event_page.h" #include "grit/extensions_renderer_resources.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" @@ -210,6 +212,7 @@ Dispatcher::Dispatcher(DispatcherDelegate* delegate) user_script_set_manager_observer_.Add(user_script_set_manager_.get()); request_sender_.reset(new RequestSender(this)); PopulateSourceMap(); + WakeEventPage::Get()->Init(content::RenderThread::Get()); // chrome-extensions: and chrome-extensions-resource: schemes should be // treated as secure because communication with them is entirely in the @@ -668,6 +671,9 @@ void Dispatcher::RegisterNativeHandlers(ModuleSystem* module_system, "test_features", scoped_ptr<NativeHandler>(new TestFeaturesNativeHandler(context))); module_system->RegisterNativeHandler( + "test_native_handler", + scoped_ptr<NativeHandler>(new TestNativeHandler(context))); + module_system->RegisterNativeHandler( "user_gestures", scoped_ptr<NativeHandler>(new UserGesturesNativeHandler(context))); module_system->RegisterNativeHandler( diff --git a/extensions/renderer/resources/test_custom_bindings.js b/extensions/renderer/resources/test_custom_bindings.js index ebb3f1b..7310f06 100644 --- a/extensions/renderer/resources/test_custom_bindings.js +++ b/extensions/renderer/resources/test_custom_bindings.js @@ -11,6 +11,7 @@ var environmentSpecificBindings = require('test_environment_specific_bindings'); var GetExtensionAPIDefinitionsForTest = requireNative('apiDefinitions').GetExtensionAPIDefinitionsForTest; var GetAPIFeatures = requireNative('test_features').GetAPIFeatures; +var natives = requireNative('test_native_handler'); var uncaughtExceptionHandler = require('uncaught_exception_handler'); var userGestures = requireNative('user_gestures'); @@ -353,6 +354,10 @@ binding.registerCustomHook(function(api) { uncaughtExceptionHandler.setHandler(callback); }); + apiFunctions.setHandleRequest('getWakeEventPage', function() { + return natives.GetWakeEventPage(); + }); + environmentSpecificBindings.registerHooks(api); }); diff --git a/extensions/renderer/test_native_handler.cc b/extensions/renderer/test_native_handler.cc new file mode 100644 index 0000000..31e7dcb --- /dev/null +++ b/extensions/renderer/test_native_handler.cc @@ -0,0 +1,24 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/renderer/test_native_handler.h" + +#include "extensions/renderer/wake_event_page.h" + +namespace extensions { + +TestNativeHandler::TestNativeHandler(ScriptContext* context) + : ObjectBackedNativeHandler(context) { + RouteFunction( + "GetWakeEventPage", + base::Bind(&TestNativeHandler::GetWakeEventPage, base::Unretained(this))); +} + +void TestNativeHandler::GetWakeEventPage( + const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(0, args.Length()); + args.GetReturnValue().Set(WakeEventPage::Get()->GetForContext(context())); +} + +} // namespace extensions diff --git a/extensions/renderer/test_native_handler.h b/extensions/renderer/test_native_handler.h new file mode 100644 index 0000000..057329d --- /dev/null +++ b/extensions/renderer/test_native_handler.h @@ -0,0 +1,28 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_RENDERER_TEST_NATIVE_HANDLER_H_ +#define EXTENSIONS_RENDERER_TEST_NATIVE_HANDLER_H_ + +#include "base/compiler_specific.h" +#include "extensions/renderer/object_backed_native_handler.h" +#include "v8/include/v8.h" + +namespace extensions { +class ScriptContext; + +// NativeHandler for the chrome.test API. +class TestNativeHandler : public ObjectBackedNativeHandler { + public: + explicit TestNativeHandler(ScriptContext* context); + + private: + void GetWakeEventPage(const v8::FunctionCallbackInfo<v8::Value>& args); + + DISALLOW_COPY_AND_ASSIGN(TestNativeHandler); +}; + +} // namespace extensions + +#endif // EXTENSIONS_RENDERER_TEST_NATIVE_HANDLER_H_ diff --git a/extensions/renderer/wake_event_page.cc b/extensions/renderer/wake_event_page.cc new file mode 100644 index 0000000..1bc4304 --- /dev/null +++ b/extensions/renderer/wake_event_page.cc @@ -0,0 +1,173 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/renderer/wake_event_page.h" + +#include "base/atomic_sequence_num.h" +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/renderer/render_thread.h" +#include "extensions/common/extension_messages.h" +#include "extensions/renderer/object_backed_native_handler.h" +#include "extensions/renderer/script_context.h" +#include "extensions/renderer/v8_helpers.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_message_macros.h" + +namespace extensions { + +using namespace v8_helpers; + +namespace { + +base::LazyInstance<WakeEventPage> g_instance = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +class WakeEventPage::WakeEventPageNativeHandler + : public ObjectBackedNativeHandler { + public: + // Handles own lifetime. + WakeEventPageNativeHandler(ScriptContext* context, + const std::string& name, + const MakeRequestCallback& make_request) + : ObjectBackedNativeHandler(context), + make_request_(make_request), + weak_ptr_factory_(this) { + // Use Unretained not a WeakPtr because RouteFunction is tied to the + // lifetime of this, so there is no way for DoWakeEventPage to be called + // after destruction. + RouteFunction(name, base::Bind(&WakeEventPageNativeHandler::DoWakeEventPage, + base::Unretained(this))); + // Delete self on invalidation. base::Unretained because by definition this + // can't be deleted before it's deleted. + context->AddInvalidationObserver(base::Bind( + &WakeEventPageNativeHandler::DeleteSelf, base::Unretained(this))); + }; + + ~WakeEventPageNativeHandler() override {} + + private: + void DeleteSelf() { + Invalidate(); + delete this; + } + + // Called by JavaScript with a single argument, the function to call when the + // event page has been woken. + void DoWakeEventPage(const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(1, args.Length()); + CHECK(args[0]->IsFunction()); + v8::Global<v8::Function> callback(args.GetIsolate(), + args[0].As<v8::Function>()); + + const std::string& extension_id = context()->GetExtensionID(); + CHECK(!extension_id.empty()); + + make_request_.Run( + extension_id, + base::Bind(&WakeEventPageNativeHandler::OnEventPageIsAwake, + weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); + } + + void OnEventPageIsAwake(v8::Global<v8::Function> callback, bool success) { + v8::Isolate* isolate = context()->isolate(); + v8::HandleScope handle_scope(isolate); + v8::Local<v8::Value> args[] = { + v8::Boolean::New(isolate, success), + }; + context()->CallFunction(v8::Local<v8::Function>::New(isolate, callback), + arraysize(args), args); + } + + MakeRequestCallback make_request_; + base::WeakPtrFactory<WakeEventPageNativeHandler> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(WakeEventPageNativeHandler); +}; + +// static +WakeEventPage* WakeEventPage::Get() { + return g_instance.Pointer(); +} + +void WakeEventPage::Init(content::RenderThread* render_thread) { + DCHECK(render_thread); + DCHECK_EQ(content::RenderThread::Get(), render_thread); + DCHECK(!message_filter_); + + message_filter_ = render_thread->GetSyncMessageFilter(); + render_thread->AddObserver(this); +} + +v8::Local<v8::Function> WakeEventPage::GetForContext(ScriptContext* context) { + DCHECK(message_filter_); + + v8::Isolate* isolate = context->isolate(); + v8::EscapableHandleScope handle_scope(isolate); + v8::Handle<v8::Context> v8_context = context->v8_context(); + v8::Context::Scope context_scope(v8_context); + + // Cache the imported function as a hidden property on the global object of + // |v8_context|. Creating it isn't free. + v8::Local<v8::String> kWakeEventPageKey = + ToV8StringUnsafe(isolate, "WakeEventPage"); + v8::Local<v8::Value> wake_event_page = + v8_context->Global()->GetHiddenValue(kWakeEventPageKey); + + if (wake_event_page.IsEmpty()) { + // Implement this using a NativeHandler, which requires a function name + // (arbitrary in this case). Handles own lifetime. + const char* kFunctionName = "WakeEventPage"; + WakeEventPageNativeHandler* native_handler = new WakeEventPageNativeHandler( + context, kFunctionName, base::Bind(&WakeEventPage::MakeRequest, + weak_ptr_factory_.GetWeakPtr())); + + // Extract and cache the wake-event-page function from the native handler. + wake_event_page = GetPropertyUnsafe( + v8_context, native_handler->NewInstance(), kFunctionName); + v8_context->Global()->SetHiddenValue(kWakeEventPageKey, wake_event_page); + } + + CHECK(wake_event_page->IsFunction()); + return handle_scope.Escape(wake_event_page.As<v8::Function>()); +} + +WakeEventPage::RequestData::RequestData(const OnResponseCallback& on_response) + : on_response(on_response) {} + +WakeEventPage::RequestData::~RequestData() {} + +WakeEventPage::WakeEventPage() : weak_ptr_factory_(this) {} + +WakeEventPage::~WakeEventPage() {} + +void WakeEventPage::MakeRequest(const std::string& extension_id, + const OnResponseCallback& on_response) { + static base::AtomicSequenceNumber sequence_number; + int request_id = sequence_number.GetNext(); + requests_.set(request_id, make_scoped_ptr(new RequestData(on_response))); + message_filter_->Send( + new ExtensionHostMsg_WakeEventPage(request_id, extension_id)); +} + +bool WakeEventPage::OnControlMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WakeEventPage, message) + IPC_MESSAGE_HANDLER(ExtensionMsg_WakeEventPageResponse, + OnWakeEventPageResponse) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void WakeEventPage::OnWakeEventPageResponse(int request_id, bool success) { + scoped_ptr<RequestData> request_data = requests_.take(request_id); + CHECK(request_data); + request_data->on_response.Run(success); +} + +} // namespace extensions diff --git a/extensions/renderer/wake_event_page.h b/extensions/renderer/wake_event_page.h new file mode 100644 index 0000000..38ceda4f --- /dev/null +++ b/extensions/renderer/wake_event_page.h @@ -0,0 +1,108 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXTENSIONS_RENDERER_WAKE_EVENT_PAGE_H_ +#define EXTENSIONS_RENDERER_WAKE_EVENT_PAGE_H_ + +#include <string> + +#include "base/callback.h" +#include "base/containers/scoped_ptr_hash_map.h" +#include "base/lazy_instance.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/public/renderer/render_process_observer.h" +#include "ipc/ipc_sync_message_filter.h" +#include "v8/include/v8.h" + +namespace content { +class RenderThread; +} + +namespace extensions { +class ScriptContext; + +// This class implements the wake-event-page JavaScript function, which wakes +// an event page and runs a callback when done. +// +// Note, the function will do a round trip to the browser even if event page is +// open. Any optimisation to prevent this must be at the JavaScript level. +class WakeEventPage : public content::RenderProcessObserver { + public: + WakeEventPage(); + ~WakeEventPage() override; + + // Returns the single instance of the WakeEventPage object. + // + // Thread safe. + static WakeEventPage* Get(); + + // Initializes the WakeEventPage. + // + // This must be called before any bindings are installed, and must be called + // on the render thread. + void Init(content::RenderThread* render_thread); + + // Returns the wake-event-page function bound to a given context. The + // function will be cached as a hidden value in the context's global object. + // + // To mix C++ and JavaScript, example usage might be: + // + // WakeEventPage::Get().GetForContext(context)(function() { + // ... + // }); + // + // Thread safe. + v8::Local<v8::Function> GetForContext(ScriptContext* context); + + private: + class WakeEventPageNativeHandler; + + // The response from an ExtensionHostMsg_WakeEvent call, passed true if the + // call was successful, false on failure. + using OnResponseCallback = base::Callback<void(bool)>; + + // Makes an ExtensionHostMsg_WakeEvent request for an extension ID. The + // second argument is a callback to run when the request has completed. + using MakeRequestCallback = + base::Callback<void(const std::string&, const OnResponseCallback&)>; + + // For |requests_|. + struct RequestData { + explicit RequestData(const OnResponseCallback& on_response); + ~RequestData(); + OnResponseCallback on_response; + }; + + // Runs |on_response|, passing it |success|. + static void RunOnResponseWithResult(const OnResponseCallback& on_response, + bool success); + + // Sends the ExtensionHostMsg_WakeEvent IPC for |extension_id|, and + // updates |requests_| bookkeeping. + void MakeRequest(const std::string& extension_id, + const OnResponseCallback& on_response); + + // content::RenderProcessObserver: + bool OnControlMessageReceived(const IPC::Message& message) override; + + // OnControlMessageReceived handlers: + void OnWakeEventPageResponse(int request_id, bool success); + + // IPC sender. Belongs to the render thread, but thread safe. + scoped_refptr<IPC::SyncMessageFilter> message_filter_; + + // All in-flight requests, keyed by request ID. + base::ScopedPtrHashMap<int, scoped_ptr<RequestData>> requests_; + + base::WeakPtrFactory<WakeEventPage> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(WakeEventPage); +}; + +} // namespace extensions + +#endif // EXTENSIONS_RENDERER_WAKE_EVENT_PAGE_H_ |