Chromium Code Reviews| Index: chrome/browser/worker_host/worker_service.cc |
| diff --git a/chrome/browser/worker_host/worker_service.cc b/chrome/browser/worker_host/worker_service.cc |
| index 334917f78052ef6273ce70797b0bfccd8d65d1e8..5b0f44e0ba48bc6a252222acce0e313bbc8d0183 100644 |
| --- a/chrome/browser/worker_host/worker_service.cc |
| +++ b/chrome/browser/worker_host/worker_service.cc |
| @@ -15,6 +15,7 @@ |
| #include "chrome/browser/worker_host/worker_process_host.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/notification_service.h" |
| +#include "chrome/common/render_messages.h" |
| #include "chrome/common/worker_messages.h" |
| #include "net/base/registry_controlled_domain.h" |
| @@ -50,7 +51,6 @@ bool WorkerService::CreateWorker(const GURL &url, |
| int renderer_id, |
| int render_view_route_id, |
| IPC::Message::Sender* sender, |
| - int sender_id, |
| int sender_route_id) { |
| // Generate a unique route id for the browser-worker communication that's |
| // unique among all worker processes. That way when the worker process sends |
| @@ -62,11 +62,10 @@ bool WorkerService::CreateWorker(const GURL &url, |
| instance.renderer_id = renderer_id; |
| instance.render_view_route_id = render_view_route_id; |
| instance.worker_route_id = next_worker_route_id(); |
| - instance.sender = sender; |
| - instance.sender_id = sender_id; |
| - instance.sender_route_id = sender_route_id; |
| instance.is_shared = is_shared; |
| + instance.AddSender(sender, sender_route_id); |
| + |
| WorkerProcessHost* worker = NULL; |
| if (CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kWebWorkerProcessPerCore)) { |
| @@ -81,6 +80,38 @@ bool WorkerService::CreateWorker(const GURL &url, |
| } |
| } |
| + // Check to see if this shared worker is already running (two pages may have |
| + // tried to start up the worker simultaneously) |
|
jam
2009/11/12 20:11:23
nit: period
|
| + if (is_shared) { |
| + // See if a worker with this name already exists. |
| + WorkerProcessHost::WorkerInstance* existing_instance = |
| + FindSharedWorkerInstance(url, name); |
| + // If this worker is already running, no need to create a new copy. Just |
| + // inform the caller that the worker has been created. |
| + if (existing_instance) { |
| + existing_instance->AddSender(sender, sender_route_id); |
| + sender->Send(new ViewMsg_WorkerCreated(sender_route_id)); |
| + return true; |
| + } |
| + |
| + // Look to see if there's a pending instance. |
| + WorkerProcessHost::WorkerInstance* pending = FindPendingInstance(url, name); |
| + // If there's no instance *and* no pending instance, then it means the |
| + // worker started up and exited already. Log a warning because this should |
| + // be a very rare occurrence and is probably a bug, but it *can* happen so |
| + // handle it gracefully. |
| + if (!pending) { |
| + DLOG(WARNING) << "Pending worker already exited"; |
| + return false; |
| + } |
| + |
| + // Assign the accumulated document set and sender list for this pending |
| + // worker to the new instance. |
| + DCHECK(!pending->document_set.empty()); |
| + instance.document_set = pending->document_set; |
| + RemovePendingInstance(url, name); |
| + } |
| + |
| if (!worker) { |
| worker = new WorkerProcessHost(resource_dispatcher_host_); |
| if (!worker->Init()) { |
| @@ -93,15 +124,88 @@ bool WorkerService::CreateWorker(const GURL &url, |
| return true; |
| } |
| -void WorkerService::CancelCreateDedicatedWorker(int sender_id, |
| +bool WorkerService::LookupSharedWorker(const GURL &url, |
| + const string16& name, |
| + unsigned long long document_id, |
| + IPC::Message::Sender* sender, |
| + int sender_route_id, |
| + bool* url_mismatch) { |
| + bool found_instance = true; |
| + WorkerProcessHost::WorkerInstance* instance = |
| + FindSharedWorkerInstance(url, name); |
| + |
| + if (!instance) { |
| + // If no worker instance currently exists, we need to create a pending |
| + // instance - this is to make sure that any subsequent lookups passing a |
| + // mismatched URL get the appropriate url_mismatch error at lookup time. |
| + // Having named shared workers was a Really Bad Idea due to details like |
| + // this. |
| + instance = CreatePendingInstance(url, name); |
| + found_instance = false; |
| + } |
| + |
| + // Make sure the passed-in instance matches the URL - if not, return an |
| + // error. |
| + if (url != instance->url) { |
| + *url_mismatch = true; |
| + return false; |
| + } else { |
| + *url_mismatch = false; |
| + } |
| + |
| + // Add our route ID to the existing instance so we can send messages to it. |
| + if (found_instance) { |
|
jam
2009/11/12 20:11:23
nit: chrome style is to not use brace brackets for
|
| + instance->AddSender(sender, sender_route_id); |
| + } |
| + // Add the passed sender/document_id to the worker instance. |
| + instance->AddToDocumentSet(sender, document_id); |
| + return found_instance; |
| +} |
| + |
| +void WorkerService::DocumentDetached(IPC::Message::Sender* sender, |
| + unsigned long long document_id) { |
| + for (ChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS); |
| + !iter.Done(); ++iter) { |
| + WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter); |
| + worker->DocumentDetached(sender, document_id); |
| + } |
| + |
| + // Remove any queued shared workers for this document. |
| + for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin(); |
|
jam
2009/11/12 20:11:23
nit: in the loop above and below, you use "iter",
|
| + i != queued_workers_.end();) { |
| + if (i->is_shared) { |
| + i->RemoveFromDocumentSet(sender, document_id); |
| + if (i->document_set.empty()) { |
| + i = queued_workers_.erase(i); |
| + continue; |
| + } |
| + } |
| + ++i; |
| + } |
| + |
| + // Remove the document from any pending shared workers. |
| + for (WorkerProcessHost::Instances::iterator iter = |
| + pending_shared_workers_.begin(); |
| + iter != pending_shared_workers_.end(); ) { |
| + iter->RemoveFromDocumentSet(sender, document_id); |
| + if (iter->document_set.empty()) { |
| + iter = pending_shared_workers_.erase(iter); |
| + } else { |
| + ++iter; |
| + } |
| + } |
| + |
| +} |
| + |
| +void WorkerService::CancelCreateDedicatedWorker(IPC::Message::Sender* sender, |
| int sender_route_id) { |
| for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin(); |
| i != queued_workers_.end(); ++i) { |
| - if (i->sender_id == sender_id && |
| - i->sender_route_id == sender_route_id) { |
| - queued_workers_.erase(i); |
| - return; |
| - } |
| + if (i->HasSender(sender, sender_route_id)) { |
| + DCHECK(!i->is_shared); |
| + queued_workers_.erase(i); |
| + return; |
| + } |
| } |
| // There could be a race condition where the WebWorkerProxy told us to cancel |
| @@ -113,12 +217,11 @@ void WorkerService::CancelCreateDedicatedWorker(int sender_id, |
| for (WorkerProcessHost::Instances::const_iterator instance = |
| worker->instances().begin(); |
| instance != worker->instances().end(); ++instance) { |
| - if (instance->sender_id == sender_id && |
| - instance->sender_route_id == sender_route_id) { |
| + if (instance->HasSender(sender, sender_route_id)) { |
| // Fake a worker destroyed message so that WorkerProcessHost cleans up |
| // properly. |
| WorkerHostMsg_WorkerContextDestroyed msg(sender_route_id); |
| - ForwardMessage(msg, sender_id); |
| + ForwardMessage(msg, sender); |
| return; |
| } |
| } |
| @@ -128,11 +231,11 @@ void WorkerService::CancelCreateDedicatedWorker(int sender_id, |
| } |
| void WorkerService::ForwardMessage(const IPC::Message& message, |
| - int sender_pid) { |
| + IPC::Message::Sender* sender) { |
| for (ChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS); |
| !iter.Done(); ++iter) { |
| WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter); |
| - if (worker->FilterMessage(message, sender_pid)) |
| + if (worker->FilterMessage(message, sender)) |
| return; |
| } |
| @@ -237,12 +340,25 @@ void WorkerService::SenderShutdown(IPC::Message::Sender* sender) { |
| // See if that render process had any queued workers. |
| for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin(); |
| i != queued_workers_.end();) { |
| - if (i->sender == sender) { |
| + i->RemoveSenders(sender); |
| + if (i->senders.empty()) { |
| i = queued_workers_.erase(i); |
| } else { |
| ++i; |
| } |
| } |
| + |
| + // Also, see if that render process had any pending shared workers. |
| + for (WorkerProcessHost::Instances::iterator iter = |
| + pending_shared_workers_.begin(); |
| + iter != pending_shared_workers_.end(); ) { |
| + iter->RemoveAllAssociatedDocuments(sender); |
| + if (iter->document_set.empty()) { |
| + iter = pending_shared_workers_.erase(iter); |
| + } else { |
| + ++iter; |
| + } |
| + } |
| } |
| void WorkerService::WorkerProcessDestroyed(WorkerProcessHost* process) { |
| @@ -281,3 +397,66 @@ const WorkerProcessHost::WorkerInstance* WorkerService::FindWorkerInstance( |
| } |
| return NULL; |
| } |
| + |
| +WorkerProcessHost::WorkerInstance* |
| +WorkerService::FindSharedWorkerInstance(const GURL& url, const string16& name) { |
| + for (ChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS); |
| + !iter.Done(); ++iter) { |
| + WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter); |
| + for (WorkerProcessHost::Instances::iterator instance_iter = |
| + worker->mutable_instances().begin(); |
| + instance_iter != worker->mutable_instances().end(); |
| + ++instance_iter) { |
| + if (instance_iter->Matches(url, name)) |
| + return &(*instance_iter); |
| + } |
| + } |
| + return NULL; |
| +} |
| + |
| +WorkerProcessHost::WorkerInstance* |
| +WorkerService::FindPendingInstance(const GURL& url, const string16& name) { |
| + // Walk the pending instances looking for a matching pending worker. |
| + for (WorkerProcessHost::Instances::iterator iter = |
| + pending_shared_workers_.begin(); |
| + iter != pending_shared_workers_.end(); |
| + ++iter) { |
| + if (iter->Matches(url, name)) { |
| + return &(*iter); |
| + } |
| + } |
| + return NULL; |
| +} |
| + |
| + |
| +void WorkerService::RemovePendingInstance(const GURL& url, |
| + const string16& name) { |
| + // Walk the pending instances looking for a matching pending worker. |
| + for (WorkerProcessHost::Instances::iterator iter = |
| + pending_shared_workers_.begin(); |
| + iter != pending_shared_workers_.end(); |
| + ++iter) { |
| + if (iter->Matches(url, name)) { |
| + pending_shared_workers_.erase(iter); |
| + break; |
| + } |
| + } |
| +} |
| + |
| +WorkerProcessHost::WorkerInstance* |
| +WorkerService::CreatePendingInstance(const GURL& url, |
| + const string16& name) { |
| + // Look for an existing pending worker. |
| + WorkerProcessHost::WorkerInstance* instance = |
| + FindPendingInstance(url, name); |
| + if (instance) |
| + return instance; |
| + |
| + // No existing pending worker - create a new one. |
| + WorkerProcessHost::WorkerInstance pending; |
| + pending.url = url; |
| + pending.name = name; |
| + pending.is_shared = true; |
| + pending_shared_workers_.push_back(pending); |
| + return &pending_shared_workers_.back(); |
| +} |