summaryrefslogtreecommitdiffstats
path: root/chrome
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
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')
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.cc4
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.cc25
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.h6
-rw-r--r--chrome/browser/renderer_host/resource_request_details.h2
-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
-rw-r--r--chrome/common/render_messages_internal.h21
-rw-r--r--chrome/common/worker_messages_internal.h2
-rw-r--r--chrome/renderer/render_view.cc18
-rw-r--r--chrome/renderer/websharedworker_proxy.cc10
-rw-r--r--chrome/renderer/websharedworker_proxy.h5
-rw-r--r--chrome/renderer/websharedworkerrepository_impl.cc21
-rw-r--r--chrome/renderer/websharedworkerrepository_impl.h10
-rw-r--r--chrome/renderer/webworker_base.h2
-rw-r--r--chrome/renderer/webworker_proxy.cc11
-rw-r--r--chrome/renderer/webworker_proxy.h4
-rw-r--r--chrome/worker/websharedworker_stub.cc10
-rw-r--r--chrome/worker/websharedworker_stub.h1
-rw-r--r--chrome/worker/webworkerclient_proxy.cc3
-rw-r--r--chrome/worker/worker_uitest.cc14
22 files changed, 656 insertions, 110 deletions
diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc
index 01aa313..35ba9be 100644
--- a/chrome/browser/renderer_host/resource_dispatcher_host.cc
+++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc
@@ -1505,8 +1505,8 @@ bool ResourceDispatcherHost::RenderViewForRequest(const URLRequest* request,
*render_view_host_id = -1;
return false;
}
- *render_process_host_id = worker_instance->renderer_id;
- *render_view_host_id = worker_instance->render_view_route_id;
+ *render_process_host_id = worker_instance->renderer_id();
+ *render_view_host_id = worker_instance->render_view_route_id();
} else {
*render_process_host_id = info->child_id();
*render_view_host_id = info->route_id();
diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc
index a7d53c0..d40e247 100644
--- a/chrome/browser/renderer_host/resource_message_filter.cc
+++ b/chrome/browser/renderer_host/resource_message_filter.cc
@@ -310,6 +310,8 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) {
OnOpenChannelToPlugin)
IPC_MESSAGE_HANDLER(ViewHostMsg_LaunchNaCl, OnLaunchNaCl)
IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWorker, OnCreateWorker)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_LookupSharedWorker, OnLookupSharedWorker)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentDetached, OnDocumentDetached)
IPC_MESSAGE_HANDLER(ViewHostMsg_CancelCreateDedicatedWorker,
OnCancelCreateDedicatedWorker)
IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardToWorker,
@@ -654,15 +656,32 @@ void ResourceMessageFilter::OnCreateWorker(const GURL& url,
int* route_id) {
*route_id = render_widget_helper_->GetNextRoutingID();
WorkerService::GetInstance()->CreateWorker(
- url, is_shared, name, id(), render_view_route_id, this, id(), *route_id);
+ url, is_shared, name, id(), render_view_route_id, this, *route_id);
+}
+
+void ResourceMessageFilter::OnLookupSharedWorker(const GURL& url,
+ const string16& name,
+ unsigned long long document_id,
+ int* route_id,
+ bool* url_mismatch) {
+ int new_route_id = render_widget_helper_->GetNextRoutingID();
+ 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 ResourceMessageFilter::OnDocumentDetached(unsigned long long document_id) {
+ // Notify the WorkerService that the passed document was detached so any
+ // associated shared workers can be shut down.
+ WorkerService::GetInstance()->DocumentDetached(this, document_id);
}
void ResourceMessageFilter::OnCancelCreateDedicatedWorker(int route_id) {
- WorkerService::GetInstance()->CancelCreateDedicatedWorker(id(), route_id);
+ WorkerService::GetInstance()->CancelCreateDedicatedWorker(this, route_id);
}
void ResourceMessageFilter::OnForwardToWorker(const IPC::Message& message) {
- WorkerService::GetInstance()->ForwardMessage(message, id());
+ WorkerService::GetInstance()->ForwardMessage(message, this);
}
void ResourceMessageFilter::OnDownloadUrl(const IPC::Message& message,
diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h
index 577e14a..31f87d4 100644
--- a/chrome/browser/renderer_host/resource_message_filter.h
+++ b/chrome/browser/renderer_host/resource_message_filter.h
@@ -173,6 +173,12 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter,
const string16& name,
int render_view_route_id,
int* route_id);
+ void OnLookupSharedWorker(const GURL& url,
+ const string16& name,
+ unsigned long long document_id,
+ int* route_id,
+ bool* url_error);
+ void OnDocumentDetached(unsigned long long document_id);
void OnCancelCreateDedicatedWorker(int route_id);
void OnForwardToWorker(const IPC::Message& msg);
void OnDownloadUrl(const IPC::Message& message,
diff --git a/chrome/browser/renderer_host/resource_request_details.h b/chrome/browser/renderer_host/resource_request_details.h
index bf84964..c34af0f 100644
--- a/chrome/browser/renderer_host/resource_request_details.h
+++ b/chrome/browser/renderer_host/resource_request_details.h
@@ -47,7 +47,7 @@ class ResourceRequestDetails {
const WorkerProcessHost::WorkerInstance* worker_instance =
WorkerService::GetInstance()->FindWorkerInstance(info->child_id());
origin_child_id_ =
- worker_instance ? worker_instance->renderer_id : info->child_id();
+ worker_instance ? worker_instance->renderer_id() : info->child_id();
}
virtual ~ResourceRequestDetails() {}
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);
};
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index 2ad32ab..a5c5f35 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -1745,6 +1745,27 @@ IPC_BEGIN_MESSAGES(ViewHost)
int /* render_view_route_id */,
int /* route_id */)
+ // This message is sent to the browser to see if an instance of this shared
+ // worker already exists (returns route_id != MSG_ROUTING_NONE). This route
+ // id can be used to forward messages to the worker via ForwardToWorker. If a
+ // non-empty name is passed, also validates that the url matches the url of
+ // the existing worker. If a matching worker is found, the passed-in
+ // document_id is associated with that worker, to ensure that the worker
+ // stays alive until the document is detached.
+ IPC_SYNC_MESSAGE_CONTROL3_2(ViewHostMsg_LookupSharedWorker,
+ GURL /* url */,
+ string16 /* name */,
+ unsigned long long /* document_id */,
+ int /* route_id */,
+ bool /* url_mismatch */)
+
+ // A renderer sends this to the browser process when a document has been
+ // detached. The browser will use this to constrain the lifecycle of worker
+ // processes (SharedWorkers are shut down when their last associated document
+ // is detached).
+ IPC_MESSAGE_CONTROL1(ViewHostMsg_DocumentDetached,
+ unsigned long long /* document_id */)
+
// A message sent to the browser on behalf of a renderer which wants to show
// a desktop notification.
IPC_MESSAGE_ROUTED3(ViewHostMsg_ShowDesktopNotification,
diff --git a/chrome/common/worker_messages_internal.h b/chrome/common/worker_messages_internal.h
index 04c983c..be0f271 100644
--- a/chrome/common/worker_messages_internal.h
+++ b/chrome/common/worker_messages_internal.h
@@ -123,5 +123,7 @@ IPC_BEGIN_MESSAGES(WorkerHost)
IPC_MESSAGE_ROUTED1(WorkerHostMsg_ReportPendingActivity,
bool /* bool has_pending_activity */)
+ IPC_MESSAGE_CONTROL1(WorkerHostMsg_WorkerContextClosed,
+ int /* worker_route_id */)
IPC_MESSAGE_ROUTED0(WorkerHostMsg_WorkerContextDestroyed)
IPC_END_MESSAGES(WorkerHost)
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 006ae43..aa8d86d 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -1805,13 +1805,19 @@ WebWorker* RenderView::createWorker(WebFrame* frame, WebWorkerClient* client) {
WebSharedWorker* RenderView::createSharedWorker(
WebFrame* frame, const WebURL& url, const WebString& name,
- unsigned long long documentId) {
+ unsigned long long document_id) {
- // TODO(atwilson): Call to the browser process to check if there's an existing
- // worker (passing MSG_ROUTING_NONE for now, to indicate no existing worker).
- int worker_route_id = MSG_ROUTING_NONE;
- return new WebSharedWorkerProxy(
- RenderThread::current(), worker_route_id, routing_id_);
+ int route_id = MSG_ROUTING_NONE;
+ bool url_mismatch = false;
+ Send(new ViewHostMsg_LookupSharedWorker(
+ url, name, document_id, &route_id, &url_mismatch));
+ if (url_mismatch) {
+ return NULL;
+ } else {
+ return new WebSharedWorkerProxy(RenderThread::current(),
+ route_id,
+ routing_id_);
+ }
}
WebMediaPlayer* RenderView::createMediaPlayer(
diff --git a/chrome/renderer/websharedworker_proxy.cc b/chrome/renderer/websharedworker_proxy.cc
index 7ad1f61..1d5425f 100644
--- a/chrome/renderer/websharedworker_proxy.cc
+++ b/chrome/renderer/websharedworker_proxy.cc
@@ -13,7 +13,7 @@ WebSharedWorkerProxy::WebSharedWorkerProxy(ChildThread* child_thread,
int route_id,
int render_view_route_id)
: WebWorkerBase(child_thread, route_id, render_view_route_id),
- m_connectListener(NULL) {
+ connect_listener_(NULL) {
}
bool WebSharedWorkerProxy::isStarted() {
@@ -25,6 +25,7 @@ void WebSharedWorkerProxy::startWorkerContext(
const WebKit::WebString& name,
const WebKit::WebString& user_agent,
const WebKit::WebString& source_code) {
+ DCHECK(!isStarted());
CreateWorkerContext(script_url, true, name, user_agent, source_code);
}
@@ -49,7 +50,7 @@ void WebSharedWorkerProxy::connect(WebKit::WebMessagePortChannel* channel,
Send(new WorkerMsg_Connect(route_id_, message_port_id, MSG_ROUTING_NONE));
if (HasQueuedMessages()) {
- m_connectListener = listener;
+ connect_listener_ = listener;
} else {
listener->connected();
// The listener may free this object, so do not access the object after
@@ -70,8 +71,7 @@ void WebSharedWorkerProxy::OnWorkerCreated() {
// Inform any listener that the pending connect event has been sent
// (this can result in this object being freed).
- if (m_connectListener) {
- m_connectListener->connected();
+ if (connect_listener_) {
+ connect_listener_->connected();
}
}
-
diff --git a/chrome/renderer/websharedworker_proxy.h b/chrome/renderer/websharedworker_proxy.h
index 68dab47..c3143db 100644
--- a/chrome/renderer/websharedworker_proxy.h
+++ b/chrome/renderer/websharedworker_proxy.h
@@ -20,6 +20,7 @@ class ChildThread;
class WebSharedWorkerProxy : public WebKit::WebSharedWorker,
private WebWorkerBase {
public:
+ // If the worker not loaded yet, route_id == MSG_ROUTING_NONE
WebSharedWorkerProxy(ChildThread* child_thread,
int route_id,
int render_view_route_id);
@@ -35,13 +36,13 @@ class WebSharedWorkerProxy : public WebKit::WebSharedWorker,
virtual void terminateWorkerContext();
virtual void clientDestroyed();
- // IPC::Channel::Listener proxyementation.
+ // IPC::Channel::Listener implementation.
void OnMessageReceived(const IPC::Message& message);
private:
void OnWorkerCreated();
- ConnectListener* m_connectListener;
+ ConnectListener* connect_listener_;
DISALLOW_COPY_AND_ASSIGN(WebSharedWorkerProxy);
};
diff --git a/chrome/renderer/websharedworkerrepository_impl.cc b/chrome/renderer/websharedworkerrepository_impl.cc
index 24664b7..99f2a3b 100644
--- a/chrome/renderer/websharedworkerrepository_impl.cc
+++ b/chrome/renderer/websharedworkerrepository_impl.cc
@@ -4,21 +4,24 @@
#include "chrome/renderer/websharedworkerrepository_impl.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/renderer/render_thread.h"
#include "chrome/renderer/websharedworker_proxy.h"
void WebSharedWorkerRepositoryImpl::addSharedWorker(
WebKit::WebSharedWorker* worker, DocumentID document) {
- // TODO(atwilson): Track shared worker creation here.
+ shared_worker_parents_.insert(document);
}
-void WebSharedWorkerRepositoryImpl::documentDetached(
- DocumentID document) {
- // TODO(atwilson): Update this to call to WorkerService to shutdown any
- // associated SharedWorkers.
+void WebSharedWorkerRepositoryImpl::documentDetached(DocumentID document) {
+ DocumentSet::iterator iter = shared_worker_parents_.find(document);
+ if (iter != shared_worker_parents_.end()) {
+ // Notify the browser process that the document has shut down.
+ ChildThread::current()->Send(new ViewHostMsg_DocumentDetached(document));
+ shared_worker_parents_.erase(iter);
+ }
}
-bool WebSharedWorkerRepositoryImpl::hasSharedWorkers(
- DocumentID document) {
- // TODO(atwilson): Update this when we track shared worker creation.
- return false;
+bool WebSharedWorkerRepositoryImpl::hasSharedWorkers(DocumentID document) {
+ return shared_worker_parents_.find(document) != shared_worker_parents_.end();
}
diff --git a/chrome/renderer/websharedworkerrepository_impl.h b/chrome/renderer/websharedworkerrepository_impl.h
index d8cec99..5806485 100644
--- a/chrome/renderer/websharedworkerrepository_impl.h
+++ b/chrome/renderer/websharedworkerrepository_impl.h
@@ -7,14 +7,24 @@
#include "third_party/WebKit/WebKit/chromium/public/WebSharedWorkerRepository.h"
+#include "base/hash_tables.h"
+
namespace WebKit {
class WebSharedWorker;
}
class WebSharedWorkerRepositoryImpl : public WebKit::WebSharedWorkerRepository {
+ public:
virtual void addSharedWorker(WebKit::WebSharedWorker*, DocumentID document);
virtual void documentDetached(DocumentID document);
+
+ // Returns true if the document has created a SharedWorker (used by the
+ // WebKit code to determine if the document can be suspended).
virtual bool hasSharedWorkers(DocumentID document);
+ private:
+ // The set of documents that have created a SharedWorker.
+ typedef base::hash_set<DocumentID> DocumentSet;
+ DocumentSet shared_worker_parents_;
};
#endif // CHROME_RENDERER_WEB_SHARED_WORKER_REPOSITORY_IMPL_H_
diff --git a/chrome/renderer/webworker_base.h b/chrome/renderer/webworker_base.h
index 0693863..fe5becc 100644
--- a/chrome/renderer/webworker_base.h
+++ b/chrome/renderer/webworker_base.h
@@ -36,7 +36,7 @@ class WebWorkerBase : public IPC::Channel::Listener {
bool IsStarted();
// Disconnects the worker (stops listening for incoming messages).
- virtual void Disconnect();
+ void Disconnect();
// Sends a message to the worker thread (forwarded via the RenderViewHost).
// If WorkerStarted() has not yet been called, message is queued.
diff --git a/chrome/renderer/webworker_proxy.cc b/chrome/renderer/webworker_proxy.cc
index a263565..fdcc9f1 100644
--- a/chrome/renderer/webworker_proxy.cc
+++ b/chrome/renderer/webworker_proxy.cc
@@ -26,16 +26,18 @@ WebWorkerProxy::WebWorkerProxy(
client_(client) {
}
-void WebWorkerProxy::Disconnect() {
+WebWorkerProxy::~WebWorkerProxy() {
+ // If we're midway through starting a worker, cancel it.
+ CancelCreation();
+}
+
+void WebWorkerProxy::CancelCreation() {
if (route_id_ == MSG_ROUTING_NONE)
return;
// Tell the browser to not start our queued worker.
if (!IsStarted())
child_thread_->Send(new ViewHostMsg_CancelCreateDedicatedWorker(route_id_));
-
- // Call our superclass to shutdown the routing
- WebWorkerBase::Disconnect();
}
void WebWorkerProxy::startWorkerContext(
@@ -48,6 +50,7 @@ void WebWorkerProxy::startWorkerContext(
void WebWorkerProxy::terminateWorkerContext() {
if (route_id_ != MSG_ROUTING_NONE) {
Send(new WorkerMsg_TerminateWorkerContext(route_id_));
+ CancelCreation();
Disconnect();
}
}
diff --git a/chrome/renderer/webworker_proxy.h b/chrome/renderer/webworker_proxy.h
index fb2db3ed..112b561 100644
--- a/chrome/renderer/webworker_proxy.h
+++ b/chrome/renderer/webworker_proxy.h
@@ -27,6 +27,7 @@ class WebWorkerProxy : public WebKit::WebWorker, private WebWorkerBase {
WebWorkerProxy(WebKit::WebWorkerClient* client,
ChildThread* child_thread,
int render_view_route_id);
+ ~WebWorkerProxy();
// WebWorker implementation.
virtual void startWorkerContext(const WebKit::WebURL& script_url,
@@ -43,8 +44,7 @@ class WebWorkerProxy : public WebKit::WebWorker, private WebWorkerBase {
void OnMessageReceived(const IPC::Message& message);
private:
- virtual void Disconnect();
-
+ void CancelCreation();
void OnWorkerCreated();
void OnWorkerContextDestroyed();
void OnPostMessage(const string16& message,
diff --git a/chrome/worker/websharedworker_stub.cc b/chrome/worker/websharedworker_stub.cc
index 3c7aa3e..7c9646e 100644
--- a/chrome/worker/websharedworker_stub.cc
+++ b/chrome/worker/websharedworker_stub.cc
@@ -13,7 +13,8 @@
WebSharedWorkerStub::WebSharedWorkerStub(
const string16& name, int route_id)
: WebWorkerStubBase(route_id),
- name_(name) {
+ name_(name),
+ started_(false) {
// TODO(atwilson): Add support for NaCl when they support MessagePorts.
impl_ = WebKit::WebSharedWorker::create(client());
@@ -35,10 +36,16 @@ void WebSharedWorkerStub::OnMessageReceived(const IPC::Message& message) {
void WebSharedWorkerStub::OnStartWorkerContext(
const GURL& url, const string16& user_agent, const string16& source_code) {
+ // Ignore multiple attempts to start this worker (can happen if two pages
+ // try to start it simultaneously).
+ if (started_)
+ return;
impl_->startWorkerContext(url, name_, user_agent, source_code);
+ started_ = true;
}
void WebSharedWorkerStub::OnConnect(int sent_message_port_id, int routing_id) {
+ DCHECK(started_);
WebKit::WebMessagePortChannel* channel =
new WebMessagePortChannelImpl(routing_id, sent_message_port_id);
impl_->connect(channel, NULL);
@@ -49,4 +56,5 @@ void WebSharedWorkerStub::OnTerminateWorkerContext() {
// Call the client to make sure context exits.
EnsureWorkerContextTerminates();
+ started_ = false;
}
diff --git a/chrome/worker/websharedworker_stub.h b/chrome/worker/websharedworker_stub.h
index f7a26f3..426aaad 100644
--- a/chrome/worker/websharedworker_stub.h
+++ b/chrome/worker/websharedworker_stub.h
@@ -33,6 +33,7 @@ class WebSharedWorkerStub : public WebWorkerStubBase {
WebKit::WebSharedWorker* impl_;
string16 name_;
+ bool started_;
DISALLOW_COPY_AND_ASSIGN(WebSharedWorkerStub);
};
diff --git a/chrome/worker/webworkerclient_proxy.cc b/chrome/worker/webworkerclient_proxy.cc
index 8519c23..6ba589f 100644
--- a/chrome/worker/webworkerclient_proxy.cc
+++ b/chrome/worker/webworkerclient_proxy.cc
@@ -92,8 +92,7 @@ void WebWorkerClientProxy::reportPendingActivity(bool has_pending_activity) {
}
void WebWorkerClientProxy::workerContextClosed() {
- // TODO(atwilson): Notify WorkerProcessHost that the worker context is closing
- // (needed for shared workers so we don't allow new connections).
+ Send(new WorkerHostMsg_WorkerContextClosed(route_id_));
}
void WebWorkerClientProxy::workerContextDestroyed() {
diff --git a/chrome/worker/worker_uitest.cc b/chrome/worker/worker_uitest.cc
index 793f889..7b46995 100644
--- a/chrome/worker/worker_uitest.cc
+++ b/chrome/worker/worker_uitest.cc
@@ -116,25 +116,25 @@ TEST_F(WorkerTest, WorkerFastLayoutTests) {
RunLayoutTest(kLayoutTestFiles[i], false);
}
-TEST_F(WorkerTest, SharedWorkerFastLayoutTests) {
+TEST_F(WorkerTest, DISABLED_SharedWorkerFastLayoutTests) {
static const char* kLayoutTestFiles[] = {
"shared-worker-constructor.html",
- // Enable remaining SharedWorker tests when functionality is
- // complete (http://crbug.com/26899)
"shared-worker-context-gc.html",
"shared-worker-event-listener.html",
- //"shared-worker-exception.html",
- //"shared-worker-frame-lifecycle.html",
+ "shared-worker-exception.html",
"shared-worker-gc.html",
+ // Lifecycle tests rely on layoutTestController.workerThreadCount which is
+ // not currently implemented.
+ //"shared-worker-frame-lifecycle.html",
//"shared-worker-lifecycle.html",
"shared-worker-load-error.html",
"shared-worker-location.html",
- //"shared-worker-name.html",
+ "shared-worker-name.html",
"shared-worker-navigator.html",
"shared-worker-replace-global-constructor.html",
"shared-worker-replace-self.html",
"shared-worker-script-error.html",
- //"shared-worker-shared.html",
+ "shared-worker-shared.html",
"shared-worker-simple.html",
};