diff options
author | atwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-13 01:13:37 +0000 |
---|---|---|
committer | atwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-13 01:13:37 +0000 |
commit | 30447b6a5d6109d455f2c6262454105d5adf8f94 (patch) | |
tree | 3ef69964f7cf3e10d662af94f88e05288e16ad8c /chrome/browser/worker_host | |
parent | 6eca5f19ffabd672349df42595551e7a4b1da987 (diff) | |
download | chromium_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.cc | 227 | ||||
-rw-r--r-- | chrome/browser/worker_host/worker_process_host.h | 104 | ||||
-rw-r--r-- | chrome/browser/worker_host/worker_service.cc | 228 | ||||
-rw-r--r-- | chrome/browser/worker_host/worker_service.h | 38 |
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); }; |