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" 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" 20 21 using blink::WebServiceWorkerError; 22 using blink::WebServiceWorkerProvider; 23 using base::ThreadLocalPointer; 24 25 namespace content { 26 27 namespace { 28 29 base::LazyInstance<ThreadLocalPointer<ServiceWorkerDispatcher> >::Leaky 30 g_dispatcher_tls = LAZY_INSTANCE_INITIALIZER; 31 32 ServiceWorkerDispatcher* const kHasBeenDeleted = 33 reinterpret_cast<ServiceWorkerDispatcher*>(0x1); 34 35 int CurrentWorkerId() { 36 return WorkerTaskRunner::Instance()->CurrentWorkerId(); 37 } 38 39 } // namespace 40 41 ServiceWorkerDispatcher::ServiceWorkerDispatcher( 42 ThreadSafeSender* thread_safe_sender) 43 : thread_safe_sender_(thread_safe_sender) { 44 g_dispatcher_tls.Pointer()->Set(this); 45 } 46 47 ServiceWorkerDispatcher::~ServiceWorkerDispatcher() { 48 g_dispatcher_tls.Pointer()->Set(kHasBeenDeleted); 49 } 50 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_SetWaitingServiceWorker, 62 OnSetWaitingServiceWorker) 63 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetCurrentServiceWorker, 64 OnSetCurrentServiceWorker) 65 IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToDocument, 66 OnPostMessage) 67 IPC_MESSAGE_UNHANDLED(handled = false) 68 IPC_END_MESSAGE_MAP() 69 DCHECK(handled) << "Unhandled message:" << msg.type(); 70 } 71 72 bool ServiceWorkerDispatcher::Send(IPC::Message* msg) { 73 return thread_safe_sender_->Send(msg); 74 } 75 76 void ServiceWorkerDispatcher::RegisterServiceWorker( 77 int provider_id, 78 const GURL& pattern, 79 const GURL& script_url, 80 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks) { 81 DCHECK(callbacks); 82 83 if (pattern.possibly_invalid_spec().size() > GetMaxURLChars() || 84 script_url.possibly_invalid_spec().size() > GetMaxURLChars()) { 85 scoped_ptr<WebServiceWorkerProvider::WebServiceWorkerCallbacks> 86 owned_callbacks(callbacks); 87 scoped_ptr<WebServiceWorkerError> error(new WebServiceWorkerError( 88 WebServiceWorkerError::ErrorTypeSecurity, "URL too long")); 89 callbacks->onError(error.release()); 90 return; 91 } 92 93 int request_id = pending_callbacks_.Add(callbacks); 94 thread_safe_sender_->Send(new ServiceWorkerHostMsg_RegisterServiceWorker( 95 CurrentWorkerId(), request_id, provider_id, pattern, script_url)); 96 } 97 98 void ServiceWorkerDispatcher::UnregisterServiceWorker( 99 int provider_id, 100 const GURL& pattern, 101 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks) { 102 DCHECK(callbacks); 103 104 if (pattern.possibly_invalid_spec().size() > GetMaxURLChars()) { 105 scoped_ptr<WebServiceWorkerProvider::WebServiceWorkerCallbacks> 106 owned_callbacks(callbacks); 107 scoped_ptr<WebServiceWorkerError> error(new WebServiceWorkerError( 108 WebServiceWorkerError::ErrorTypeSecurity, "URL too long")); 109 callbacks->onError(error.release()); 110 return; 111 } 112 113 int request_id = pending_callbacks_.Add(callbacks); 114 thread_safe_sender_->Send(new ServiceWorkerHostMsg_UnregisterServiceWorker( 115 CurrentWorkerId(), request_id, provider_id, pattern)); 116 } 117 118 void ServiceWorkerDispatcher::AddProviderContext( 119 ServiceWorkerProviderContext* provider_context) { 120 DCHECK(provider_context); 121 int provider_id = provider_context->provider_id(); 122 DCHECK(!ContainsKey(provider_contexts_, provider_id)); 123 provider_contexts_[provider_id] = provider_context; 124 } 125 126 void ServiceWorkerDispatcher::RemoveProviderContext( 127 ServiceWorkerProviderContext* provider_context) { 128 DCHECK(provider_context); 129 DCHECK(ContainsKey(provider_contexts_, provider_context->provider_id())); 130 provider_contexts_.erase(provider_context->provider_id()); 131 worker_to_provider_.erase(provider_context->waiting_handle_id()); 132 worker_to_provider_.erase(provider_context->current_handle_id()); 133 } 134 135 void ServiceWorkerDispatcher::AddScriptClient( 136 int provider_id, 137 blink::WebServiceWorkerProviderClient* client) { 138 DCHECK(client); 139 DCHECK(!ContainsKey(script_clients_, provider_id)); 140 script_clients_[provider_id] = client; 141 } 142 143 void ServiceWorkerDispatcher::RemoveScriptClient(int provider_id) { 144 // This could be possibly called multiple times to ensure termination. 145 if (ContainsKey(script_clients_, provider_id)) 146 script_clients_.erase(provider_id); 147 } 148 149 ServiceWorkerDispatcher* 150 ServiceWorkerDispatcher::GetOrCreateThreadSpecificInstance( 151 ThreadSafeSender* thread_safe_sender) { 152 if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) { 153 NOTREACHED() << "Re-instantiating TLS ServiceWorkerDispatcher."; 154 g_dispatcher_tls.Pointer()->Set(NULL); 155 } 156 if (g_dispatcher_tls.Pointer()->Get()) 157 return g_dispatcher_tls.Pointer()->Get(); 158 159 ServiceWorkerDispatcher* dispatcher = 160 new ServiceWorkerDispatcher(thread_safe_sender); 161 if (WorkerTaskRunner::Instance()->CurrentWorkerId()) 162 WorkerTaskRunner::Instance()->AddStopObserver(dispatcher); 163 return dispatcher; 164 } 165 166 ServiceWorkerDispatcher* ServiceWorkerDispatcher::GetThreadSpecificInstance() { 167 if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted) 168 return NULL; 169 return g_dispatcher_tls.Pointer()->Get(); 170 } 171 172 void ServiceWorkerDispatcher::OnWorkerRunLoopStopped() { 173 delete this; 174 } 175 176 WebServiceWorkerImpl* ServiceWorkerDispatcher::GetServiceWorker( 177 const ServiceWorkerObjectInfo& info, 178 bool adopt_handle) { 179 if (info.handle_id == kInvalidServiceWorkerHandleId) 180 return NULL; 181 182 WorkerObjectMap::iterator existing_worker = 183 service_workers_.find(info.handle_id); 184 185 if (existing_worker != service_workers_.end()) { 186 if (adopt_handle) { 187 // We are instructed to adopt a handle but we already have one, so 188 // adopt and destroy a handle ref. 189 ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_); 190 } 191 return existing_worker->second; 192 } 193 194 scoped_ptr<ServiceWorkerHandleReference> handle_ref = 195 adopt_handle 196 ? ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_) 197 : ServiceWorkerHandleReference::Create(info, thread_safe_sender_); 198 // WebServiceWorkerImpl constructor calls AddServiceWorker. 199 return new WebServiceWorkerImpl(handle_ref.Pass(), thread_safe_sender_); 200 } 201 202 void ServiceWorkerDispatcher::OnRegistered( 203 int thread_id, 204 int request_id, 205 const ServiceWorkerObjectInfo& info) { 206 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks = 207 pending_callbacks_.Lookup(request_id); 208 DCHECK(callbacks); 209 if (!callbacks) 210 return; 211 212 callbacks->onSuccess(GetServiceWorker(info, true)); 213 pending_callbacks_.Remove(request_id); 214 } 215 216 void ServiceWorkerDispatcher::OnUnregistered( 217 int thread_id, 218 int request_id) { 219 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks = 220 pending_callbacks_.Lookup(request_id); 221 DCHECK(callbacks); 222 if (!callbacks) 223 return; 224 225 callbacks->onSuccess(NULL); 226 pending_callbacks_.Remove(request_id); 227 } 228 229 void ServiceWorkerDispatcher::OnRegistrationError( 230 int thread_id, 231 int request_id, 232 WebServiceWorkerError::ErrorType error_type, 233 const base::string16& message) { 234 WebServiceWorkerProvider::WebServiceWorkerCallbacks* callbacks = 235 pending_callbacks_.Lookup(request_id); 236 DCHECK(callbacks); 237 if (!callbacks) 238 return; 239 240 scoped_ptr<WebServiceWorkerError> error( 241 new WebServiceWorkerError(error_type, message)); 242 callbacks->onError(error.release()); 243 pending_callbacks_.Remove(request_id); 244 } 245 246 void ServiceWorkerDispatcher::OnServiceWorkerStateChanged( 247 int thread_id, 248 int handle_id, 249 blink::WebServiceWorkerState state) { 250 WorkerObjectMap::iterator worker = service_workers_.find(handle_id); 251 if (worker != service_workers_.end()) 252 worker->second->OnStateChanged(state); 253 254 WorkerToProviderMap::iterator provider = worker_to_provider_.find(handle_id); 255 if (provider != worker_to_provider_.end()) 256 provider->second->OnServiceWorkerStateChanged(handle_id, state); 257 } 258 259 void ServiceWorkerDispatcher::OnSetWaitingServiceWorker( 260 int thread_id, 261 int provider_id, 262 const ServiceWorkerObjectInfo& info) { 263 ProviderContextMap::iterator provider = provider_contexts_.find(provider_id); 264 if (provider != provider_contexts_.end()) { 265 int existing_waiting_id = provider->second->waiting_handle_id(); 266 if (existing_waiting_id != info.handle_id && 267 existing_waiting_id != kInvalidServiceWorkerHandleId) { 268 WorkerToProviderMap::iterator associated_provider = 269 worker_to_provider_.find(existing_waiting_id); 270 DCHECK(associated_provider != worker_to_provider_.end()); 271 DCHECK(associated_provider->second->provider_id() == provider_id); 272 worker_to_provider_.erase(associated_provider); 273 } 274 provider->second->OnSetWaitingServiceWorker(provider_id, info); 275 if (info.handle_id != kInvalidServiceWorkerHandleId) 276 worker_to_provider_[info.handle_id] = provider->second; 277 } 278 279 ScriptClientMap::iterator found = script_clients_.find(provider_id); 280 if (found != script_clients_.end()) { 281 // Populate the .waiting field with the new worker object. 282 found->second->setWaiting(GetServiceWorker(info, false)); 283 } 284 } 285 286 void ServiceWorkerDispatcher::OnSetCurrentServiceWorker( 287 int thread_id, 288 int provider_id, 289 const ServiceWorkerObjectInfo& info) { 290 ProviderContextMap::iterator provider = provider_contexts_.find(provider_id); 291 if (provider != provider_contexts_.end()) { 292 provider->second->OnSetCurrentServiceWorker(provider_id, info); 293 worker_to_provider_[info.handle_id] = provider->second; 294 } 295 296 ScriptClientMap::iterator found = script_clients_.find(provider_id); 297 if (found != script_clients_.end()) { 298 // Populate the .controller field with the new worker object. 299 found->second->setController(GetServiceWorker(info, false)); 300 } 301 } 302 303 void ServiceWorkerDispatcher::OnPostMessage( 304 int thread_id, 305 int provider_id, 306 const base::string16& message, 307 const std::vector<int>& sent_message_port_ids, 308 const std::vector<int>& new_routing_ids) { 309 // Make sure we're on the main document thread. (That must be the only 310 // thread we get this message) 311 DCHECK(ChildThread::current()); 312 313 ScriptClientMap::iterator found = script_clients_.find(provider_id); 314 if (found == script_clients_.end()) { 315 // For now we do no queueing for messages sent to nonexistent / unattached 316 // client. 317 return; 318 } 319 320 std::vector<WebMessagePortChannelImpl*> ports; 321 if (!sent_message_port_ids.empty()) { 322 ports.resize(sent_message_port_ids.size()); 323 for (size_t i = 0; i < sent_message_port_ids.size(); ++i) { 324 ports[i] = new WebMessagePortChannelImpl( 325 new_routing_ids[i], sent_message_port_ids[i], 326 base::MessageLoopProxy::current()); 327 } 328 } 329 330 found->second->dispatchMessageEvent(message, ports); 331 } 332 333 void ServiceWorkerDispatcher::AddServiceWorker( 334 int handle_id, WebServiceWorkerImpl* worker) { 335 DCHECK(!ContainsKey(service_workers_, handle_id)); 336 service_workers_[handle_id] = worker; 337 } 338 339 void ServiceWorkerDispatcher::RemoveServiceWorker(int handle_id) { 340 DCHECK(ContainsKey(service_workers_, handle_id)); 341 service_workers_.erase(handle_id); 342 } 343 344 } // namespace content 345