1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
// 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 <memory>
#include <utility>
#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/ptr_util.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<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()->SafeCallFunction(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::Private> kWakeEventPageKey =
v8::Private::ForApi(isolate, ToV8StringUnsafe(isolate, "WakeEventPage"));
v8::Local<v8::Value> 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<v8::Function>());
}
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();
{
base::AutoLock lock(requests_lock_);
requests_[request_id] = base::MakeUnique<RequestData>(
content::WorkerThread::GetCurrentId(), 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) {
std::unique_ptr<RequestData> request_data;
{
base::AutoLock lock(requests_lock_);
auto it = requests_.find(request_id);
CHECK(it != requests_.end()) << "No request with ID " << request_id;
request_data = std::move(it->second);
requests_.erase(it);
}
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
|