Home | History | Annotate | Download | only in shared_worker
      1 // Copyright 2014 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/browser/shared_worker/shared_worker_host.h"
      6 
      7 #include "base/metrics/histogram.h"
      8 #include "content/browser/devtools/embedded_worker_devtools_manager.h"
      9 #include "content/browser/frame_host/render_frame_host_delegate.h"
     10 #include "content/browser/frame_host/render_frame_host_impl.h"
     11 #include "content/browser/message_port_service.h"
     12 #include "content/browser/shared_worker/shared_worker_instance.h"
     13 #include "content/browser/shared_worker/shared_worker_message_filter.h"
     14 #include "content/browser/shared_worker/shared_worker_service_impl.h"
     15 #include "content/browser/shared_worker/worker_document_set.h"
     16 #include "content/common/view_messages.h"
     17 #include "content/common/worker_messages.h"
     18 #include "content/public/browser/browser_thread.h"
     19 #include "content/public/browser/content_browser_client.h"
     20 #include "content/public/browser/render_process_host.h"
     21 #include "content/public/common/content_client.h"
     22 
     23 namespace content {
     24 namespace {
     25 
     26 // Notifies RenderViewHost that one or more worker objects crashed.
     27 void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) {
     28   RenderFrameHostImpl* host =
     29       RenderFrameHostImpl::FromID(render_process_unique_id, render_frame_id);
     30   if (host)
     31     host->delegate()->WorkerCrashed(host);
     32 }
     33 
     34 void NotifyWorkerReadyForInspection(int worker_process_id,
     35                                     int worker_route_id) {
     36   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
     37     BrowserThread::PostTask(BrowserThread::UI,
     38                             FROM_HERE,
     39                             base::Bind(NotifyWorkerReadyForInspection,
     40                                        worker_process_id,
     41                                        worker_route_id));
     42     return;
     43   }
     44   EmbeddedWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection(
     45       worker_process_id, worker_route_id);
     46 }
     47 
     48 void NotifyWorkerContextStarted(int worker_process_id, int worker_route_id) {
     49   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
     50     BrowserThread::PostTask(
     51         BrowserThread::UI,
     52         FROM_HERE,
     53         base::Bind(
     54             NotifyWorkerContextStarted, worker_process_id, worker_route_id));
     55     return;
     56   }
     57   EmbeddedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
     58       worker_process_id, worker_route_id);
     59 }
     60 
     61 void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id) {
     62   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
     63     BrowserThread::PostTask(
     64         BrowserThread::UI,
     65         FROM_HERE,
     66         base::Bind(NotifyWorkerDestroyed, worker_process_id, worker_route_id));
     67     return;
     68   }
     69   EmbeddedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(
     70       worker_process_id, worker_route_id);
     71 }
     72 
     73 }  // namespace
     74 
     75 SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance* instance,
     76                                    SharedWorkerMessageFilter* filter,
     77                                    int worker_route_id)
     78     : instance_(instance),
     79       worker_document_set_(new WorkerDocumentSet()),
     80       container_render_filter_(filter),
     81       worker_process_id_(filter->render_process_id()),
     82       worker_route_id_(worker_route_id),
     83       load_failed_(false),
     84       closed_(false),
     85       creation_time_(base::TimeTicks::Now()),
     86       weak_factory_(this) {
     87   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     88 }
     89 
     90 SharedWorkerHost::~SharedWorkerHost() {
     91   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     92   UMA_HISTOGRAM_LONG_TIMES("SharedWorker.TimeToDeleted",
     93                            base::TimeTicks::Now() - creation_time_);
     94   // If we crashed, tell the RenderViewHosts.
     95   if (instance_ && !load_failed_) {
     96     const WorkerDocumentSet::DocumentInfoSet& parents =
     97         worker_document_set_->documents();
     98     for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
     99              parents.begin();
    100          parent_iter != parents.end();
    101          ++parent_iter) {
    102       BrowserThread::PostTask(BrowserThread::UI,
    103                               FROM_HERE,
    104                               base::Bind(&WorkerCrashCallback,
    105                                          parent_iter->render_process_id(),
    106                                          parent_iter->render_frame_id()));
    107     }
    108   }
    109   if (!closed_)
    110     NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
    111   SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
    112       worker_process_id_, worker_route_id_);
    113 }
    114 
    115 bool SharedWorkerHost::Send(IPC::Message* message) {
    116   if (!container_render_filter_) {
    117     delete message;
    118     return false;
    119   }
    120   return container_render_filter_->Send(message);
    121 }
    122 
    123 void SharedWorkerHost::Start(bool pause_on_start) {
    124   WorkerProcessMsg_CreateWorker_Params params;
    125   params.url = instance_->url();
    126   params.name = instance_->name();
    127   params.content_security_policy = instance_->content_security_policy();
    128   params.security_policy_type = instance_->security_policy_type();
    129   params.pause_on_start = pause_on_start;
    130   params.route_id = worker_route_id_;
    131   Send(new WorkerProcessMsg_CreateWorker(params));
    132 
    133   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
    134        ++i) {
    135     i->filter()->Send(new ViewMsg_WorkerCreated(i->route_id()));
    136   }
    137 }
    138 
    139 bool SharedWorkerHost::FilterMessage(const IPC::Message& message,
    140                                      SharedWorkerMessageFilter* filter) {
    141   if (!instance_)
    142     return false;
    143 
    144   if (!closed_ && HasFilter(filter, message.routing_id())) {
    145     RelayMessage(message, filter);
    146     return true;
    147   }
    148 
    149   return false;
    150 }
    151 
    152 void SharedWorkerHost::FilterShutdown(SharedWorkerMessageFilter* filter) {
    153   if (!instance_)
    154     return;
    155   RemoveFilters(filter);
    156   worker_document_set_->RemoveAll(filter);
    157   if (worker_document_set_->IsEmpty()) {
    158     // This worker has no more associated documents - shut it down.
    159     Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
    160   }
    161 }
    162 
    163 void SharedWorkerHost::DocumentDetached(SharedWorkerMessageFilter* filter,
    164                                         unsigned long long document_id) {
    165   if (!instance_)
    166     return;
    167   // Walk all instances and remove the document from their document set.
    168   worker_document_set_->Remove(filter, document_id);
    169   if (worker_document_set_->IsEmpty()) {
    170     // This worker has no more associated documents - shut it down.
    171     Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
    172   }
    173 }
    174 
    175 void SharedWorkerHost::WorkerContextClosed() {
    176   if (!instance_)
    177     return;
    178   // Set the closed flag - this will stop any further messages from
    179   // being sent to the worker (messages can still be sent from the worker,
    180   // for exception reporting, etc).
    181   closed_ = true;
    182   NotifyWorkerDestroyed(worker_process_id_, worker_route_id_);
    183 }
    184 
    185 void SharedWorkerHost::WorkerContextDestroyed() {
    186   if (!instance_)
    187     return;
    188   instance_.reset();
    189   worker_document_set_ = NULL;
    190 }
    191 
    192 void SharedWorkerHost::WorkerReadyForInspection() {
    193   NotifyWorkerReadyForInspection(worker_process_id_, worker_route_id_);
    194 }
    195 
    196 void SharedWorkerHost::WorkerScriptLoaded() {
    197   UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded",
    198                       base::TimeTicks::Now() - creation_time_);
    199   NotifyWorkerContextStarted(worker_process_id_, worker_route_id_);
    200 }
    201 
    202 void SharedWorkerHost::WorkerScriptLoadFailed() {
    203   UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoadFailed",
    204                       base::TimeTicks::Now() - creation_time_);
    205   if (!instance_)
    206     return;
    207   load_failed_ = true;
    208   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
    209        ++i) {
    210     i->filter()->Send(new ViewMsg_WorkerScriptLoadFailed(i->route_id()));
    211   }
    212 }
    213 
    214 void SharedWorkerHost::WorkerConnected(int message_port_id) {
    215   if (!instance_)
    216     return;
    217   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
    218        ++i) {
    219     if (i->message_port_id() != message_port_id)
    220       continue;
    221     i->filter()->Send(new ViewMsg_WorkerConnected(i->route_id()));
    222     return;
    223   }
    224 }
    225 
    226 void SharedWorkerHost::AllowDatabase(const GURL& url,
    227                                      const base::string16& name,
    228                                      const base::string16& display_name,
    229                                      unsigned long estimated_size,
    230                                      bool* result) {
    231   if (!instance_)
    232     return;
    233   *result = GetContentClient()->browser()->AllowWorkerDatabase(
    234       url,
    235       name,
    236       display_name,
    237       estimated_size,
    238       instance_->resource_context(),
    239       GetRenderFrameIDsForWorker());
    240 }
    241 
    242 void SharedWorkerHost::AllowFileSystem(const GURL& url,
    243                                        scoped_ptr<IPC::Message> reply_msg) {
    244   if (!instance_)
    245     return;
    246   GetContentClient()->browser()->AllowWorkerFileSystem(
    247       url,
    248       instance_->resource_context(),
    249       GetRenderFrameIDsForWorker(),
    250       base::Bind(&SharedWorkerHost::AllowFileSystemResponse,
    251                  weak_factory_.GetWeakPtr(),
    252                  base::Passed(&reply_msg)));
    253 }
    254 
    255 void SharedWorkerHost::AllowFileSystemResponse(
    256     scoped_ptr<IPC::Message> reply_msg,
    257     bool allowed) {
    258   WorkerProcessHostMsg_RequestFileSystemAccessSync::WriteReplyParams(
    259       reply_msg.get(),
    260       allowed);
    261   Send(reply_msg.release());
    262 }
    263 
    264 void SharedWorkerHost::AllowIndexedDB(const GURL& url,
    265                                       const base::string16& name,
    266                                       bool* result) {
    267   if (!instance_)
    268     return;
    269   *result = GetContentClient()->browser()->AllowWorkerIndexedDB(
    270       url, name, instance_->resource_context(), GetRenderFrameIDsForWorker());
    271 }
    272 
    273 void SharedWorkerHost::RelayMessage(
    274     const IPC::Message& message,
    275     SharedWorkerMessageFilter* incoming_filter) {
    276   if (!instance_)
    277     return;
    278   if (message.type() == WorkerMsg_Connect::ID) {
    279     // Crack the SharedWorker Connect message to setup routing for the port.
    280     WorkerMsg_Connect::Param param;
    281     if (!WorkerMsg_Connect::Read(&message, &param))
    282       return;
    283     int sent_message_port_id = param.a;
    284     int new_routing_id = param.b;
    285 
    286     DCHECK(container_render_filter_);
    287     new_routing_id = container_render_filter_->GetNextRoutingID();
    288     MessagePortService::GetInstance()->UpdateMessagePort(
    289         sent_message_port_id,
    290         container_render_filter_->message_port_message_filter(),
    291         new_routing_id);
    292     SetMessagePortID(
    293         incoming_filter, message.routing_id(), sent_message_port_id);
    294     // Resend the message with the new routing id.
    295     Send(new WorkerMsg_Connect(
    296         worker_route_id_, sent_message_port_id, new_routing_id));
    297 
    298     // Send any queued messages for the sent port.
    299     MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
    300         sent_message_port_id);
    301   } else {
    302     IPC::Message* new_message = new IPC::Message(message);
    303     new_message->set_routing_id(worker_route_id_);
    304     Send(new_message);
    305     return;
    306   }
    307 }
    308 
    309 void SharedWorkerHost::TerminateWorker() {
    310   Send(new WorkerMsg_TerminateWorkerContext(worker_route_id_));
    311 }
    312 
    313 std::vector<std::pair<int, int> >
    314 SharedWorkerHost::GetRenderFrameIDsForWorker() {
    315   std::vector<std::pair<int, int> > result;
    316   if (!instance_)
    317     return result;
    318   const WorkerDocumentSet::DocumentInfoSet& documents =
    319       worker_document_set_->documents();
    320   for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
    321            documents.begin();
    322        doc != documents.end();
    323        ++doc) {
    324     result.push_back(
    325         std::make_pair(doc->render_process_id(), doc->render_frame_id()));
    326   }
    327   return result;
    328 }
    329 
    330 void SharedWorkerHost::AddFilter(SharedWorkerMessageFilter* filter,
    331                                  int route_id) {
    332   CHECK(filter);
    333   if (!HasFilter(filter, route_id)) {
    334     FilterInfo info(filter, route_id);
    335     filters_.push_back(info);
    336   }
    337 }
    338 
    339 void SharedWorkerHost::RemoveFilters(SharedWorkerMessageFilter* filter) {
    340   for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
    341     if (i->filter() == filter)
    342       i = filters_.erase(i);
    343     else
    344       ++i;
    345   }
    346 }
    347 
    348 bool SharedWorkerHost::HasFilter(SharedWorkerMessageFilter* filter,
    349                                  int route_id) const {
    350   for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
    351        ++i) {
    352     if (i->filter() == filter && i->route_id() == route_id)
    353       return true;
    354   }
    355   return false;
    356 }
    357 
    358 void SharedWorkerHost::SetMessagePortID(SharedWorkerMessageFilter* filter,
    359                                         int route_id,
    360                                         int message_port_id) {
    361   for (FilterList::iterator i = filters_.begin(); i != filters_.end(); ++i) {
    362     if (i->filter() == filter && i->route_id() == route_id) {
    363       i->set_message_port_id(message_port_id);
    364       return;
    365     }
    366   }
    367 }
    368 
    369 }  // namespace content
    370