Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / content / child / service_worker / service_worker_dispatcher.cc
blob74ec956efc857173f7ee24cf789b0fe598de44e5
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;
27 namespace content {
29 namespace {
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();
41 } // namespace
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) {
54 bool handled = true;
55 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcher, msg)
56 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistered, OnRegistered)
57 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistered,
58 OnUnregistered)
59 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistrationError,
60 OnRegistrationError)
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,
68 OnPostMessage)
69 IPC_MESSAGE_UNHANDLED(handled = false)
70 IPC_END_MESSAGE_MAP()
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(
79 int provider_id,
80 const GURL& pattern,
81 const GURL& script_url,
82 WebServiceWorkerRegistrationCallbacks* callbacks) {
83 DCHECK(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());
92 return;
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(
101 int provider_id,
102 const GURL& pattern,
103 WebServiceWorkerRegistrationCallbacks* callbacks) {
104 DCHECK(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());
112 return;
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(
140 int provider_id,
141 blink::WebServiceWorkerProviderClient* client) {
142 DCHECK(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);
167 return dispatcher;
170 ServiceWorkerDispatcher* ServiceWorkerDispatcher::GetThreadSpecificInstance() {
171 if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted)
172 return NULL;
173 return g_dispatcher_tls.Pointer()->Get();
176 void ServiceWorkerDispatcher::OnWorkerRunLoopStopped() {
177 delete this;
180 WebServiceWorkerImpl* ServiceWorkerDispatcher::GetServiceWorker(
181 const ServiceWorkerObjectInfo& info,
182 bool adopt_handle) {
183 if (info.handle_id == kInvalidServiceWorkerHandleId)
184 return NULL;
186 WorkerObjectMap::iterator existing_worker =
187 service_workers_.find(info.handle_id);
189 if (existing_worker != service_workers_.end()) {
190 if (adopt_handle) {
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 =
199 adopt_handle
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,
210 bool adopt_handle) {
211 if (info.handle_id == kInvalidServiceWorkerRegistrationHandleId)
212 return NULL;
214 RegistrationObjectMap::iterator existing_registration =
215 registrations_.find(info.handle_id);
217 if (existing_registration != registrations_.end()) {
218 if (adopt_handle) {
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(
239 int thread_id,
240 int request_id,
241 const ServiceWorkerRegistrationObjectInfo& info) {
242 WebServiceWorkerRegistrationCallbacks* callbacks =
243 pending_callbacks_.Lookup(request_id);
244 DCHECK(callbacks);
245 if (!callbacks)
246 return;
248 callbacks->onSuccess(GetServiceWorkerRegistration(info, true));
249 pending_callbacks_.Remove(request_id);
252 void ServiceWorkerDispatcher::OnUnregistered(
253 int thread_id,
254 int request_id) {
255 WebServiceWorkerRegistrationCallbacks* callbacks =
256 pending_callbacks_.Lookup(request_id);
257 DCHECK(callbacks);
258 if (!callbacks)
259 return;
261 callbacks->onSuccess(NULL);
262 pending_callbacks_.Remove(request_id);
265 void ServiceWorkerDispatcher::OnRegistrationError(
266 int thread_id,
267 int request_id,
268 WebServiceWorkerError::ErrorType error_type,
269 const base::string16& message) {
270 WebServiceWorkerRegistrationCallbacks* callbacks =
271 pending_callbacks_.Lookup(request_id);
272 DCHECK(callbacks);
273 if (!callbacks)
274 return;
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(
283 int thread_id,
284 int handle_id,
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(
296 int thread_id,
297 int provider_id,
298 int registration_handle_id,
299 int changed_mask,
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,
310 attributes.waiting);
312 if (mask.active_changed()) {
313 SetActiveServiceWorker(provider_id,
314 registration_handle_id,
315 attributes.active);
319 void ServiceWorkerDispatcher::SetInstallingServiceWorker(
320 int provider_id,
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(
350 int provider_id,
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(
378 int provider_id,
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(
406 int thread_id,
407 int provider_id,
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(
423 int thread_id,
424 int provider_id,
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
435 // client.
436 return;
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