1 // Copyright 2013 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 "content/renderer/service_worker/embedded_worker_context_client.h"
10 #include "base/lazy_instance.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/pickle.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/threading/thread_local.h"
16 #include "content/child/request_extra_data.h"
17 #include "content/child/service_worker/service_worker_network_provider.h"
18 #include "content/child/thread_safe_sender.h"
19 #include "content/child/worker_task_runner.h"
20 #include "content/child/worker_thread_task_runner.h"
21 #include "content/common/devtools_messages.h"
22 #include "content/common/service_worker/embedded_worker_messages.h"
23 #include "content/common/service_worker/service_worker_types.h"
24 #include "content/public/renderer/document_state.h"
25 #include "content/renderer/render_thread_impl.h"
26 #include "content/renderer/service_worker/embedded_worker_dispatcher.h"
27 #include "content/renderer/service_worker/service_worker_script_context.h"
28 #include "ipc/ipc_message_macros.h"
29 #include "third_party/WebKit/public/platform/WebServiceWorkerResponse.h"
30 #include "third_party/WebKit/public/platform/WebString.h"
31 #include "third_party/WebKit/public/web/WebDataSource.h"
32 #include "third_party/WebKit/public/web/WebServiceWorkerNetworkProvider.h"
38 // For now client must be a per-thread instance.
39 // TODO(kinuko): This needs to be refactored when we start using thread pool
40 // or having multiple clients per one thread.
41 base::LazyInstance
<base::ThreadLocalPointer
<EmbeddedWorkerContextClient
> >::
42 Leaky g_worker_client_tls
= LAZY_INSTANCE_INITIALIZER
;
44 void CallWorkerContextDestroyedOnMainThread(int embedded_worker_id
) {
45 if (!RenderThreadImpl::current() ||
46 !RenderThreadImpl::current()->embedded_worker_dispatcher())
48 RenderThreadImpl::current()->embedded_worker_dispatcher()->
49 WorkerContextDestroyed(embedded_worker_id
);
52 // We store an instance of this class in the "extra data" of the WebDataSource
53 // and attach a ServiceWorkerNetworkProvider to it as base::UserData.
54 // (see createServiceWorkerNetworkProvider).
55 class DataSourceExtraData
56 : public blink::WebDataSource::ExtraData
,
57 public base::SupportsUserData
{
59 DataSourceExtraData() {}
60 virtual ~DataSourceExtraData() {}
63 // Called on the main thread only and blink owns it.
64 class WebServiceWorkerNetworkProviderImpl
65 : public blink::WebServiceWorkerNetworkProvider
{
67 // Blink calls this method for each request starting with the main script,
68 // we tag them with the provider id.
69 virtual void willSendRequest(
70 blink::WebDataSource
* data_source
,
71 blink::WebURLRequest
& request
) {
72 ServiceWorkerNetworkProvider
* provider
=
73 ServiceWorkerNetworkProvider::FromDocumentState(
74 static_cast<DataSourceExtraData
*>(data_source
->extraData()));
75 scoped_ptr
<RequestExtraData
> extra_data(new RequestExtraData
);
76 extra_data
->set_service_worker_provider_id(provider
->provider_id());
77 request
.setExtraData(extra_data
.release());
83 EmbeddedWorkerContextClient
*
84 EmbeddedWorkerContextClient::ThreadSpecificInstance() {
85 return g_worker_client_tls
.Pointer()->Get();
88 EmbeddedWorkerContextClient::EmbeddedWorkerContextClient(
89 int embedded_worker_id
,
90 int64 service_worker_version_id
,
91 const GURL
& service_worker_scope
,
92 const GURL
& script_url
,
93 int worker_devtools_agent_route_id
)
94 : embedded_worker_id_(embedded_worker_id
),
95 service_worker_version_id_(service_worker_version_id
),
96 service_worker_scope_(service_worker_scope
),
97 script_url_(script_url
),
98 worker_devtools_agent_route_id_(worker_devtools_agent_route_id
),
99 sender_(ChildThread::current()->thread_safe_sender()),
100 main_thread_proxy_(base::MessageLoopProxy::current()),
101 weak_factory_(this) {
104 EmbeddedWorkerContextClient::~EmbeddedWorkerContextClient() {
107 bool EmbeddedWorkerContextClient::OnMessageReceived(
108 const IPC::Message
& msg
) {
110 IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerContextClient
, msg
)
111 IPC_MESSAGE_HANDLER(EmbeddedWorkerContextMsg_MessageToWorker
,
113 IPC_MESSAGE_UNHANDLED(handled
= false)
114 IPC_END_MESSAGE_MAP()
118 void EmbeddedWorkerContextClient::Send(IPC::Message
* message
) {
119 sender_
->Send(message
);
122 blink::WebURL
EmbeddedWorkerContextClient::scope() const {
123 return service_worker_scope_
;
126 blink::WebServiceWorkerCacheStorage
*
127 EmbeddedWorkerContextClient::cacheStorage() {
128 return script_context_
->cache_storage();
131 void EmbeddedWorkerContextClient::didPauseAfterDownload() {
132 Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_
));
135 void EmbeddedWorkerContextClient::getClients(
136 blink::WebServiceWorkerClientsCallbacks
* callbacks
) {
137 DCHECK(script_context_
);
138 script_context_
->GetClientDocuments(callbacks
);
141 void EmbeddedWorkerContextClient::workerReadyForInspection() {
142 Send(new EmbeddedWorkerHostMsg_WorkerReadyForInspection(embedded_worker_id_
));
145 void EmbeddedWorkerContextClient::workerContextFailedToStart() {
146 DCHECK(main_thread_proxy_
->RunsTasksOnCurrentThread());
147 DCHECK(!script_context_
);
149 Send(new EmbeddedWorkerHostMsg_WorkerScriptLoadFailed(embedded_worker_id_
));
151 RenderThreadImpl::current()->embedded_worker_dispatcher()->
152 WorkerContextDestroyed(embedded_worker_id_
);
155 void EmbeddedWorkerContextClient::workerContextStarted(
156 blink::WebServiceWorkerContextProxy
* proxy
) {
157 DCHECK(!worker_task_runner_
.get());
158 worker_task_runner_
= new WorkerThreadTaskRunner(
159 WorkerTaskRunner::Instance()->CurrentWorkerId());
160 DCHECK_NE(0, WorkerTaskRunner::Instance()->CurrentWorkerId());
161 // g_worker_client_tls.Pointer()->Get() could return NULL if this context
162 // gets deleted before workerContextStarted() is called.
163 DCHECK(g_worker_client_tls
.Pointer()->Get() == NULL
);
164 DCHECK(!script_context_
);
165 g_worker_client_tls
.Pointer()->Set(this);
166 script_context_
.reset(new ServiceWorkerScriptContext(this, proxy
));
168 Send(new EmbeddedWorkerHostMsg_WorkerScriptLoaded(
170 WorkerTaskRunner::Instance()->CurrentWorkerId()));
172 // Schedule a task to send back WorkerStarted asynchronously,
173 // so that at the time we send it we can be sure that the worker
174 // script has been evaluated and worker run loop has been started.
175 worker_task_runner_
->PostTask(
177 base::Bind(&EmbeddedWorkerContextClient::SendWorkerStarted
,
178 weak_factory_
.GetWeakPtr()));
181 void EmbeddedWorkerContextClient::willDestroyWorkerContext() {
182 // At this point OnWorkerRunLoopStopped is already called, so
183 // worker_task_runner_->RunsTasksOnCurrentThread() returns false
184 // (while we're still on the worker thread).
185 script_context_
.reset();
187 // This also lets the message filter stop dispatching messages to
189 g_worker_client_tls
.Pointer()->Set(NULL
);
192 void EmbeddedWorkerContextClient::workerContextDestroyed() {
193 DCHECK(g_worker_client_tls
.Pointer()->Get() == NULL
);
195 // Now we should be able to free the WebEmbeddedWorker container on the
197 main_thread_proxy_
->PostTask(
199 base::Bind(&CallWorkerContextDestroyedOnMainThread
,
200 embedded_worker_id_
));
203 void EmbeddedWorkerContextClient::reportException(
204 const blink::WebString
& error_message
,
207 const blink::WebString
& source_url
) {
208 Send(new EmbeddedWorkerHostMsg_ReportException(
209 embedded_worker_id_
, error_message
, line_number
,
210 column_number
, GURL(source_url
)));
213 void EmbeddedWorkerContextClient::reportConsoleMessage(
216 const blink::WebString
& message
,
218 const blink::WebString
& source_url
) {
219 EmbeddedWorkerHostMsg_ReportConsoleMessage_Params params
;
220 params
.source_identifier
= source
;
221 params
.message_level
= level
;
222 params
.message
= message
;
223 params
.line_number
= line_number
;
224 params
.source_url
= GURL(source_url
);
226 Send(new EmbeddedWorkerHostMsg_ReportConsoleMessage(
227 embedded_worker_id_
, params
));
230 void EmbeddedWorkerContextClient::dispatchDevToolsMessage(
231 const blink::WebString
& message
) {
232 sender_
->Send(new DevToolsClientMsg_DispatchOnInspectorFrontend(
233 worker_devtools_agent_route_id_
, message
.utf8()));
236 void EmbeddedWorkerContextClient::saveDevToolsAgentState(
237 const blink::WebString
& state
) {
238 sender_
->Send(new DevToolsHostMsg_SaveAgentRuntimeState(
239 worker_devtools_agent_route_id_
, state
.utf8()));
242 void EmbeddedWorkerContextClient::didHandleActivateEvent(
244 blink::WebServiceWorkerEventResult result
) {
245 DCHECK(script_context_
);
246 script_context_
->DidHandleActivateEvent(request_id
, result
);
249 void EmbeddedWorkerContextClient::didHandleInstallEvent(
251 blink::WebServiceWorkerEventResult result
) {
252 DCHECK(script_context_
);
253 script_context_
->DidHandleInstallEvent(request_id
, result
);
256 void EmbeddedWorkerContextClient::didHandleFetchEvent(int request_id
) {
257 DCHECK(script_context_
);
258 script_context_
->DidHandleFetchEvent(
260 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK
,
261 ServiceWorkerResponse());
264 void EmbeddedWorkerContextClient::didHandleFetchEvent(
266 const blink::WebServiceWorkerResponse
& web_response
) {
267 DCHECK(script_context_
);
268 std::map
<std::string
, std::string
> headers
;
269 const blink::WebVector
<blink::WebString
>& header_keys
=
270 web_response
.getHeaderKeys();
271 for (size_t i
= 0; i
< header_keys
.size(); ++i
) {
272 const base::string16
& key
= header_keys
[i
];
273 headers
[base::UTF16ToUTF8(key
)] =
274 base::UTF16ToUTF8(web_response
.getHeader(key
));
276 ServiceWorkerResponse
response(web_response
.url(),
277 web_response
.status(),
278 web_response
.statusText().utf8(),
280 web_response
.blobUUID().utf8());
281 script_context_
->DidHandleFetchEvent(
282 request_id
, SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE
, response
);
285 void EmbeddedWorkerContextClient::didHandleSyncEvent(int request_id
) {
286 DCHECK(script_context_
);
287 script_context_
->DidHandleSyncEvent(request_id
);
290 blink::WebServiceWorkerNetworkProvider
*
291 EmbeddedWorkerContextClient::createServiceWorkerNetworkProvider(
292 blink::WebDataSource
* data_source
) {
293 // Create a content::ServiceWorkerNetworkProvider for this data source so
294 // we can observe its requests.
295 scoped_ptr
<ServiceWorkerNetworkProvider
> provider(
296 new ServiceWorkerNetworkProvider());
298 // Tell the network provider about which version to load.
299 provider
->SetServiceWorkerVersionId(service_worker_version_id_
);
301 // The provider is kept around for the lifetime of the DataSource
302 // and ownership is transferred to the DataSource.
303 DataSourceExtraData
* extra_data
= new DataSourceExtraData();
304 data_source
->setExtraData(extra_data
);
305 ServiceWorkerNetworkProvider::AttachToDocumentState(
306 extra_data
, provider
.Pass());
308 // Blink is responsible for deleting the returned object.
309 return new WebServiceWorkerNetworkProviderImpl();
312 void EmbeddedWorkerContextClient::postMessageToClient(
314 const blink::WebString
& message
,
315 blink::WebMessagePortChannelArray
* channels
) {
316 DCHECK(script_context_
);
317 script_context_
->PostMessageToDocument(client_id
, message
,
318 make_scoped_ptr(channels
));
321 void EmbeddedWorkerContextClient::OnMessageToWorker(
323 int embedded_worker_id
,
324 const IPC::Message
& message
) {
325 if (!script_context_
)
327 DCHECK_EQ(embedded_worker_id_
, embedded_worker_id
);
328 script_context_
->OnMessageReceived(message
);
331 void EmbeddedWorkerContextClient::SendWorkerStarted() {
332 DCHECK(worker_task_runner_
->RunsTasksOnCurrentThread());
333 Send(new EmbeddedWorkerHostMsg_WorkerStarted(embedded_worker_id_
));
336 } // namespace content