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