[SyncFS] Build indexes from FileTracker entries on disk.
[chromium-blink-merge.git] / content / child / service_worker / service_worker_dispatcher.cc
blob81f2c4ec0b7ab245ab8049716ebd4d8ea8fd964a
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/web_service_worker_impl.h"
14 #include "content/child/thread_safe_sender.h"
15 #include "content/child/webmessageportchannel_impl.h"
16 #include "content/common/service_worker/service_worker_messages.h"
17 #include "content/public/common/url_utils.h"
18 #include "third_party/WebKit/public/platform/WebServiceWorkerProviderClient.h"
19 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
21 using blink::WebServiceWorkerError;
22 using blink::WebServiceWorkerProvider;
23 using base::ThreadLocalPointer;
25 namespace content {
27 namespace {
29 base::LazyInstance<ThreadLocalPointer<ServiceWorkerDispatcher> >::Leaky
30 g_dispatcher_tls = LAZY_INSTANCE_INITIALIZER;
32 ServiceWorkerDispatcher* const kHasBeenDeleted =
33 reinterpret_cast<ServiceWorkerDispatcher*>(0x1);
35 int CurrentWorkerId() {
36 return WorkerTaskRunner::Instance()->CurrentWorkerId();
39 } // namespace
41 ServiceWorkerDispatcher::ServiceWorkerDispatcher(
42 ThreadSafeSender* thread_safe_sender)
43 : thread_safe_sender_(thread_safe_sender) {
44 g_dispatcher_tls.Pointer()->Set(this);
47 ServiceWorkerDispatcher::~ServiceWorkerDispatcher() {
48 g_dispatcher_tls.Pointer()->Set(kHasBeenDeleted);
51 void ServiceWorkerDispatcher::OnMessageReceived(const IPC::Message& msg) {
52 bool handled = true;
53 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcher, msg)
54 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistered, OnRegistered)
55 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistered,
56 OnUnregistered)
57 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistrationError,
58 OnRegistrationError)
59 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerStateChanged,
60 OnServiceWorkerStateChanged)
61 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetInstallingServiceWorker,
62 OnSetInstallingServiceWorker)
63 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetWaitingServiceWorker,
64 OnSetWaitingServiceWorker)
65 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetActiveServiceWorker,
66 OnSetActiveServiceWorker)
67 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetControllerServiceWorker,
68 OnSetControllerServiceWorker)
69 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToDocument,
70 OnPostMessage)
71 IPC_MESSAGE_UNHANDLED(handled = false)
72 IPC_END_MESSAGE_MAP()
73 DCHECK(handled) << "Unhandled message:" << msg.type();
76 bool ServiceWorkerDispatcher::Send(IPC::Message* msg) {
77 return thread_safe_sender_->Send(msg);
80 void ServiceWorkerDispatcher::RegisterServiceWorker(
81 int provider_id,
82 const GURL& pattern,
83 const GURL& script_url,
84 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks) {
85 DCHECK(callbacks);
87 if (pattern.possibly_invalid_spec().size() > GetMaxURLChars() ||
88 script_url.possibly_invalid_spec().size() > GetMaxURLChars()) {
89 scoped_ptr<WebServiceWorkerProvider::WebServiceWorkerCallbacks>
90 owned_callbacks(callbacks);
91 scoped_ptr<WebServiceWorkerError> error(new WebServiceWorkerError(
92 WebServiceWorkerError::ErrorTypeSecurity, "URL too long"));
93 callbacks->onError(error.release());
94 return;
97 int request_id = pending_callbacks_.Add(callbacks);
98 thread_safe_sender_->Send(new ServiceWorkerHostMsg_RegisterServiceWorker(
99 CurrentWorkerId(), request_id, provider_id, pattern, script_url));
102 void ServiceWorkerDispatcher::UnregisterServiceWorker(
103 int provider_id,
104 const GURL& pattern,
105 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks) {
106 DCHECK(callbacks);
108 if (pattern.possibly_invalid_spec().size() > GetMaxURLChars()) {
109 scoped_ptr<WebServiceWorkerProvider::WebServiceWorkerCallbacks>
110 owned_callbacks(callbacks);
111 scoped_ptr<WebServiceWorkerError> error(new WebServiceWorkerError(
112 WebServiceWorkerError::ErrorTypeSecurity, "URL too long"));
113 callbacks->onError(error.release());
114 return;
117 int request_id = pending_callbacks_.Add(callbacks);
118 thread_safe_sender_->Send(new ServiceWorkerHostMsg_UnregisterServiceWorker(
119 CurrentWorkerId(), request_id, provider_id, pattern));
122 void ServiceWorkerDispatcher::AddProviderContext(
123 ServiceWorkerProviderContext* provider_context) {
124 DCHECK(provider_context);
125 int provider_id = provider_context->provider_id();
126 DCHECK(!ContainsKey(provider_contexts_, provider_id));
127 provider_contexts_[provider_id] = provider_context;
130 void ServiceWorkerDispatcher::RemoveProviderContext(
131 ServiceWorkerProviderContext* provider_context) {
132 DCHECK(provider_context);
133 DCHECK(ContainsKey(provider_contexts_, provider_context->provider_id()));
134 provider_contexts_.erase(provider_context->provider_id());
135 worker_to_provider_.erase(provider_context->installing_handle_id());
136 worker_to_provider_.erase(provider_context->waiting_handle_id());
137 worker_to_provider_.erase(provider_context->active_handle_id());
138 worker_to_provider_.erase(provider_context->controller_handle_id());
141 void ServiceWorkerDispatcher::AddScriptClient(
142 int provider_id,
143 blink::WebServiceWorkerProviderClient* client) {
144 DCHECK(client);
145 DCHECK(!ContainsKey(script_clients_, provider_id));
146 script_clients_[provider_id] = client;
149 void ServiceWorkerDispatcher::RemoveScriptClient(int provider_id) {
150 // This could be possibly called multiple times to ensure termination.
151 if (ContainsKey(script_clients_, provider_id))
152 script_clients_.erase(provider_id);
155 ServiceWorkerDispatcher*
156 ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance(
157 ThreadSafeSender* thread_safe_sender) {
158 if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) {
159 NOTREACHED() << "Re-instantiating TLS ServiceWorkerDispatcher.";
160 g_dispatcher_tls.Pointer()->Set(NULL);
162 if (g_dispatcher_tls.Pointer()->Get())
163 return g_dispatcher_tls.Pointer()->Get();
165 ServiceWorkerDispatcher* dispatcher =
166 new ServiceWorkerDispatcher(thread_safe_sender);
167 if (WorkerTaskRunner::Instance()->CurrentWorkerId())
168 WorkerTaskRunner::Instance()->AddStopObserver(dispatcher);
169 return dispatcher;
172 ServiceWorkerDispatcher* ServiceWorkerDispatcher::GetThreadSpecificInstance() {
173 if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted)
174 return NULL;
175 return g_dispatcher_tls.Pointer()->Get();
178 void ServiceWorkerDispatcher::OnWorkerRunLoopStopped() {
179 delete this;
182 WebServiceWorkerImpl* ServiceWorkerDispatcher::GetServiceWorker(
183 const ServiceWorkerObjectInfo& info,
184 bool adopt_handle) {
185 if (info.handle_id == kInvalidServiceWorkerHandleId)
186 return NULL;
188 WorkerObjectMap::iterator existing_worker =
189 service_workers_.find(info.handle_id);
191 if (existing_worker != service_workers_.end()) {
192 if (adopt_handle) {
193 // We are instructed to adopt a handle but we already have one, so
194 // adopt and destroy a handle ref.
195 ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_);
197 return existing_worker->second;
200 scoped_ptr<ServiceWorkerHandleReference> handle_ref =
201 adopt_handle
202 ? ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_)
203 : ServiceWorkerHandleReference::Create(info, thread_safe_sender_);
204 // WebServiceWorkerImpl constructor calls AddServiceWorker.
205 return new WebServiceWorkerImpl(handle_ref.Pass(), thread_safe_sender_);
208 void ServiceWorkerDispatcher::OnRegistered(
209 int thread_id,
210 int request_id,
211 const ServiceWorkerObjectInfo& info) {
212 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks =
213 pending_callbacks_.Lookup(request_id);
214 DCHECK(callbacks);
215 if (!callbacks)
216 return;
218 callbacks->onSuccess(GetServiceWorker(info, true));
219 pending_callbacks_.Remove(request_id);
222 void ServiceWorkerDispatcher::OnUnregistered(
223 int thread_id,
224 int request_id) {
225 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks =
226 pending_callbacks_.Lookup(request_id);
227 DCHECK(callbacks);
228 if (!callbacks)
229 return;
231 callbacks->onSuccess(NULL);
232 pending_callbacks_.Remove(request_id);
235 void ServiceWorkerDispatcher::OnRegistrationError(
236 int thread_id,
237 int request_id,
238 WebServiceWorkerError::ErrorType error_type,
239 const base::string16& message) {
240 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks =
241 pending_callbacks_.Lookup(request_id);
242 DCHECK(callbacks);
243 if (!callbacks)
244 return;
246 scoped_ptr<WebServiceWorkerError> error(
247 new WebServiceWorkerError(error_type, message));
248 callbacks->onError(error.release());
249 pending_callbacks_.Remove(request_id);
252 void ServiceWorkerDispatcher::OnServiceWorkerStateChanged(
253 int thread_id,
254 int handle_id,
255 blink::WebServiceWorkerState state) {
256 WorkerObjectMap::iterator worker = service_workers_.find(handle_id);
257 if (worker != service_workers_.end())
258 worker->second->OnStateChanged(state);
260 WorkerToProviderMap::iterator provider = worker_to_provider_.find(handle_id);
261 if (provider != worker_to_provider_.end())
262 provider->second->OnServiceWorkerStateChanged(handle_id, state);
265 void ServiceWorkerDispatcher::OnSetInstallingServiceWorker(
266 int thread_id,
267 int provider_id,
268 const ServiceWorkerObjectInfo& info) {
269 ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
270 if (provider != provider_contexts_.end()) {
271 int existing_installing_id = provider->second->installing_handle_id();
272 if (existing_installing_id != info.handle_id &&
273 existing_installing_id != kInvalidServiceWorkerHandleId) {
274 WorkerToProviderMap::iterator associated_provider =
275 worker_to_provider_.find(existing_installing_id);
276 DCHECK(associated_provider != worker_to_provider_.end());
277 DCHECK(associated_provider->second->provider_id() == provider_id);
278 worker_to_provider_.erase(associated_provider);
280 provider->second->OnSetInstallingServiceWorker(provider_id, info);
281 if (info.handle_id != kInvalidServiceWorkerHandleId)
282 worker_to_provider_[info.handle_id] = provider->second;
285 ScriptClientMap::iterator found = script_clients_.find(provider_id);
286 if (found != script_clients_.end()) {
287 // Populate the .installing field with the new worker object.
288 found->second->setInstalling(GetServiceWorker(info, false));
292 void ServiceWorkerDispatcher::OnSetWaitingServiceWorker(
293 int thread_id,
294 int provider_id,
295 const ServiceWorkerObjectInfo& info) {
296 ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
297 if (provider != provider_contexts_.end()) {
298 int existing_waiting_id = provider->second->waiting_handle_id();
299 if (existing_waiting_id != info.handle_id &&
300 existing_waiting_id != kInvalidServiceWorkerHandleId) {
301 WorkerToProviderMap::iterator associated_provider =
302 worker_to_provider_.find(existing_waiting_id);
303 DCHECK(associated_provider != worker_to_provider_.end());
304 DCHECK(associated_provider->second->provider_id() == provider_id);
305 worker_to_provider_.erase(associated_provider);
307 provider->second->OnSetWaitingServiceWorker(provider_id, info);
308 if (info.handle_id != kInvalidServiceWorkerHandleId)
309 worker_to_provider_[info.handle_id] = provider->second;
312 ScriptClientMap::iterator found = script_clients_.find(provider_id);
313 if (found != script_clients_.end()) {
314 // Populate the .waiting field with the new worker object.
315 found->second->setWaiting(GetServiceWorker(info, false));
319 void ServiceWorkerDispatcher::OnSetActiveServiceWorker(
320 int thread_id,
321 int provider_id,
322 const ServiceWorkerObjectInfo& info) {
323 ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
324 if (provider != provider_contexts_.end()) {
325 int existing_active_id = provider->second->active_handle_id();
326 if (existing_active_id != info.handle_id &&
327 existing_active_id != kInvalidServiceWorkerHandleId) {
328 WorkerToProviderMap::iterator associated_provider =
329 worker_to_provider_.find(existing_active_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->OnSetActiveServiceWorker(provider_id, info);
335 if (info.handle_id != kInvalidServiceWorkerHandleId)
336 worker_to_provider_[info.handle_id] = provider->second;
339 ScriptClientMap::iterator found = script_clients_.find(provider_id);
340 if (found != script_clients_.end()) {
341 // Populate the .active field with the new worker object.
342 found->second->setActive(GetServiceWorker(info, false));
346 void ServiceWorkerDispatcher::OnSetControllerServiceWorker(
347 int thread_id,
348 int provider_id,
349 const ServiceWorkerObjectInfo& info) {
350 ProviderContextMap::iterator provider = provider_contexts_.find(provider_id);
351 if (provider != provider_contexts_.end()) {
352 provider->second->OnSetControllerServiceWorker(provider_id, info);
353 worker_to_provider_[info.handle_id] = provider->second;
356 ScriptClientMap::iterator found = script_clients_.find(provider_id);
357 if (found != script_clients_.end()) {
358 // Populate the .controller field with the new worker object.
359 found->second->setController(GetServiceWorker(info, false));
363 void ServiceWorkerDispatcher::OnPostMessage(
364 int thread_id,
365 int provider_id,
366 const base::string16& message,
367 const std::vector<int>& sent_message_port_ids,
368 const std::vector<int>& new_routing_ids) {
369 // Make sure we're on the main document thread. (That must be the only
370 // thread we get this message)
371 DCHECK(ChildThread::current());
373 ScriptClientMap::iterator found = script_clients_.find(provider_id);
374 if (found == script_clients_.end()) {
375 // For now we do no queueing for messages sent to nonexistent / unattached
376 // client.
377 return;
380 std::vector<WebMessagePortChannelImpl*> ports;
381 if (!sent_message_port_ids.empty()) {
382 ports.resize(sent_message_port_ids.size());
383 for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
384 ports[i] = new WebMessagePortChannelImpl(
385 new_routing_ids[i], sent_message_port_ids[i],
386 base::MessageLoopProxy::current());
390 found->second->dispatchMessageEvent(message, ports);
393 void ServiceWorkerDispatcher::AddServiceWorker(
394 int handle_id, WebServiceWorkerImpl* worker) {
395 DCHECK(!ContainsKey(service_workers_, handle_id));
396 service_workers_[handle_id] = worker;
399 void ServiceWorkerDispatcher::RemoveServiceWorker(int handle_id) {
400 DCHECK(ContainsKey(service_workers_, handle_id));
401 service_workers_.erase(handle_id);
404 } // namespace content