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/thread_task_runner_handle.h"
16 #include "base/threading/thread_local.h"
17 #include "base/trace_event/trace_event.h"
18 #include "content/child/request_extra_data.h"
19 #include "content/child/service_worker/service_worker_dispatcher.h"
20 #include "content/child/service_worker/service_worker_network_provider.h"
21 #include "content/child/service_worker/service_worker_provider_context.h"
22 #include "content/child/service_worker/service_worker_registration_handle_reference.h"
23 #include "content/child/service_worker/web_service_worker_impl.h"
24 #include "content/child/service_worker/web_service_worker_provider_impl.h"
25 #include "content/child/service_worker/web_service_worker_registration_impl.h"
26 #include "content/child/thread_safe_sender.h"
27 #include "content/child/worker_task_runner.h"
28 #include "content/common/devtools_messages.h"
29 #include "content/common/service_worker/embedded_worker_messages.h"
30 #include "content/common/service_worker/service_worker_types.h"
31 #include "content/public/renderer/document_state.h"
32 #include "content/renderer/devtools/devtools_agent.h"
33 #include "content/renderer/render_thread_impl.h"
34 #include "content/renderer/service_worker/embedded_worker_dispatcher.h"
35 #include "content/renderer/service_worker/service_worker_script_context.h"
36 #include "content/renderer/service_worker/service_worker_type_util.h"
37 #include "ipc/ipc_message_macros.h"
38 #include "third_party/WebKit/public/platform/WebServiceWorkerResponse.h"
39 #include "third_party/WebKit/public/platform/WebString.h"
40 #include "third_party/WebKit/public/web/WebDataSource.h"
41 #include "third_party/WebKit/public/web/WebServiceWorkerNetworkProvider.h"
47 // For now client must be a per-thread instance.
48 // TODO(kinuko): This needs to be refactored when we start using thread pool
49 // or having multiple clients per one thread.
50 base::LazyInstance
<base::ThreadLocalPointer
<EmbeddedWorkerContextClient
> >::
51 Leaky g_worker_client_tls
= LAZY_INSTANCE_INITIALIZER
;
53 void CallWorkerContextDestroyedOnMainThread(int embedded_worker_id
) {
54 if (!RenderThreadImpl::current() ||
55 !RenderThreadImpl::current()->embedded_worker_dispatcher())
57 RenderThreadImpl::current()->embedded_worker_dispatcher()->
58 WorkerContextDestroyed(embedded_worker_id
);
61 // We store an instance of this class in the "extra data" of the WebDataSource
62 // and attach a ServiceWorkerNetworkProvider to it as base::UserData.
63 // (see createServiceWorkerNetworkProvider).
64 class DataSourceExtraData
65 : public blink::WebDataSource::ExtraData
,
66 public base::SupportsUserData
{
68 DataSourceExtraData() {}
69 virtual ~DataSourceExtraData() {}
72 // Called on the main thread only and blink owns it.
73 class WebServiceWorkerNetworkProviderImpl
74 : public blink::WebServiceWorkerNetworkProvider
{
76 // Blink calls this method for each request starting with the main script,
77 // we tag them with the provider id.
78 virtual void willSendRequest(
79 blink::WebDataSource
* data_source
,
80 blink::WebURLRequest
& request
) {
81 ServiceWorkerNetworkProvider
* provider
=
82 ServiceWorkerNetworkProvider::FromDocumentState(
83 static_cast<DataSourceExtraData
*>(data_source
->extraData()));
84 scoped_ptr
<RequestExtraData
> extra_data(new RequestExtraData
);
85 extra_data
->set_service_worker_provider_id(provider
->provider_id());
86 request
.setExtraData(extra_data
.release());
92 EmbeddedWorkerContextClient
*
93 EmbeddedWorkerContextClient::ThreadSpecificInstance() {
94 return g_worker_client_tls
.Pointer()->Get();
97 EmbeddedWorkerContextClient::EmbeddedWorkerContextClient(
98 int embedded_worker_id
,
99 int64 service_worker_version_id
,
100 const GURL
& service_worker_scope
,
101 const GURL
& script_url
,
102 int worker_devtools_agent_route_id
)
103 : embedded_worker_id_(embedded_worker_id
),
104 service_worker_version_id_(service_worker_version_id
),
105 service_worker_scope_(service_worker_scope
),
106 script_url_(script_url
),
107 worker_devtools_agent_route_id_(worker_devtools_agent_route_id
),
108 sender_(ChildThreadImpl::current()->thread_safe_sender()),
109 main_thread_task_runner_(RenderThreadImpl::current()->GetTaskRunner()),
110 weak_factory_(this) {
111 TRACE_EVENT_ASYNC_BEGIN0("ServiceWorker",
112 "EmbeddedWorkerContextClient::StartingWorkerContext",
114 TRACE_EVENT_ASYNC_STEP_INTO0(
116 "EmbeddedWorkerContextClient::StartingWorkerContext",
121 EmbeddedWorkerContextClient::~EmbeddedWorkerContextClient() {
124 bool EmbeddedWorkerContextClient::OnMessageReceived(
125 const IPC::Message
& msg
) {
127 IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerContextClient
, msg
)
128 IPC_MESSAGE_HANDLER(EmbeddedWorkerContextMsg_MessageToWorker
,
130 IPC_MESSAGE_UNHANDLED(handled
= false)
131 IPC_END_MESSAGE_MAP()
135 void EmbeddedWorkerContextClient::Send(IPC::Message
* message
) {
136 sender_
->Send(message
);
139 blink::WebURL
EmbeddedWorkerContextClient::scope() const {
140 return service_worker_scope_
;
143 void EmbeddedWorkerContextClient::didPauseAfterDownload() {
144 Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_
));
147 void EmbeddedWorkerContextClient::getClients(
148 const blink::WebServiceWorkerClientQueryOptions
& options
,
149 blink::WebServiceWorkerClientsCallbacks
* callbacks
) {
150 DCHECK(script_context_
);
151 script_context_
->GetClients(options
, callbacks
);
154 void EmbeddedWorkerContextClient::openWindow(
155 const blink::WebURL
& url
,
156 blink::WebServiceWorkerClientCallbacks
* callbacks
) {
157 DCHECK(script_context_
);
158 script_context_
->OpenWindow(url
, callbacks
);
161 void EmbeddedWorkerContextClient::setCachedMetadata(const blink::WebURL
& url
,
164 DCHECK(script_context_
);
165 script_context_
->SetCachedMetadata(url
, data
, size
);
168 void EmbeddedWorkerContextClient::clearCachedMetadata(
169 const blink::WebURL
& url
) {
170 DCHECK(script_context_
);
171 script_context_
->ClearCachedMetadata(url
);
174 void EmbeddedWorkerContextClient::workerReadyForInspection() {
175 Send(new EmbeddedWorkerHostMsg_WorkerReadyForInspection(embedded_worker_id_
));
178 void EmbeddedWorkerContextClient::workerContextFailedToStart() {
179 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
180 DCHECK(!script_context_
);
182 Send(new EmbeddedWorkerHostMsg_WorkerScriptLoadFailed(embedded_worker_id_
));
184 RenderThreadImpl::current()->embedded_worker_dispatcher()->
185 WorkerContextDestroyed(embedded_worker_id_
);
188 void EmbeddedWorkerContextClient::workerContextStarted(
189 blink::WebServiceWorkerContextProxy
* proxy
) {
190 DCHECK(!worker_task_runner_
.get());
191 DCHECK_NE(0, WorkerTaskRunner::Instance()->CurrentWorkerId());
192 worker_task_runner_
= base::ThreadTaskRunnerHandle::Get();
193 // g_worker_client_tls.Pointer()->Get() could return NULL if this context
194 // gets deleted before workerContextStarted() is called.
195 DCHECK(g_worker_client_tls
.Pointer()->Get() == NULL
);
196 DCHECK(!script_context_
);
197 g_worker_client_tls
.Pointer()->Set(this);
198 script_context_
.reset(new ServiceWorkerScriptContext(this, proxy
));
200 SetRegistrationInServiceWorkerGlobalScope();
202 Send(new EmbeddedWorkerHostMsg_WorkerScriptLoaded(
204 WorkerTaskRunner::Instance()->CurrentWorkerId(),
205 provider_context_
->provider_id()));
207 TRACE_EVENT_ASYNC_STEP_INTO0(
209 "EmbeddedWorkerContextClient::StartingWorkerContext",
214 void EmbeddedWorkerContextClient::didEvaluateWorkerScript(bool success
) {
215 Send(new EmbeddedWorkerHostMsg_WorkerScriptEvaluated(
216 embedded_worker_id_
, success
));
218 // Schedule a task to send back WorkerStarted asynchronously,
219 // so that at the time we send it we can be sure that the
220 // worker run loop has been started.
221 worker_task_runner_
->PostTask(
222 FROM_HERE
, base::Bind(&EmbeddedWorkerContextClient::SendWorkerStarted
,
223 weak_factory_
.GetWeakPtr()));
226 void EmbeddedWorkerContextClient::willDestroyWorkerContext() {
227 // At this point OnWorkerRunLoopStopped is already called, so
228 // worker_task_runner_->RunsTasksOnCurrentThread() returns false
229 // (while we're still on the worker thread).
230 script_context_
.reset();
232 // This also lets the message filter stop dispatching messages to
234 g_worker_client_tls
.Pointer()->Set(NULL
);
237 void EmbeddedWorkerContextClient::workerContextDestroyed() {
238 DCHECK(g_worker_client_tls
.Pointer()->Get() == NULL
);
240 // Now we should be able to free the WebEmbeddedWorker container on the
242 main_thread_task_runner_
->PostTask(
244 base::Bind(&CallWorkerContextDestroyedOnMainThread
,
245 embedded_worker_id_
));
248 void EmbeddedWorkerContextClient::reportException(
249 const blink::WebString
& error_message
,
252 const blink::WebString
& source_url
) {
253 Send(new EmbeddedWorkerHostMsg_ReportException(
254 embedded_worker_id_
, error_message
, line_number
,
255 column_number
, GURL(source_url
)));
258 void EmbeddedWorkerContextClient::reportConsoleMessage(
261 const blink::WebString
& message
,
263 const blink::WebString
& source_url
) {
264 EmbeddedWorkerHostMsg_ReportConsoleMessage_Params params
;
265 params
.source_identifier
= source
;
266 params
.message_level
= level
;
267 params
.message
= message
;
268 params
.line_number
= line_number
;
269 params
.source_url
= GURL(source_url
);
271 Send(new EmbeddedWorkerHostMsg_ReportConsoleMessage(
272 embedded_worker_id_
, params
));
275 void EmbeddedWorkerContextClient::sendDevToolsMessage(
277 const blink::WebString
& message
,
278 const blink::WebString
& state_cookie
) {
279 DevToolsAgent::SendChunkedProtocolMessage(
280 sender_
.get(), worker_devtools_agent_route_id_
,
281 call_id
, message
.utf8(), state_cookie
.utf8());
284 void EmbeddedWorkerContextClient::didHandleActivateEvent(
286 blink::WebServiceWorkerEventResult result
) {
287 DCHECK(script_context_
);
288 script_context_
->DidHandleActivateEvent(request_id
, result
);
291 void EmbeddedWorkerContextClient::didHandleInstallEvent(
293 blink::WebServiceWorkerEventResult result
) {
294 DCHECK(script_context_
);
295 script_context_
->DidHandleInstallEvent(request_id
, result
);
298 void EmbeddedWorkerContextClient::didHandleFetchEvent(int request_id
) {
299 DCHECK(script_context_
);
300 script_context_
->DidHandleFetchEvent(
302 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK
,
303 ServiceWorkerResponse());
306 void EmbeddedWorkerContextClient::didHandleFetchEvent(
308 const blink::WebServiceWorkerResponse
& web_response
) {
309 DCHECK(script_context_
);
310 ServiceWorkerHeaderMap headers
;
311 GetServiceWorkerHeaderMapFromWebResponse(web_response
, &headers
);
312 ServiceWorkerResponse
response(web_response
.url(),
313 web_response
.status(),
314 web_response
.statusText().utf8(),
315 web_response
.responseType(),
317 web_response
.blobUUID().utf8(),
318 web_response
.blobSize(),
319 web_response
.streamURL());
320 script_context_
->DidHandleFetchEvent(
321 request_id
, SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE
, response
);
324 void EmbeddedWorkerContextClient::didHandleNotificationClickEvent(
326 blink::WebServiceWorkerEventResult result
) {
327 DCHECK(script_context_
);
328 script_context_
->DidHandleNotificationClickEvent(request_id
, result
);
331 void EmbeddedWorkerContextClient::didHandlePushEvent(
333 blink::WebServiceWorkerEventResult result
) {
334 DCHECK(script_context_
);
335 script_context_
->DidHandlePushEvent(request_id
, result
);
338 void EmbeddedWorkerContextClient::didHandleSyncEvent(int request_id
) {
339 DCHECK(script_context_
);
340 script_context_
->DidHandleSyncEvent(request_id
);
343 void EmbeddedWorkerContextClient::didHandleCrossOriginConnectEvent(
345 bool accept_connection
) {
346 DCHECK(script_context_
);
347 script_context_
->DidHandleCrossOriginConnectEvent(request_id
,
351 blink::WebServiceWorkerNetworkProvider
*
352 EmbeddedWorkerContextClient::createServiceWorkerNetworkProvider(
353 blink::WebDataSource
* data_source
) {
354 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
356 // Create a content::ServiceWorkerNetworkProvider for this data source so
357 // we can observe its requests.
358 scoped_ptr
<ServiceWorkerNetworkProvider
> provider(
359 new ServiceWorkerNetworkProvider(
360 MSG_ROUTING_NONE
, SERVICE_WORKER_PROVIDER_FOR_CONTROLLER
));
361 provider_context_
= provider
->context();
363 // Tell the network provider about which version to load.
364 provider
->SetServiceWorkerVersionId(service_worker_version_id_
);
366 // The provider is kept around for the lifetime of the DataSource
367 // and ownership is transferred to the DataSource.
368 DataSourceExtraData
* extra_data
= new DataSourceExtraData();
369 data_source
->setExtraData(extra_data
);
370 ServiceWorkerNetworkProvider::AttachToDocumentState(
371 extra_data
, provider
.Pass());
373 // Blink is responsible for deleting the returned object.
374 return new WebServiceWorkerNetworkProviderImpl();
377 blink::WebServiceWorkerProvider
*
378 EmbeddedWorkerContextClient::createServiceWorkerProvider() {
379 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
380 DCHECK(provider_context_
);
382 // Blink is responsible for deleting the returned object.
383 return new WebServiceWorkerProviderImpl(
384 thread_safe_sender(), provider_context_
.get());
387 void EmbeddedWorkerContextClient::postMessageToClient(
388 const blink::WebString
& uuid
,
389 const blink::WebString
& message
,
390 blink::WebMessagePortChannelArray
* channels
) {
391 DCHECK(script_context_
);
392 script_context_
->PostMessageToClient(
393 uuid
, message
, make_scoped_ptr(channels
));
396 void EmbeddedWorkerContextClient::postMessageToCrossOriginClient(
397 const blink::WebCrossOriginServiceWorkerClient
& client
,
398 const blink::WebString
& message
,
399 blink::WebMessagePortChannelArray
* channels
) {
400 DCHECK(script_context_
);
401 script_context_
->PostCrossOriginMessageToClient(client
, message
,
402 make_scoped_ptr(channels
));
405 void EmbeddedWorkerContextClient::focus(
406 const blink::WebString
& uuid
,
407 blink::WebServiceWorkerClientCallbacks
* callback
) {
408 DCHECK(script_context_
);
409 script_context_
->FocusClient(uuid
, callback
);
412 void EmbeddedWorkerContextClient::skipWaiting(
413 blink::WebServiceWorkerSkipWaitingCallbacks
* callbacks
) {
414 DCHECK(script_context_
);
415 script_context_
->SkipWaiting(callbacks
);
418 void EmbeddedWorkerContextClient::claim(
419 blink::WebServiceWorkerClientsClaimCallbacks
* callbacks
) {
420 DCHECK(script_context_
);
421 script_context_
->ClaimClients(callbacks
);
424 void EmbeddedWorkerContextClient::OnMessageToWorker(
426 int embedded_worker_id
,
427 const IPC::Message
& message
) {
428 if (!script_context_
)
430 DCHECK_EQ(embedded_worker_id_
, embedded_worker_id
);
431 script_context_
->OnMessageReceived(message
);
434 void EmbeddedWorkerContextClient::SendWorkerStarted() {
435 DCHECK(worker_task_runner_
->RunsTasksOnCurrentThread());
436 TRACE_EVENT_ASYNC_END0("ServiceWorker",
437 "EmbeddedWorkerContextClient::StartingWorkerContext",
439 Send(new EmbeddedWorkerHostMsg_WorkerStarted(embedded_worker_id_
));
442 void EmbeddedWorkerContextClient::SetRegistrationInServiceWorkerGlobalScope() {
443 DCHECK(worker_task_runner_
->RunsTasksOnCurrentThread());
444 DCHECK(provider_context_
);
445 DCHECK(script_context_
);
447 ServiceWorkerRegistrationObjectInfo info
;
448 ServiceWorkerVersionAttributes attrs
;
450 provider_context_
->GetRegistrationInfoAndVersionAttributes(&info
, &attrs
);
452 return; // Cannot be associated with a registration in some tests.
454 ServiceWorkerDispatcher
* dispatcher
=
455 ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance(
456 thread_safe_sender());
458 // Register a registration and its version attributes with the dispatcher
459 // living on the worker thread.
460 scoped_ptr
<WebServiceWorkerRegistrationImpl
> registration(
461 dispatcher
->CreateServiceWorkerRegistration(info
, false));
462 registration
->SetInstalling(
463 dispatcher
->GetServiceWorker(attrs
.installing
, false));
464 registration
->SetWaiting(
465 dispatcher
->GetServiceWorker(attrs
.waiting
, false));
466 registration
->SetActive(
467 dispatcher
->GetServiceWorker(attrs
.active
, false));
469 script_context_
->SetRegistrationInServiceWorkerGlobalScope(
470 registration
.Pass());
473 } // namespace content