• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CurrentWorkerId()35 int CurrentWorkerId() {
36   return WorkerTaskRunner::Instance()->CurrentWorkerId();
37 }
38 
39 }  // namespace
40 
ServiceWorkerDispatcher(ThreadSafeSender * thread_safe_sender)41 ServiceWorkerDispatcher::ServiceWorkerDispatcher(
42     ThreadSafeSender* thread_safe_sender)
43     : thread_safe_sender_(thread_safe_sender) {
44   g_dispatcher_tls.Pointer()->Set(this);
45 }
46 
~ServiceWorkerDispatcher()47 ServiceWorkerDispatcher::~ServiceWorkerDispatcher() {
48   g_dispatcher_tls.Pointer()->Set(kHasBeenDeleted);
49 }
50 
OnMessageReceived(const IPC::Message & msg)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 
Send(IPC::Message * msg)72 bool ServiceWorkerDispatcher::Send(IPC::Message* msg) {
73   return thread_safe_sender_->Send(msg);
74 }
75 
RegisterServiceWorker(int provider_id,const GURL & pattern,const GURL & script_url,WebServiceWorkerProvider::WebServiceWorkerCallbacks * callbacks)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 
UnregisterServiceWorker(int provider_id,const GURL & pattern,WebServiceWorkerProvider::WebServiceWorkerCallbacks * callbacks)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 
AddProviderContext(ServiceWorkerProviderContext * provider_context)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 
RemoveProviderContext(ServiceWorkerProviderContext * provider_context)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 
AddScriptClient(int provider_id,blink::WebServiceWorkerProviderClient * client)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 
RemoveScriptClient(int provider_id)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*
GetOrCreateThreadSpecificInstance(ThreadSafeSender * thread_safe_sender)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 
GetThreadSpecificInstance()166 ServiceWorkerDispatcher* ServiceWorkerDispatcher::GetThreadSpecificInstance() {
167   if (g_dispatcher_tls.Pointer()->Get() == kHasBeenDeleted)
168     return NULL;
169   return g_dispatcher_tls.Pointer()->Get();
170 }
171 
OnWorkerRunLoopStopped()172 void ServiceWorkerDispatcher::OnWorkerRunLoopStopped() {
173   delete this;
174 }
175 
GetServiceWorker(const ServiceWorkerObjectInfo & info,bool adopt_handle)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 
OnRegistered(int thread_id,int request_id,const ServiceWorkerObjectInfo & info)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 
OnUnregistered(int thread_id,int request_id)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 
OnRegistrationError(int thread_id,int request_id,WebServiceWorkerError::ErrorType error_type,const base::string16 & message)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 
OnServiceWorkerStateChanged(int thread_id,int handle_id,blink::WebServiceWorkerState state)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 
OnSetWaitingServiceWorker(int thread_id,int provider_id,const ServiceWorkerObjectInfo & info)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 
OnSetCurrentServiceWorker(int thread_id,int provider_id,const ServiceWorkerObjectInfo & info)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 
OnPostMessage(int thread_id,int provider_id,const base::string16 & message,const std::vector<int> & sent_message_port_ids,const std::vector<int> & new_routing_ids)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 
AddServiceWorker(int handle_id,WebServiceWorkerImpl * worker)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 
RemoveServiceWorker(int handle_id)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