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(); |
+} |