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