summaryrefslogtreecommitdiffstats
path: root/chrome/browser/worker_host
diff options
context:
space:
mode:
authoratwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-13 01:13:37 +0000
committeratwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-13 01:13:37 +0000
commit30447b6a5d6109d455f2c6262454105d5adf8f94 (patch)
tree3ef69964f7cf3e10d662af94f88e05288e16ad8c /chrome/browser/worker_host
parent6eca5f19ffabd672349df42595551e7a4b1da987 (diff)
downloadchromium_src-30447b6a5d6109d455f2c6262454105d5adf8f94.zip
chromium_src-30447b6a5d6109d455f2c6262454105d5adf8f94.tar.gz
chromium_src-30447b6a5d6109d455f2c6262454105d5adf8f94.tar.bz2
Added lifecycle management and sharing support for SharedWorkers. SharedWorkers
can now outlive their parent pages and can be shared by multiple instances across multiple tabs. BUG=26233 TEST=ui tests Review URL: http://codereview.chromium.org/390017 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31868 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/worker_host')
-rw-r--r--chrome/browser/worker_host/worker_process_host.cc227
-rw-r--r--chrome/browser/worker_host/worker_process_host.h104
-rw-r--r--chrome/browser/worker_host/worker_service.cc228
-rw-r--r--chrome/browser/worker_host/worker_service.h38
4 files changed, 532 insertions, 65 deletions
diff --git a/chrome/browser/worker_host/worker_process_host.cc b/chrome/browser/worker_host/worker_process_host.cc
index a5cbbb8..de292e9 100644
--- a/chrome/browser/worker_host/worker_process_host.cc
+++ b/chrome/browser/worker_host/worker_process_host.cc
@@ -80,7 +80,7 @@ WorkerProcessHost::~WorkerProcessHost() {
for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
ChromeThread::PostTask(
ChromeThread::UI, FROM_HERE,
- new WorkerCrashTask(i->renderer_id, i->render_view_route_id));
+ new WorkerCrashTask(i->renderer_id(), i->render_view_route_id()));
}
ChildProcessSecurityPolicy::GetInstance()->Remove(id());
@@ -141,26 +141,25 @@ bool WorkerProcessHost::Init() {
void WorkerProcessHost::CreateWorker(const WorkerInstance& instance) {
ChildProcessSecurityPolicy::GetInstance()->GrantRequestURL(
- id(), instance.url);
+ id(), instance.url());
instances_.push_back(instance);
- Send(new WorkerProcessMsg_CreateWorker(instance.url,
- instance.is_shared,
- instance.name,
- instance.worker_route_id));
+ Send(new WorkerProcessMsg_CreateWorker(instance.url(),
+ instance.is_shared(),
+ instance.name(),
+ instance.worker_route_id()));
UpdateTitle();
- instances_.back().sender->Send(
- new ViewMsg_WorkerCreated(instance.sender_route_id));
+ WorkerInstance::SenderInfo info = instances_.back().GetSender();
+ info.first->Send(new ViewMsg_WorkerCreated(info.second));
}
bool WorkerProcessHost::FilterMessage(const IPC::Message& message,
- int sender_pid) {
+ IPC::Message::Sender* sender) {
for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
- if (i->sender_id == sender_pid &&
- i->sender_route_id == message.routing_id()) {
+ if (!i->is_closed() && i->HasSender(sender, message.routing_id())) {
RelayMessage(
- message, this, i->worker_route_id, next_route_id_callback_.get());
+ message, this, i->worker_route_id(), next_route_id_callback_.get());
return true;
}
}
@@ -174,6 +173,20 @@ URLRequestContext* WorkerProcessHost::GetRequestContext(
return NULL;
}
+// Sent to notify the browser process when a worker context invokes close(), so
+// no new connections are sent to shared workers.
+void WorkerProcessHost::OnWorkerContextClosed(int worker_route_id) {
+ for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
+ if (i->worker_route_id() == worker_route_id) {
+ // Set the closed flag - this will stop any further messages from
+ // being sent to the worker (messages can still be sent from the worker,
+ // for exception reporting, etc).
+ i->set_closed(true);
+ break;
+ }
+ }
+}
+
void WorkerProcessHost::OnMessageReceived(const IPC::Message& message) {
bool msg_is_ok = true;
bool handled = MessagePortDispatcher::GetInstance()->OnMessageReceived(
@@ -183,8 +196,11 @@ void WorkerProcessHost::OnMessageReceived(const IPC::Message& message) {
handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(WorkerProcessHost, message, msg_is_ok)
IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWorker, OnCreateWorker)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_LookupSharedWorker, OnLookupSharedWorker)
IPC_MESSAGE_HANDLER(ViewHostMsg_CancelCreateDedicatedWorker,
OnCancelCreateDedicatedWorker)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed,
+ OnWorkerContextClosed);
IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardToWorker,
OnForwardToWorker)
IPC_MESSAGE_UNHANDLED(handled = false)
@@ -200,10 +216,15 @@ void WorkerProcessHost::OnMessageReceived(const IPC::Message& message) {
return;
for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
- if (i->worker_route_id == message.routing_id()) {
- CallbackWithReturnValue<int>::Type* next_route_id =
- GetNextRouteIdCallback(i->sender);
- RelayMessage(message, i->sender, i->sender_route_id, next_route_id);
+ if (i->worker_route_id() == message.routing_id()) {
+ if (!i->is_shared()) {
+ // Don't relay messages from shared workers (all communication is via
+ // the message port).
+ WorkerInstance::SenderInfo info = i->GetSender();
+ CallbackWithReturnValue<int>::Type* next_route_id =
+ GetNextRouteIdCallback(info.first);
+ RelayMessage(message, info.first, info.second, next_route_id);
+ }
if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) {
instances_.erase(i);
@@ -293,8 +314,18 @@ void WorkerProcessHost::RelayMessage(
void WorkerProcessHost::SenderShutdown(IPC::Message::Sender* sender) {
for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
- if (i->sender == sender) {
- Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id));
+ bool shutdown = false;
+ i->RemoveSenders(sender);
+ if (i->is_shared()) {
+ i->RemoveAllAssociatedDocuments(sender);
+ if (i->IsDocumentSetEmpty()) {
+ shutdown = true;
+ }
+ } else if (i->NumSenders() == 0) {
+ shutdown = true;
+ }
+ if (shutdown) {
+ Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
i = instances_.erase(i);
} else {
++i;
@@ -306,13 +337,13 @@ void WorkerProcessHost::UpdateTitle() {
std::set<std::string> titles;
for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
std::string title =
- net::RegistryControlledDomainService::GetDomainAndRegistry(i->url);
+ net::RegistryControlledDomainService::GetDomainAndRegistry(i->url());
// Use the host name if the domain is empty, i.e. localhost or IP address.
if (title.empty())
- title = i->url.host();
+ title = i->url().host();
// If the host name is empty, i.e. file url, use the path.
if (title.empty())
- title = i->url.path();
+ title = i->url().path();
titles.insert(title);
}
@@ -327,6 +358,17 @@ void WorkerProcessHost::UpdateTitle() {
set_name(ASCIIToWide(display_title));
}
+void WorkerProcessHost::OnLookupSharedWorker(const GURL& url,
+ const string16& name,
+ unsigned long long document_id,
+ int* route_id,
+ bool* url_mismatch) {
+ int new_route_id = WorkerService::GetInstance()->next_worker_route_id();
+ bool worker_found = WorkerService::GetInstance()->LookupSharedWorker(
+ url, name, document_id, this, new_route_id, url_mismatch);
+ *route_id = worker_found ? new_route_id : MSG_ROUTING_NONE;
+}
+
void WorkerProcessHost::OnCreateWorker(const GURL& url,
bool is_shared,
const string16& name,
@@ -335,14 +377,149 @@ void WorkerProcessHost::OnCreateWorker(const GURL& url,
DCHECK(instances_.size() == 1); // Only called when one process per worker.
*route_id = WorkerService::GetInstance()->next_worker_route_id();
WorkerService::GetInstance()->CreateWorker(
- url, is_shared, name, instances_.front().renderer_id,
- instances_.front().render_view_route_id, this, id(), *route_id);
+ url, is_shared, name, instances_.front().renderer_id(),
+ instances_.front().render_view_route_id(), this, *route_id);
}
void WorkerProcessHost::OnCancelCreateDedicatedWorker(int route_id) {
- WorkerService::GetInstance()->CancelCreateDedicatedWorker(id(), route_id);
+ WorkerService::GetInstance()->CancelCreateDedicatedWorker(this, route_id);
}
void WorkerProcessHost::OnForwardToWorker(const IPC::Message& message) {
- WorkerService::GetInstance()->ForwardMessage(message, id());
+ WorkerService::GetInstance()->ForwardMessage(message, this);
+}
+
+void WorkerProcessHost::DocumentDetached(IPC::Message::Sender* parent,
+ unsigned long long document_id)
+{
+ // Walk all instances and remove the document from their document set.
+ for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
+ if (!i->is_shared()) {
+ ++i;
+ } else {
+ i->RemoveFromDocumentSet(parent, document_id);
+ if (i->IsDocumentSetEmpty()) {
+ // This worker has no more associated documents - shut it down.
+ Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
+ i = instances_.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+}
+
+WorkerProcessHost::WorkerInstance::WorkerInstance(const GURL& url,
+ bool is_shared,
+ const string16& name,
+ int renderer_id,
+ int render_view_route_id,
+ int worker_route_id)
+ : url_(url),
+ shared_(is_shared),
+ closed_(false),
+ name_(name),
+ renderer_id_(renderer_id),
+ render_view_route_id_(render_view_route_id),
+ worker_route_id_(worker_route_id) {
+}
+
+// Compares an instance based on the algorithm in the WebWorkers spec - an
+// instance matches if the origins of the URLs match, and:
+// a) the names are non-empty and equal
+// -or-
+// b) the names are both empty, and the urls are equal
+bool WorkerProcessHost::WorkerInstance::Matches(
+ const GURL& match_url, const string16& match_name) const {
+ // Only match open shared workers.
+ if (!shared_ || closed_)
+ return false;
+
+ if (url_.GetOrigin() != match_url.GetOrigin())
+ return false;
+
+ if (name_.empty() && match_name.empty())
+ return url_ == match_url;
+
+ return name_ == match_name;
+}
+
+void WorkerProcessHost::WorkerInstance::AddToDocumentSet(
+ IPC::Message::Sender* parent, unsigned long long document_id) {
+ DocumentInfo info(parent, document_id);
+ document_set_.insert(info);
+}
+
+bool WorkerProcessHost::WorkerInstance::IsInDocumentSet(
+ IPC::Message::Sender* parent, unsigned long long document_id) const {
+ DocumentInfo info(parent, document_id);
+ return document_set_.find(info) != document_set_.end();
+}
+
+void WorkerProcessHost::WorkerInstance::RemoveFromDocumentSet(
+ IPC::Message::Sender* parent, unsigned long long document_id) {
+ DocumentInfo info(parent, document_id);
+ document_set_.erase(info);
}
+
+void WorkerProcessHost::WorkerInstance::RemoveAllAssociatedDocuments(
+ IPC::Message::Sender* parent) {
+ for (DocumentSet::iterator i = document_set_.begin();
+ i != document_set_.end();) {
+ // Windows set.erase() has a non-standard API (invalidates the iter).
+#if defined(OS_WIN)
+ if (i->first == parent)
+ i = document_set_.erase(i);
+ else
+ ++i;
+#else
+ if (i->first == parent)
+ document_set_.erase(i);
+ ++i;
+#endif
+ }
+}
+
+void WorkerProcessHost::WorkerInstance::AddSender(IPC::Message::Sender* sender,
+ int sender_route_id) {
+ SenderInfo info(sender, sender_route_id);
+ senders_.insert(info);
+ // Only shared workers can have more than one associated sender.
+ DCHECK(shared_ || senders_.size() == 1);
+}
+
+void WorkerProcessHost::WorkerInstance::RemoveSender(
+ IPC::Message::Sender* sender, int sender_route_id) {
+ SenderInfo info(sender, sender_route_id);
+ senders_.erase(info);
+}
+
+void WorkerProcessHost::WorkerInstance::RemoveSenders(
+ IPC::Message::Sender* sender) {
+ for (SenderSet::iterator i = senders_.begin(); i != senders_.end();) {
+ // Windows set.erase() has a non-standard API (invalidates the iter).
+#if defined(OS_WIN)
+ if (i->first == sender)
+ i = senders_.erase(i);
+ else
+ ++i;
+#else
+ if (i->first == sender)
+ senders_.erase(i);
+ ++i;
+#endif
+ }
+}
+
+bool WorkerProcessHost::WorkerInstance::HasSender(
+ IPC::Message::Sender* sender, int sender_route_id) const {
+ SenderInfo info(sender, sender_route_id);
+ return senders_.find(info) != senders_.end();
+}
+
+WorkerProcessHost::WorkerInstance::SenderInfo
+WorkerProcessHost::WorkerInstance::GetSender() const {
+ DCHECK(NumSenders() == 1);
+ return *senders_.begin();
+}
+
diff --git a/chrome/browser/worker_host/worker_process_host.h b/chrome/browser/worker_host/worker_process_host.h
index 1fae373..16ddd13 100644
--- a/chrome/browser/worker_host/worker_process_host.h
+++ b/chrome/browser/worker_host/worker_process_host.h
@@ -17,16 +17,82 @@ class WorkerProcessHost : public ChildProcessHost {
public:
// Contains information about each worker instance, needed to forward messages
// between the renderer and worker processes.
- struct WorkerInstance {
- GURL url;
- bool is_shared;
- string16 name;
- int renderer_id;
- int render_view_route_id;
- int worker_route_id;
- IPC::Message::Sender* sender;
- int sender_id;
- int sender_route_id;
+ class WorkerInstance {
+ public:
+ WorkerInstance(const GURL& url,
+ bool is_shared,
+ const string16& name,
+ int renderer_id,
+ int render_view_route_id,
+ int worker_route_id);
+
+ // Unique identifier for a worker client.
+ typedef std::pair<IPC::Message::Sender*, int> SenderInfo;
+
+ // APIs to manage the sender list for a given instance.
+ void AddSender(IPC::Message::Sender* sender, int sender_route_id);
+ void RemoveSender(IPC::Message::Sender* sender, int sender_route_id);
+ void RemoveSenders(IPC::Message::Sender* sender);
+ bool HasSender(IPC::Message::Sender* sender, int sender_route_id) const;
+ int NumSenders() const { return senders_.size(); }
+ // Returns the single sender (must only be one).
+ SenderInfo GetSender() const;
+
+ // Checks if this WorkerInstance matches the passed url/name params
+ // (per the comparison algorithm in the WebWorkers spec). This API only
+ // applies to shared workers.
+ bool Matches(const GURL& url, const string16& name) const;
+
+ // Adds a document to a shared worker's document set.
+ void AddToDocumentSet(IPC::Message::Sender* parent,
+ unsigned long long document_id);
+
+ // Checks to see if a document is in a shared worker's document set.
+ bool IsInDocumentSet(IPC::Message::Sender* parent,
+ unsigned long long document_id) const;
+
+ // Removes a specific document from a shared worker's document set when
+ // that document is detached.
+ void RemoveFromDocumentSet(IPC::Message::Sender* parent,
+ unsigned long long document_id);
+
+ // Copies the document set from one instance to another
+ void CopyDocumentSet(const WorkerInstance& instance) {
+ document_set_ = instance.document_set_;
+ };
+
+ // Invoked when a render process exits, to remove all associated documents
+ // from a shared worker's document set.
+ void RemoveAllAssociatedDocuments(IPC::Message::Sender* parent);
+
+ bool IsDocumentSetEmpty() const { return document_set_.empty(); }
+
+
+ // Accessors
+ bool is_shared() const { return shared_; }
+ bool is_closed() const { return closed_; }
+ void set_closed(bool closed) { closed_ = closed; }
+ const GURL& url() const { return url_; }
+ const string16 name() const { return name_; }
+ int renderer_id() const { return renderer_id_; }
+ int render_view_route_id() const { return render_view_route_id_; }
+ int worker_route_id() const { return worker_route_id_; }
+
+ private:
+ // Unique identifier for an associated document.
+ typedef std::pair<IPC::Message::Sender*, unsigned long long> DocumentInfo;
+ typedef std::set<DocumentInfo> DocumentSet;
+ // Set of all senders (clients) associated with this worker.
+ typedef std::set<SenderInfo> SenderSet;
+ GURL url_;
+ bool shared_;
+ bool closed_;
+ string16 name_;
+ int renderer_id_;
+ int render_view_route_id_;
+ int worker_route_id_;
+ SenderSet senders_;
+ DocumentSet document_set_;
};
WorkerProcessHost(ResourceDispatcherHost* resource_dispatcher_host_);
@@ -40,15 +106,21 @@ class WorkerProcessHost : public ChildProcessHost {
// Returns true iff the given message from a renderer process was forwarded to
// the worker.
- bool FilterMessage(const IPC::Message& message, int sender_pid);
+ bool FilterMessage(const IPC::Message& message, IPC::Message::Sender* sender);
void SenderShutdown(IPC::Message::Sender* sender);
+ // Shuts down any shared workers that are no longer referenced by active
+ // documents.
+ void DocumentDetached(IPC::Message::Sender* sender,
+ unsigned long long document_id);
+
protected:
friend class WorkerService;
typedef std::list<WorkerInstance> Instances;
const Instances& instances() const { return instances_; }
+ Instances& mutable_instances() { return instances_; }
private:
// ResourceDispatcherHost::Receiver implementation:
@@ -59,6 +131,16 @@ class WorkerProcessHost : public ChildProcessHost {
// Called when a message arrives from the worker process.
void OnMessageReceived(const IPC::Message& message);
+ // Called when the app invokes close() from within worker context.
+ void OnWorkerContextClosed(int worker_route_id);
+
+ // Called if a worker tries to connect to a shared worker.
+ void OnLookupSharedWorker(const GURL& url,
+ const string16& name,
+ unsigned long long document_id,
+ int* route_id,
+ bool* url_error);
+
// Given a Sender, returns the callback that generates a new routing id.
static CallbackWithReturnValue<int>::Type* GetNextRouteIdCallback(
IPC::Message::Sender* sender);
diff --git a/chrome/browser/worker_host/worker_service.cc b/chrome/browser/worker_host/worker_service.cc
index 334917f..404f0a7 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,22 +51,18 @@ 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
// a wrapped IPC message through us, we know which WorkerProcessHost to give
// it to.
- WorkerProcessHost::WorkerInstance instance;
- instance.url = url;
- instance.name = name;
- 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;
+ WorkerProcessHost::WorkerInstance instance(url,
+ is_shared,
+ name,
+ renderer_id,
+ render_view_route_id,
+ next_worker_route_id());
+ instance.AddSender(sender, sender_route_id);
WorkerProcessHost* worker = NULL;
if (CommandLine::ForCurrentProcess()->HasSwitch(
@@ -81,6 +78,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).
+ 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->IsDocumentSetEmpty());
+ instance.CopyDocumentSet(*pending);
+ RemovePendingInstance(url, name);
+ }
+
if (!worker) {
worker = new WorkerProcessHost(resource_dispatcher_host_);
if (!worker->Init()) {
@@ -93,15 +122,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)
+ 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 iter = queued_workers_.begin();
+ iter != queued_workers_.end();) {
+ if (iter->is_shared()) {
+ iter->RemoveFromDocumentSet(sender, document_id);
+ if (iter->IsDocumentSetEmpty()) {
+ iter = queued_workers_.erase(iter);
+ continue;
+ }
+ }
+ ++iter;
+ }
+
+ // 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->IsDocumentSetEmpty()) {
+ 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 +215,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 +229,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;
}
@@ -151,7 +252,7 @@ WorkerProcessHost* WorkerService::GetProcessForDomain(const GURL& url) {
worker->instances().begin();
instance != worker->instances().end(); ++instance) {
if (net::RegistryControlledDomainService::GetDomainAndRegistry(
- instance->url) == domain) {
+ instance->url()) == domain) {
return worker;
}
}
@@ -200,8 +301,9 @@ bool WorkerService::CanCreateWorkerProcess(
total_workers++;
if (total_workers >= kMaxWorkersWhenSeparate)
return false;
- if (cur_instance->renderer_id == instance.renderer_id &&
- cur_instance->render_view_route_id == instance.render_view_route_id) {
+ if (cur_instance->renderer_id() == instance.renderer_id() &&
+ cur_instance->render_view_route_id() ==
+ instance.render_view_route_id()) {
workers_per_tab++;
if (workers_per_tab >= kMaxWorkersPerTabWhenSeparate)
return false;
@@ -237,12 +339,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->NumSenders() == 0) {
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->IsDocumentSetEmpty()) {
+ iter = pending_shared_workers_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
}
void WorkerService::WorkerProcessDestroyed(WorkerProcessHost* process) {
@@ -281,3 +396,64 @@ 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(
+ url, true, name, 0, MSG_ROUTING_NONE, MSG_ROUTING_NONE);
+ pending_shared_workers_.push_back(pending);
+ return &pending_shared_workers_.back();
+}
diff --git a/chrome/browser/worker_host/worker_service.h b/chrome/browser/worker_host/worker_service.h
index 40267aa..0de4121 100644
--- a/chrome/browser/worker_host/worker_service.h
+++ b/chrome/browser/worker_host/worker_service.h
@@ -32,15 +32,32 @@ class WorkerService : public NotificationObserver {
int renderer_pid,
int render_view_route_id,
IPC::Message::Sender* sender,
- int sender_id,
int sender_route_id);
+ // Validates the passed URL and checks for the existence of matching shared
+ // worker. Returns true if the url was found, and sets the url_mismatch out
+ // param to true/false depending on whether there's a url mismatch with an
+ // existing shared worker with the same name.
+ bool LookupSharedWorker(const GURL &url,
+ const string16& name,
+ unsigned long long document_id,
+ IPC::Message::Sender* sender,
+ int sender_route_id,
+ bool* url_mismatch);
+
+ // Notification from the renderer that a given document has detached, so any
+ // associated shared workers can be shut down.
+ void DocumentDetached(IPC::Message::Sender* sender,
+ unsigned long long document_id);
+
// Cancel creation of a dedicated worker that hasn't started yet.
- void CancelCreateDedicatedWorker(int sender_id, int sender_route_id);
+ void CancelCreateDedicatedWorker(IPC::Message::Sender* sender,
+ int sender_route_id);
// Called by the worker creator when a message arrives that should be
// forwarded to the worker process.
- void ForwardMessage(const IPC::Message& message, int sender_id);
+ void ForwardMessage(const IPC::Message& message,
+ IPC::Message::Sender* sender);
int next_worker_route_id() { return ++next_worker_route_id_; }
@@ -49,6 +66,9 @@ class WorkerService : public NotificationObserver {
const WorkerProcessHost::WorkerInstance* FindWorkerInstance(
int worker_process_id);
+ WorkerProcessHost::WorkerInstance* FindSharedWorkerInstance(
+ const GURL& url, const string16& name);
+
// Used when multiple workers can run in the same process.
static const int kMaxWorkerProcessesWhenSharing;
@@ -90,12 +110,24 @@ class WorkerService : public NotificationObserver {
// Notifies us that a worker process has closed.
void WorkerProcessDestroyed(WorkerProcessHost* process);
+ // APIs for manipulating our set of pending shared worker instances.
+ WorkerProcessHost::WorkerInstance* CreatePendingInstance(
+ const GURL& url, const string16& name);
+ WorkerProcessHost::WorkerInstance* FindPendingInstance(
+ const GURL& url, const string16& name);
+ void RemovePendingInstance(const GURL& url, const string16& name);
+
NotificationRegistrar registrar_;
int next_worker_route_id_;
ResourceDispatcherHost* resource_dispatcher_host_;
WorkerProcessHost::Instances queued_workers_;
+ // These are shared workers that have been looked up, but not created yet.
+ // We need to keep a list of these to synchronously detect shared worker
+ // URL mismatches when two pages launch shared workers simultaneously.
+ WorkerProcessHost::Instances pending_shared_workers_;
+
DISALLOW_COPY_AND_ASSIGN(WorkerService);
};