Home | History | Annotate | Download | only in worker_host
      1 // Copyright (c) 2012 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/worker_host/worker_service_impl.h"
      6 
      7 #include <string>
      8 
      9 #include "base/command_line.h"
     10 #include "base/logging.h"
     11 #include "base/threading/thread.h"
     12 #include "content/browser/devtools/worker_devtools_manager.h"
     13 #include "content/browser/renderer_host/render_widget_host_impl.h"
     14 #include "content/browser/worker_host/worker_message_filter.h"
     15 #include "content/browser/worker_host/worker_process_host.h"
     16 #include "content/common/view_messages.h"
     17 #include "content/common/worker_messages.h"
     18 #include "content/public/browser/child_process_data.h"
     19 #include "content/public/browser/notification_service.h"
     20 #include "content/public/browser/notification_types.h"
     21 #include "content/public/browser/render_process_host.h"
     22 #include "content/public/browser/render_view_host.h"
     23 #include "content/public/browser/render_widget_host.h"
     24 #include "content/public/browser/render_widget_host_iterator.h"
     25 #include "content/public/browser/render_widget_host_view.h"
     26 #include "content/public/browser/resource_context.h"
     27 #include "content/public/browser/web_contents.h"
     28 #include "content/public/browser/worker_service_observer.h"
     29 #include "content/public/common/content_switches.h"
     30 #include "content/public/common/process_type.h"
     31 
     32 namespace content {
     33 
     34 const int WorkerServiceImpl::kMaxWorkersWhenSeparate = 64;
     35 const int WorkerServiceImpl::kMaxWorkersPerTabWhenSeparate = 16;
     36 
     37 class WorkerPrioritySetter
     38     : public NotificationObserver,
     39       public base::RefCountedThreadSafe<WorkerPrioritySetter,
     40                                         BrowserThread::DeleteOnUIThread> {
     41  public:
     42   WorkerPrioritySetter();
     43 
     44   // Posts a task to the UI thread to register to receive notifications.
     45   void Initialize();
     46 
     47   // Invoked by WorkerServiceImpl when a worker process is created.
     48   void NotifyWorkerProcessCreated();
     49 
     50  private:
     51   friend class base::RefCountedThreadSafe<WorkerPrioritySetter>;
     52   friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
     53   friend class base::DeleteHelper<WorkerPrioritySetter>;
     54   virtual ~WorkerPrioritySetter();
     55 
     56   // Posts a task to perform a worker priority update.
     57   void PostTaskToGatherAndUpdateWorkerPriorities();
     58 
     59   // Gathers up a list of the visible tabs and then updates priorities for
     60   // all the shared workers.
     61   void GatherVisibleIDsAndUpdateWorkerPriorities();
     62 
     63   // Registers as an observer to receive notifications about
     64   // widgets being shown.
     65   void RegisterObserver();
     66 
     67   // Sets priorities for shared workers given a set of visible tabs (as a
     68   // std::set of std::pair<render_process, render_view> ids.
     69   void UpdateWorkerPrioritiesFromVisibleSet(
     70       const std::set<std::pair<int, int> >* visible);
     71 
     72   // Called to refresh worker priorities when focus changes between tabs.
     73   void OnRenderWidgetVisibilityChanged(std::pair<int, int>);
     74 
     75   // NotificationObserver implementation.
     76   virtual void Observe(int type,
     77                        const NotificationSource& source,
     78                        const NotificationDetails& details) OVERRIDE;
     79 
     80   NotificationRegistrar registrar_;
     81 };
     82 
     83 WorkerPrioritySetter::WorkerPrioritySetter() {
     84 }
     85 
     86 WorkerPrioritySetter::~WorkerPrioritySetter() {
     87   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     88 }
     89 
     90 void WorkerPrioritySetter::Initialize() {
     91   BrowserThread::PostTask(
     92       BrowserThread::UI, FROM_HERE,
     93       base::Bind(&WorkerPrioritySetter::RegisterObserver, this));
     94 }
     95 
     96 void WorkerPrioritySetter::NotifyWorkerProcessCreated() {
     97   PostTaskToGatherAndUpdateWorkerPriorities();
     98 }
     99 
    100 void WorkerPrioritySetter::PostTaskToGatherAndUpdateWorkerPriorities() {
    101   BrowserThread::PostTask(
    102       BrowserThread::UI, FROM_HERE,
    103       base::Bind(
    104           &WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities,
    105           this));
    106 }
    107 
    108 void WorkerPrioritySetter::GatherVisibleIDsAndUpdateWorkerPriorities() {
    109   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    110   std::set<std::pair<int, int> >* visible_renderer_ids =
    111       new std::set<std::pair<int, int> >();
    112 
    113   // Gather up all the visible renderer process/view pairs
    114   scoped_ptr<RenderWidgetHostIterator> widgets(
    115       RenderWidgetHost::GetRenderWidgetHosts());
    116   while (RenderWidgetHost* widget = widgets->GetNextHost()) {
    117     if (widget->GetProcess()->VisibleWidgetCount() == 0)
    118       continue;
    119 
    120     RenderWidgetHostView* render_view = widget->GetView();
    121     if (render_view && render_view->IsShowing()) {
    122       visible_renderer_ids->insert(
    123           std::pair<int, int>(widget->GetProcess()->GetID(),
    124                               widget->GetRoutingID()));
    125     }
    126   }
    127 
    128   BrowserThread::PostTask(
    129       BrowserThread::IO, FROM_HERE,
    130       base::Bind(&WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet,
    131                  this, base::Owned(visible_renderer_ids)));
    132 }
    133 
    134 void WorkerPrioritySetter::UpdateWorkerPrioritiesFromVisibleSet(
    135     const std::set<std::pair<int, int> >* visible_renderer_ids) {
    136   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    137 
    138   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    139     if (!iter->process_launched())
    140       continue;
    141     bool throttle = true;
    142 
    143     for (WorkerProcessHost::Instances::const_iterator instance =
    144         iter->instances().begin(); instance != iter->instances().end();
    145         ++instance) {
    146 
    147       // This code assumes one worker per process
    148       WorkerProcessHost::Instances::const_iterator first_instance =
    149           iter->instances().begin();
    150       if (first_instance == iter->instances().end())
    151         continue;
    152 
    153       WorkerDocumentSet::DocumentInfoSet::const_iterator info =
    154           first_instance->worker_document_set()->documents().begin();
    155 
    156       for (; info != first_instance->worker_document_set()->documents().end();
    157           ++info) {
    158         std::pair<int, int> id(
    159             info->render_process_id(), info->render_view_id());
    160         if (visible_renderer_ids->find(id) != visible_renderer_ids->end()) {
    161           throttle = false;
    162           break;
    163         }
    164       }
    165 
    166       if (!throttle ) {
    167         break;
    168       }
    169     }
    170 
    171     iter->SetBackgrounded(throttle);
    172   }
    173 }
    174 
    175 void WorkerPrioritySetter::OnRenderWidgetVisibilityChanged(
    176     std::pair<int, int> id) {
    177   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    178   std::set<std::pair<int, int> > visible_renderer_ids;
    179 
    180   visible_renderer_ids.insert(id);
    181 
    182   UpdateWorkerPrioritiesFromVisibleSet(&visible_renderer_ids);
    183 }
    184 
    185 void WorkerPrioritySetter::RegisterObserver() {
    186   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    187   registrar_.Add(this, NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
    188                  NotificationService::AllBrowserContextsAndSources());
    189   registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CREATED,
    190                  NotificationService::AllBrowserContextsAndSources());
    191 }
    192 
    193 void WorkerPrioritySetter::Observe(int type,
    194     const NotificationSource& source, const NotificationDetails& details) {
    195   if (type == NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) {
    196     bool visible = *Details<bool>(details).ptr();
    197 
    198     if (visible) {
    199       int render_widget_id =
    200           Source<RenderWidgetHost>(source).ptr()->GetRoutingID();
    201       int render_process_pid =
    202           Source<RenderWidgetHost>(source).ptr()->GetProcess()->GetID();
    203 
    204       BrowserThread::PostTask(
    205           BrowserThread::IO, FROM_HERE,
    206           base::Bind(&WorkerPrioritySetter::OnRenderWidgetVisibilityChanged,
    207               this, std::pair<int, int>(render_process_pid, render_widget_id)));
    208     }
    209   }
    210   else if (type == NOTIFICATION_RENDERER_PROCESS_CREATED) {
    211     PostTaskToGatherAndUpdateWorkerPriorities();
    212   }
    213 }
    214 
    215 WorkerService* WorkerService::GetInstance() {
    216   return WorkerServiceImpl::GetInstance();
    217 }
    218 
    219 WorkerServiceImpl* WorkerServiceImpl::GetInstance() {
    220   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    221   return Singleton<WorkerServiceImpl>::get();
    222 }
    223 
    224 WorkerServiceImpl::WorkerServiceImpl()
    225     : priority_setter_(new WorkerPrioritySetter()),
    226       next_worker_route_id_(0) {
    227   priority_setter_->Initialize();
    228 }
    229 
    230 WorkerServiceImpl::~WorkerServiceImpl() {
    231   // The observers in observers_ can't be used here because they might be
    232   // gone already.
    233 }
    234 
    235 void WorkerServiceImpl::PerformTeardownForTesting() {
    236   priority_setter_ = NULL;
    237 }
    238 
    239 void WorkerServiceImpl::OnWorkerMessageFilterClosing(
    240     WorkerMessageFilter* filter) {
    241   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    242     iter->FilterShutdown(filter);
    243   }
    244 
    245   // See if that process had any queued workers.
    246   for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin();
    247        i != queued_workers_.end();) {
    248     i->RemoveFilters(filter);
    249     if (i->NumFilters() == 0) {
    250       i = queued_workers_.erase(i);
    251     } else {
    252       ++i;
    253     }
    254   }
    255 
    256   for (WorkerProcessHost::Instances::iterator i =
    257            pending_shared_workers_.begin();
    258        i != pending_shared_workers_.end(); ) {
    259      i->RemoveFilters(filter);
    260      if (i->NumFilters() == 0) {
    261       i = pending_shared_workers_.erase(i);
    262     } else {
    263       ++i;
    264     }
    265   }
    266 
    267   // Also, see if that process had any pending shared workers.
    268   for (WorkerProcessHost::Instances::iterator iter =
    269            pending_shared_workers_.begin();
    270        iter != pending_shared_workers_.end(); ) {
    271     iter->worker_document_set()->RemoveAll(filter);
    272     if (iter->worker_document_set()->IsEmpty()) {
    273       iter = pending_shared_workers_.erase(iter);
    274     } else {
    275       ++iter;
    276     }
    277   }
    278 
    279   // Either a worker proceess has shut down, in which case we can start one of
    280   // the queued workers, or a renderer has shut down, in which case it doesn't
    281   // affect anything.  We call this function in both scenarios because then we
    282   // don't have to keep track which filters are from worker processes.
    283   TryStartingQueuedWorker();
    284 }
    285 
    286 void WorkerServiceImpl::CreateWorker(
    287     const ViewHostMsg_CreateWorker_Params& params,
    288     int route_id,
    289     WorkerMessageFilter* filter,
    290     ResourceContext* resource_context,
    291     const WorkerStoragePartition& partition) {
    292   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    293   // Generate a unique route id for the browser-worker communication that's
    294   // unique among all worker processes.  That way when the worker process sends
    295   // a wrapped IPC message through us, we know which WorkerProcessHost to give
    296   // it to.
    297   WorkerProcessHost::WorkerInstance instance(
    298       params.url,
    299       params.name,
    300       next_worker_route_id(),
    301       0,
    302       params.script_resource_appcache_id,
    303       resource_context,
    304       partition);
    305   instance.AddFilter(filter, route_id);
    306   instance.worker_document_set()->Add(
    307       filter, params.document_id, filter->render_process_id(),
    308       params.render_view_route_id);
    309 
    310   CreateWorkerFromInstance(instance);
    311 }
    312 
    313 void WorkerServiceImpl::LookupSharedWorker(
    314     const ViewHostMsg_CreateWorker_Params& params,
    315     int route_id,
    316     WorkerMessageFilter* filter,
    317     ResourceContext* resource_context,
    318     const WorkerStoragePartition& partition,
    319     bool* exists,
    320     bool* url_mismatch) {
    321   *exists = true;
    322   WorkerProcessHost::WorkerInstance* instance = FindSharedWorkerInstance(
    323       params.url, params.name, partition, resource_context);
    324 
    325   if (!instance) {
    326     // If no worker instance currently exists, we need to create a pending
    327     // instance - this is to make sure that any subsequent lookups passing a
    328     // mismatched URL get the appropriate url_mismatch error at lookup time.
    329     // Having named shared workers was a Really Bad Idea due to details like
    330     // this.
    331     instance = CreatePendingInstance(params.url, params.name,
    332                                      resource_context, partition);
    333     *exists = false;
    334   }
    335 
    336   // Make sure the passed-in instance matches the URL - if not, return an
    337   // error.
    338   if (params.url != instance->url()) {
    339     *url_mismatch = true;
    340     *exists = false;
    341   } else {
    342     *url_mismatch = false;
    343     // Add our route ID to the existing instance so we can send messages to it.
    344     instance->AddFilter(filter, route_id);
    345 
    346     // Add the passed filter/document_id to the worker instance.
    347     // TODO(atwilson): This won't work if the message is from a worker process.
    348     // We don't support that yet though (this message is only sent from
    349     // renderers) but when we do, we'll need to add code to pass in the current
    350     // worker's document set for nested workers.
    351     instance->worker_document_set()->Add(
    352         filter, params.document_id, filter->render_process_id(),
    353         params.render_view_route_id);
    354   }
    355 }
    356 
    357 void WorkerServiceImpl::ForwardToWorker(const IPC::Message& message,
    358                                         WorkerMessageFilter* filter) {
    359   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    360     if (iter->FilterMessage(message, filter))
    361       return;
    362   }
    363 
    364   // TODO(jabdelmalek): tell filter that callee is gone
    365 }
    366 
    367 void WorkerServiceImpl::DocumentDetached(unsigned long long document_id,
    368                                          WorkerMessageFilter* filter) {
    369   // Any associated shared workers can be shut down.
    370   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter)
    371     iter->DocumentDetached(filter, document_id);
    372 
    373   // Remove any queued shared workers for this document.
    374   for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin();
    375        iter != queued_workers_.end();) {
    376 
    377     iter->worker_document_set()->Remove(filter, document_id);
    378     if (iter->worker_document_set()->IsEmpty()) {
    379       iter = queued_workers_.erase(iter);
    380       continue;
    381     }
    382     ++iter;
    383   }
    384 
    385   // Remove the document from any pending shared workers.
    386   for (WorkerProcessHost::Instances::iterator iter =
    387            pending_shared_workers_.begin();
    388        iter != pending_shared_workers_.end(); ) {
    389     iter->worker_document_set()->Remove(filter, document_id);
    390     if (iter->worker_document_set()->IsEmpty()) {
    391       iter = pending_shared_workers_.erase(iter);
    392     } else {
    393       ++iter;
    394     }
    395   }
    396 }
    397 
    398 bool WorkerServiceImpl::CreateWorkerFromInstance(
    399     WorkerProcessHost::WorkerInstance instance) {
    400   if (!CanCreateWorkerProcess(instance)) {
    401     queued_workers_.push_back(instance);
    402     return true;
    403   }
    404 
    405   // Check to see if this shared worker is already running (two pages may have
    406   // tried to start up the worker simultaneously).
    407   // See if a worker with this name already exists.
    408   WorkerProcessHost::WorkerInstance* existing_instance =
    409       FindSharedWorkerInstance(
    410           instance.url(), instance.name(), instance.partition(),
    411           instance.resource_context());
    412   WorkerProcessHost::WorkerInstance::FilterInfo filter_info =
    413       instance.GetFilter();
    414   // If this worker is already running, no need to create a new copy. Just
    415   // inform the caller that the worker has been created.
    416   if (existing_instance) {
    417     // Walk the worker's filter list to see if this client is listed. If not,
    418     // then it means that the worker started by the client already exited so
    419     // we should not attach to this new one (http://crbug.com/29243).
    420     if (!existing_instance->HasFilter(filter_info.first, filter_info.second))
    421       return false;
    422     filter_info.first->Send(new ViewMsg_WorkerCreated(filter_info.second));
    423     return true;
    424   }
    425 
    426   // Look to see if there's a pending instance.
    427   WorkerProcessHost::WorkerInstance* pending = FindPendingInstance(
    428       instance.url(), instance.name(), instance.partition(),
    429       instance.resource_context());
    430   // If there's no instance *and* no pending instance (or there is a pending
    431   // instance but it does not contain our filter info), then it means the
    432   // worker started up and exited already. Log a warning because this should
    433   // be a very rare occurrence and is probably a bug, but it *can* happen so
    434   // handle it gracefully.
    435   if (!pending ||
    436       !pending->HasFilter(filter_info.first, filter_info.second)) {
    437     DLOG(WARNING) << "Pending worker already exited";
    438     return false;
    439   }
    440 
    441   // Assign the accumulated document set and filter list for this pending
    442   // worker to the new instance.
    443   DCHECK(!pending->worker_document_set()->IsEmpty());
    444   instance.ShareDocumentSet(*pending);
    445   for (WorkerProcessHost::WorkerInstance::FilterList::const_iterator i =
    446            pending->filters().begin();
    447        i != pending->filters().end(); ++i) {
    448     instance.AddFilter(i->first, i->second);
    449   }
    450   RemovePendingInstances(instance.url(), instance.name(),
    451                          instance.partition(), instance.resource_context());
    452 
    453   // Remove any queued instances of this worker and copy over the filter to
    454   // this instance.
    455   for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin();
    456        iter != queued_workers_.end();) {
    457     if (iter->Matches(instance.url(), instance.name(),
    458                       instance.partition(), instance.resource_context())) {
    459       DCHECK(iter->NumFilters() == 1);
    460       WorkerProcessHost::WorkerInstance::FilterInfo filter_info =
    461           iter->GetFilter();
    462       instance.AddFilter(filter_info.first, filter_info.second);
    463       iter = queued_workers_.erase(iter);
    464     } else {
    465       ++iter;
    466     }
    467   }
    468 
    469   WorkerMessageFilter* first_filter = instance.filters().begin()->first;
    470   WorkerProcessHost* worker = new WorkerProcessHost(
    471       instance.resource_context(), instance.partition());
    472   // TODO(atwilson): This won't work if the message is from a worker process.
    473   // We don't support that yet though (this message is only sent from
    474   // renderers) but when we do, we'll need to add code to pass in the current
    475   // worker's document set for nested workers.
    476   if (!worker->Init(first_filter->render_process_id())) {
    477     delete worker;
    478     return false;
    479   }
    480 
    481   worker->CreateWorker(instance);
    482   FOR_EACH_OBSERVER(
    483       WorkerServiceObserver, observers_,
    484       WorkerCreated(instance.url(), instance.name(), worker->GetData().id,
    485                     instance.worker_route_id()));
    486   WorkerDevToolsManager::GetInstance()->WorkerCreated(worker, instance);
    487   return true;
    488 }
    489 
    490 bool WorkerServiceImpl::CanCreateWorkerProcess(
    491     const WorkerProcessHost::WorkerInstance& instance) {
    492   // Worker can be fired off if *any* parent has room.
    493   const WorkerDocumentSet::DocumentInfoSet& parents =
    494         instance.worker_document_set()->documents();
    495 
    496   for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
    497            parents.begin();
    498        parent_iter != parents.end(); ++parent_iter) {
    499     bool hit_total_worker_limit = false;
    500     if (TabCanCreateWorkerProcess(parent_iter->render_process_id(),
    501                                   parent_iter->render_view_id(),
    502                                   &hit_total_worker_limit)) {
    503       return true;
    504     }
    505     // Return false if already at the global worker limit (no need to continue
    506     // checking parent tabs).
    507     if (hit_total_worker_limit)
    508       return false;
    509   }
    510   // If we've reached here, none of the parent tabs is allowed to create an
    511   // instance.
    512   return false;
    513 }
    514 
    515 bool WorkerServiceImpl::TabCanCreateWorkerProcess(
    516     int render_process_id,
    517     int render_view_id,
    518     bool* hit_total_worker_limit) {
    519   int total_workers = 0;
    520   int workers_per_tab = 0;
    521   *hit_total_worker_limit = false;
    522   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    523     for (WorkerProcessHost::Instances::const_iterator cur_instance =
    524              iter->instances().begin();
    525          cur_instance != iter->instances().end(); ++cur_instance) {
    526       total_workers++;
    527       if (total_workers >= kMaxWorkersWhenSeparate) {
    528         *hit_total_worker_limit = true;
    529         return false;
    530       }
    531       if (cur_instance->RendererIsParent(render_process_id, render_view_id)) {
    532         workers_per_tab++;
    533         if (workers_per_tab >= kMaxWorkersPerTabWhenSeparate)
    534           return false;
    535       }
    536     }
    537   }
    538 
    539   return true;
    540 }
    541 
    542 void WorkerServiceImpl::TryStartingQueuedWorker() {
    543   if (queued_workers_.empty())
    544     return;
    545 
    546   for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin();
    547        i != queued_workers_.end();) {
    548     if (CanCreateWorkerProcess(*i)) {
    549       WorkerProcessHost::WorkerInstance instance = *i;
    550       queued_workers_.erase(i);
    551       CreateWorkerFromInstance(instance);
    552 
    553       // CreateWorkerFromInstance can modify the queued_workers_ list when it
    554       // coalesces queued instances after starting a shared worker, so we
    555       // have to rescan the list from the beginning (our iterator is now
    556       // invalid). This is not a big deal as having any queued workers will be
    557       // rare in practice so the list will be small.
    558       i = queued_workers_.begin();
    559     } else {
    560       ++i;
    561     }
    562   }
    563 }
    564 
    565 bool WorkerServiceImpl::GetRendererForWorker(int worker_process_id,
    566                                              int* render_process_id,
    567                                              int* render_view_id) const {
    568   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    569     if (iter.GetData().id != worker_process_id)
    570       continue;
    571 
    572     // This code assumes one worker per process, see function comment in header!
    573     WorkerProcessHost::Instances::const_iterator first_instance =
    574         iter->instances().begin();
    575     if (first_instance == iter->instances().end())
    576       return false;
    577 
    578     WorkerDocumentSet::DocumentInfoSet::const_iterator info =
    579         first_instance->worker_document_set()->documents().begin();
    580     *render_process_id = info->render_process_id();
    581     *render_view_id = info->render_view_id();
    582     return true;
    583   }
    584   return false;
    585 }
    586 
    587 const WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindWorkerInstance(
    588       int worker_process_id) {
    589   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    590     if (iter.GetData().id != worker_process_id)
    591         continue;
    592 
    593     WorkerProcessHost::Instances::const_iterator instance =
    594         iter->instances().begin();
    595     return instance == iter->instances().end() ? NULL : &*instance;
    596   }
    597   return NULL;
    598 }
    599 
    600 bool WorkerServiceImpl::TerminateWorker(int process_id, int route_id) {
    601   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    602     if (iter.GetData().id == process_id) {
    603       iter->TerminateWorker(route_id);
    604       return true;
    605     }
    606   }
    607   return false;
    608 }
    609 
    610 std::vector<WorkerService::WorkerInfo> WorkerServiceImpl::GetWorkers() {
    611   std::vector<WorkerService::WorkerInfo> results;
    612   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    613     const WorkerProcessHost::Instances& instances = (*iter)->instances();
    614     for (WorkerProcessHost::Instances::const_iterator i = instances.begin();
    615          i != instances.end(); ++i) {
    616       WorkerService::WorkerInfo info;
    617       info.url = i->url();
    618       info.name = i->name();
    619       info.route_id = i->worker_route_id();
    620       info.process_id = iter.GetData().id;
    621       info.handle = iter.GetData().handle;
    622       results.push_back(info);
    623     }
    624   }
    625   return results;
    626 }
    627 
    628 void WorkerServiceImpl::AddObserver(WorkerServiceObserver* observer) {
    629   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    630   observers_.AddObserver(observer);
    631 }
    632 
    633 void WorkerServiceImpl::RemoveObserver(WorkerServiceObserver* observer) {
    634   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    635   observers_.RemoveObserver(observer);
    636 }
    637 
    638 void WorkerServiceImpl::NotifyWorkerDestroyed(
    639     WorkerProcessHost* process,
    640     int worker_route_id) {
    641   WorkerDevToolsManager::GetInstance()->WorkerDestroyed(
    642       process, worker_route_id);
    643   FOR_EACH_OBSERVER(WorkerServiceObserver, observers_,
    644                     WorkerDestroyed(process->GetData().id, worker_route_id));
    645 }
    646 
    647 void WorkerServiceImpl::NotifyWorkerProcessCreated() {
    648   priority_setter_->NotifyWorkerProcessCreated();
    649 }
    650 
    651 WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindSharedWorkerInstance(
    652     const GURL& url,
    653     const base::string16& name,
    654     const WorkerStoragePartition& partition,
    655     ResourceContext* resource_context) {
    656   for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
    657     for (WorkerProcessHost::Instances::iterator instance_iter =
    658              iter->mutable_instances().begin();
    659          instance_iter != iter->mutable_instances().end();
    660          ++instance_iter) {
    661       if (instance_iter->Matches(url, name, partition, resource_context))
    662         return &(*instance_iter);
    663     }
    664   }
    665   return NULL;
    666 }
    667 
    668 WorkerProcessHost::WorkerInstance* WorkerServiceImpl::FindPendingInstance(
    669     const GURL& url,
    670     const base::string16& name,
    671     const WorkerStoragePartition& partition,
    672     ResourceContext* resource_context) {
    673   // Walk the pending instances looking for a matching pending worker.
    674   for (WorkerProcessHost::Instances::iterator iter =
    675            pending_shared_workers_.begin();
    676        iter != pending_shared_workers_.end();
    677        ++iter) {
    678     if (iter->Matches(url, name, partition, resource_context))
    679       return &(*iter);
    680   }
    681   return NULL;
    682 }
    683 
    684 
    685 void WorkerServiceImpl::RemovePendingInstances(
    686     const GURL& url,
    687     const base::string16& name,
    688     const WorkerStoragePartition& partition,
    689     ResourceContext* resource_context) {
    690   // Walk the pending instances looking for a matching pending worker.
    691   for (WorkerProcessHost::Instances::iterator iter =
    692            pending_shared_workers_.begin();
    693        iter != pending_shared_workers_.end(); ) {
    694     if (iter->Matches(url, name, partition, resource_context)) {
    695       iter = pending_shared_workers_.erase(iter);
    696     } else {
    697       ++iter;
    698     }
    699   }
    700 }
    701 
    702 WorkerProcessHost::WorkerInstance* WorkerServiceImpl::CreatePendingInstance(
    703     const GURL& url,
    704     const base::string16& name,
    705     ResourceContext* resource_context,
    706     const WorkerStoragePartition& partition) {
    707   // Look for an existing pending shared worker.
    708   WorkerProcessHost::WorkerInstance* instance =
    709       FindPendingInstance(url, name, partition, resource_context);
    710   if (instance)
    711     return instance;
    712 
    713   // No existing pending worker - create a new one.
    714   WorkerProcessHost::WorkerInstance pending(
    715       url, true, name, resource_context, partition);
    716   pending_shared_workers_.push_back(pending);
    717   return &pending_shared_workers_.back();
    718 }
    719 
    720 }  // namespace content
    721