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 blink::WebServiceWorkerCacheStorage
*
144 EmbeddedWorkerContextClient::cacheStorage() {
145 return script_context_
->cache_storage();
148 void EmbeddedWorkerContextClient::didPauseAfterDownload() {
149 Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_
));
152 void EmbeddedWorkerContextClient::getClients(
153 blink::WebServiceWorkerClientsCallbacks
* callbacks
) {
154 DCHECK(script_context_
);
155 script_context_
->GetClientDocuments(callbacks
);
158 void EmbeddedWorkerContextClient::openWindow(
159 const blink::WebURL
& url
,
160 blink::WebServiceWorkerClientCallbacks
* callbacks
) {
161 DCHECK(script_context_
);
162 script_context_
->OpenWindow(url
, callbacks
);
165 void EmbeddedWorkerContextClient::setCachedMetadata(const blink::WebURL
& url
,
168 DCHECK(script_context_
);
169 script_context_
->SetCachedMetadata(url
, data
, size
);
172 void EmbeddedWorkerContextClient::clearCachedMetadata(
173 const blink::WebURL
& url
) {
174 DCHECK(script_context_
);
175 script_context_
->ClearCachedMetadata(url
);
178 void EmbeddedWorkerContextClient::workerReadyForInspection() {
179 Send(new EmbeddedWorkerHostMsg_WorkerReadyForInspection(embedded_worker_id_
));
182 void EmbeddedWorkerContextClient::workerContextFailedToStart() {
183 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
184 DCHECK(!script_context_
);
186 Send(new EmbeddedWorkerHostMsg_WorkerScriptLoadFailed(embedded_worker_id_
));
188 RenderThreadImpl::current()->embedded_worker_dispatcher()->
189 WorkerContextDestroyed(embedded_worker_id_
);
192 void EmbeddedWorkerContextClient::workerContextStarted(
193 blink::WebServiceWorkerContextProxy
* proxy
) {
194 DCHECK(!worker_task_runner_
.get());
195 DCHECK_NE(0, WorkerTaskRunner::Instance()->CurrentWorkerId());
196 worker_task_runner_
= base::ThreadTaskRunnerHandle::Get();
197 // g_worker_client_tls.Pointer()->Get() could return NULL if this context
198 // gets deleted before workerContextStarted() is called.
199 DCHECK(g_worker_client_tls
.Pointer()->Get() == NULL
);
200 DCHECK(!script_context_
);
201 g_worker_client_tls
.Pointer()->Set(this);
202 script_context_
.reset(new ServiceWorkerScriptContext(this, proxy
));
204 SetRegistrationInServiceWorkerGlobalScope();
206 Send(new EmbeddedWorkerHostMsg_WorkerScriptLoaded(
208 WorkerTaskRunner::Instance()->CurrentWorkerId(),
209 provider_context_
->provider_id()));
211 // Schedule a task to send back WorkerStarted asynchronously,
212 // so that at the time we send it we can be sure that the worker
213 // script has been evaluated and worker run loop has been started.
214 worker_task_runner_
->PostTask(
216 base::Bind(&EmbeddedWorkerContextClient::SendWorkerStarted
,
217 weak_factory_
.GetWeakPtr()));
218 TRACE_EVENT_ASYNC_STEP_INTO0(
220 "EmbeddedWorkerContextClient::StartingWorkerContext",
225 void EmbeddedWorkerContextClient::didEvaluateWorkerScript(bool success
) {
226 Send(new EmbeddedWorkerHostMsg_WorkerScriptEvaluated(
227 embedded_worker_id_
, success
));
230 void EmbeddedWorkerContextClient::willDestroyWorkerContext() {
231 // At this point OnWorkerRunLoopStopped is already called, so
232 // worker_task_runner_->RunsTasksOnCurrentThread() returns false
233 // (while we're still on the worker thread).
234 script_context_
.reset();
236 // This also lets the message filter stop dispatching messages to
238 g_worker_client_tls
.Pointer()->Set(NULL
);
241 void EmbeddedWorkerContextClient::workerContextDestroyed() {
242 DCHECK(g_worker_client_tls
.Pointer()->Get() == NULL
);
244 // Now we should be able to free the WebEmbeddedWorker container on the
246 main_thread_task_runner_
->PostTask(
248 base::Bind(&CallWorkerContextDestroyedOnMainThread
,
249 embedded_worker_id_
));
252 void EmbeddedWorkerContextClient::reportException(
253 const blink::WebString
& error_message
,
256 const blink::WebString
& source_url
) {
257 Send(new EmbeddedWorkerHostMsg_ReportException(
258 embedded_worker_id_
, error_message
, line_number
,
259 column_number
, GURL(source_url
)));
262 void EmbeddedWorkerContextClient::reportConsoleMessage(
265 const blink::WebString
& message
,
267 const blink::WebString
& source_url
) {
268 EmbeddedWorkerHostMsg_ReportConsoleMessage_Params params
;
269 params
.source_identifier
= source
;
270 params
.message_level
= level
;
271 params
.message
= message
;
272 params
.line_number
= line_number
;
273 params
.source_url
= GURL(source_url
);
275 Send(new EmbeddedWorkerHostMsg_ReportConsoleMessage(
276 embedded_worker_id_
, params
));
279 void EmbeddedWorkerContextClient::sendDevToolsMessage(
281 const blink::WebString
& message
,
282 const blink::WebString
& state_cookie
) {
283 DevToolsAgent::SendChunkedProtocolMessage(
284 sender_
.get(), worker_devtools_agent_route_id_
,
285 call_id
, message
.utf8(), state_cookie
.utf8());
288 void EmbeddedWorkerContextClient::didHandleActivateEvent(
290 blink::WebServiceWorkerEventResult result
) {
291 DCHECK(script_context_
);
292 script_context_
->DidHandleActivateEvent(request_id
, result
);
295 void EmbeddedWorkerContextClient::didHandleInstallEvent(
297 blink::WebServiceWorkerEventResult result
) {
298 DCHECK(script_context_
);
299 script_context_
->DidHandleInstallEvent(request_id
, result
);
302 void EmbeddedWorkerContextClient::didHandleFetchEvent(int request_id
) {
303 DCHECK(script_context_
);
304 script_context_
->DidHandleFetchEvent(
306 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK
,
307 ServiceWorkerResponse());
310 void EmbeddedWorkerContextClient::didHandleFetchEvent(
312 const blink::WebServiceWorkerResponse
& web_response
) {
313 DCHECK(script_context_
);
314 ServiceWorkerHeaderMap headers
;
315 GetServiceWorkerHeaderMapFromWebResponse(web_response
, &headers
);
316 ServiceWorkerResponse
response(web_response
.url(),
317 web_response
.status(),
318 web_response
.statusText().utf8(),
319 web_response
.responseType(),
321 web_response
.blobUUID().utf8(),
322 web_response
.blobSize(),
323 web_response
.streamURL());
324 script_context_
->DidHandleFetchEvent(
325 request_id
, SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE
, response
);
328 void EmbeddedWorkerContextClient::didHandleNotificationClickEvent(
330 blink::WebServiceWorkerEventResult result
) {
331 DCHECK(script_context_
);
332 script_context_
->DidHandleNotificationClickEvent(request_id
, result
);
335 void EmbeddedWorkerContextClient::didHandlePushEvent(
337 blink::WebServiceWorkerEventResult result
) {
338 DCHECK(script_context_
);
339 script_context_
->DidHandlePushEvent(request_id
, result
);
342 void EmbeddedWorkerContextClient::didHandleSyncEvent(int request_id
) {
343 DCHECK(script_context_
);
344 script_context_
->DidHandleSyncEvent(request_id
);
347 void EmbeddedWorkerContextClient::didHandleCrossOriginConnectEvent(
349 bool accept_connection
) {
350 DCHECK(script_context_
);
351 script_context_
->DidHandleCrossOriginConnectEvent(request_id
,
355 blink::WebServiceWorkerNetworkProvider
*
356 EmbeddedWorkerContextClient::createServiceWorkerNetworkProvider(
357 blink::WebDataSource
* data_source
) {
358 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
360 // Create a content::ServiceWorkerNetworkProvider for this data source so
361 // we can observe its requests.
362 scoped_ptr
<ServiceWorkerNetworkProvider
> provider(
363 new ServiceWorkerNetworkProvider(
364 MSG_ROUTING_NONE
, SERVICE_WORKER_PROVIDER_FOR_CONTROLLER
));
365 provider_context_
= provider
->context();
367 // Tell the network provider about which version to load.
368 provider
->SetServiceWorkerVersionId(service_worker_version_id_
);
370 // The provider is kept around for the lifetime of the DataSource
371 // and ownership is transferred to the DataSource.
372 DataSourceExtraData
* extra_data
= new DataSourceExtraData();
373 data_source
->setExtraData(extra_data
);
374 ServiceWorkerNetworkProvider::AttachToDocumentState(
375 extra_data
, provider
.Pass());
377 // Blink is responsible for deleting the returned object.
378 return new WebServiceWorkerNetworkProviderImpl();
381 blink::WebServiceWorkerProvider
*
382 EmbeddedWorkerContextClient::createServiceWorkerProvider() {
383 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
384 DCHECK(provider_context_
);
386 // Blink is responsible for deleting the returned object.
387 return new WebServiceWorkerProviderImpl(
388 thread_safe_sender(), provider_context_
.get());
391 void EmbeddedWorkerContextClient::postMessageToClient(
393 const blink::WebString
& message
,
394 blink::WebMessagePortChannelArray
* channels
) {
395 DCHECK(script_context_
);
396 script_context_
->PostMessageToDocument(client_id
, message
,
397 make_scoped_ptr(channels
));
400 void EmbeddedWorkerContextClient::postMessageToCrossOriginClient(
401 const blink::WebCrossOriginServiceWorkerClient
& client
,
402 const blink::WebString
& message
,
403 blink::WebMessagePortChannelArray
* channels
) {
404 DCHECK(script_context_
);
405 script_context_
->PostCrossOriginMessageToClient(client
, message
,
406 make_scoped_ptr(channels
));
409 void EmbeddedWorkerContextClient::focus(
410 int client_id
, blink::WebServiceWorkerClientCallbacks
* callback
) {
411 DCHECK(script_context_
);
412 script_context_
->FocusClient(client_id
, callback
);
415 void EmbeddedWorkerContextClient::skipWaiting(
416 blink::WebServiceWorkerSkipWaitingCallbacks
* callbacks
) {
417 DCHECK(script_context_
);
418 script_context_
->SkipWaiting(callbacks
);
421 void EmbeddedWorkerContextClient::claim(
422 blink::WebServiceWorkerClientsClaimCallbacks
* callbacks
) {
423 DCHECK(script_context_
);
424 script_context_
->ClaimClients(callbacks
);
427 void EmbeddedWorkerContextClient::OnMessageToWorker(
429 int embedded_worker_id
,
430 const IPC::Message
& message
) {
431 if (!script_context_
)
433 DCHECK_EQ(embedded_worker_id_
, embedded_worker_id
);
434 script_context_
->OnMessageReceived(message
);
437 void EmbeddedWorkerContextClient::SendWorkerStarted() {
438 DCHECK(worker_task_runner_
->RunsTasksOnCurrentThread());
439 TRACE_EVENT_ASYNC_END0("ServiceWorker",
440 "EmbeddedWorkerContextClient::StartingWorkerContext",
442 Send(new EmbeddedWorkerHostMsg_WorkerStarted(embedded_worker_id_
));
445 void EmbeddedWorkerContextClient::SetRegistrationInServiceWorkerGlobalScope() {
446 DCHECK(worker_task_runner_
->RunsTasksOnCurrentThread());
447 DCHECK(provider_context_
);
448 DCHECK(script_context_
);
450 ServiceWorkerRegistrationObjectInfo info
;
451 ServiceWorkerVersionAttributes attrs
;
453 provider_context_
->GetRegistrationInfoAndVersionAttributes(&info
, &attrs
);
455 return; // Cannot be associated with a registration in some tests.
457 ServiceWorkerDispatcher
* dispatcher
=
458 ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance(
459 thread_safe_sender());
461 // Register a registration and its version attributes with the dispatcher
462 // living on the worker thread.
463 scoped_ptr
<WebServiceWorkerRegistrationImpl
> registration(
464 dispatcher
->CreateServiceWorkerRegistration(info
, false));
465 registration
->SetInstalling(
466 dispatcher
->GetServiceWorker(attrs
.installing
, false));
467 registration
->SetWaiting(
468 dispatcher
->GetServiceWorker(attrs
.waiting
, false));
469 registration
->SetActive(
470 dispatcher
->GetServiceWorker(attrs
.active
, false));
472 script_context_
->SetRegistrationInServiceWorkerGlobalScope(
473 registration
.Pass());
476 } // namespace content