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/child/service_worker/service_worker_dispatcher.h"
7 #include "base/lazy_instance.h"
8 #include "base/stl_util.h"
9 #include "base/threading/thread_local.h"
10 #include "content/child/child_thread.h"
11 #include "content/child/service_worker/service_worker_handle_reference.h"
12 #include "content/child/service_worker/service_worker_provider_context.h"
13 #include "content/child/service_worker/service_worker_registration_handle_reference.h"
14 #include "content/child/service_worker/web_service_worker_impl.h"
15 #include "content/child/service_worker/web_service_worker_registration_impl.h"
16 #include "content/child/thread_safe_sender.h"
17 #include "content/child/webmessageportchannel_impl.h"
18 #include "content/common/service_worker/service_worker_messages.h"
19 #include "content/public/common/url_utils.h"
20 #include "third_party/WebKit/public/platform/WebServiceWorkerProviderClient.h"
21 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
23 using blink::WebServiceWorkerError
;
24 using blink::WebServiceWorkerProvider
;
25 using base::ThreadLocalPointer
;
31 base::LazyInstance
<ThreadLocalPointer
<ServiceWorkerDispatcher
> >::Leaky
32 g_dispatcher_tls
= LAZY_INSTANCE_INITIALIZER
;
34 ServiceWorkerDispatcher
* const kHasBeenDeleted
=
35 reinterpret_cast<ServiceWorkerDispatcher
*>(0x1);
37 int CurrentWorkerId() {
38 return WorkerTaskRunner::Instance()->CurrentWorkerId();
43 ServiceWorkerDispatcher::ServiceWorkerDispatcher(
44 ThreadSafeSender
* thread_safe_sender
)
45 : thread_safe_sender_(thread_safe_sender
) {
46 g_dispatcher_tls
.Pointer()->Set(this);
49 ServiceWorkerDispatcher::~ServiceWorkerDispatcher() {
50 g_dispatcher_tls
.Pointer()->Set(kHasBeenDeleted
);
53 void ServiceWorkerDispatcher::OnMessageReceived(const IPC::Message
& msg
) {
55 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcher
, msg
)
56 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistered
, OnRegistered
)
57 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistered
,
59 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistrationError
,
61 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerStateChanged
,
62 OnServiceWorkerStateChanged
)
63 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetVersionAttributes
,
64 OnSetVersionAttributes
)
65 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetControllerServiceWorker
,
66 OnSetControllerServiceWorker
)
67 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToDocument
,
69 IPC_MESSAGE_UNHANDLED(handled
= false)
71 DCHECK(handled
) << "Unhandled message:" << msg
.type();
74 bool ServiceWorkerDispatcher::Send(IPC::Message
* msg
) {
75 return thread_safe_sender_
->Send(msg
);
78 void ServiceWorkerDispatcher::RegisterServiceWorker(
81 const GURL
& script_url
,
82 WebServiceWorkerRegistrationCallbacks
* callbacks
) {
85 if (pattern
.possibly_invalid_spec().size() > GetMaxURLChars() ||
86 script_url
.possibly_invalid_spec().size() > GetMaxURLChars()) {
87 scoped_ptr
<WebServiceWorkerRegistrationCallbacks
>
88 owned_callbacks(callbacks
);
89 scoped_ptr
<WebServiceWorkerError
> error(new WebServiceWorkerError(
90 WebServiceWorkerError::ErrorTypeSecurity
, "URL too long"));
91 callbacks
->onError(error
.release());
95 int request_id
= pending_callbacks_
.Add(callbacks
);
96 thread_safe_sender_
->Send(new ServiceWorkerHostMsg_RegisterServiceWorker(
97 CurrentWorkerId(), request_id
, provider_id
, pattern
, script_url
));
100 void ServiceWorkerDispatcher::UnregisterServiceWorker(
103 WebServiceWorkerRegistrationCallbacks
* callbacks
) {
106 if (pattern
.possibly_invalid_spec().size() > GetMaxURLChars()) {
107 scoped_ptr
<WebServiceWorkerRegistrationCallbacks
>
108 owned_callbacks(callbacks
);
109 scoped_ptr
<WebServiceWorkerError
> error(new WebServiceWorkerError(
110 WebServiceWorkerError::ErrorTypeSecurity
, "URL too long"));
111 callbacks
->onError(error
.release());
115 int request_id
= pending_callbacks_
.Add(callbacks
);
116 thread_safe_sender_
->Send(new ServiceWorkerHostMsg_UnregisterServiceWorker(
117 CurrentWorkerId(), request_id
, provider_id
, pattern
));
120 void ServiceWorkerDispatcher::AddProviderContext(
121 ServiceWorkerProviderContext
* provider_context
) {
122 DCHECK(provider_context
);
123 int provider_id
= provider_context
->provider_id();
124 DCHECK(!ContainsKey(provider_contexts_
, provider_id
));
125 provider_contexts_
[provider_id
] = provider_context
;
128 void ServiceWorkerDispatcher::RemoveProviderContext(
129 ServiceWorkerProviderContext
* provider_context
) {
130 DCHECK(provider_context
);
131 DCHECK(ContainsKey(provider_contexts_
, provider_context
->provider_id()));
132 provider_contexts_
.erase(provider_context
->provider_id());
133 worker_to_provider_
.erase(provider_context
->installing_handle_id());
134 worker_to_provider_
.erase(provider_context
->waiting_handle_id());
135 worker_to_provider_
.erase(provider_context
->active_handle_id());
136 worker_to_provider_
.erase(provider_context
->controller_handle_id());
139 void ServiceWorkerDispatcher::AddScriptClient(
141 blink::WebServiceWorkerProviderClient
* client
) {
143 DCHECK(!ContainsKey(script_clients_
, provider_id
));
144 script_clients_
[provider_id
] = client
;
147 void ServiceWorkerDispatcher::RemoveScriptClient(int provider_id
) {
148 // This could be possibly called multiple times to ensure termination.
149 if (ContainsKey(script_clients_
, provider_id
))
150 script_clients_
.erase(provider_id
);
153 ServiceWorkerDispatcher
*
154 ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance(
155 ThreadSafeSender
* thread_safe_sender
) {
156 if (g_dispatcher_tls
.Pointer()->Get() == kHasBeenDeleted
) {
157 NOTREACHED() << "Re-instantiating TLS ServiceWorkerDispatcher.";
158 g_dispatcher_tls
.Pointer()->Set(NULL
);
160 if (g_dispatcher_tls
.Pointer()->Get())
161 return g_dispatcher_tls
.Pointer()->Get();
163 ServiceWorkerDispatcher
* dispatcher
=
164 new ServiceWorkerDispatcher(thread_safe_sender
);
165 if (WorkerTaskRunner::Instance()->CurrentWorkerId())
166 WorkerTaskRunner::Instance()->AddStopObserver(dispatcher
);
170 ServiceWorkerDispatcher
* ServiceWorkerDispatcher::GetThreadSpecificInstance() {
171 if (g_dispatcher_tls
.Pointer()->Get() == kHasBeenDeleted
)
173 return g_dispatcher_tls
.Pointer()->Get();
176 void ServiceWorkerDispatcher::OnWorkerRunLoopStopped() {
180 WebServiceWorkerImpl
* ServiceWorkerDispatcher::GetServiceWorker(
181 const ServiceWorkerObjectInfo
& info
,
183 if (info
.handle_id
== kInvalidServiceWorkerHandleId
)
186 WorkerObjectMap::iterator existing_worker
=
187 service_workers_
.find(info
.handle_id
);
189 if (existing_worker
!= service_workers_
.end()) {
191 // We are instructed to adopt a handle but we already have one, so
192 // adopt and destroy a handle ref.
193 ServiceWorkerHandleReference::Adopt(info
, thread_safe_sender_
.get());
195 return existing_worker
->second
;
198 scoped_ptr
<ServiceWorkerHandleReference
> handle_ref
=
200 ? ServiceWorkerHandleReference::Adopt(info
, thread_safe_sender_
.get())
201 : ServiceWorkerHandleReference::Create(info
,
202 thread_safe_sender_
.get());
203 // WebServiceWorkerImpl constructor calls AddServiceWorker.
204 return new WebServiceWorkerImpl(handle_ref
.Pass(), thread_safe_sender_
.get());
207 WebServiceWorkerRegistrationImpl
*
208 ServiceWorkerDispatcher::GetServiceWorkerRegistration(
209 const ServiceWorkerRegistrationObjectInfo
& info
,
211 if (info
.handle_id
== kInvalidServiceWorkerRegistrationHandleId
)
214 RegistrationObjectMap::iterator existing_registration
=
215 registrations_
.find(info
.handle_id
);
217 if (existing_registration
!= registrations_
.end()) {
219 // We are instructed to adopt a handle but we already have one, so
220 // adopt and destroy a handle ref.
221 ServiceWorkerRegistrationHandleReference::Adopt(
222 info
, thread_safe_sender_
.get());
224 return existing_registration
->second
;
227 scoped_ptr
<ServiceWorkerRegistrationHandleReference
> handle_ref
=
228 adopt_handle
? ServiceWorkerRegistrationHandleReference::Adopt(
229 info
, thread_safe_sender_
.get())
230 : ServiceWorkerRegistrationHandleReference::Create(
231 info
, thread_safe_sender_
.get());
233 // WebServiceWorkerRegistrationImpl constructor calls
234 // AddServiceWorkerRegistration.
235 return new WebServiceWorkerRegistrationImpl(handle_ref
.Pass());
238 void ServiceWorkerDispatcher::OnRegistered(
241 const ServiceWorkerRegistrationObjectInfo
& info
) {
242 WebServiceWorkerRegistrationCallbacks
* callbacks
=
243 pending_callbacks_
.Lookup(request_id
);
248 callbacks
->onSuccess(GetServiceWorkerRegistration(info
, true));
249 pending_callbacks_
.Remove(request_id
);
252 void ServiceWorkerDispatcher::OnUnregistered(
255 WebServiceWorkerRegistrationCallbacks
* callbacks
=
256 pending_callbacks_
.Lookup(request_id
);
261 callbacks
->onSuccess(NULL
);
262 pending_callbacks_
.Remove(request_id
);
265 void ServiceWorkerDispatcher::OnRegistrationError(
268 WebServiceWorkerError::ErrorType error_type
,
269 const base::string16
& message
) {
270 WebServiceWorkerRegistrationCallbacks
* callbacks
=
271 pending_callbacks_
.Lookup(request_id
);
276 scoped_ptr
<WebServiceWorkerError
> error(
277 new WebServiceWorkerError(error_type
, message
));
278 callbacks
->onError(error
.release());
279 pending_callbacks_
.Remove(request_id
);
282 void ServiceWorkerDispatcher::OnServiceWorkerStateChanged(
285 blink::WebServiceWorkerState state
) {
286 WorkerObjectMap::iterator worker
= service_workers_
.find(handle_id
);
287 if (worker
!= service_workers_
.end())
288 worker
->second
->OnStateChanged(state
);
290 WorkerToProviderMap::iterator provider
= worker_to_provider_
.find(handle_id
);
291 if (provider
!= worker_to_provider_
.end())
292 provider
->second
->OnServiceWorkerStateChanged(handle_id
, state
);
295 void ServiceWorkerDispatcher::OnSetVersionAttributes(
298 int registration_handle_id
,
300 const ServiceWorkerVersionAttributes
& attributes
) {
301 ChangedVersionAttributesMask
mask(changed_mask
);
302 if (mask
.installing_changed()) {
303 SetInstallingServiceWorker(provider_id
,
304 registration_handle_id
,
305 attributes
.installing
);
307 if (mask
.waiting_changed()) {
308 SetWaitingServiceWorker(provider_id
,
309 registration_handle_id
,
312 if (mask
.active_changed()) {
313 SetActiveServiceWorker(provider_id
,
314 registration_handle_id
,
319 void ServiceWorkerDispatcher::SetInstallingServiceWorker(
321 int registration_handle_id
,
322 const ServiceWorkerObjectInfo
& info
) {
323 ProviderContextMap::iterator provider
= provider_contexts_
.find(provider_id
);
324 if (provider
!= provider_contexts_
.end()) {
325 int existing_installing_id
= provider
->second
->installing_handle_id();
326 if (existing_installing_id
!= info
.handle_id
&&
327 existing_installing_id
!= kInvalidServiceWorkerHandleId
) {
328 WorkerToProviderMap::iterator associated_provider
=
329 worker_to_provider_
.find(existing_installing_id
);
330 DCHECK(associated_provider
!= worker_to_provider_
.end());
331 DCHECK(associated_provider
->second
->provider_id() == provider_id
);
332 worker_to_provider_
.erase(associated_provider
);
334 provider
->second
->OnSetInstallingServiceWorker(provider_id
, info
);
335 if (info
.handle_id
!= kInvalidServiceWorkerHandleId
)
336 worker_to_provider_
[info
.handle_id
] = provider
->second
;
339 RegistrationObjectMap::iterator found
=
340 registrations_
.find(registration_handle_id
);
341 if (found
!= registrations_
.end()) {
342 // Populate the .installing field with the new worker object.
343 found
->second
->SetInstalling(GetServiceWorker(info
, false));
344 if (info
.handle_id
!= kInvalidServiceWorkerHandleId
)
345 found
->second
->OnUpdateFound();
349 void ServiceWorkerDispatcher::SetWaitingServiceWorker(
351 int registration_handle_id
,
352 const ServiceWorkerObjectInfo
& info
) {
353 ProviderContextMap::iterator provider
= provider_contexts_
.find(provider_id
);
354 if (provider
!= provider_contexts_
.end()) {
355 int existing_waiting_id
= provider
->second
->waiting_handle_id();
356 if (existing_waiting_id
!= info
.handle_id
&&
357 existing_waiting_id
!= kInvalidServiceWorkerHandleId
) {
358 WorkerToProviderMap::iterator associated_provider
=
359 worker_to_provider_
.find(existing_waiting_id
);
360 DCHECK(associated_provider
!= worker_to_provider_
.end());
361 DCHECK(associated_provider
->second
->provider_id() == provider_id
);
362 worker_to_provider_
.erase(associated_provider
);
364 provider
->second
->OnSetWaitingServiceWorker(provider_id
, info
);
365 if (info
.handle_id
!= kInvalidServiceWorkerHandleId
)
366 worker_to_provider_
[info
.handle_id
] = provider
->second
;
369 RegistrationObjectMap::iterator found
=
370 registrations_
.find(registration_handle_id
);
371 if (found
!= registrations_
.end()) {
372 // Populate the .waiting field with the new worker object.
373 found
->second
->SetWaiting(GetServiceWorker(info
, false));
377 void ServiceWorkerDispatcher::SetActiveServiceWorker(
379 int registration_handle_id
,
380 const ServiceWorkerObjectInfo
& info
) {
381 ProviderContextMap::iterator provider
= provider_contexts_
.find(provider_id
);
382 if (provider
!= provider_contexts_
.end()) {
383 int existing_active_id
= provider
->second
->active_handle_id();
384 if (existing_active_id
!= info
.handle_id
&&
385 existing_active_id
!= kInvalidServiceWorkerHandleId
) {
386 WorkerToProviderMap::iterator associated_provider
=
387 worker_to_provider_
.find(existing_active_id
);
388 DCHECK(associated_provider
!= worker_to_provider_
.end());
389 DCHECK(associated_provider
->second
->provider_id() == provider_id
);
390 worker_to_provider_
.erase(associated_provider
);
392 provider
->second
->OnSetActiveServiceWorker(provider_id
, info
);
393 if (info
.handle_id
!= kInvalidServiceWorkerHandleId
)
394 worker_to_provider_
[info
.handle_id
] = provider
->second
;
397 RegistrationObjectMap::iterator found
=
398 registrations_
.find(registration_handle_id
);
399 if (found
!= registrations_
.end()) {
400 // Populate the .active field with the new worker object.
401 found
->second
->SetActive(GetServiceWorker(info
, false));
405 void ServiceWorkerDispatcher::OnSetControllerServiceWorker(
408 const ServiceWorkerObjectInfo
& info
) {
409 ProviderContextMap::iterator provider
= provider_contexts_
.find(provider_id
);
410 if (provider
!= provider_contexts_
.end()) {
411 provider
->second
->OnSetControllerServiceWorker(provider_id
, info
);
412 worker_to_provider_
[info
.handle_id
] = provider
->second
;
415 ScriptClientMap::iterator found
= script_clients_
.find(provider_id
);
416 if (found
!= script_clients_
.end()) {
417 // Populate the .controller field with the new worker object.
418 found
->second
->setController(GetServiceWorker(info
, false));
422 void ServiceWorkerDispatcher::OnPostMessage(
425 const base::string16
& message
,
426 const std::vector
<int>& sent_message_port_ids
,
427 const std::vector
<int>& new_routing_ids
) {
428 // Make sure we're on the main document thread. (That must be the only
429 // thread we get this message)
430 DCHECK(ChildThread::current());
432 ScriptClientMap::iterator found
= script_clients_
.find(provider_id
);
433 if (found
== script_clients_
.end()) {
434 // For now we do no queueing for messages sent to nonexistent / unattached
439 std::vector
<WebMessagePortChannelImpl
*> ports
;
440 if (!sent_message_port_ids
.empty()) {
441 ports
.resize(sent_message_port_ids
.size());
442 for (size_t i
= 0; i
< sent_message_port_ids
.size(); ++i
) {
443 ports
[i
] = new WebMessagePortChannelImpl(
444 new_routing_ids
[i
], sent_message_port_ids
[i
],
445 base::MessageLoopProxy::current());
449 found
->second
->dispatchMessageEvent(message
, ports
);
452 void ServiceWorkerDispatcher::AddServiceWorker(
453 int handle_id
, WebServiceWorkerImpl
* worker
) {
454 DCHECK(!ContainsKey(service_workers_
, handle_id
));
455 service_workers_
[handle_id
] = worker
;
458 void ServiceWorkerDispatcher::RemoveServiceWorker(int handle_id
) {
459 DCHECK(ContainsKey(service_workers_
, handle_id
));
460 service_workers_
.erase(handle_id
);
463 void ServiceWorkerDispatcher::AddServiceWorkerRegistration(
464 int registration_handle_id
,
465 WebServiceWorkerRegistrationImpl
* registration
) {
466 DCHECK(!ContainsKey(registrations_
, registration_handle_id
));
467 registrations_
[registration_handle_id
] = registration
;
470 void ServiceWorkerDispatcher::RemoveServiceWorkerRegistration(
471 int registration_handle_id
) {
472 DCHECK(ContainsKey(registrations_
, registration_handle_id
));
473 registrations_
.erase(registration_handle_id
);
476 } // namespace content