Home | History | Annotate | Download | only in service_worker
      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