OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/worker_host/worker_service_impl.h" | 5 #include "content/browser/worker_host/worker_service_impl.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 375 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
386 iter != pending_shared_workers_.end(); ) { | 386 iter != pending_shared_workers_.end(); ) { |
387 iter->worker_document_set()->Remove(filter, document_id); | 387 iter->worker_document_set()->Remove(filter, document_id); |
388 if (iter->worker_document_set()->IsEmpty()) { | 388 if (iter->worker_document_set()->IsEmpty()) { |
389 iter = pending_shared_workers_.erase(iter); | 389 iter = pending_shared_workers_.erase(iter); |
390 } else { | 390 } else { |
391 ++iter; | 391 ++iter; |
392 } | 392 } |
393 } | 393 } |
394 } | 394 } |
395 | 395 |
| 396 int WorkerServiceImpl::CreateAndStartEmbeddedWorker( |
| 397 ResourceContext* resource_context, |
| 398 const WorkerStoragePartition& partition, |
| 399 const GURL& url, |
| 400 const std::string& raw_handler_source, |
| 401 int64 associated_appcache_id) { |
| 402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 403 // Generate a unique route id for the browser-worker communication that's |
| 404 // unique among all worker processes. That way when the worker process sends |
| 405 // a wrapped IPC message through us, we know which WorkerProcessHost to give |
| 406 // it to. |
| 407 int worker_route_id = next_worker_route_id(); |
| 408 WorkerProcessHost::WorkerInstance instance( |
| 409 url, |
| 410 string16(), |
| 411 worker_route_id, |
| 412 0, |
| 413 0, // associated_appcache_id, |
| 414 resource_context, |
| 415 partition); |
| 416 instance.is_embedded_worker_ = true; |
| 417 CreateWorkerFromInstance(instance); |
| 418 SendToEmbeddedWorker( |
| 419 worker_route_id, |
| 420 new WorkerMsg_StartEmbeddedWorkerContext( |
| 421 worker_route_id, url, raw_handler_source)); |
| 422 return worker_route_id; |
| 423 } |
| 424 |
| 425 namespace { |
| 426 // Helper to get the WPH for a given embedded worker id. |
| 427 WorkerProcessHost* FindEmbeddedWorkerProcessHost(int worker_route_id) { |
| 428 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { |
| 429 const WorkerProcessHost::Instances& instances = (*iter)->instances(); |
| 430 for (WorkerProcessHost::Instances::const_iterator i = instances.begin(); |
| 431 i != instances.end(); ++i) { |
| 432 if (i->is_embedded_worker_ && i->worker_route_id() == worker_route_id) { |
| 433 return *iter; |
| 434 } |
| 435 } |
| 436 } |
| 437 return NULL; |
| 438 } |
| 439 } // namespace |
| 440 |
| 441 bool WorkerServiceImpl::SendToEmbeddedWorker( |
| 442 int worker_route_id, IPC::Message* message) { |
| 443 WorkerProcessHost* host = FindEmbeddedWorkerProcessHost(worker_route_id); |
| 444 if (!host) { |
| 445 delete message; |
| 446 return false; |
| 447 } |
| 448 return host->Send(message); |
| 449 } |
| 450 |
| 451 void WorkerServiceImpl::TerminateEmbeddedWorker(int worker_route_id) { |
| 452 WorkerProcessHost* host = FindEmbeddedWorkerProcessHost(worker_route_id); |
| 453 if (!host) |
| 454 return; |
| 455 host->TerminateWorker(worker_route_id); |
| 456 } |
| 457 |
396 bool WorkerServiceImpl::CreateWorkerFromInstance( | 458 bool WorkerServiceImpl::CreateWorkerFromInstance( |
397 WorkerProcessHost::WorkerInstance instance) { | 459 WorkerProcessHost::WorkerInstance instance) { |
398 if (!CanCreateWorkerProcess(instance)) { | 460 if (!instance.is_embedded_worker_) { |
399 queued_workers_.push_back(instance); | 461 if (!CanCreateWorkerProcess(instance)) { |
400 return true; | 462 queued_workers_.push_back(instance); |
| 463 return true; |
| 464 } |
| 465 |
| 466 // Check to see if this shared worker is already running (two pages may have |
| 467 // tried to start up the worker simultaneously). |
| 468 // See if a worker with this name already exists. |
| 469 WorkerProcessHost::WorkerInstance* existing_instance = |
| 470 FindSharedWorkerInstance( |
| 471 instance.url(), instance.name(), instance.partition(), |
| 472 instance.resource_context()); |
| 473 WorkerProcessHost::WorkerInstance::FilterInfo filter_info = |
| 474 instance.GetFilter(); |
| 475 // If this worker is already running, no need to create a new copy. Just |
| 476 // inform the caller that the worker has been created. |
| 477 if (existing_instance) { |
| 478 // Walk the worker's filter list to see if this client is listed. If not, |
| 479 // then it means that the worker started by the client already exited so |
| 480 // we should not attach to this new one (http://crbug.com/29243). |
| 481 if (!existing_instance->HasFilter(filter_info.first, filter_info.second)) |
| 482 return false; |
| 483 filter_info.first->Send(new ViewMsg_WorkerCreated(filter_info.second)); |
| 484 return true; |
| 485 } |
| 486 |
| 487 // Look to see if there's a pending instance. |
| 488 WorkerProcessHost::WorkerInstance* pending = FindPendingInstance( |
| 489 instance.url(), instance.name(), instance.partition(), |
| 490 instance.resource_context()); |
| 491 // If there's no instance *and* no pending instance (or there is a pending |
| 492 // instance but it does not contain our filter info), then it means the |
| 493 // worker started up and exited already. Log a warning because this should |
| 494 // be a very rare occurrence and is probably a bug, but it *can* happen so |
| 495 // handle it gracefully. |
| 496 if (!pending || |
| 497 !pending->HasFilter(filter_info.first, filter_info.second)) { |
| 498 DLOG(WARNING) << "Pending worker already exited"; |
| 499 return false; |
| 500 } |
| 501 |
| 502 // Assign the accumulated document set and filter list for this pending |
| 503 // worker to the new instance. |
| 504 DCHECK(!pending->worker_document_set()->IsEmpty()); |
| 505 instance.ShareDocumentSet(*pending); |
| 506 for (WorkerProcessHost::WorkerInstance::FilterList::const_iterator i = |
| 507 pending->filters().begin(); |
| 508 i != pending->filters().end(); ++i) { |
| 509 instance.AddFilter(i->first, i->second); |
| 510 } |
| 511 RemovePendingInstances(instance.url(), instance.name(), |
| 512 instance.partition(), instance.resource_context()); |
| 513 |
| 514 // Remove any queued instances of this worker and copy over the filter to |
| 515 // this instance. |
| 516 for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin(); |
| 517 iter != queued_workers_.end();) { |
| 518 if (iter->Matches(instance.url(), instance.name(), |
| 519 instance.partition(), instance.resource_context())) { |
| 520 DCHECK(iter->NumFilters() == 1); |
| 521 WorkerProcessHost::WorkerInstance::FilterInfo filter_info = |
| 522 iter->GetFilter(); |
| 523 instance.AddFilter(filter_info.first, filter_info.second); |
| 524 iter = queued_workers_.erase(iter); |
| 525 } else { |
| 526 ++iter; |
| 527 } |
| 528 } |
| 529 DCHECK_EQ(instance.filters().begin()->first->render_process_id(), |
| 530 instance.parent_process_id()); |
401 } | 531 } |
402 | 532 |
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( | 533 WorkerProcessHost* worker = new WorkerProcessHost( |
469 instance.resource_context(), instance.partition()); | 534 instance.resource_context(), instance.partition()); |
470 // TODO(atwilson): This won't work if the message is from a worker process. | 535 // 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 | 536 // 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 | 537 // renderers) but when we do, we'll need to add code to pass in the current |
473 // worker's document set for nested workers. | 538 // worker's document set for nested workers. |
474 if (!worker->Init(first_filter->render_process_id())) { | 539 if (!worker->Init(instance.parent_process_id())) { |
475 delete worker; | 540 delete worker; |
476 return false; | 541 return false; |
477 } | 542 } |
478 | 543 |
479 worker->CreateWorker(instance); | 544 worker->CreateWorker(instance); |
480 FOR_EACH_OBSERVER( | 545 FOR_EACH_OBSERVER( |
481 WorkerServiceObserver, observers_, | 546 WorkerServiceObserver, observers_, |
482 WorkerCreated(instance.url(), instance.name(), worker->GetData().id, | 547 WorkerCreated(instance.url(), instance.name(), worker->GetData().id, |
483 instance.worker_route_id())); | 548 instance.worker_route_id())); |
484 WorkerDevToolsManager::GetInstance()->WorkerCreated(worker, instance); | 549 WorkerDevToolsManager::GetInstance()->WorkerCreated(worker, instance); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
561 } | 626 } |
562 | 627 |
563 bool WorkerServiceImpl::GetRendererForWorker(int worker_process_id, | 628 bool WorkerServiceImpl::GetRendererForWorker(int worker_process_id, |
564 int* render_process_id, | 629 int* render_process_id, |
565 int* render_view_id) const { | 630 int* render_view_id) const { |
566 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | 631 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { |
567 if (iter.GetData().id != worker_process_id) | 632 if (iter.GetData().id != worker_process_id) |
568 continue; | 633 continue; |
569 | 634 |
570 // This code assumes one worker per process, see function comment in header! | 635 // This code assumes one worker per process, see function comment in header! |
| 636 // ^^^ gee, lovely ^^^ |
571 WorkerProcessHost::Instances::const_iterator first_instance = | 637 WorkerProcessHost::Instances::const_iterator first_instance = |
572 iter->instances().begin(); | 638 iter->instances().begin(); |
573 if (first_instance == iter->instances().end()) | 639 if (first_instance == iter->instances().end()) |
574 return false; | 640 return false; |
575 | 641 |
576 WorkerDocumentSet::DocumentInfoSet::const_iterator info = | 642 WorkerDocumentSet::DocumentInfoSet::const_iterator info = |
577 first_instance->worker_document_set()->documents().begin(); | 643 first_instance->worker_document_set()->documents().begin(); |
578 *render_process_id = info->render_process_id(); | 644 *render_process_id = info->render_process_id(); |
579 *render_view_id = info->render_view_id(); | 645 *render_view_id = info->render_view_id(); |
580 return true; | 646 return true; |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
709 return instance; | 775 return instance; |
710 | 776 |
711 // No existing pending worker - create a new one. | 777 // No existing pending worker - create a new one. |
712 WorkerProcessHost::WorkerInstance pending( | 778 WorkerProcessHost::WorkerInstance pending( |
713 url, true, name, resource_context, partition); | 779 url, true, name, resource_context, partition); |
714 pending_shared_workers_.push_back(pending); | 780 pending_shared_workers_.push_back(pending); |
715 return &pending_shared_workers_.back(); | 781 return &pending_shared_workers_.back(); |
716 } | 782 } |
717 | 783 |
718 } // namespace content | 784 } // namespace content |
OLD | NEW |