// 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 #include "base/atomic_sequence_num.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "content/public/child/worker_thread.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 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& args) { CHECK_EQ(1, args.Length()); CHECK(args[0]->IsFunction()); v8::Global callback(args.GetIsolate(), args[0].As()); 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 callback, bool success) { v8::Isolate* isolate = context()->isolate(); v8::HandleScope handle_scope(isolate); v8::Local args[] = { v8::Boolean::New(isolate, success), }; context()->CallFunction(v8::Local::New(isolate, callback), arraysize(args), args); } MakeRequestCallback make_request_; base::WeakPtrFactory 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 WakeEventPage::GetForContext(ScriptContext* context) { DCHECK(message_filter_); v8::Isolate* isolate = context->isolate(); v8::EscapableHandleScope handle_scope(isolate); v8::Handle 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 kWakeEventPageKey = v8::Private::ForApi(isolate, ToV8StringUnsafe(isolate, "WakeEventPage")); v8::Local wake_event_page; if (!v8_context->Global() ->GetPrivate(v8_context, kWakeEventPageKey) .ToLocal(&wake_event_page) || wake_event_page->IsUndefined()) { // 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, // Safe, owned by a LazyInstance. base::Unretained(this))); // 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() ->SetPrivate(v8_context, kWakeEventPageKey, wake_event_page) .FromJust(); } CHECK(wake_event_page->IsFunction()); return handle_scope.Escape(wake_event_page.As()); } WakeEventPage::RequestData::RequestData(int thread_id, const OnResponseCallback& on_response) : thread_id(thread_id), on_response(on_response) {} WakeEventPage::RequestData::~RequestData() {} WakeEventPage::WakeEventPage() {} 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(); { scoped_ptr request_data( new RequestData(content::WorkerThread::GetCurrentId(), on_response)); base::AutoLock lock(requests_lock_); requests_.set(request_id, std::move(request_data)); } 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 request_data; { base::AutoLock lock(requests_lock_); request_data = requests_.take(request_id); } CHECK(request_data) << "No request with ID " << request_id; if (request_data->thread_id == 0) { // Thread ID of 0 means it wasn't called on a worker thread, so safe to // call immediately. request_data->on_response.Run(success); } else { content::WorkerThread::PostTask( request_data->thread_id, base::Bind(request_data->on_response, success)); } } } // namespace extensions