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/service_worker/web_service_worker_impl.h"
11 #include "content/child/thread_safe_sender.h"
12 #include "content/common/service_worker/service_worker_messages.h"
13 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
15 using blink::WebServiceWorkerError
;
16 using blink::WebServiceWorkerProvider
;
17 using base::ThreadLocalPointer
;
23 base::LazyInstance
<ThreadLocalPointer
<ServiceWorkerDispatcher
> >::Leaky
24 g_dispatcher_tls
= LAZY_INSTANCE_INITIALIZER
;
26 ServiceWorkerDispatcher
* const kHasBeenDeleted
=
27 reinterpret_cast<ServiceWorkerDispatcher
*>(0x1);
29 int CurrentWorkerId() {
30 return WorkerTaskRunner::Instance()->CurrentWorkerId();
35 ServiceWorkerDispatcher::ServiceWorkerDispatcher(
36 ThreadSafeSender
* thread_safe_sender
)
37 : thread_safe_sender_(thread_safe_sender
) {
38 g_dispatcher_tls
.Pointer()->Set(this);
41 ServiceWorkerDispatcher::~ServiceWorkerDispatcher() {
42 for (ScriptClientMap::iterator it
= script_clients_
.begin();
43 it
!= script_clients_
.end();
45 Send(new ServiceWorkerHostMsg_RemoveScriptClient(
46 CurrentWorkerId(), it
->first
));
49 g_dispatcher_tls
.Pointer()->Set(kHasBeenDeleted
);
52 void ServiceWorkerDispatcher::OnMessageReceived(const IPC::Message
& msg
) {
54 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcher
, msg
)
55 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistered
, OnRegistered
)
56 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistered
,
58 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistrationError
,
60 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerStateChanged
,
61 OnServiceWorkerStateChanged
)
62 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetCurrentServiceWorker
,
63 OnSetCurrentServiceWorker
)
64 IPC_MESSAGE_UNHANDLED(handled
= false)
66 DCHECK(handled
) << "Unhandled message:" << msg
.type();
69 bool ServiceWorkerDispatcher::Send(IPC::Message
* msg
) {
70 return thread_safe_sender_
->Send(msg
);
73 void ServiceWorkerDispatcher::RegisterServiceWorker(
76 const GURL
& script_url
,
77 WebServiceWorkerProvider::WebServiceWorkerCallbacks
* callbacks
) {
79 int request_id
= pending_callbacks_
.Add(callbacks
);
80 thread_safe_sender_
->Send(new ServiceWorkerHostMsg_RegisterServiceWorker(
81 CurrentWorkerId(), request_id
, provider_id
, pattern
, script_url
));
84 void ServiceWorkerDispatcher::UnregisterServiceWorker(
87 WebServiceWorkerProvider::WebServiceWorkerCallbacks
* callbacks
) {
89 int request_id
= pending_callbacks_
.Add(callbacks
);
90 thread_safe_sender_
->Send(new ServiceWorkerHostMsg_UnregisterServiceWorker(
91 CurrentWorkerId(), request_id
, provider_id
, pattern
));
94 void ServiceWorkerDispatcher::AddScriptClient(
96 blink::WebServiceWorkerProviderClient
* client
) {
98 DCHECK(!ContainsKey(script_clients_
, provider_id
));
99 script_clients_
[provider_id
] = client
;
100 thread_safe_sender_
->Send(new ServiceWorkerHostMsg_AddScriptClient(
101 CurrentWorkerId(), provider_id
));
104 void ServiceWorkerDispatcher::RemoveScriptClient(int provider_id
) {
105 // This could be possibly called multiple times to ensure termination.
106 if (ContainsKey(script_clients_
, provider_id
)) {
107 script_clients_
.erase(provider_id
);
108 thread_safe_sender_
->Send(new ServiceWorkerHostMsg_RemoveScriptClient(
109 CurrentWorkerId(), provider_id
));
113 ServiceWorkerDispatcher
*
114 ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance(
115 ThreadSafeSender
* thread_safe_sender
) {
116 if (g_dispatcher_tls
.Pointer()->Get() == kHasBeenDeleted
) {
117 NOTREACHED() << "Re-instantiating TLS ServiceWorkerDispatcher.";
118 g_dispatcher_tls
.Pointer()->Set(NULL
);
120 if (g_dispatcher_tls
.Pointer()->Get())
121 return g_dispatcher_tls
.Pointer()->Get();
123 ServiceWorkerDispatcher
* dispatcher
=
124 new ServiceWorkerDispatcher(thread_safe_sender
);
125 if (WorkerTaskRunner::Instance()->CurrentWorkerId())
126 WorkerTaskRunner::Instance()->AddStopObserver(dispatcher
);
130 ServiceWorkerDispatcher
* ServiceWorkerDispatcher::GetThreadSpecificInstance() {
131 if (g_dispatcher_tls
.Pointer()->Get() == kHasBeenDeleted
)
133 return g_dispatcher_tls
.Pointer()->Get();
136 void ServiceWorkerDispatcher::OnWorkerRunLoopStopped() {
140 void ServiceWorkerDispatcher::OnRegistered(
143 const ServiceWorkerObjectInfo
& info
) {
144 WebServiceWorkerProvider::WebServiceWorkerCallbacks
* callbacks
=
145 pending_callbacks_
.Lookup(request_id
);
150 // The browser has to generate the registration_id so the same
151 // worker can be called from different renderer contexts. However,
152 // the impl object doesn't have to be the same instance across calls
153 // unless we require the DOM objects to be identical when there's a
154 // duplicate registration. So for now we mint a new object each
157 // WebServiceWorkerImpl's ctor internally calls AddServiceWorker.
158 scoped_ptr
<WebServiceWorkerImpl
> worker(
159 new WebServiceWorkerImpl(info
, thread_safe_sender_
));
160 callbacks
->onSuccess(worker
.release());
161 pending_callbacks_
.Remove(request_id
);
164 void ServiceWorkerDispatcher::OnUnregistered(
167 WebServiceWorkerProvider::WebServiceWorkerCallbacks
* callbacks
=
168 pending_callbacks_
.Lookup(request_id
);
173 callbacks
->onSuccess(NULL
);
174 pending_callbacks_
.Remove(request_id
);
177 void ServiceWorkerDispatcher::OnRegistrationError(
180 WebServiceWorkerError::ErrorType error_type
,
181 const base::string16
& message
) {
182 WebServiceWorkerProvider::WebServiceWorkerCallbacks
* callbacks
=
183 pending_callbacks_
.Lookup(request_id
);
188 scoped_ptr
<WebServiceWorkerError
> error(
189 new WebServiceWorkerError(error_type
, message
));
190 callbacks
->onError(error
.release());
191 pending_callbacks_
.Remove(request_id
);
194 void ServiceWorkerDispatcher::OnServiceWorkerStateChanged(
197 blink::WebServiceWorkerState state
) {
198 ServiceWorkerMap::iterator found
= service_workers_
.find(handle_id
);
199 if (found
== service_workers_
.end())
201 found
->second
->OnStateChanged(state
);
204 void ServiceWorkerDispatcher::OnSetCurrentServiceWorker(
207 const ServiceWorkerObjectInfo
& info
) {
208 scoped_ptr
<WebServiceWorkerImpl
> worker(
209 new WebServiceWorkerImpl(info
, thread_safe_sender_
));
210 ScriptClientMap::iterator found
= script_clients_
.find(provider_id
);
211 if (found
== script_clients_
.end()) {
212 // Note that |worker|'s destructor sends a ServiceWorkerObjectDestroyed
213 // message so the browser-side can clean up the ServiceWorkerHandle it
214 // created when sending us this message.
217 // TODO(falken): Call client->setCurrentServiceWorker(worker) when the Blink
218 // change to add that function rolls in.
221 void ServiceWorkerDispatcher::AddServiceWorker(
222 int handle_id
, WebServiceWorkerImpl
* worker
) {
223 DCHECK(!ContainsKey(service_workers_
, handle_id
));
224 service_workers_
[handle_id
] = worker
;
227 void ServiceWorkerDispatcher::RemoveServiceWorker(int handle_id
) {
228 DCHECK(ContainsKey(service_workers_
, handle_id
));
229 service_workers_
.erase(handle_id
);
232 } // namespace content