Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 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. |
| 4 | |
| 5 | #include "content/child/service_worker/service_worker_dispatcher.h" |
| 6 | |
| 7 | #include "base/lazy_instance.h" |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 8 | #include "base/stl_util.h" |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 9 | #include "base/threading/thread_local.h" |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 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" |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 13 | #include "content/child/service_worker/web_service_worker_impl.h" |
| 14 | #include "content/child/thread_safe_sender.h" |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 15 | #include "content/child/webmessageportchannel_impl.h" |
Torne (Richard Coles) | 5d1f7b1 | 2014-02-21 12:16:55 +0000 | [diff] [blame] | 16 | #include "content/common/service_worker/service_worker_messages.h" |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 17 | #include "third_party/WebKit/public/platform/WebServiceWorkerProviderClient.h" |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 18 | #include "third_party/WebKit/public/web/WebSecurityOrigin.h" |
| 19 | |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 20 | using blink::WebServiceWorkerError; |
| 21 | using blink::WebServiceWorkerProvider; |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 22 | using base::ThreadLocalPointer; |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 23 | |
| 24 | namespace content { |
| 25 | |
| 26 | namespace { |
| 27 | |
| 28 | base::LazyInstance<ThreadLocalPointer<ServiceWorkerDispatcher> >::Leaky |
| 29 | g_dispatcher_tls = LAZY_INSTANCE_INITIALIZER; |
| 30 | |
| 31 | ServiceWorkerDispatcher* const kHasBeenDeleted = |
| 32 | reinterpret_cast<ServiceWorkerDispatcher*>(0x1); |
| 33 | |
| 34 | int CurrentWorkerId() { |
| 35 | return WorkerTaskRunner::Instance()->CurrentWorkerId(); |
| 36 | } |
| 37 | |
| 38 | } // namespace |
| 39 | |
| 40 | ServiceWorkerDispatcher::ServiceWorkerDispatcher( |
| 41 | ThreadSafeSender* thread_safe_sender) |
| 42 | : thread_safe_sender_(thread_safe_sender) { |
| 43 | g_dispatcher_tls.Pointer()->Set(this); |
| 44 | } |
| 45 | |
| 46 | ServiceWorkerDispatcher::~ServiceWorkerDispatcher() { |
| 47 | g_dispatcher_tls.Pointer()->Set(kHasBeenDeleted); |
| 48 | } |
| 49 | |
| 50 | void ServiceWorkerDispatcher::OnMessageReceived(const IPC::Message& msg) { |
| 51 | bool handled = true; |
| 52 | IPC_BEGIN_MESSAGE_MAP(ServiceWorkerDispatcher, msg) |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 53 | IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistered, OnRegistered) |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 54 | IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerUnregistered, |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 55 | OnUnregistered) |
| 56 | IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistrationError, |
| 57 | OnRegistrationError) |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 58 | IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerStateChanged, |
| 59 | OnServiceWorkerStateChanged) |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 60 | IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetCurrentServiceWorker, |
| 61 | OnSetCurrentServiceWorker) |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 62 | IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToDocument, |
| 63 | OnPostMessage) |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 64 | IPC_MESSAGE_UNHANDLED(handled = false) |
| 65 | IPC_END_MESSAGE_MAP() |
| 66 | DCHECK(handled) << "Unhandled message:" << msg.type(); |
| 67 | } |
| 68 | |
| 69 | bool ServiceWorkerDispatcher::Send(IPC::Message* msg) { |
| 70 | return thread_safe_sender_->Send(msg); |
| 71 | } |
| 72 | |
| 73 | void ServiceWorkerDispatcher::RegisterServiceWorker( |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 74 | int provider_id, |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 75 | const GURL& pattern, |
| 76 | const GURL& script_url, |
| 77 | WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks) { |
| 78 | DCHECK(callbacks); |
| 79 | int request_id = pending_callbacks_.Add(callbacks); |
| 80 | thread_safe_sender_->Send(new ServiceWorkerHostMsg_RegisterServiceWorker( |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 81 | CurrentWorkerId(), request_id, provider_id, pattern, script_url)); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | void ServiceWorkerDispatcher::UnregisterServiceWorker( |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 85 | int provider_id, |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 86 | const GURL& pattern, |
| 87 | WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks) { |
| 88 | DCHECK(callbacks); |
| 89 | int request_id = pending_callbacks_.Add(callbacks); |
| 90 | thread_safe_sender_->Send(new ServiceWorkerHostMsg_UnregisterServiceWorker( |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 91 | CurrentWorkerId(), request_id, provider_id, pattern)); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 92 | } |
| 93 | |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 94 | void ServiceWorkerDispatcher::AddProviderContext( |
| 95 | ServiceWorkerProviderContext* provider_context) { |
| 96 | DCHECK(provider_context); |
| 97 | int provider_id = provider_context->provider_id(); |
| 98 | DCHECK(!ContainsKey(provider_contexts_, provider_id)); |
| 99 | provider_contexts_[provider_id] = provider_context; |
| 100 | } |
| 101 | |
| 102 | void ServiceWorkerDispatcher::RemoveProviderContext( |
| 103 | ServiceWorkerProviderContext* provider_context) { |
| 104 | DCHECK(provider_context); |
| 105 | DCHECK(ContainsKey(provider_contexts_, provider_context->provider_id())); |
| 106 | provider_contexts_.erase(provider_context->provider_id()); |
| 107 | worker_to_provider_.erase(provider_context->current_handle_id()); |
| 108 | } |
| 109 | |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 110 | void ServiceWorkerDispatcher::AddScriptClient( |
| 111 | int provider_id, |
| 112 | blink::WebServiceWorkerProviderClient* client) { |
| 113 | DCHECK(client); |
| 114 | DCHECK(!ContainsKey(script_clients_, provider_id)); |
| 115 | script_clients_[provider_id] = client; |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | void ServiceWorkerDispatcher::RemoveScriptClient(int provider_id) { |
| 119 | // This could be possibly called multiple times to ensure termination. |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 120 | if (ContainsKey(script_clients_, provider_id)) |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 121 | script_clients_.erase(provider_id); |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 122 | } |
| 123 | |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 124 | ServiceWorkerDispatcher* |
| 125 | ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance( |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 126 | ThreadSafeSender* thread_safe_sender) { |
| 127 | if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) { |
| 128 | NOTREACHED() << "Re-instantiating TLS ServiceWorkerDispatcher."; |
| 129 | g_dispatcher_tls.Pointer()->Set(NULL); |
| 130 | } |
| 131 | if (g_dispatcher_tls.Pointer()->Get()) |
| 132 | return g_dispatcher_tls.Pointer()->Get(); |
| 133 | |
| 134 | ServiceWorkerDispatcher* dispatcher = |
| 135 | new ServiceWorkerDispatcher(thread_safe_sender); |
| 136 | if (WorkerTaskRunner::Instance()->CurrentWorkerId()) |
Torne (Richard Coles) | a140131 | 2014-03-18 10:20:56 +0000 | [diff] [blame] | 137 | WorkerTaskRunner::Instance()->AddStopObserver(dispatcher); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 138 | return dispatcher; |
| 139 | } |
| 140 | |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 141 | ServiceWorkerDispatcher* ServiceWorkerDispatcher::GetThreadSpecificInstance() { |
| 142 | if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) |
| 143 | return NULL; |
| 144 | return g_dispatcher_tls.Pointer()->Get(); |
| 145 | } |
| 146 | |
| 147 | void ServiceWorkerDispatcher::OnWorkerRunLoopStopped() { |
| 148 | delete this; |
| 149 | } |
| 150 | |
| 151 | void ServiceWorkerDispatcher::OnRegistered( |
| 152 | int thread_id, |
| 153 | int request_id, |
| 154 | const ServiceWorkerObjectInfo& info) { |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 155 | WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks = |
| 156 | pending_callbacks_.Lookup(request_id); |
| 157 | DCHECK(callbacks); |
| 158 | if (!callbacks) |
| 159 | return; |
| 160 | |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 161 | // The browser has to generate the registration_id so the same |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 162 | // worker can be called from different renderer contexts. However, |
| 163 | // the impl object doesn't have to be the same instance across calls |
| 164 | // unless we require the DOM objects to be identical when there's a |
| 165 | // duplicate registration. So for now we mint a new object each |
| 166 | // time. |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 167 | // |
| 168 | // WebServiceWorkerImpl's ctor internally calls AddServiceWorker. |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 169 | scoped_ptr<WebServiceWorkerImpl> worker( |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 170 | new WebServiceWorkerImpl(info, thread_safe_sender_)); |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 171 | callbacks->onSuccess(worker.release()); |
| 172 | pending_callbacks_.Remove(request_id); |
| 173 | } |
| 174 | |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 175 | void ServiceWorkerDispatcher::OnUnregistered( |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 176 | int thread_id, |
| 177 | int request_id) { |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 178 | WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks = |
| 179 | pending_callbacks_.Lookup(request_id); |
| 180 | DCHECK(callbacks); |
| 181 | if (!callbacks) |
| 182 | return; |
| 183 | |
| 184 | callbacks->onSuccess(NULL); |
| 185 | pending_callbacks_.Remove(request_id); |
| 186 | } |
| 187 | |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 188 | void ServiceWorkerDispatcher::OnRegistrationError( |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 189 | int thread_id, |
| 190 | int request_id, |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 191 | WebServiceWorkerError::ErrorType error_type, |
Torne (Richard Coles) | a3f6a49 | 2013-12-18 16:25:09 +0000 | [diff] [blame] | 192 | const base::string16& message) { |
Torne (Richard Coles) | f2477e0 | 2013-11-28 11:55:43 +0000 | [diff] [blame] | 193 | WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks = |
| 194 | pending_callbacks_.Lookup(request_id); |
| 195 | DCHECK(callbacks); |
| 196 | if (!callbacks) |
| 197 | return; |
| 198 | |
| 199 | scoped_ptr<WebServiceWorkerError> error( |
| 200 | new WebServiceWorkerError(error_type, message)); |
| 201 | callbacks->onError(error.release()); |
| 202 | pending_callbacks_.Remove(request_id); |
| 203 | } |
| 204 | |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 205 | void ServiceWorkerDispatcher::OnServiceWorkerStateChanged( |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 206 | int thread_id, |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 207 | int handle_id, |
| 208 | blink::WebServiceWorkerState state) { |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 209 | WorkerObjectMap::iterator worker = service_workers_.find(handle_id); |
| 210 | if (worker != service_workers_.end()) |
| 211 | worker->second->OnStateChanged(state); |
| 212 | |
| 213 | WorkerToProviderMap::iterator provider = worker_to_provider_.find(handle_id); |
| 214 | if (provider != worker_to_provider_.end()) |
| 215 | provider->second->OnServiceWorkerStateChanged(handle_id, state); |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 216 | } |
| 217 | |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 218 | void ServiceWorkerDispatcher::OnSetCurrentServiceWorker( |
| 219 | int thread_id, |
| 220 | int provider_id, |
| 221 | const ServiceWorkerObjectInfo& info) { |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 222 | ProviderContextMap::iterator provider = provider_contexts_.find(provider_id); |
| 223 | if (provider != provider_contexts_.end()) { |
| 224 | provider->second->OnSetCurrentServiceWorker(provider_id, info); |
| 225 | worker_to_provider_[info.handle_id] = provider->second; |
| 226 | } |
| 227 | |
| 228 | ScriptClientMap::iterator found = script_clients_.find(provider_id); |
| 229 | if (found != script_clients_.end()) { |
| 230 | // Populate the .current field with the new worker object. |
| 231 | scoped_ptr<ServiceWorkerHandleReference> handle_ref( |
| 232 | ServiceWorkerHandleReference::Create(info, thread_safe_sender_)); |
| 233 | found->second->setCurrentServiceWorker( |
| 234 | new WebServiceWorkerImpl(handle_ref.Pass(), thread_safe_sender_)); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | void ServiceWorkerDispatcher::OnPostMessage( |
| 239 | int thread_id, |
| 240 | int provider_id, |
| 241 | const base::string16& message, |
| 242 | const std::vector<int>& sent_message_port_ids, |
| 243 | const std::vector<int>& new_routing_ids) { |
| 244 | // Make sure we're on the main document thread. (That must be the only |
| 245 | // thread we get this message) |
| 246 | DCHECK(ChildThread::current()); |
| 247 | |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 248 | ScriptClientMap::iterator found = script_clients_.find(provider_id); |
| 249 | if (found == script_clients_.end()) { |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 250 | // For now we do no queueing for messages sent to nonexistent / unattached |
| 251 | // client. |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 252 | return; |
| 253 | } |
Torne (Richard Coles) | 010d83a | 2014-05-14 12:12:37 +0100 | [diff] [blame^] | 254 | |
| 255 | std::vector<WebMessagePortChannelImpl*> ports; |
| 256 | if (!sent_message_port_ids.empty()) { |
| 257 | ports.resize(sent_message_port_ids.size()); |
| 258 | for (size_t i = 0; i < sent_message_port_ids.size(); ++i) { |
| 259 | ports[i] = new WebMessagePortChannelImpl( |
| 260 | new_routing_ids[i], sent_message_port_ids[i], |
| 261 | base::MessageLoopProxy::current()); |
| 262 | } |
| 263 | } |
| 264 | |
| 265 | found->second->dispatchMessageEvent(message, ports); |
Ben Murdoch | 0529e5d | 2014-04-24 10:50:13 +0100 | [diff] [blame] | 266 | } |
| 267 | |
Ben Murdoch | a02191e | 2014-04-16 11:17:03 +0100 | [diff] [blame] | 268 | void ServiceWorkerDispatcher::AddServiceWorker( |
| 269 | int handle_id, WebServiceWorkerImpl* worker) { |
| 270 | DCHECK(!ContainsKey(service_workers_, handle_id)); |
| 271 | service_workers_[handle_id] = worker; |
| 272 | } |
| 273 | |
| 274 | void ServiceWorkerDispatcher::RemoveServiceWorker(int handle_id) { |
| 275 | DCHECK(ContainsKey(service_workers_, handle_id)); |
| 276 | service_workers_.erase(handle_id); |
| 277 | } |
Torne (Richard Coles) | 1e9bf3e | 2013-10-31 11:16:26 +0000 | [diff] [blame] | 278 | |
| 279 | } // namespace content |