summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-28 21:30:40 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-28 21:30:40 +0000
commit1b8af731dccaaf3b040e32481aedf9e113f32854 (patch)
tree24e10ac7e200637d362171c9b3b51f158fb3c660 /content
parentd45271ca30365429b189bc71217349cb2ea5f852 (diff)
downloadchromium_src-1b8af731dccaaf3b040e32481aedf9e113f32854.zip
chromium_src-1b8af731dccaaf3b040e32481aedf9e113f32854.tar.gz
chromium_src-1b8af731dccaaf3b040e32481aedf9e113f32854.tar.bz2
Move worker_host code to content.
TBR=avi Review URL: http://codereview.chromium.org/6592041 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76263 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host.cc2
-rw-r--r--content/browser/renderer_host/resource_request_details.cc2
-rw-r--r--content/browser/worker_host/message_port_service.cc236
-rw-r--r--content/browser/worker_host/message_port_service.h75
-rw-r--r--content/browser/worker_host/worker_document_set.cc69
-rw-r--r--content/browser/worker_host/worker_document_set.h90
-rw-r--r--content/browser/worker_host/worker_message_filter.cc116
-rw-r--r--content/browser/worker_host/worker_message_filter.h62
-rw-r--r--content/browser/worker_host/worker_process_host.cc611
-rw-r--r--content/browser/worker_host/worker_process_host.h187
-rw-r--r--content/browser/worker_host/worker_service.cc558
-rw-r--r--content/browser/worker_host/worker_service.h121
-rw-r--r--content/content_browser.gypi10
13 files changed, 2137 insertions, 2 deletions
diff --git a/content/browser/renderer_host/resource_dispatcher_host.cc b/content/browser/renderer_host/resource_dispatcher_host.cc
index 89440cb..a4238a1 100644
--- a/content/browser/renderer_host/resource_dispatcher_host.cc
+++ b/content/browser/renderer_host/resource_dispatcher_host.cc
@@ -37,7 +37,6 @@
#include "chrome/browser/ssl/ssl_client_auth_handler.h"
#include "chrome/browser/ssl/ssl_manager.h"
#include "chrome/browser/ui/login/login_prompt.h"
-#include "chrome/browser/worker_host/worker_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/render_messages.h"
@@ -61,6 +60,7 @@
#include "content/browser/renderer_host/resource_queue.h"
#include "content/browser/renderer_host/resource_request_details.h"
#include "content/browser/renderer_host/sync_resource_handler.h"
+#include "content/browser/worker_host/worker_service.h"
#include "net/base/auth.h"
#include "net/base/cert_status_flags.h"
#include "net/base/cookie_monster.h"
diff --git a/content/browser/renderer_host/resource_request_details.cc b/content/browser/renderer_host/resource_request_details.cc
index 5570181..151268c 100644
--- a/content/browser/renderer_host/resource_request_details.cc
+++ b/content/browser/renderer_host/resource_request_details.cc
@@ -6,7 +6,7 @@
#include "content/browser/renderer_host/resource_dispatcher_host.h"
#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
-#include "chrome/browser/worker_host/worker_service.h"
+#include "content/browser/worker_host/worker_service.h"
ResourceRequestDetails::ResourceRequestDetails(const net::URLRequest* request,
int cert_id)
diff --git a/content/browser/worker_host/message_port_service.cc b/content/browser/worker_host/message_port_service.cc
new file mode 100644
index 0000000..93b5f3d
--- /dev/null
+++ b/content/browser/worker_host/message_port_service.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/worker_host/message_port_service.h"
+
+#include "chrome/common/worker_messages.h"
+#include "content/browser/worker_host/worker_message_filter.h"
+
+struct MessagePortService::MessagePort {
+ // |filter| and |route_id| are what we need to send messages to the port.
+ // |filter| is just a weak pointer since we get notified when its process has
+ // gone away and remove it.
+ WorkerMessageFilter* filter;
+ int route_id;
+ // A globally unique id for this message port.
+ int message_port_id;
+ // The globally unique id of the entangled message port.
+ int entangled_message_port_id;
+ // If true, all messages to this message port are queued and not delivered.
+ bool queue_messages;
+ QueuedMessages queued_messages;
+};
+
+MessagePortService* MessagePortService::GetInstance() {
+ return Singleton<MessagePortService>::get();
+}
+
+MessagePortService::MessagePortService()
+ : next_message_port_id_(0) {
+}
+
+MessagePortService::~MessagePortService() {
+}
+
+void MessagePortService::UpdateMessagePort(
+ int message_port_id,
+ WorkerMessageFilter* filter,
+ int routing_id) {
+ if (!message_ports_.count(message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ MessagePort& port = message_ports_[message_port_id];
+ port.filter = filter;
+ port.route_id = routing_id;
+}
+
+void MessagePortService::OnWorkerMessageFilterClosing(
+ WorkerMessageFilter* filter) {
+ // Check if the (possibly) crashed process had any message ports.
+ for (MessagePorts::iterator iter = message_ports_.begin();
+ iter != message_ports_.end();) {
+ MessagePorts::iterator cur_item = iter++;
+ if (cur_item->second.filter == filter) {
+ Erase(cur_item->first);
+ }
+ }
+}
+
+void MessagePortService::Create(int route_id,
+ WorkerMessageFilter* filter,
+ int* message_port_id) {
+ *message_port_id = ++next_message_port_id_;
+
+ MessagePort port;
+ port.filter = filter;
+ port.route_id = route_id;
+ port.message_port_id = *message_port_id;
+ port.entangled_message_port_id = MSG_ROUTING_NONE;
+ port.queue_messages = false;
+ message_ports_[*message_port_id] = port;
+}
+
+void MessagePortService::Destroy(int message_port_id) {
+ if (!message_ports_.count(message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ DCHECK(message_ports_[message_port_id].queued_messages.empty());
+ Erase(message_port_id);
+}
+
+void MessagePortService::Entangle(int local_message_port_id,
+ int remote_message_port_id) {
+ if (!message_ports_.count(local_message_port_id) ||
+ !message_ports_.count(remote_message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ DCHECK(message_ports_[remote_message_port_id].entangled_message_port_id ==
+ MSG_ROUTING_NONE);
+ message_ports_[remote_message_port_id].entangled_message_port_id =
+ local_message_port_id;
+}
+
+void MessagePortService::PostMessage(
+ int sender_message_port_id,
+ const string16& message,
+ const std::vector<int>& sent_message_port_ids) {
+ if (!message_ports_.count(sender_message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ int entangled_message_port_id =
+ message_ports_[sender_message_port_id].entangled_message_port_id;
+ if (entangled_message_port_id == MSG_ROUTING_NONE)
+ return; // Process could have crashed.
+
+ if (!message_ports_.count(entangled_message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ PostMessageTo(entangled_message_port_id, message, sent_message_port_ids);
+}
+
+void MessagePortService::PostMessageTo(
+ int message_port_id,
+ const string16& message,
+ const std::vector<int>& sent_message_port_ids) {
+ if (!message_ports_.count(message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+ for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
+ if (!message_ports_.count(sent_message_port_ids[i])) {
+ NOTREACHED();
+ return;
+ }
+ }
+
+ MessagePort& entangled_port = message_ports_[message_port_id];
+
+ std::vector<MessagePort*> sent_ports(sent_message_port_ids.size());
+ for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
+ sent_ports[i] = &message_ports_[sent_message_port_ids[i]];
+ sent_ports[i]->queue_messages = true;
+ }
+
+ if (entangled_port.queue_messages) {
+ entangled_port.queued_messages.push_back(
+ std::make_pair(message, sent_message_port_ids));
+ return;
+ }
+
+ if (!entangled_port.filter) {
+ NOTREACHED();
+ return;
+ }
+
+ // If a message port was sent around, the new location will need a routing
+ // id. Instead of having the created port send us a sync message to get it,
+ // send along with the message.
+ std::vector<int> new_routing_ids(sent_message_port_ids.size());
+ for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
+ new_routing_ids[i] = entangled_port.filter->GetNextRoutingID();
+ sent_ports[i]->filter = entangled_port.filter;
+
+ // Update the entry for the sent port as it can be in a different process.
+ sent_ports[i]->route_id = new_routing_ids[i];
+ }
+
+ // Now send the message to the entangled port.
+ entangled_port.filter->Send(new WorkerProcessMsg_Message(
+ entangled_port.route_id, message, sent_message_port_ids,
+ new_routing_ids));
+}
+
+void MessagePortService::QueueMessages(int message_port_id) {
+ if (!message_ports_.count(message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ MessagePort& port = message_ports_[message_port_id];
+ if (port.filter) {
+ port.filter->Send(new WorkerProcessMsg_MessagesQueued(port.route_id));
+ port.queue_messages = true;
+ port.filter = NULL;
+ }
+}
+
+void MessagePortService::SendQueuedMessages(
+ int message_port_id,
+ const QueuedMessages& queued_messages) {
+ if (!message_ports_.count(message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ // Send the queued messages to the port again. This time they'll reach the
+ // new location.
+ MessagePort& port = message_ports_[message_port_id];
+ port.queue_messages = false;
+ port.queued_messages.insert(port.queued_messages.begin(),
+ queued_messages.begin(),
+ queued_messages.end());
+ SendQueuedMessagesIfPossible(message_port_id);
+}
+
+void MessagePortService::SendQueuedMessagesIfPossible(int message_port_id) {
+ if (!message_ports_.count(message_port_id)) {
+ NOTREACHED();
+ return;
+ }
+
+ MessagePort& port = message_ports_[message_port_id];
+ if (port.queue_messages || !port.filter)
+ return;
+
+ for (QueuedMessages::iterator iter = port.queued_messages.begin();
+ iter != port.queued_messages.end(); ++iter) {
+ PostMessageTo(message_port_id, iter->first, iter->second);
+ }
+ port.queued_messages.clear();
+}
+
+void MessagePortService::Erase(int message_port_id) {
+ MessagePorts::iterator erase_item = message_ports_.find(message_port_id);
+ DCHECK(erase_item != message_ports_.end());
+
+ int entangled_id = erase_item->second.entangled_message_port_id;
+ if (entangled_id != MSG_ROUTING_NONE) {
+ // Do the disentanglement (and be paranoid about the other side existing
+ // just in case something unusual happened during entanglement).
+ if (message_ports_.count(entangled_id)) {
+ message_ports_[entangled_id].entangled_message_port_id = MSG_ROUTING_NONE;
+ }
+ }
+ message_ports_.erase(erase_item);
+}
diff --git a/content/browser/worker_host/message_port_service.h b/content/browser/worker_host/message_port_service.h
new file mode 100644
index 0000000..0d5321a
--- /dev/null
+++ b/content/browser/worker_host/message_port_service.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WORKER_HOST_MESSAGE_PORT_SERVICE_H_
+#define CONTENT_BROWSER_WORKER_HOST_MESSAGE_PORT_SERVICE_H_
+#pragma once
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/singleton.h"
+#include "base/string16.h"
+#include "base/task.h"
+#include "ipc/ipc_message.h"
+
+class WorkerMessageFilter;
+
+class MessagePortService {
+ public:
+ typedef std::vector<std::pair<string16, std::vector<int> > > QueuedMessages;
+
+ // Returns the MessagePortService singleton.
+ static MessagePortService* GetInstance();
+
+ // These methods correspond to the message port related IPCs.
+ void Create(int route_id, WorkerMessageFilter* filter, int* message_port_id);
+ void Destroy(int message_port_id);
+ void Entangle(int local_message_port_id, int remote_message_port_id);
+ void PostMessage(int sender_message_port_id,
+ const string16& message,
+ const std::vector<int>& sent_message_port_ids);
+ void QueueMessages(int message_port_id);
+ void SendQueuedMessages(int message_port_id,
+ const QueuedMessages& queued_messages);
+
+ // Updates the information needed to reach a message port when it's sent to a
+ // (possibly different) process.
+ void UpdateMessagePort(
+ int message_port_id,
+ WorkerMessageFilter* filter,
+ int routing_id);
+
+ void OnWorkerMessageFilterClosing(WorkerMessageFilter* filter);
+
+ // Attempts to send the queued messages for a message port.
+ void SendQueuedMessagesIfPossible(int message_port_id);
+
+ private:
+ friend struct DefaultSingletonTraits<MessagePortService>;
+
+ MessagePortService();
+ ~MessagePortService();
+
+ void PostMessageTo(int message_port_id,
+ const string16& message,
+ const std::vector<int>& sent_message_port_ids);
+
+ // Handles the details of removing a message port id. Before calling this,
+ // verify that the message port id exists.
+ void Erase(int message_port_id);
+
+ struct MessagePort;
+ typedef std::map<int, MessagePort> MessagePorts;
+ MessagePorts message_ports_;
+
+ // We need globally unique identifiers for each message port.
+ int next_message_port_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePortService);
+};
+
+#endif // CONTENT_BROWSER_WORKER_HOST_MESSAGE_PORT_SERVICE_H_
diff --git a/content/browser/worker_host/worker_document_set.cc b/content/browser/worker_host/worker_document_set.cc
new file mode 100644
index 0000000..06a3907
--- /dev/null
+++ b/content/browser/worker_host/worker_document_set.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/worker_host/worker_document_set.h"
+
+#include "base/logging.h"
+
+WorkerDocumentSet::WorkerDocumentSet() {
+}
+
+void WorkerDocumentSet::Add(WorkerMessageFilter* parent,
+ unsigned long long document_id,
+ int render_process_id,
+ int render_view_id) {
+ DocumentInfo info(parent, document_id, render_process_id, render_view_id);
+ document_set_.insert(info);
+}
+
+bool WorkerDocumentSet::Contains(WorkerMessageFilter* parent,
+ unsigned long long document_id) const {
+ for (DocumentInfoSet::const_iterator i = document_set_.begin();
+ i != document_set_.end(); ++i) {
+ if (i->filter() == parent && i->document_id() == document_id)
+ return true;
+ }
+ return false;
+}
+
+void WorkerDocumentSet::Remove(WorkerMessageFilter* parent,
+ unsigned long long document_id) {
+ for (DocumentInfoSet::iterator i = document_set_.begin();
+ i != document_set_.end(); i++) {
+ if (i->filter() == parent && i->document_id() == document_id) {
+ document_set_.erase(i);
+ break;
+ }
+ }
+ // Should not be duplicate copies in the document set.
+ DCHECK(!Contains(parent, document_id));
+}
+
+void WorkerDocumentSet::RemoveAll(WorkerMessageFilter* parent) {
+ for (DocumentInfoSet::iterator i = document_set_.begin();
+ i != document_set_.end();) {
+
+ // Note this idiom is somewhat tricky - calling document_set_.erase(iter)
+ // invalidates any iterators that point to the element being removed, so
+ // bump the iterator beyond the item being removed before calling erase.
+ if (i->filter() == parent) {
+ DocumentInfoSet::iterator item_to_delete = i++;
+ document_set_.erase(item_to_delete);
+ } else {
+ ++i;
+ }
+ }
+}
+
+WorkerDocumentSet::DocumentInfo::DocumentInfo(
+ WorkerMessageFilter* filter, unsigned long long document_id,
+ int render_process_id, int render_view_id)
+ : filter_(filter),
+ document_id_(document_id),
+ render_process_id_(render_process_id),
+ render_view_id_(render_view_id) {
+}
+
+WorkerDocumentSet::~WorkerDocumentSet() {
+}
diff --git a/content/browser/worker_host/worker_document_set.h b/content/browser/worker_host/worker_document_set.h
new file mode 100644
index 0000000..45666c4
--- /dev/null
+++ b/content/browser/worker_host/worker_document_set.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WORKER_HOST_WORKER_DOCUMENT_SET_H_
+#define CONTENT_BROWSER_WORKER_HOST_WORKER_DOCUMENT_SET_H_
+#pragma once
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+
+class WorkerMessageFilter;
+
+// The WorkerDocumentSet tracks all of the DOM documents associated with a
+// set of workers. With nested workers, multiple workers can share the same
+// WorkerDocumentSet (meaning that they all share the same lifetime, and will
+// all exit when the last document in that set exits, per the WebWorkers spec).
+class WorkerDocumentSet : public base::RefCounted<WorkerDocumentSet> {
+ public:
+ WorkerDocumentSet();
+
+ // The information we track for each document
+ class DocumentInfo {
+ public:
+ DocumentInfo(WorkerMessageFilter* filter, unsigned long long document_id,
+ int renderer_process_id, int render_view_id);
+ WorkerMessageFilter* filter() const { return filter_; }
+ unsigned long long document_id() const { return document_id_; }
+ int render_process_id() const { return render_process_id_; }
+ int render_view_id() const { return render_view_id_; }
+
+ // Define operator "<", which is used to determine uniqueness within
+ // the set.
+ bool operator <(const DocumentInfo& other) const {
+ // Items are identical if the sender and document_id are identical,
+ // otherwise create an arbitrary stable ordering based on the document
+ // id/filter.
+ if (filter() == other.filter()) {
+ return document_id() < other.document_id();
+ } else {
+ return reinterpret_cast<unsigned long long>(filter()) <
+ reinterpret_cast<unsigned long long>(other.filter());
+ }
+ }
+
+ private:
+ WorkerMessageFilter* filter_;
+ unsigned long long document_id_;
+ int render_process_id_;
+ int render_view_id_;
+ };
+
+ // Adds a document to a shared worker's document set. Also includes the
+ // associated render_process_id the document is associated with, to enable
+ // communication with the parent tab for things like http auth dialogs.
+ void Add(WorkerMessageFilter* parent,
+ unsigned long long document_id,
+ int render_process_id,
+ int render_view_id);
+
+ // Checks to see if a document is in a shared worker's document set.
+ bool Contains(WorkerMessageFilter* parent,
+ unsigned long long document_id) const;
+
+ // Removes a specific document from a worker's document set when that document
+ // is detached.
+ void Remove(WorkerMessageFilter* parent, unsigned long long document_id);
+
+ // Invoked when a render process exits, to remove all associated documents
+ // from a worker's document set.
+ void RemoveAll(WorkerMessageFilter* parent);
+
+ bool IsEmpty() const { return document_set_.empty(); }
+
+ // Define a typedef for convenience here when declaring iterators, etc.
+ typedef std::set<DocumentInfo> DocumentInfoSet;
+
+ // Returns the set of documents associated with this worker.
+ const DocumentInfoSet& documents() { return document_set_; }
+
+ private:
+ friend class base::RefCounted<WorkerDocumentSet>;
+ virtual ~WorkerDocumentSet();
+
+ DocumentInfoSet document_set_;
+};
+
+#endif // CONTENT_BROWSER_WORKER_HOST_WORKER_DOCUMENT_SET_H_
diff --git a/content/browser/worker_host/worker_message_filter.cc b/content/browser/worker_host/worker_message_filter.cc
new file mode 100644
index 0000000..abfef0a
--- /dev/null
+++ b/content/browser/worker_host/worker_message_filter.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/worker_host/worker_message_filter.h"
+
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/common/net/url_request_context_getter.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/worker_messages.h"
+#include "content/browser/worker_host/message_port_service.h"
+#include "content/browser/worker_host/worker_service.h"
+
+WorkerMessageFilter::WorkerMessageFilter(
+ int render_process_id,
+ URLRequestContextGetter* request_context,
+ ResourceDispatcherHost* resource_dispatcher_host,
+ CallbackWithReturnValue<int>::Type* next_routing_id)
+ : render_process_id_(render_process_id),
+ request_context_(request_context),
+ resource_dispatcher_host_(resource_dispatcher_host),
+ next_routing_id_(next_routing_id) {
+}
+
+WorkerMessageFilter::~WorkerMessageFilter() {
+}
+
+void WorkerMessageFilter::OnChannelClosing() {
+ BrowserMessageFilter::OnChannelClosing();
+
+ MessagePortService::GetInstance()->OnWorkerMessageFilterClosing(this);
+ WorkerService::GetInstance()->OnWorkerMessageFilterClosing(this);
+}
+
+bool WorkerMessageFilter::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(WorkerMessageFilter, message, *message_was_ok)
+ // Worker messages.
+ // Only sent from renderer for now, until we have nested workers.
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWorker, OnCreateWorker)
+ // Only sent from renderer for now, until we have nested workers.
+ IPC_MESSAGE_HANDLER(ViewHostMsg_LookupSharedWorker, OnLookupSharedWorker)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CancelCreateDedicatedWorker,
+ OnCancelCreateDedicatedWorker)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardToWorker, OnForwardToWorker)
+ // Only sent from renderer.
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentDetached, OnDocumentDetached)
+ // Message Port related messages.
+ IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_CreateMessagePort,
+ OnCreateMessagePort)
+ IPC_MESSAGE_FORWARD(WorkerProcessHostMsg_DestroyMessagePort,
+ MessagePortService::GetInstance(),
+ MessagePortService::Destroy)
+ IPC_MESSAGE_FORWARD(WorkerProcessHostMsg_Entangle,
+ MessagePortService::GetInstance(),
+ MessagePortService::Entangle)
+ IPC_MESSAGE_FORWARD(WorkerProcessHostMsg_PostMessage,
+ MessagePortService::GetInstance(),
+ MessagePortService::PostMessage)
+ IPC_MESSAGE_FORWARD(WorkerProcessHostMsg_QueueMessages,
+ MessagePortService::GetInstance(),
+ MessagePortService::QueueMessages)
+ IPC_MESSAGE_FORWARD(WorkerProcessHostMsg_SendQueuedMessages,
+ MessagePortService::GetInstance(),
+ MessagePortService::SendQueuedMessages)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+
+ return handled;
+}
+
+int WorkerMessageFilter::GetNextRoutingID() {
+ return next_routing_id_->Run();
+}
+
+void WorkerMessageFilter::OnCreateWorker(
+ const ViewHostMsg_CreateWorker_Params& params,
+ int* route_id) {
+ *route_id = params.route_id != MSG_ROUTING_NONE ?
+ params.route_id : next_routing_id_->Run();
+ WorkerService::GetInstance()->CreateWorker(
+ params, *route_id, this, request_context_);
+}
+
+void WorkerMessageFilter::OnLookupSharedWorker(
+ const ViewHostMsg_CreateWorker_Params& params,
+ bool* exists,
+ int* route_id,
+ bool* url_error) {
+ *route_id = next_routing_id_->Run();
+
+ bool off_the_record = static_cast<ChromeURLRequestContext*>(
+ request_context_->GetURLRequestContext())->is_off_the_record();
+ WorkerService::GetInstance()->LookupSharedWorker(
+ params, *route_id, this, off_the_record, exists, url_error);
+}
+
+void WorkerMessageFilter::OnCancelCreateDedicatedWorker(int route_id) {
+ WorkerService::GetInstance()->CancelCreateDedicatedWorker(route_id, this);
+}
+
+void WorkerMessageFilter::OnForwardToWorker(const IPC::Message& message) {
+ WorkerService::GetInstance()->ForwardToWorker(message, this);
+}
+
+void WorkerMessageFilter::OnDocumentDetached(unsigned long long document_id) {
+ WorkerService::GetInstance()->DocumentDetached(document_id, this);
+}
+
+void WorkerMessageFilter::OnCreateMessagePort(int *route_id,
+ int* message_port_id) {
+ *route_id = next_routing_id_->Run();
+ MessagePortService::GetInstance()->Create(*route_id, this, message_port_id);
+}
diff --git a/content/browser/worker_host/worker_message_filter.h b/content/browser/worker_host/worker_message_filter.h
new file mode 100644
index 0000000..f2de440
--- /dev/null
+++ b/content/browser/worker_host/worker_message_filter.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WORKER_HOST_WORKER_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_WORKER_HOST_WORKER_MESSAGE_FILTER_H_
+
+#include "base/callback.h"
+#include "content/browser/browser_message_filter.h"
+
+class ResourceDispatcherHost;
+class URLRequestContextGetter;
+struct ViewHostMsg_CreateWorker_Params;
+
+class WorkerMessageFilter : public BrowserMessageFilter {
+ public:
+ // |next_routing_id| is owned by this object. It can be used up until
+ // OnChannelClosing.
+ WorkerMessageFilter(
+ int render_process_id,
+ URLRequestContextGetter* request_context,
+ ResourceDispatcherHost* resource_dispatcher_host,
+ CallbackWithReturnValue<int>::Type* next_routing_id);
+
+ // BrowserMessageFilter implementation.
+ virtual void OnChannelClosing();
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+
+ int GetNextRoutingID();
+ int render_process_id() const { return render_process_id_; }
+ ResourceDispatcherHost* resource_dispatcher_host() const {
+ return resource_dispatcher_host_;
+ }
+
+ private:
+ ~WorkerMessageFilter();
+
+ // Message handlers.
+ void OnCreateWorker(const ViewHostMsg_CreateWorker_Params& params,
+ int* route_id);
+ void OnLookupSharedWorker(const ViewHostMsg_CreateWorker_Params& params,
+ bool* exists,
+ int* route_id,
+ bool* url_error);
+ void OnCancelCreateDedicatedWorker(int route_id);
+ void OnForwardToWorker(const IPC::Message& message);
+ void OnDocumentDetached(unsigned long long document_id);
+ void OnCreateMessagePort(int* route_id, int* message_port_id);
+
+ int render_process_id_;
+ scoped_refptr<URLRequestContextGetter> request_context_;
+ ResourceDispatcherHost* resource_dispatcher_host_;
+
+ // This is guaranteed to be valid until OnChannelClosing is closed, and it's
+ // not used after.
+ scoped_ptr<CallbackWithReturnValue<int>::Type> next_routing_id_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WorkerMessageFilter);
+};
+
+#endif // CONTENT_BROWSER_WORKER_HOST_WORKER_MESSAGE_FILTER_H_
diff --git a/content/browser/worker_host/worker_process_host.cc b/content/browser/worker_host/worker_process_host.cc
new file mode 100644
index 0000000..0a38d50
--- /dev/null
+++ b/content/browser/worker_host/worker_process_host.cc
@@ -0,0 +1,611 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/worker_host/worker_process_host.h"
+
+#include <set>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/content_settings/host_content_settings_map.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/debug_flags.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/result_codes.h"
+#include "chrome/common/worker_messages.h"
+#include "content/browser/appcache/appcache_dispatcher_host.h"
+#include "content/browser/browser_thread.h"
+#include "content/browser/child_process_security_policy.h"
+#include "content/browser/file_system/file_system_dispatcher_host.h"
+#include "content/browser/mime_registry_message_filter.h"
+#include "content/browser/renderer_host/blob_message_filter.h"
+#include "content/browser/renderer_host/database_message_filter.h"
+#include "content/browser/renderer_host/file_utilities_message_filter.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/renderer_host/render_view_host_notification_task.h"
+#include "content/browser/renderer_host/socket_stream_dispatcher_host.h"
+#include "content/browser/worker_host/message_port_service.h"
+#include "content/browser/worker_host/worker_message_filter.h"
+#include "content/browser/worker_host/worker_service.h"
+#include "net/base/mime_util.h"
+#include "ipc/ipc_switches.h"
+#include "net/base/registry_controlled_domain.h"
+#include "webkit/fileapi/file_system_path_manager.h"
+
+namespace {
+
+// Helper class that we pass to SocketStreamDispatcherHost so that it can find
+// the right net::URLRequestContext for a request.
+class URLRequestContextOverride
+ : public ResourceMessageFilter::URLRequestContextOverride {
+ public:
+ explicit URLRequestContextOverride(
+ net::URLRequestContext* url_request_context)
+ : url_request_context_(url_request_context) {
+ }
+ virtual ~URLRequestContextOverride() {}
+
+ virtual net::URLRequestContext* GetRequestContext(
+ const ViewHostMsg_Resource_Request& resource_request) {
+ return url_request_context_;
+ }
+
+ private:
+ net::URLRequestContext* url_request_context_;
+};
+
+} // namespace
+
+// Notifies RenderViewHost that one or more worker objects crashed.
+class WorkerCrashTask : public Task {
+ public:
+ WorkerCrashTask(int render_process_unique_id, int render_view_id)
+ : render_process_unique_id_(render_process_unique_id),
+ render_view_id_(render_view_id) { }
+
+ void Run() {
+ RenderViewHost* host =
+ RenderViewHost::FromID(render_process_unique_id_, render_view_id_);
+ if (host)
+ host->delegate()->WorkerCrashed();
+ }
+
+ private:
+ int render_process_unique_id_;
+ int render_view_id_;
+};
+
+WorkerProcessHost::WorkerProcessHost(
+ ResourceDispatcherHost* resource_dispatcher_host,
+ URLRequestContextGetter* request_context)
+ : BrowserChildProcessHost(WORKER_PROCESS, resource_dispatcher_host),
+ request_context_(request_context) {
+}
+
+WorkerProcessHost::~WorkerProcessHost() {
+ // If we crashed, tell the RenderViewHosts.
+ for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
+ const WorkerDocumentSet::DocumentInfoSet& parents =
+ i->worker_document_set()->documents();
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
+ parents.begin(); parent_iter != parents.end(); ++parent_iter) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ new WorkerCrashTask(parent_iter->render_process_id(),
+ parent_iter->render_view_id()));
+ }
+ }
+
+ ChildProcessSecurityPolicy::GetInstance()->Remove(id());
+}
+
+bool WorkerProcessHost::Init(int render_process_id) {
+ if (!CreateChannel())
+ return false;
+
+ FilePath exe_path = GetChildPath(true);
+ if (exe_path.empty())
+ return false;
+
+ CommandLine* cmd_line = new CommandLine(exe_path);
+ cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kWorkerProcess);
+ cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id());
+ SetCrashReporterCommandLine(cmd_line);
+
+ static const char* const kSwitchNames[] = {
+ switches::kWebWorkerShareProcesses,
+ switches::kDisableApplicationCache,
+ switches::kDisableDatabases,
+ switches::kEnableLogging,
+ switches::kLoggingLevel,
+ switches::kDisableWebSockets,
+#if defined(OS_WIN)
+ switches::kDisableDesktopNotifications,
+#endif
+ switches::kDisableFileSystem,
+ };
+ cmd_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kSwitchNames,
+ arraysize(kSwitchNames));
+
+#if defined(OS_POSIX)
+ bool use_zygote = true;
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kWaitForDebuggerChildren)) {
+ // Look to pass-on the kWaitForDebugger flag.
+ std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kWaitForDebuggerChildren);
+ if (value.empty() || value == switches::kWorkerProcess) {
+ cmd_line->AppendSwitch(switches::kWaitForDebugger);
+ use_zygote = false;
+ }
+ }
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren)) {
+ // Look to pass-on the kDebugOnStart flag.
+ std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kDebugChildren);
+ if (value.empty() || value == switches::kWorkerProcess) {
+ // launches a new xterm, and runs the worker process in gdb, reading
+ // optional commands from gdb_chrome file in the working directory.
+ cmd_line->PrependWrapper("xterm -e gdb -x gdb_chrome --args");
+ use_zygote = false;
+ }
+ }
+#endif
+
+ Launch(
+#if defined(OS_WIN)
+ FilePath(),
+#elif defined(OS_POSIX)
+ use_zygote,
+ base::environment_vector(),
+#endif
+ cmd_line);
+
+ ChildProcessSecurityPolicy::GetInstance()->Add(id());
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableFileSystem)) {
+ // Grant most file permissions to this worker.
+ // PLATFORM_FILE_TEMPORARY, PLATFORM_FILE_HIDDEN and
+ // PLATFORM_FILE_DELETE_ON_CLOSE are not granted, because no existing API
+ // requests them.
+ ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
+ id(),
+ GetChromeURLRequestContext()->file_system_context()->
+ path_manager()->base_path(),
+ base::PLATFORM_FILE_OPEN |
+ base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_OPEN_ALWAYS |
+ base::PLATFORM_FILE_CREATE_ALWAYS |
+ base::PLATFORM_FILE_READ |
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_EXCLUSIVE_READ |
+ base::PLATFORM_FILE_EXCLUSIVE_WRITE |
+ base::PLATFORM_FILE_ASYNC |
+ base::PLATFORM_FILE_TRUNCATE |
+ base::PLATFORM_FILE_WRITE_ATTRIBUTES);
+ }
+
+ CreateMessageFilters(render_process_id);
+
+ return true;
+}
+
+void WorkerProcessHost::CreateMessageFilters(int render_process_id) {
+ ChromeURLRequestContext* chrome_url_context = GetChromeURLRequestContext();
+
+ worker_message_filter_= new WorkerMessageFilter(
+ render_process_id,
+ request_context_,
+ resource_dispatcher_host(),
+ NewCallbackWithReturnValue(
+ WorkerService::GetInstance(), &WorkerService::next_worker_route_id));
+ AddFilter(worker_message_filter_);
+ AddFilter(new AppCacheDispatcherHost(chrome_url_context, id()));
+ AddFilter(new FileSystemDispatcherHost(chrome_url_context));
+ AddFilter(new FileUtilitiesMessageFilter(id()));
+ AddFilter(
+ new BlobMessageFilter(id(), chrome_url_context->blob_storage_context()));
+ AddFilter(new MimeRegistryMessageFilter());
+ AddFilter(new DatabaseMessageFilter(
+ chrome_url_context->database_tracker(),
+ chrome_url_context->host_content_settings_map()));
+
+ SocketStreamDispatcherHost* socket_stream_dispatcher_host =
+ new SocketStreamDispatcherHost();
+ socket_stream_dispatcher_host->set_url_request_context_override(
+ new URLRequestContextOverride(chrome_url_context));
+ AddFilter(socket_stream_dispatcher_host);
+}
+
+void WorkerProcessHost::CreateWorker(const WorkerInstance& instance) {
+ ChildProcessSecurityPolicy::GetInstance()->GrantRequestURL(
+ id(), instance.url());
+
+ instances_.push_back(instance);
+
+ WorkerProcessMsg_CreateWorker_Params params;
+ params.url = instance.url();
+ params.is_shared = instance.shared();
+ params.name = instance.name();
+ params.route_id = instance.worker_route_id();
+ params.creator_process_id = instance.parent_process_id();
+ params.creator_appcache_host_id = instance.parent_appcache_host_id();
+ params.shared_worker_appcache_id = instance.main_resource_appcache_id();
+ Send(new WorkerProcessMsg_CreateWorker(params));
+
+ UpdateTitle();
+
+ // Walk all pending filters and let them know the worker has been created
+ // (could be more than one in the case where we had to queue up worker
+ // creation because the worker process limit was reached).
+ for (WorkerInstance::FilterList::const_iterator i =
+ instance.filters().begin();
+ i != instance.filters().end(); ++i) {
+ i->first->Send(new ViewMsg_WorkerCreated(i->second));
+ }
+}
+
+bool WorkerProcessHost::FilterMessage(const IPC::Message& message,
+ WorkerMessageFilter* filter) {
+ for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
+ if (!i->closed() && i->HasFilter(filter, message.routing_id())) {
+ RelayMessage(message, worker_message_filter_, i->worker_route_id());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void WorkerProcessHost::OnProcessLaunched() {
+}
+
+bool WorkerProcessHost::OnMessageReceived(const IPC::Message& message) {
+ bool msg_is_ok = true;
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(WorkerProcessHost, message, msg_is_ok)
+ IPC_MESSAGE_HANDLER(WorkerHostMsg_WorkerContextClosed,
+ OnWorkerContextClosed)
+ IPC_MESSAGE_HANDLER(WorkerProcessHostMsg_AllowDatabase, OnAllowDatabase)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+
+ if (!msg_is_ok) {
+ NOTREACHED();
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_WPH"));
+ base::KillProcess(handle(), ResultCodes::KILLED_BAD_MESSAGE, false);
+ }
+
+ if (handled)
+ return true;
+
+ for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
+ if (i->worker_route_id() == message.routing_id()) {
+ if (!i->shared()) {
+ // Don't relay messages from shared workers (all communication is via
+ // the message port).
+ WorkerInstance::FilterInfo info = i->GetFilter();
+ RelayMessage(message, info.first, info.second);
+ }
+
+ if (message.type() == WorkerHostMsg_WorkerContextDestroyed::ID) {
+ instances_.erase(i);
+ UpdateTitle();
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// 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::OnAllowDatabase(int worker_route_id,
+ const GURL& url,
+ const string16& name,
+ const string16& display_name,
+ unsigned long estimated_size,
+ bool* result) {
+ ContentSetting content_setting = GetChromeURLRequestContext()->
+ host_content_settings_map()->GetContentSetting(
+ url, CONTENT_SETTINGS_TYPE_COOKIES, "");
+
+ *result = content_setting != CONTENT_SETTING_BLOCK;
+
+ // Find the worker instance and forward the message to all attached documents.
+ for (Instances::iterator i = instances_.begin(); i != instances_.end(); ++i) {
+ if (i->worker_route_id() != worker_route_id)
+ continue;
+ const WorkerDocumentSet::DocumentInfoSet& documents =
+ i->worker_document_set()->documents();
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator doc =
+ documents.begin(); doc != documents.end(); ++doc) {
+ CallRenderViewHostContentSettingsDelegate(
+ doc->render_process_id(), doc->render_view_id(),
+ &RenderViewHostDelegate::ContentSettings::OnWebDatabaseAccessed,
+ url, name, display_name, estimated_size, !*result);
+ }
+ break;
+ }
+}
+
+void WorkerProcessHost::RelayMessage(
+ const IPC::Message& message,
+ WorkerMessageFilter* filter,
+ int route_id) {
+ if (message.type() == WorkerMsg_PostMessage::ID) {
+ // We want to send the receiver a routing id for the new channel, so
+ // crack the message first.
+ string16 msg;
+ std::vector<int> sent_message_port_ids;
+ std::vector<int> new_routing_ids;
+ if (!WorkerMsg_PostMessage::Read(
+ &message, &msg, &sent_message_port_ids, &new_routing_ids)) {
+ return;
+ }
+ if (sent_message_port_ids.size() != new_routing_ids.size())
+ return;
+
+ for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
+ new_routing_ids[i] = filter->GetNextRoutingID();
+ MessagePortService::GetInstance()->UpdateMessagePort(
+ sent_message_port_ids[i], filter, new_routing_ids[i]);
+ }
+
+ filter->Send(new WorkerMsg_PostMessage(
+ route_id, msg, sent_message_port_ids, new_routing_ids));
+
+ // Send any queued messages to the sent message ports. We can only do this
+ // after sending the above message, since it's the one that sets up the
+ // message port route which the queued messages are sent to.
+ for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
+ MessagePortService::GetInstance()->
+ SendQueuedMessagesIfPossible(sent_message_port_ids[i]);
+ }
+ } else if (message.type() == WorkerMsg_Connect::ID) {
+ // Crack the SharedWorker Connect message to setup routing for the port.
+ int sent_message_port_id;
+ int new_routing_id;
+ if (!WorkerMsg_Connect::Read(
+ &message, &sent_message_port_id, &new_routing_id)) {
+ return;
+ }
+ new_routing_id = filter->GetNextRoutingID();
+ MessagePortService::GetInstance()->UpdateMessagePort(
+ sent_message_port_id, filter, new_routing_id);
+
+ // Resend the message with the new routing id.
+ filter->Send(new WorkerMsg_Connect(
+ route_id, sent_message_port_id, new_routing_id));
+
+ // Send any queued messages for the sent port.
+ MessagePortService::GetInstance()->SendQueuedMessagesIfPossible(
+ sent_message_port_id);
+ } else {
+ IPC::Message* new_message = new IPC::Message(message);
+ new_message->set_routing_id(route_id);
+ filter->Send(new_message);
+ return;
+ }
+}
+
+void WorkerProcessHost::FilterShutdown(WorkerMessageFilter* filter) {
+ for (Instances::iterator i = instances_.begin(); i != instances_.end();) {
+ bool shutdown = false;
+ i->RemoveFilters(filter);
+ if (i->shared()) {
+ i->worker_document_set()->RemoveAll(filter);
+ if (i->worker_document_set()->IsEmpty()) {
+ shutdown = true;
+ }
+ } else if (i->NumFilters() == 0) {
+ shutdown = true;
+ }
+ if (shutdown) {
+ Send(new WorkerMsg_TerminateWorkerContext(i->worker_route_id()));
+ i = instances_.erase(i);
+ } else {
+ ++i;
+ }
+ }
+}
+
+bool WorkerProcessHost::CanShutdown() {
+ return instances_.empty();
+}
+
+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());
+ // Use the host name if the domain is empty, i.e. localhost or IP address.
+ if (title.empty())
+ title = i->url().host();
+
+ // Check if it's an extension-created worker, in which case we want to use
+ // the name of the extension.
+ std::string extension_name = GetChromeURLRequestContext()->
+ extension_info_map()->GetNameForExtension(title);
+ if (!extension_name.empty()) {
+ titles.insert(extension_name);
+ continue;
+ }
+
+ // If the host name is empty, i.e. file url, use the path.
+ if (title.empty())
+ title = i->url().path();
+ titles.insert(title);
+ }
+
+ std::string display_title;
+ for (std::set<std::string>::iterator i = titles.begin();
+ i != titles.end(); ++i) {
+ if (!display_title.empty())
+ display_title += ", ";
+ display_title += *i;
+ }
+
+ set_name(ASCIIToWide(display_title));
+}
+
+ChromeURLRequestContext* WorkerProcessHost::GetChromeURLRequestContext() {
+ return static_cast<ChromeURLRequestContext*>(
+ request_context_->GetURLRequestContext());
+}
+
+void WorkerProcessHost::DocumentDetached(WorkerMessageFilter* filter,
+ 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->shared()) {
+ ++i;
+ } else {
+ i->worker_document_set()->Remove(filter, document_id);
+ if (i->worker_document_set()->IsEmpty()) {
+ // 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 shared,
+ bool off_the_record,
+ const string16& name,
+ int worker_route_id,
+ int parent_process_id,
+ int parent_appcache_host_id,
+ int64 main_resource_appcache_id,
+ URLRequestContextGetter* request_context)
+ : url_(url),
+ shared_(shared),
+ off_the_record_(off_the_record),
+ closed_(false),
+ name_(name),
+ worker_route_id_(worker_route_id),
+ parent_process_id_(parent_process_id),
+ parent_appcache_host_id_(parent_appcache_host_id),
+ main_resource_appcache_id_(main_resource_appcache_id),
+ request_context_(request_context),
+ worker_document_set_(new WorkerDocumentSet()) {
+}
+
+WorkerProcessHost::WorkerInstance::~WorkerInstance() {
+}
+
+// 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,
+ bool off_the_record) const {
+ // Only match open shared workers.
+ if (!shared_ || closed_)
+ return false;
+
+ // Incognito workers don't match non-incognito workers.
+ if (off_the_record_ != off_the_record)
+ 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::AddFilter(WorkerMessageFilter* filter,
+ int route_id) {
+ if (!HasFilter(filter, route_id)) {
+ FilterInfo info(filter, route_id);
+ filters_.push_back(info);
+ }
+ // Only shared workers can have more than one associated filter.
+ DCHECK(shared_ || filters_.size() == 1);
+}
+
+void WorkerProcessHost::WorkerInstance::RemoveFilter(
+ WorkerMessageFilter* filter, int route_id) {
+ for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
+ if (i->first == filter && i->second == route_id)
+ i = filters_.erase(i);
+ else
+ ++i;
+ }
+ // Should not be duplicate copies in the filter set.
+ DCHECK(!HasFilter(filter, route_id));
+}
+
+void WorkerProcessHost::WorkerInstance::RemoveFilters(
+ WorkerMessageFilter* filter) {
+ for (FilterList::iterator i = filters_.begin(); i != filters_.end();) {
+ if (i->first == filter)
+ i = filters_.erase(i);
+ else
+ ++i;
+ }
+}
+
+bool WorkerProcessHost::WorkerInstance::HasFilter(
+ WorkerMessageFilter* filter, int route_id) const {
+ for (FilterList::const_iterator i = filters_.begin(); i != filters_.end();
+ ++i) {
+ if (i->first == filter && i->second == route_id)
+ return true;
+ }
+ return false;
+}
+
+bool WorkerProcessHost::WorkerInstance::RendererIsParent(
+ int render_process_id, int render_view_id) const {
+ const WorkerDocumentSet::DocumentInfoSet& parents =
+ worker_document_set()->documents();
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
+ parents.begin();
+ parent_iter != parents.end(); ++parent_iter) {
+ if (parent_iter->render_process_id() == render_process_id &&
+ parent_iter->render_view_id() == render_view_id) {
+ return true;
+ }
+ }
+ return false;
+}
+
+WorkerProcessHost::WorkerInstance::FilterInfo
+WorkerProcessHost::WorkerInstance::GetFilter() const {
+ DCHECK(NumFilters() == 1);
+ return *filters_.begin();
+}
diff --git a/content/browser/worker_host/worker_process_host.h b/content/browser/worker_host/worker_process_host.h
new file mode 100644
index 0000000..af446f0
--- /dev/null
+++ b/content/browser/worker_host/worker_process_host.h
@@ -0,0 +1,187 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WORKER_HOST_WORKER_PROCESS_HOST_H_
+#define CONTENT_BROWSER_WORKER_HOST_WORKER_PROCESS_HOST_H_
+#pragma once
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "content/browser/browser_child_process_host.h"
+#include "content/browser/worker_host/worker_document_set.h"
+#include "googleurl/src/gurl.h"
+
+class URLRequestContextGetter;
+
+// The WorkerProcessHost is the interface that represents the browser side of
+// the browser <-> worker communication channel. There will be one
+// WorkerProcessHost per worker process. Currently each worker runs in its own
+// process, but that may change. However, we do assume (by storing a
+// net::URLRequestContext) that a WorkerProcessHost serves a single Profile.
+class WorkerProcessHost : public BrowserChildProcessHost {
+ public:
+
+ // Contains information about each worker instance, needed to forward messages
+ // between the renderer and worker processes.
+ class WorkerInstance {
+ public:
+ WorkerInstance(const GURL& url,
+ bool shared,
+ bool off_the_record,
+ const string16& name,
+ int worker_route_id,
+ int parent_process_id,
+ int parent_appcache_host_id,
+ int64 main_resource_appcache_id,
+ URLRequestContextGetter* request_context);
+ ~WorkerInstance();
+
+ // Unique identifier for a worker client.
+ typedef std::pair<WorkerMessageFilter*, int> FilterInfo;
+
+ // APIs to manage the filter list for a given instance.
+ void AddFilter(WorkerMessageFilter* filter, int route_id);
+ void RemoveFilter(WorkerMessageFilter* filter, int route_id);
+ void RemoveFilters(WorkerMessageFilter* filter);
+ bool HasFilter(WorkerMessageFilter* filter, int route_id) const;
+ bool RendererIsParent(int render_process_id, int render_view_id) const;
+ int NumFilters() const { return filters_.size(); }
+ // Returns the single filter (must only be one).
+ FilterInfo GetFilter() const;
+
+ typedef std::list<FilterInfo> FilterList;
+ const FilterList& filters() const { return filters_; }
+
+ // 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, bool off_the_record) const;
+
+ // Shares the passed instance's WorkerDocumentSet with this instance. This
+ // instance's current WorkerDocumentSet is dereferenced (and freed if this
+ // is the only reference) as a result.
+ void ShareDocumentSet(const WorkerInstance& instance) {
+ worker_document_set_ = instance.worker_document_set_;
+ };
+
+ // Accessors
+ bool shared() const { return shared_; }
+ bool off_the_record() const { return off_the_record_; }
+ bool closed() const { return closed_; }
+ void set_closed(bool closed) { closed_ = closed; }
+ const GURL& url() const { return url_; }
+ const string16 name() const { return name_; }
+ int worker_route_id() const { return worker_route_id_; }
+ int parent_process_id() const { return parent_process_id_; }
+ int parent_appcache_host_id() const { return parent_appcache_host_id_; }
+ int64 main_resource_appcache_id() const {
+ return main_resource_appcache_id_;
+ }
+ WorkerDocumentSet* worker_document_set() const {
+ return worker_document_set_;
+ }
+ URLRequestContextGetter* request_context() const {
+ return request_context_;
+ }
+
+ private:
+ // Set of all filters (clients) associated with this worker.
+ GURL url_;
+ bool shared_;
+ bool off_the_record_;
+ bool closed_;
+ string16 name_;
+ int worker_route_id_;
+ int parent_process_id_;
+ int parent_appcache_host_id_;
+ int64 main_resource_appcache_id_;
+ scoped_refptr<URLRequestContextGetter> request_context_;
+ FilterList filters_;
+ scoped_refptr<WorkerDocumentSet> worker_document_set_;
+ };
+
+ WorkerProcessHost(
+ ResourceDispatcherHost* resource_dispatcher_host,
+ URLRequestContextGetter* request_context);
+ ~WorkerProcessHost();
+
+ // Starts the process. Returns true iff it succeeded.
+ // |render_process_id| is the renderer process responsible for starting this
+ // worker.
+ bool Init(int render_process_id);
+
+ // Creates a worker object in the process.
+ void CreateWorker(const WorkerInstance& instance);
+
+ // Returns true iff the given message from a renderer process was forwarded to
+ // the worker.
+ bool FilterMessage(const IPC::Message& message, WorkerMessageFilter* filter);
+
+ void FilterShutdown(WorkerMessageFilter* filter);
+
+ // Shuts down any shared workers that are no longer referenced by active
+ // documents.
+ void DocumentDetached(WorkerMessageFilter* filter,
+ unsigned long long document_id);
+
+ URLRequestContextGetter* request_context() const {
+ return request_context_;
+ }
+
+ protected:
+ friend class WorkerService;
+
+ typedef std::list<WorkerInstance> Instances;
+ const Instances& instances() const { return instances_; }
+ Instances& mutable_instances() { return instances_; }
+
+ private:
+ // Called when the process has been launched successfully.
+ virtual void OnProcessLaunched();
+
+ // Creates and adds the message filters.
+ void CreateMessageFilters(int render_process_id);
+
+ // IPC::Channel::Listener implementation:
+ // Called when a message arrives from the worker process.
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ void OnWorkerContextClosed(int worker_route_id);
+ void OnAllowDatabase(int worker_route_id,
+ const GURL& url,
+ const string16& name,
+ const string16& display_name,
+ unsigned long estimated_size,
+ bool* result);
+
+ // Relays a message to the given endpoint. Takes care of parsing the message
+ // if it contains a message port and sending it a valid route id.
+ static void RelayMessage(const IPC::Message& message,
+ WorkerMessageFilter* filter,
+ int route_id);
+
+ virtual bool CanShutdown();
+
+ // Updates the title shown in the task manager.
+ void UpdateTitle();
+
+ ChromeURLRequestContext* GetChromeURLRequestContext();
+
+ Instances instances_;
+
+ scoped_refptr<URLRequestContextGetter> request_context_;
+
+ // A reference to the filter associated with this worker process. We need to
+ // keep this around since we'll use it when forward messages to the worker
+ // process.
+ scoped_refptr<WorkerMessageFilter> worker_message_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerProcessHost);
+};
+
+#endif // CONTENT_BROWSER_WORKER_HOST_WORKER_PROCESS_HOST_H_
diff --git a/content/browser/worker_host/worker_service.cc b/content/browser/worker_host/worker_service.cc
new file mode 100644
index 0000000..ac4c979
--- /dev/null
+++ b/content/browser/worker_host/worker_service.cc
@@ -0,0 +1,558 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/worker_host/worker_service.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/sys_info.h"
+#include "base/threading/thread.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/worker_messages.h"
+#include "content/browser/worker_host/worker_message_filter.h"
+#include "content/browser/worker_host/worker_process_host.h"
+#include "net/base/registry_controlled_domain.h"
+
+const int WorkerService::kMaxWorkerProcessesWhenSharing = 10;
+const int WorkerService::kMaxWorkersWhenSeparate = 64;
+const int WorkerService::kMaxWorkersPerTabWhenSeparate = 16;
+
+WorkerService* WorkerService::GetInstance() {
+ return Singleton<WorkerService>::get();
+}
+
+WorkerService::WorkerService() : next_worker_route_id_(0) {
+}
+
+WorkerService::~WorkerService() {
+}
+
+void WorkerService::OnWorkerMessageFilterClosing(WorkerMessageFilter* filter) {
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ worker->FilterShutdown(filter);
+ }
+
+ // See if that process had any queued workers.
+ for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin();
+ i != queued_workers_.end();) {
+ i->RemoveFilters(filter);
+ if (i->NumFilters() == 0) {
+ i = queued_workers_.erase(i);
+ } else {
+ ++i;
+ }
+ }
+
+ // Also, see if that process had any pending shared workers.
+ for (WorkerProcessHost::Instances::iterator iter =
+ pending_shared_workers_.begin();
+ iter != pending_shared_workers_.end(); ) {
+ iter->worker_document_set()->RemoveAll(filter);
+ if (iter->worker_document_set()->IsEmpty()) {
+ iter = pending_shared_workers_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ // Either a worker proceess has shut down, in which case we can start one of
+ // the queued workers, or a renderer has shut down, in which case it doesn't
+ // affect anything. We call this function in both scenarios because then we
+ // don't have to keep track which filters are from worker processes.
+ TryStartingQueuedWorker();
+}
+
+void WorkerService::CreateWorker(const ViewHostMsg_CreateWorker_Params& params,
+ int route_id,
+ WorkerMessageFilter* filter,
+ URLRequestContextGetter* request_context) {
+
+ ChromeURLRequestContext* context = static_cast<ChromeURLRequestContext*>(
+ request_context->GetURLRequestContext());
+
+ // 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(
+ params.url,
+ params.is_shared,
+ context->is_off_the_record(),
+ params.name,
+ next_worker_route_id(),
+ params.is_shared ? 0 : filter->render_process_id(),
+ params.is_shared ? 0 : params.parent_appcache_host_id,
+ params.is_shared ? params.script_resource_appcache_id : 0,
+ request_context);
+ instance.AddFilter(filter, route_id);
+ instance.worker_document_set()->Add(
+ filter, params.document_id, filter->render_process_id(),
+ params.render_view_route_id);
+
+ CreateWorkerFromInstance(instance);
+}
+
+void WorkerService::LookupSharedWorker(
+ const ViewHostMsg_CreateWorker_Params& params,
+ int route_id,
+ WorkerMessageFilter* filter,
+ bool off_the_record,
+ bool* exists,
+ bool* url_mismatch) {
+
+ *exists = true;
+ WorkerProcessHost::WorkerInstance* instance = FindSharedWorkerInstance(
+ params.url, params.name, off_the_record);
+
+ 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(params.url, params.name, off_the_record);
+ *exists = false;
+ }
+
+ // Make sure the passed-in instance matches the URL - if not, return an
+ // error.
+ if (params.url != instance->url()) {
+ *url_mismatch = true;
+ *exists = false;
+ } else {
+ *url_mismatch = false;
+ // Add our route ID to the existing instance so we can send messages to it.
+ instance->AddFilter(filter, route_id);
+
+ // Add the passed filter/document_id to the worker instance.
+ // TODO(atwilson): This won't work if the message is from a worker process.
+ // We don't support that yet though (this message is only sent from
+ // renderers) but when we do, we'll need to add code to pass in the current
+ // worker's document set for nested workers.
+ instance->worker_document_set()->Add(
+ filter, params.document_id, filter->render_process_id(),
+ params.render_view_route_id);
+ }
+}
+
+void WorkerService::CancelCreateDedicatedWorker(
+ int route_id,
+ WorkerMessageFilter* filter) {
+ for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin();
+ i != queued_workers_.end(); ++i) {
+ if (i->HasFilter(filter, route_id)) {
+ DCHECK(!i->shared());
+ queued_workers_.erase(i);
+ return;
+ }
+ }
+
+ // There could be a race condition where the WebWorkerProxy told us to cancel
+ // the worker right as we sent it a message say it's been created. Look at
+ // the running workers.
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ for (WorkerProcessHost::Instances::const_iterator instance =
+ worker->instances().begin();
+ instance != worker->instances().end(); ++instance) {
+ if (instance->HasFilter(filter, route_id)) {
+ // Fake a worker destroyed message so that WorkerProcessHost cleans up
+ // properly.
+ WorkerMsg_TerminateWorkerContext message(route_id);
+ ForwardToWorker(message, filter);
+ return;
+ }
+ }
+ }
+
+ DCHECK(false) << "Couldn't find worker to cancel";
+}
+
+void WorkerService::ForwardToWorker(const IPC::Message& message,
+ WorkerMessageFilter* filter) {
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ if (worker->FilterMessage(message, filter))
+ return;
+ }
+
+ // TODO(jabdelmalek): tell filter that callee is gone
+}
+
+void WorkerService::DocumentDetached(unsigned long long document_id,
+ WorkerMessageFilter* filter) {
+ // Any associated shared workers can be shut down.
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ worker->DocumentDetached(filter, document_id);
+ }
+
+ // Remove any queued shared workers for this document.
+ for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin();
+ iter != queued_workers_.end();) {
+ if (iter->shared()) {
+ iter->worker_document_set()->Remove(filter, document_id);
+ if (iter->worker_document_set()->IsEmpty()) {
+ 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->worker_document_set()->Remove(filter, document_id);
+ if (iter->worker_document_set()->IsEmpty()) {
+ iter = pending_shared_workers_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+bool WorkerService::CreateWorkerFromInstance(
+ WorkerProcessHost::WorkerInstance instance) {
+ // TODO(michaeln): We need to ensure that a process is working
+ // on behalf of a single profile. The process sharing logic below
+ // does not ensure that. Consider making WorkerService a per profile
+ // object to help with this.
+ WorkerProcessHost* worker = NULL;
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kWebWorkerProcessPerCore)) {
+ worker = GetProcessToFillUpCores();
+ } else if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kWebWorkerShareProcesses)) {
+ worker = GetProcessForDomain(instance.url());
+ } else { // One process per worker.
+ if (!CanCreateWorkerProcess(instance)) {
+ queued_workers_.push_back(instance);
+ return true;
+ }
+ }
+
+ // Check to see if this shared worker is already running (two pages may have
+ // tried to start up the worker simultaneously).
+ if (instance.shared()) {
+ // See if a worker with this name already exists.
+ WorkerProcessHost::WorkerInstance* existing_instance =
+ FindSharedWorkerInstance(
+ instance.url(), instance.name(), instance.off_the_record());
+ WorkerProcessHost::WorkerInstance::FilterInfo filter_info =
+ instance.GetFilter();
+ // 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) {
+ // Walk the worker's filter list to see if this client is listed. If not,
+ // then it means that the worker started by the client already exited so
+ // we should not attach to this new one (http://crbug.com/29243).
+ if (!existing_instance->HasFilter(filter_info.first, filter_info.second))
+ return false;
+ filter_info.first->Send(new ViewMsg_WorkerCreated(filter_info.second));
+ return true;
+ }
+
+ // Look to see if there's a pending instance.
+ WorkerProcessHost::WorkerInstance* pending = FindPendingInstance(
+ instance.url(), instance.name(), instance.off_the_record());
+ // If there's no instance *and* no pending instance (or there is a pending
+ // instance but it does not contain our filter info), 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 ||
+ !pending->HasFilter(filter_info.first, filter_info.second)) {
+ DLOG(WARNING) << "Pending worker already exited";
+ return false;
+ }
+
+ // Assign the accumulated document set and filter list for this pending
+ // worker to the new instance.
+ DCHECK(!pending->worker_document_set()->IsEmpty());
+ instance.ShareDocumentSet(*pending);
+ for (WorkerProcessHost::WorkerInstance::FilterList::const_iterator i =
+ pending->filters().begin();
+ i != pending->filters().end(); ++i) {
+ instance.AddFilter(i->first, i->second);
+ }
+ RemovePendingInstances(
+ instance.url(), instance.name(), instance.off_the_record());
+
+ // Remove any queued instances of this worker and copy over the filter to
+ // this instance.
+ for (WorkerProcessHost::Instances::iterator iter = queued_workers_.begin();
+ iter != queued_workers_.end();) {
+ if (iter->Matches(instance.url(), instance.name(),
+ instance.off_the_record())) {
+ DCHECK(iter->NumFilters() == 1);
+ WorkerProcessHost::WorkerInstance::FilterInfo filter_info =
+ iter->GetFilter();
+ instance.AddFilter(filter_info.first, filter_info.second);
+ iter = queued_workers_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ }
+
+ if (!worker) {
+ WorkerMessageFilter* first_filter = instance.filters().begin()->first;
+ worker = new WorkerProcessHost(
+ first_filter->resource_dispatcher_host(),
+ instance.request_context());
+ // TODO(atwilson): This won't work if the message is from a worker process.
+ // We don't support that yet though (this message is only sent from
+ // renderers) but when we do, we'll need to add code to pass in the current
+ // worker's document set for nested workers.
+ if (!worker->Init(first_filter->render_process_id())) {
+ delete worker;
+ return false;
+ }
+ }
+
+ // TODO(michaeln): As written, test can fail per my earlier comment in
+ // this method, but that's a bug.
+ // DCHECK(worker->request_context() == instance.request_context());
+
+ worker->CreateWorker(instance);
+ return true;
+}
+
+WorkerProcessHost* WorkerService::GetProcessForDomain(const GURL& url) {
+ int num_processes = 0;
+ std::string domain =
+ net::RegistryControlledDomainService::GetDomainAndRegistry(url);
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ num_processes++;
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ for (WorkerProcessHost::Instances::const_iterator instance =
+ worker->instances().begin();
+ instance != worker->instances().end(); ++instance) {
+ if (net::RegistryControlledDomainService::GetDomainAndRegistry(
+ instance->url()) == domain) {
+ return worker;
+ }
+ }
+ }
+
+ if (num_processes >= kMaxWorkerProcessesWhenSharing)
+ return GetLeastLoadedWorker();
+
+ return NULL;
+}
+
+WorkerProcessHost* WorkerService::GetProcessToFillUpCores() {
+ int num_processes = 0;
+ BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ for (; !iter.Done(); ++iter)
+ num_processes++;
+
+ if (num_processes >= base::SysInfo::NumberOfProcessors())
+ return GetLeastLoadedWorker();
+
+ return NULL;
+}
+
+WorkerProcessHost* WorkerService::GetLeastLoadedWorker() {
+ WorkerProcessHost* smallest = NULL;
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ if (!smallest || worker->instances().size() < smallest->instances().size())
+ smallest = worker;
+ }
+
+ return smallest;
+}
+
+bool WorkerService::CanCreateWorkerProcess(
+ const WorkerProcessHost::WorkerInstance& instance) {
+ // Worker can be fired off if *any* parent has room.
+ const WorkerDocumentSet::DocumentInfoSet& parents =
+ instance.worker_document_set()->documents();
+
+ for (WorkerDocumentSet::DocumentInfoSet::const_iterator parent_iter =
+ parents.begin();
+ parent_iter != parents.end(); ++parent_iter) {
+ bool hit_total_worker_limit = false;
+ if (TabCanCreateWorkerProcess(parent_iter->render_process_id(),
+ parent_iter->render_view_id(),
+ &hit_total_worker_limit)) {
+ return true;
+ }
+ // Return false if already at the global worker limit (no need to continue
+ // checking parent tabs).
+ if (hit_total_worker_limit)
+ return false;
+ }
+ // If we've reached here, none of the parent tabs is allowed to create an
+ // instance.
+ return false;
+}
+
+bool WorkerService::TabCanCreateWorkerProcess(int render_process_id,
+ int render_view_id,
+ bool* hit_total_worker_limit) {
+ int total_workers = 0;
+ int workers_per_tab = 0;
+ *hit_total_worker_limit = false;
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ for (WorkerProcessHost::Instances::const_iterator cur_instance =
+ worker->instances().begin();
+ cur_instance != worker->instances().end(); ++cur_instance) {
+ total_workers++;
+ if (total_workers >= kMaxWorkersWhenSeparate) {
+ *hit_total_worker_limit = true;
+ return false;
+ }
+ if (cur_instance->RendererIsParent(render_process_id, render_view_id)) {
+ workers_per_tab++;
+ if (workers_per_tab >= kMaxWorkersPerTabWhenSeparate)
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void WorkerService::TryStartingQueuedWorker() {
+ if (queued_workers_.empty())
+ return;
+
+ for (WorkerProcessHost::Instances::iterator i = queued_workers_.begin();
+ i != queued_workers_.end();) {
+ if (CanCreateWorkerProcess(*i)) {
+ WorkerProcessHost::WorkerInstance instance = *i;
+ queued_workers_.erase(i);
+ CreateWorkerFromInstance(instance);
+
+ // CreateWorkerFromInstance can modify the queued_workers_ list when it
+ // coalesces queued instances after starting a shared worker, so we
+ // have to rescan the list from the beginning (our iterator is now
+ // invalid). This is not a big deal as having any queued workers will be
+ // rare in practice so the list will be small.
+ i = queued_workers_.begin();
+ } else {
+ ++i;
+ }
+ }
+}
+
+bool WorkerService::GetRendererForWorker(int worker_process_id,
+ int* render_process_id,
+ int* render_view_id) const {
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ if (iter->id() != worker_process_id)
+ continue;
+
+ // This code assumes one worker per process, see function comment in header!
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ WorkerProcessHost::Instances::const_iterator first_instance =
+ worker->instances().begin();
+ if (first_instance == worker->instances().end())
+ return false;
+
+ WorkerDocumentSet::DocumentInfoSet::const_iterator info =
+ first_instance->worker_document_set()->documents().begin();
+ *render_process_id = info->render_process_id();
+ *render_view_id = info->render_view_id();
+ return true;
+ }
+ return false;
+}
+
+const WorkerProcessHost::WorkerInstance* WorkerService::FindWorkerInstance(
+ int worker_process_id) {
+ for (BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS);
+ !iter.Done(); ++iter) {
+ if (iter->id() != worker_process_id)
+ continue;
+
+ WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter);
+ WorkerProcessHost::Instances::const_iterator instance =
+ worker->instances().begin();
+ return instance == worker->instances().end() ? NULL : &*instance;
+ }
+ return NULL;
+}
+
+WorkerProcessHost::WorkerInstance*
+WorkerService::FindSharedWorkerInstance(const GURL& url, const string16& name,
+ bool off_the_record) {
+ for (BrowserChildProcessHost::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, off_the_record))
+ return &(*instance_iter);
+ }
+ }
+ return NULL;
+}
+
+WorkerProcessHost::WorkerInstance*
+WorkerService::FindPendingInstance(const GURL& url, const string16& name,
+ bool off_the_record) {
+ // 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, off_the_record)) {
+ return &(*iter);
+ }
+ }
+ return NULL;
+}
+
+
+void WorkerService::RemovePendingInstances(const GURL& url,
+ const string16& name,
+ bool off_the_record) {
+ // Walk the pending instances looking for a matching pending worker.
+ for (WorkerProcessHost::Instances::iterator iter =
+ pending_shared_workers_.begin();
+ iter != pending_shared_workers_.end(); ) {
+ if (iter->Matches(url, name, off_the_record)) {
+ iter = pending_shared_workers_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+WorkerProcessHost::WorkerInstance*
+WorkerService::CreatePendingInstance(const GURL& url,
+ const string16& name,
+ bool off_the_record) {
+ // Look for an existing pending shared worker.
+ WorkerProcessHost::WorkerInstance* instance =
+ FindPendingInstance(url, name, off_the_record);
+ if (instance)
+ return instance;
+
+ // No existing pending worker - create a new one.
+ WorkerProcessHost::WorkerInstance pending(
+ url, true, off_the_record, name, MSG_ROUTING_NONE, 0, 0, 0, NULL);
+ pending_shared_workers_.push_back(pending);
+ return &pending_shared_workers_.back();
+}
diff --git a/content/browser/worker_host/worker_service.h b/content/browser/worker_host/worker_service.h
new file mode 100644
index 0000000..2b054aa
--- /dev/null
+++ b/content/browser/worker_host/worker_service.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_WORKER_HOST_WORKER_SERVICE_H_
+#define CONTENT_BROWSER_WORKER_HOST_WORKER_SERVICE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/singleton.h"
+#include "content/browser/worker_host/worker_process_host.h"
+#include "googleurl/src/gurl.h"
+
+class URLRequestContextGetter;
+struct ViewHostMsg_CreateWorker_Params;
+
+// A singelton for managing HTML5 web workers.
+class WorkerService {
+ public:
+ // Returns the WorkerService singleton.
+ static WorkerService* GetInstance();
+
+ // These methods correspond to worker related IPCs.
+ void CreateWorker(const ViewHostMsg_CreateWorker_Params& params,
+ int route_id,
+ WorkerMessageFilter* filter,
+ URLRequestContextGetter* request_context);
+ void LookupSharedWorker(const ViewHostMsg_CreateWorker_Params& params,
+ int route_id,
+ WorkerMessageFilter* filter,
+ bool off_the_record,
+ bool* exists,
+ bool* url_error);
+ void CancelCreateDedicatedWorker(int route_id, WorkerMessageFilter* filter);
+ void ForwardToWorker(const IPC::Message& message,
+ WorkerMessageFilter* filter);
+ void DocumentDetached(unsigned long long document_id,
+ WorkerMessageFilter* filter);
+
+ void OnWorkerMessageFilterClosing(WorkerMessageFilter* filter);
+
+ int next_worker_route_id() { return ++next_worker_route_id_; }
+
+ // Given a worker's process id, return the IDs of the renderer process and
+ // render view that created it. For shared workers, this returns the first
+ // parent.
+ // TODO(dimich): This code assumes there is 1 worker per worker process, which
+ // is how it is today until V8 can run in separate threads.
+ bool GetRendererForWorker(int worker_process_id,
+ int* render_process_id,
+ int* render_view_id) const;
+ const WorkerProcessHost::WorkerInstance* FindWorkerInstance(
+ int worker_process_id);
+
+ // Used when multiple workers can run in the same process.
+ static const int kMaxWorkerProcessesWhenSharing;
+
+ // Used when we run each worker in a separate process.
+ static const int kMaxWorkersWhenSeparate;
+ static const int kMaxWorkersPerTabWhenSeparate;
+
+ private:
+ friend struct DefaultSingletonTraits<WorkerService>;
+
+ WorkerService();
+ ~WorkerService();
+
+ // Given a WorkerInstance, create an associated worker process.
+ bool CreateWorkerFromInstance(WorkerProcessHost::WorkerInstance instance);
+
+ // Returns a WorkerProcessHost object if one exists for the given domain, or
+ // NULL if there are no such workers yet.
+ WorkerProcessHost* GetProcessForDomain(const GURL& url);
+
+ // Returns a WorkerProcessHost based on a strategy of creating one worker per
+ // core.
+ WorkerProcessHost* GetProcessToFillUpCores();
+
+ // Returns the WorkerProcessHost from the existing set that has the least
+ // number of worker instance running.
+ WorkerProcessHost* GetLeastLoadedWorker();
+
+ // Checks if we can create a worker process based on the process limit when
+ // we're using a strategy of one process per core.
+ bool CanCreateWorkerProcess(
+ const WorkerProcessHost::WorkerInstance& instance);
+
+ // Checks if the tab associated with the passed RenderView can create a
+ // worker process based on the process limit when we're using a strategy of
+ // one worker per process.
+ bool TabCanCreateWorkerProcess(
+ int render_process_id, int render_route_id, bool* hit_total_worker_limit);
+
+ // Tries to see if any of the queued workers can be created.
+ void TryStartingQueuedWorker();
+
+ // APIs for manipulating our set of pending shared worker instances.
+ WorkerProcessHost::WorkerInstance* CreatePendingInstance(
+ const GURL& url, const string16& name, bool off_the_record);
+ WorkerProcessHost::WorkerInstance* FindPendingInstance(
+ const GURL& url, const string16& name, bool off_the_record);
+ void RemovePendingInstances(
+ const GURL& url, const string16& name, bool off_the_record);
+
+ WorkerProcessHost::WorkerInstance* FindSharedWorkerInstance(
+ const GURL& url, const string16& name, bool off_the_record);
+
+ NotificationRegistrar registrar_;
+ int next_worker_route_id_;
+
+ 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);
+};
+
+#endif // CONTENT_BROWSER_WORKER_HOST_WORKER_SERVICE_H_
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 8324a8a..eafd1a2 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -299,6 +299,16 @@
'browser/webui/web_ui_factory.h',
'browser/webui/web_ui_util.cc',
'browser/webui/web_ui_util.h',
+ 'browser/worker_host/message_port_service.cc',
+ 'browser/worker_host/message_port_service.h',
+ 'browser/worker_host/worker_document_set.cc',
+ 'browser/worker_host/worker_document_set.h',
+ 'browser/worker_host/worker_message_filter.cc',
+ 'browser/worker_host/worker_message_filter.h',
+ 'browser/worker_host/worker_process_host.cc',
+ 'browser/worker_host/worker_process_host.h',
+ 'browser/worker_host/worker_service.cc',
+ 'browser/worker_host/worker_service.h',
'browser/zygote_host_linux.cc',
'browser/zygote_host_linux.h',
'browser/zygote_main_linux.cc',