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