1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/renderer/wake_event_page.h"
7 #include "base/atomic_sequence_num.h"
9 #include "base/bind_helpers.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "content/public/child/worker_thread.h"
14 #include "content/public/renderer/render_thread.h"
15 #include "extensions/common/extension_messages.h"
16 #include "extensions/renderer/object_backed_native_handler.h"
17 #include "extensions/renderer/script_context.h"
18 #include "extensions/renderer/v8_helpers.h"
19 #include "ipc/ipc_message.h"
20 #include "ipc/ipc_message_macros.h"
22 namespace extensions
{
24 using namespace v8_helpers
;
28 base::LazyInstance
<WakeEventPage
> g_instance
= LAZY_INSTANCE_INITIALIZER
;
32 class WakeEventPage::WakeEventPageNativeHandler
33 : public ObjectBackedNativeHandler
{
35 // Handles own lifetime.
36 WakeEventPageNativeHandler(ScriptContext
* context
,
37 const std::string
& name
,
38 const MakeRequestCallback
& make_request
)
39 : ObjectBackedNativeHandler(context
),
40 make_request_(make_request
),
41 weak_ptr_factory_(this) {
42 // Use Unretained not a WeakPtr because RouteFunction is tied to the
43 // lifetime of this, so there is no way for DoWakeEventPage to be called
45 RouteFunction(name
, base::Bind(&WakeEventPageNativeHandler::DoWakeEventPage
,
46 base::Unretained(this)));
47 // Delete self on invalidation. base::Unretained because by definition this
48 // can't be deleted before it's deleted.
49 context
->AddInvalidationObserver(base::Bind(
50 &WakeEventPageNativeHandler::DeleteSelf
, base::Unretained(this)));
53 ~WakeEventPageNativeHandler() override
{}
61 // Called by JavaScript with a single argument, the function to call when the
62 // event page has been woken.
63 void DoWakeEventPage(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
64 CHECK_EQ(1, args
.Length());
65 CHECK(args
[0]->IsFunction());
66 v8::Global
<v8::Function
> callback(args
.GetIsolate(),
67 args
[0].As
<v8::Function
>());
69 const std::string
& extension_id
= context()->GetExtensionID();
70 CHECK(!extension_id
.empty());
74 base::Bind(&WakeEventPageNativeHandler::OnEventPageIsAwake
,
75 weak_ptr_factory_
.GetWeakPtr(), base::Passed(&callback
)));
78 void OnEventPageIsAwake(v8::Global
<v8::Function
> callback
, bool success
) {
79 v8::Isolate
* isolate
= context()->isolate();
80 v8::HandleScope
handle_scope(isolate
);
81 v8::Local
<v8::Value
> args
[] = {
82 v8::Boolean::New(isolate
, success
),
84 context()->CallFunction(v8::Local
<v8::Function
>::New(isolate
, callback
),
85 arraysize(args
), args
);
88 MakeRequestCallback make_request_
;
89 base::WeakPtrFactory
<WakeEventPageNativeHandler
> weak_ptr_factory_
;
91 DISALLOW_COPY_AND_ASSIGN(WakeEventPageNativeHandler
);
95 WakeEventPage
* WakeEventPage::Get() {
96 return g_instance
.Pointer();
99 void WakeEventPage::Init(content::RenderThread
* render_thread
) {
100 DCHECK(render_thread
);
101 DCHECK_EQ(content::RenderThread::Get(), render_thread
);
102 DCHECK(!message_filter_
);
104 message_filter_
= render_thread
->GetSyncMessageFilter();
105 render_thread
->AddObserver(this);
108 v8::Local
<v8::Function
> WakeEventPage::GetForContext(ScriptContext
* context
) {
109 DCHECK(message_filter_
);
111 v8::Isolate
* isolate
= context
->isolate();
112 v8::EscapableHandleScope
handle_scope(isolate
);
113 v8::Handle
<v8::Context
> v8_context
= context
->v8_context();
114 v8::Context::Scope
context_scope(v8_context
);
116 // Cache the imported function as a hidden property on the global object of
117 // |v8_context|. Creating it isn't free.
118 v8::Local
<v8::String
> kWakeEventPageKey
=
119 ToV8StringUnsafe(isolate
, "WakeEventPage");
120 v8::Local
<v8::Value
> wake_event_page
=
121 v8_context
->Global()->GetHiddenValue(kWakeEventPageKey
);
123 if (wake_event_page
.IsEmpty()) {
124 // Implement this using a NativeHandler, which requires a function name
125 // (arbitrary in this case). Handles own lifetime.
126 const char* kFunctionName
= "WakeEventPage";
127 WakeEventPageNativeHandler
* native_handler
= new WakeEventPageNativeHandler(
128 context
, kFunctionName
, base::Bind(&WakeEventPage::MakeRequest
,
129 // Safe, owned by a LazyInstance.
130 base::Unretained(this)));
132 // Extract and cache the wake-event-page function from the native handler.
133 wake_event_page
= GetPropertyUnsafe(
134 v8_context
, native_handler
->NewInstance(), kFunctionName
);
135 v8_context
->Global()->SetHiddenValue(kWakeEventPageKey
, wake_event_page
);
138 CHECK(wake_event_page
->IsFunction());
139 return handle_scope
.Escape(wake_event_page
.As
<v8::Function
>());
142 WakeEventPage::RequestData::RequestData(int thread_id
,
143 const OnResponseCallback
& on_response
)
144 : thread_id(thread_id
), on_response(on_response
) {}
146 WakeEventPage::RequestData::~RequestData() {}
148 WakeEventPage::WakeEventPage() {}
150 WakeEventPage::~WakeEventPage() {}
152 void WakeEventPage::MakeRequest(const std::string
& extension_id
,
153 const OnResponseCallback
& on_response
) {
154 static base::AtomicSequenceNumber sequence_number
;
155 int request_id
= sequence_number
.GetNext();
157 scoped_ptr
<RequestData
> request_data(
158 new RequestData(content::WorkerThread::GetCurrentId(), on_response
));
159 base::AutoLock
lock(requests_lock_
);
160 requests_
.set(request_id
, request_data
.Pass());
162 message_filter_
->Send(
163 new ExtensionHostMsg_WakeEventPage(request_id
, extension_id
));
166 bool WakeEventPage::OnControlMessageReceived(const IPC::Message
& message
) {
168 IPC_BEGIN_MESSAGE_MAP(WakeEventPage
, message
)
169 IPC_MESSAGE_HANDLER(ExtensionMsg_WakeEventPageResponse
,
170 OnWakeEventPageResponse
)
171 IPC_MESSAGE_UNHANDLED(handled
= false)
172 IPC_END_MESSAGE_MAP()
176 void WakeEventPage::OnWakeEventPageResponse(int request_id
, bool success
) {
177 scoped_ptr
<RequestData
> request_data
;
179 base::AutoLock
lock(requests_lock_
);
180 request_data
= requests_
.take(request_id
);
182 CHECK(request_data
) << "No request with ID " << request_id
;
183 if (request_data
->thread_id
== 0) {
184 // Thread ID of 0 means it wasn't called on a worker thread, so safe to
186 request_data
->on_response
.Run(success
);
188 content::WorkerThread::PostTask(
189 request_data
->thread_id
,
190 base::Bind(request_data
->on_response
, success
));
194 } // namespace extensions