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/pickle.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/threading/thread_local.h"
16 #include "base/trace_event/trace_event.h"
17 #include "content/child/request_extra_data.h"
18 #include "content/child/service_worker/service_worker_dispatcher.h"
19 #include "content/child/service_worker/service_worker_network_provider.h"
20 #include "content/child/service_worker/service_worker_provider_context.h"
21 #include "content/child/service_worker/service_worker_registration_handle_reference.h"
22 #include "content/child/service_worker/web_service_worker_impl.h"
23 #include "content/child/service_worker/web_service_worker_provider_impl.h"
24 #include "content/child/service_worker/web_service_worker_registration_impl.h"
25 #include "content/child/thread_safe_sender.h"
26 #include "content/child/worker_task_runner.h"
27 #include "content/common/devtools_messages.h"
28 #include "content/common/service_worker/embedded_worker_messages.h"
29 #include "content/common/service_worker/service_worker_types.h"
30 #include "content/public/renderer/document_state.h"
31 #include "content/renderer/devtools/devtools_agent.h"
32 #include "content/renderer/render_thread_impl.h"
33 #include "content/renderer/service_worker/embedded_worker_dispatcher.h"
34 #include "content/renderer/service_worker/service_worker_script_context.h"
35 #include "content/renderer/service_worker/service_worker_type_util.h"
36 #include "ipc/ipc_message_macros.h"
37 #include "third_party/WebKit/public/platform/WebServiceWorkerResponse.h"
38 #include "third_party/WebKit/public/platform/WebString.h"
39 #include "third_party/WebKit/public/web/WebDataSource.h"
40 #include "third_party/WebKit/public/web/WebServiceWorkerNetworkProvider.h"
46 // For now client must be a per-thread instance.
47 // TODO(kinuko): This needs to be refactored when we start using thread pool
48 // or having multiple clients per one thread.
49 base::LazyInstance
<base::ThreadLocalPointer
<EmbeddedWorkerContextClient
> >::
50 Leaky g_worker_client_tls
= LAZY_INSTANCE_INITIALIZER
;
52 void CallWorkerContextDestroyedOnMainThread(int embedded_worker_id
) {
53 if (!RenderThreadImpl::current() ||
54 !RenderThreadImpl::current()->embedded_worker_dispatcher())
56 RenderThreadImpl::current()->embedded_worker_dispatcher()->
57 WorkerContextDestroyed(embedded_worker_id
);
60 // We store an instance of this class in the "extra data" of the WebDataSource
61 // and attach a ServiceWorkerNetworkProvider to it as base::UserData.
62 // (see createServiceWorkerNetworkProvider).
63 class DataSourceExtraData
64 : public blink::WebDataSource::ExtraData
,
65 public base::SupportsUserData
{
67 DataSourceExtraData() {}
68 virtual ~DataSourceExtraData() {}
71 // Called on the main thread only and blink owns it.
72 class WebServiceWorkerNetworkProviderImpl
73 : public blink::WebServiceWorkerNetworkProvider
{
75 // Blink calls this method for each request starting with the main script,
76 // we tag them with the provider id.
77 virtual void willSendRequest(
78 blink::WebDataSource
* data_source
,
79 blink::WebURLRequest
& request
) {
80 ServiceWorkerNetworkProvider
* provider
=
81 ServiceWorkerNetworkProvider::FromDocumentState(
82 static_cast<DataSourceExtraData
*>(data_source
->extraData()));
83 scoped_ptr
<RequestExtraData
> extra_data(new RequestExtraData
);
84 extra_data
->set_service_worker_provider_id(provider
->provider_id());
85 request
.setExtraData(extra_data
.release());
91 EmbeddedWorkerContextClient
*
92 EmbeddedWorkerContextClient::ThreadSpecificInstance() {
93 return g_worker_client_tls
.Pointer()->Get();
96 EmbeddedWorkerContextClient::EmbeddedWorkerContextClient(
97 int embedded_worker_id
,
98 int64 service_worker_version_id
,
99 const GURL
& service_worker_scope
,
100 const GURL
& script_url
,
101 int worker_devtools_agent_route_id
)
102 : embedded_worker_id_(embedded_worker_id
),
103 service_worker_version_id_(service_worker_version_id
),
104 service_worker_scope_(service_worker_scope
),
105 script_url_(script_url
),
106 worker_devtools_agent_route_id_(worker_devtools_agent_route_id
),
107 sender_(ChildThreadImpl::current()->thread_safe_sender()),
108 main_thread_task_runner_(RenderThreadImpl::current()->GetTaskRunner()),
109 weak_factory_(this) {
110 TRACE_EVENT_ASYNC_BEGIN0("ServiceWorker",
111 "EmbeddedWorkerContextClient::StartingWorkerContext",
113 TRACE_EVENT_ASYNC_STEP_INTO0(
115 "EmbeddedWorkerContextClient::StartingWorkerContext",
120 EmbeddedWorkerContextClient::~EmbeddedWorkerContextClient() {
123 bool EmbeddedWorkerContextClient::OnMessageReceived(
124 const IPC::Message
& msg
) {
126 IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerContextClient
, msg
)
127 IPC_MESSAGE_HANDLER(EmbeddedWorkerContextMsg_MessageToWorker
,
129 IPC_MESSAGE_UNHANDLED(handled
= false)
130 IPC_END_MESSAGE_MAP()
134 void EmbeddedWorkerContextClient::Send(IPC::Message
* message
) {
135 sender_
->Send(message
);
138 blink::WebURL
EmbeddedWorkerContextClient::scope() const {
139 return service_worker_scope_
;
142 void EmbeddedWorkerContextClient::didPauseAfterDownload() {
143 Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_
));
146 void EmbeddedWorkerContextClient::getClients(
147 const blink::WebServiceWorkerClientQueryOptions
& options
,
148 blink::WebServiceWorkerClientsCallbacks
* callbacks
) {
149 DCHECK(script_context_
);
150 script_context_
->GetClients(options
, callbacks
);
153 void EmbeddedWorkerContextClient::openWindow(
154 const blink::WebURL
& url
,
155 blink::WebServiceWorkerClientCallbacks
* callbacks
) {
156 DCHECK(script_context_
);
157 script_context_
->OpenWindow(url
, callbacks
);
160 void EmbeddedWorkerContextClient::setCachedMetadata(const blink::WebURL
& url
,
163 DCHECK(script_context_
);
164 script_context_
->SetCachedMetadata(url
, data
, size
);
167 void EmbeddedWorkerContextClient::clearCachedMetadata(
168 const blink::WebURL
& url
) {
169 DCHECK(script_context_
);
170 script_context_
->ClearCachedMetadata(url
);
173 void EmbeddedWorkerContextClient::workerReadyForInspection() {
174 Send(new EmbeddedWorkerHostMsg_WorkerReadyForInspection(embedded_worker_id_
));
177 void EmbeddedWorkerContextClient::workerContextFailedToStart() {
178 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
179 DCHECK(!script_context_
);
181 Send(new EmbeddedWorkerHostMsg_WorkerScriptLoadFailed(embedded_worker_id_
));
183 RenderThreadImpl::current()->embedded_worker_dispatcher()->
184 WorkerContextDestroyed(embedded_worker_id_
);
187 void EmbeddedWorkerContextClient::workerContextStarted(
188 blink::WebServiceWorkerContextProxy
* proxy
) {
189 DCHECK(!worker_task_runner_
.get());
190 DCHECK_NE(0, WorkerTaskRunner::Instance()->CurrentWorkerId());
191 worker_task_runner_
= base::ThreadTaskRunnerHandle::Get();
192 // g_worker_client_tls.Pointer()->Get() could return NULL if this context
193 // gets deleted before workerContextStarted() is called.
194 DCHECK(g_worker_client_tls
.Pointer()->Get() == NULL
);
195 DCHECK(!script_context_
);
196 g_worker_client_tls
.Pointer()->Set(this);
197 script_context_
.reset(new ServiceWorkerScriptContext(this, proxy
));
199 SetRegistrationInServiceWorkerGlobalScope();
201 Send(new EmbeddedWorkerHostMsg_WorkerScriptLoaded(
203 WorkerTaskRunner::Instance()->CurrentWorkerId(),
204 provider_context_
->provider_id()));
206 TRACE_EVENT_ASYNC_STEP_INTO0(
208 "EmbeddedWorkerContextClient::StartingWorkerContext",
213 void EmbeddedWorkerContextClient::didEvaluateWorkerScript(bool success
) {
214 Send(new EmbeddedWorkerHostMsg_WorkerScriptEvaluated(
215 embedded_worker_id_
, success
));
217 // Schedule a task to send back WorkerStarted asynchronously,
218 // so that at the time we send it we can be sure that the
219 // worker run loop has been started.
220 worker_task_runner_
->PostTask(
221 FROM_HERE
, base::Bind(&EmbeddedWorkerContextClient::SendWorkerStarted
,
222 weak_factory_
.GetWeakPtr()));
225 void EmbeddedWorkerContextClient::willDestroyWorkerContext() {
226 // At this point OnWorkerRunLoopStopped is already called, so
227 // worker_task_runner_->RunsTasksOnCurrentThread() returns false
228 // (while we're still on the worker thread).
229 script_context_
.reset();
231 // This also lets the message filter stop dispatching messages to
233 g_worker_client_tls
.Pointer()->Set(NULL
);
236 void EmbeddedWorkerContextClient::workerContextDestroyed() {
237 DCHECK(g_worker_client_tls
.Pointer()->Get() == NULL
);
239 // Now we should be able to free the WebEmbeddedWorker container on the
241 main_thread_task_runner_
->PostTask(
243 base::Bind(&CallWorkerContextDestroyedOnMainThread
,
244 embedded_worker_id_
));
247 void EmbeddedWorkerContextClient::reportException(
248 const blink::WebString
& error_message
,
251 const blink::WebString
& source_url
) {
252 Send(new EmbeddedWorkerHostMsg_ReportException(
253 embedded_worker_id_
, error_message
, line_number
,
254 column_number
, GURL(source_url
)));
257 void EmbeddedWorkerContextClient::reportConsoleMessage(
260 const blink::WebString
& message
,
262 const blink::WebString
& source_url
) {
263 EmbeddedWorkerHostMsg_ReportConsoleMessage_Params params
;
264 params
.source_identifier
= source
;
265 params
.message_level
= level
;
266 params
.message
= message
;
267 params
.line_number
= line_number
;
268 params
.source_url
= GURL(source_url
);
270 Send(new EmbeddedWorkerHostMsg_ReportConsoleMessage(
271 embedded_worker_id_
, params
));
274 void EmbeddedWorkerContextClient::sendDevToolsMessage(
276 const blink::WebString
& message
,
277 const blink::WebString
& state_cookie
) {
278 DevToolsAgent::SendChunkedProtocolMessage(
279 sender_
.get(), worker_devtools_agent_route_id_
,
280 call_id
, message
.utf8(), state_cookie
.utf8());
283 void EmbeddedWorkerContextClient::didHandleActivateEvent(
285 blink::WebServiceWorkerEventResult result
) {
286 DCHECK(script_context_
);
287 script_context_
->DidHandleActivateEvent(request_id
, result
);
290 void EmbeddedWorkerContextClient::didHandleInstallEvent(
292 blink::WebServiceWorkerEventResult result
) {
293 DCHECK(script_context_
);
294 script_context_
->DidHandleInstallEvent(request_id
, result
);
297 void EmbeddedWorkerContextClient::didHandleFetchEvent(int request_id
) {
298 DCHECK(script_context_
);
299 script_context_
->DidHandleFetchEvent(
301 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK
,
302 ServiceWorkerResponse());
305 void EmbeddedWorkerContextClient::didHandleFetchEvent(
307 const blink::WebServiceWorkerResponse
& web_response
) {
308 DCHECK(script_context_
);
309 ServiceWorkerHeaderMap headers
;
310 GetServiceWorkerHeaderMapFromWebResponse(web_response
, &headers
);
311 ServiceWorkerResponse
response(web_response
.url(),
312 web_response
.status(),
313 web_response
.statusText().utf8(),
314 web_response
.responseType(),
316 web_response
.blobUUID().utf8(),
317 web_response
.blobSize(),
318 web_response
.streamURL());
319 script_context_
->DidHandleFetchEvent(
320 request_id
, SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE
, response
);
323 void EmbeddedWorkerContextClient::didHandleNotificationClickEvent(
325 blink::WebServiceWorkerEventResult result
) {
326 DCHECK(script_context_
);
327 script_context_
->DidHandleNotificationClickEvent(request_id
, result
);
330 void EmbeddedWorkerContextClient::didHandlePushEvent(
332 blink::WebServiceWorkerEventResult result
) {
333 DCHECK(script_context_
);
334 script_context_
->DidHandlePushEvent(request_id
, result
);
337 void EmbeddedWorkerContextClient::didHandleSyncEvent(int request_id
) {
338 DCHECK(script_context_
);
339 script_context_
->DidHandleSyncEvent(request_id
);
342 void EmbeddedWorkerContextClient::didHandleCrossOriginConnectEvent(
344 bool accept_connection
) {
345 DCHECK(script_context_
);
346 script_context_
->DidHandleCrossOriginConnectEvent(request_id
,
350 blink::WebServiceWorkerNetworkProvider
*
351 EmbeddedWorkerContextClient::createServiceWorkerNetworkProvider(
352 blink::WebDataSource
* data_source
) {
353 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
355 // Create a content::ServiceWorkerNetworkProvider for this data source so
356 // we can observe its requests.
357 scoped_ptr
<ServiceWorkerNetworkProvider
> provider(
358 new ServiceWorkerNetworkProvider(
359 MSG_ROUTING_NONE
, SERVICE_WORKER_PROVIDER_FOR_CONTROLLER
));
360 provider_context_
= provider
->context();
362 // Tell the network provider about which version to load.
363 provider
->SetServiceWorkerVersionId(service_worker_version_id_
);
365 // The provider is kept around for the lifetime of the DataSource
366 // and ownership is transferred to the DataSource.
367 DataSourceExtraData
* extra_data
= new DataSourceExtraData();
368 data_source
->setExtraData(extra_data
);
369 ServiceWorkerNetworkProvider::AttachToDocumentState(
370 extra_data
, provider
.Pass());
372 // Blink is responsible for deleting the returned object.
373 return new WebServiceWorkerNetworkProviderImpl();
376 blink::WebServiceWorkerProvider
*
377 EmbeddedWorkerContextClient::createServiceWorkerProvider() {
378 DCHECK(main_thread_task_runner_
->RunsTasksOnCurrentThread());
379 DCHECK(provider_context_
);
381 // Blink is responsible for deleting the returned object.
382 return new WebServiceWorkerProviderImpl(
383 thread_safe_sender(), provider_context_
.get());
386 void EmbeddedWorkerContextClient::postMessageToClient(
387 const blink::WebString
& uuid
,
388 const blink::WebString
& message
,
389 blink::WebMessagePortChannelArray
* channels
) {
390 DCHECK(script_context_
);
391 script_context_
->PostMessageToClient(
392 uuid
, message
, make_scoped_ptr(channels
));
395 void EmbeddedWorkerContextClient::postMessageToCrossOriginClient(
396 const blink::WebCrossOriginServiceWorkerClient
& client
,
397 const blink::WebString
& message
,
398 blink::WebMessagePortChannelArray
* channels
) {
399 DCHECK(script_context_
);
400 script_context_
->PostCrossOriginMessageToClient(client
, message
,
401 make_scoped_ptr(channels
));
404 void EmbeddedWorkerContextClient::focus(
405 const blink::WebString
& uuid
,
406 blink::WebServiceWorkerClientCallbacks
* callback
) {
407 DCHECK(script_context_
);
408 script_context_
->FocusClient(uuid
, callback
);
411 void EmbeddedWorkerContextClient::skipWaiting(
412 blink::WebServiceWorkerSkipWaitingCallbacks
* callbacks
) {
413 DCHECK(script_context_
);
414 script_context_
->SkipWaiting(callbacks
);
417 void EmbeddedWorkerContextClient::claim(
418 blink::WebServiceWorkerClientsClaimCallbacks
* callbacks
) {
419 DCHECK(script_context_
);
420 script_context_
->ClaimClients(callbacks
);
423 void EmbeddedWorkerContextClient::stashMessagePort(
424 blink::WebMessagePortChannel
* channel
,
425 const blink::WebString
& name
) {
426 DCHECK(script_context_
);
427 script_context_
->StashMessagePort(channel
, name
);
430 void EmbeddedWorkerContextClient::OnMessageToWorker(
432 int embedded_worker_id
,
433 const IPC::Message
& message
) {
434 if (!script_context_
)
436 DCHECK_EQ(embedded_worker_id_
, embedded_worker_id
);
437 script_context_
->OnMessageReceived(message
);
440 void EmbeddedWorkerContextClient::SendWorkerStarted() {
441 DCHECK(worker_task_runner_
->RunsTasksOnCurrentThread());
442 TRACE_EVENT_ASYNC_END0("ServiceWorker",
443 "EmbeddedWorkerContextClient::StartingWorkerContext",
445 Send(new EmbeddedWorkerHostMsg_WorkerStarted(embedded_worker_id_
));
448 void EmbeddedWorkerContextClient::SetRegistrationInServiceWorkerGlobalScope() {
449 DCHECK(worker_task_runner_
->RunsTasksOnCurrentThread());
450 DCHECK(provider_context_
);
451 DCHECK(script_context_
);
453 ServiceWorkerRegistrationObjectInfo info
;
454 ServiceWorkerVersionAttributes attrs
;
456 provider_context_
->GetRegistrationInfoAndVersionAttributes(&info
, &attrs
);
458 return; // Cannot be associated with a registration in some tests.
460 ServiceWorkerDispatcher
* dispatcher
=
461 ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance(
462 thread_safe_sender());
464 // Register a registration and its version attributes with the dispatcher
465 // living on the worker thread.
466 scoped_ptr
<WebServiceWorkerRegistrationImpl
> registration(
467 dispatcher
->CreateServiceWorkerRegistration(info
, false));
468 registration
->SetInstalling(
469 dispatcher
->GetServiceWorker(attrs
.installing
, false));
470 registration
->SetWaiting(
471 dispatcher
->GetServiceWorker(attrs
.waiting
, false));
472 registration
->SetActive(
473 dispatcher
->GetServiceWorker(attrs
.active
, false));
475 script_context_
->SetRegistrationInServiceWorkerGlobalScope(
476 registration
.Pass());
479 } // namespace content