Commit eefb7c07 authored by horo@chromium.org's avatar horo@chromium.org

Make DevTools support for the embedded SharedWorker.

Original patch is https://codereview.chromium.org/196503005/.
But it was reverted because of memory leaks.
https://codereview.chromium.org/223583002/

This is the fixed version.

BUG=327256

Review URL: https://codereview.chromium.org/223123003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@261432 0039d316-1c4b-4281-b951-d872f2087c98
parent 23a8406b
// Copyright 2014 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/devtools/shared_worker_devtools_manager.h"
#include "content/browser/devtools/devtools_manager_impl.h"
#include "content/browser/devtools/devtools_protocol.h"
#include "content/browser/devtools/devtools_protocol_constants.h"
#include "content/browser/devtools/ipc_devtools_agent_host.h"
#include "content/browser/shared_worker/shared_worker_instance.h"
#include "content/common/devtools_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/worker_service.h"
#include "ipc/ipc_listener.h"
namespace content {
namespace {
bool SendMessageToWorker(const SharedWorkerDevToolsManager::WorkerId& worker_id,
IPC::Message* message) {
RenderProcessHost* host = RenderProcessHost::FromID(worker_id.first);
if (!host) {
delete message;
return false;
}
message->set_routing_id(worker_id.second);
host->Send(message);
return true;
}
} // namespace
class SharedWorkerDevToolsManager::SharedWorkerDevToolsAgentHost
: public IPCDevToolsAgentHost,
public IPC::Listener {
public:
explicit SharedWorkerDevToolsAgentHost(WorkerId worker_id)
: worker_id_(worker_id), worker_attached_(true) {
AddRef();
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
host->AddRoute(worker_id_.second, this);
}
// IPCDevToolsAgentHost implementation.
virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE {
if (worker_attached_)
SendMessageToWorker(worker_id_, message);
else
delete message;
}
virtual void OnClientAttached() OVERRIDE {}
virtual void OnClientDetached() OVERRIDE {}
// IPC::Listener implementation.
virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(SharedWorkerDevToolsAgentHost, msg)
IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
OnDispatchOnInspectorFrontend)
IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
OnSaveAgentRuntimeState)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ReattachToWorker(WorkerId worker_id) {
CHECK(!worker_attached_);
worker_attached_ = true;
worker_id_ = worker_id;
AddRef();
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
host->AddRoute(worker_id_.second, this);
Reattach(state_);
}
void DetachFromWorker() {
CHECK(worker_attached_);
worker_attached_ = false;
if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
host->RemoveRoute(worker_id_.second);
Release();
}
WorkerId worker_id() const { return worker_id_; }
private:
virtual ~SharedWorkerDevToolsAgentHost() {
CHECK(!worker_attached_);
SharedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(this);
}
void OnDispatchOnInspectorFrontend(const std::string& message) {
DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(this,
message);
}
void OnSaveAgentRuntimeState(const std::string& state) { state_ = state; }
WorkerId worker_id_;
bool worker_attached_;
std::string state_;
DISALLOW_COPY_AND_ASSIGN(SharedWorkerDevToolsAgentHost);
};
// static
SharedWorkerDevToolsManager* SharedWorkerDevToolsManager::GetInstance() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return Singleton<SharedWorkerDevToolsManager>::get();
}
DevToolsAgentHost* SharedWorkerDevToolsManager::GetDevToolsAgentHostForWorker(
int worker_process_id,
int worker_route_id) {
WorkerId id(worker_process_id, worker_route_id);
WorkerInfoMap::iterator it = workers_.find(id);
if (it == workers_.end())
return NULL;
WorkerInfo* info = it->second;
if (info->state() != WORKER_UNINSPECTED)
return info->agent_host();
SharedWorkerDevToolsAgentHost* agent_host =
new SharedWorkerDevToolsAgentHost(id);
info->set_agent_host(agent_host);
info->set_state(WORKER_INSPECTED);
return agent_host;
}
SharedWorkerDevToolsManager::SharedWorkerDevToolsManager() {}
SharedWorkerDevToolsManager::~SharedWorkerDevToolsManager() {}
bool SharedWorkerDevToolsManager::WorkerCreated(
int worker_process_id,
int worker_route_id,
const SharedWorkerInstance& instance) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
const WorkerId id(worker_process_id, worker_route_id);
WorkerInfoMap::iterator it = FindExistingWorkerInfo(instance);
if (it == workers_.end()) {
scoped_ptr<WorkerInfo> info(new WorkerInfo(instance));
workers_.set(id, info.Pass());
return false;
}
DCHECK_EQ(WORKER_TERMINATED, it->second->state());
scoped_ptr<WorkerInfo> info = workers_.take_and_erase(it);
info->set_state(WORKER_PAUSED);
workers_.set(id, info.Pass());
return true;
}
void SharedWorkerDevToolsManager::WorkerDestroyed(int worker_process_id,
int worker_route_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
const WorkerId id(worker_process_id, worker_route_id);
WorkerInfoMap::iterator it = workers_.find(id);
DCHECK(it != workers_.end());
WorkerInfo* info = it->second;
switch (info->state()) {
case WORKER_UNINSPECTED:
workers_.erase(it);
break;
case WORKER_INSPECTED: {
SharedWorkerDevToolsAgentHost* agent_host = info->agent_host();
if (!agent_host->IsAttached()) {
scoped_ptr<WorkerInfo> worker_info = workers_.take_and_erase(it);
agent_host->DetachFromWorker();
return;
}
info->set_state(WORKER_TERMINATED);
// Client host is debugging this worker agent host.
std::string notification =
DevToolsProtocol::CreateNotification(
devtools::Worker::disconnectedFromWorker::kName, NULL)
->Serialize();
DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
agent_host, notification);
agent_host->DetachFromWorker();
break;
}
case WORKER_TERMINATED:
NOTREACHED();
break;
case WORKER_PAUSED: {
scoped_ptr<WorkerInfo> worker_info = workers_.take_and_erase(it);
worker_info->set_state(WORKER_TERMINATED);
const WorkerId old_id = worker_info->agent_host()->worker_id();
workers_.set(old_id, worker_info.Pass());
break;
}
}
}
void SharedWorkerDevToolsManager::WorkerContextStarted(int worker_process_id,
int worker_route_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
const WorkerId id(worker_process_id, worker_route_id);
WorkerInfoMap::iterator it = workers_.find(id);
DCHECK(it != workers_.end());
WorkerInfo* info = it->second;
if (info->state() != WORKER_PAUSED)
return;
info->agent_host()->ReattachToWorker(id);
info->set_state(WORKER_INSPECTED);
}
void SharedWorkerDevToolsManager::RemoveInspectedWorkerData(
SharedWorkerDevToolsAgentHost* agent_host) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
const WorkerId id(agent_host->worker_id());
scoped_ptr<WorkerInfo> worker_info = workers_.take_and_erase(id);
if (worker_info) {
DCHECK_EQ(WORKER_TERMINATED, worker_info->state());
return;
}
for (WorkerInfoMap::iterator it = workers_.begin(); it != workers_.end();
++it) {
if (it->second->agent_host() == agent_host) {
DCHECK_EQ(WORKER_PAUSED, it->second->state());
SendMessageToWorker(
it->first,
new DevToolsAgentMsg_ResumeWorkerContext(it->first.second));
it->second->set_agent_host(NULL);
it->second->set_state(WORKER_UNINSPECTED);
return;
}
}
}
SharedWorkerDevToolsManager::WorkerInfoMap::iterator
SharedWorkerDevToolsManager::FindExistingWorkerInfo(
const SharedWorkerInstance& instance) {
WorkerInfoMap::iterator it = workers_.begin();
for (; it != workers_.end(); ++it) {
if (it->second->instance().Matches(instance))
break;
}
return it;
}
void SharedWorkerDevToolsManager::ResetForTesting() { workers_.clear(); }
} // namespace content
// Copyright 2014 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_DEVTOOLS_SHARED_WORKER_DEVTOOLS_MANAGER_H_
#define CONTENT_BROWSER_DEVTOOLS_SHARED_WORKER_DEVTOOLS_MANAGER_H_
#include "base/basictypes.h"
#include "base/containers/scoped_ptr_hash_map.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_vector.h"
#include "base/memory/singleton.h"
#include "base/strings/string16.h"
#include "content/browser/shared_worker/shared_worker_instance.h"
#include "content/common/content_export.h"
class GURL;
namespace content {
class DevToolsAgentHost;
// SharedWorkerDevToolsManager is used instead of WorkerDevToolsManager when
// "enable-embedded-shared-worker" flag is set.
// This class lives on UI thread.
class CONTENT_EXPORT SharedWorkerDevToolsManager {
public:
typedef std::pair<int, int> WorkerId;
class SharedWorkerDevToolsAgentHost;
// Returns the SharedWorkerDevToolsManager singleton.
static SharedWorkerDevToolsManager* GetInstance();
DevToolsAgentHost* GetDevToolsAgentHostForWorker(int worker_process_id,
int worker_route_id);
// Returns true when the worker must be paused on start.
bool WorkerCreated(int worker_process_id,
int worker_route_id,
const SharedWorkerInstance& instance);
void WorkerDestroyed(int worker_process_id, int worker_route_id);
void WorkerContextStarted(int worker_process_id, int worker_route_id);
private:
friend struct DefaultSingletonTraits<SharedWorkerDevToolsManager>;
friend class SharedWorkerDevToolsManagerTest;
FRIEND_TEST_ALL_PREFIXES(SharedWorkerDevToolsManagerTest, BasicTest);
FRIEND_TEST_ALL_PREFIXES(SharedWorkerDevToolsManagerTest, AttachTest);
enum WorkerState {
WORKER_UNINSPECTED,
WORKER_INSPECTED,
WORKER_TERMINATED,
WORKER_PAUSED,
};
class WorkerInfo {
public:
explicit WorkerInfo(const SharedWorkerInstance& instance)
: instance_(instance), state_(WORKER_UNINSPECTED), agent_host_(NULL) {}
const SharedWorkerInstance& instance() const { return instance_; }
WorkerState state() { return state_; }
void set_state(WorkerState new_state) { state_ = new_state; }
SharedWorkerDevToolsAgentHost* agent_host() { return agent_host_; }
void set_agent_host(SharedWorkerDevToolsAgentHost* agent_host) {
agent_host_ = agent_host;
}
private:
const SharedWorkerInstance instance_;
WorkerState state_;
SharedWorkerDevToolsAgentHost* agent_host_;
};
typedef base::ScopedPtrHashMap<WorkerId, WorkerInfo> WorkerInfoMap;
SharedWorkerDevToolsManager();
virtual ~SharedWorkerDevToolsManager();
void RemoveInspectedWorkerData(SharedWorkerDevToolsAgentHost* agent_host);
WorkerInfoMap::iterator FindExistingWorkerInfo(
const SharedWorkerInstance& instance);
// Resets to its initial state as if newly created.
void ResetForTesting();
WorkerInfoMap workers_;
DISALLOW_COPY_AND_ASSIGN(SharedWorkerDevToolsManager);
};
} // namespace content
#endif // CONTENT_BROWSER_DEVTOOLS_SHARED_WORKER_DEVTOOLS_MANAGER_H_
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "content/browser/devtools/devtools_protocol.h" #include "content/browser/devtools/devtools_protocol.h"
#include "content/browser/devtools/devtools_protocol_constants.h" #include "content/browser/devtools/devtools_protocol_constants.h"
#include "content/browser/devtools/ipc_devtools_agent_host.h" #include "content/browser/devtools/ipc_devtools_agent_host.h"
#include "content/browser/devtools/shared_worker_devtools_manager.h"
#include "content/browser/devtools/worker_devtools_message_filter.h" #include "content/browser/devtools/worker_devtools_message_filter.h"
#include "content/browser/worker_host/worker_service_impl.h" #include "content/browser/worker_host/worker_service_impl.h"
#include "content/common/devtools_messages.h" #include "content/common/devtools_messages.h"
...@@ -27,9 +28,13 @@ namespace content { ...@@ -27,9 +28,13 @@ namespace content {
scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForWorker( scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForWorker(
int worker_process_id, int worker_process_id,
int worker_route_id) { int worker_route_id) {
return WorkerDevToolsManager::GetDevToolsAgentHostForWorker( if (WorkerService::EmbeddedSharedWorkerEnabled()) {
worker_process_id, return SharedWorkerDevToolsManager::GetInstance()
worker_route_id); ->GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id);
} else {
return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
worker_process_id, worker_route_id);
}
} }
namespace { namespace {
...@@ -207,6 +212,7 @@ struct WorkerDevToolsManager::InspectedWorker { ...@@ -207,6 +212,7 @@ struct WorkerDevToolsManager::InspectedWorker {
// static // static
WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() { WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled());
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
return Singleton<WorkerDevToolsManager>::get(); return Singleton<WorkerDevToolsManager>::get();
} }
...@@ -215,6 +221,7 @@ WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() { ...@@ -215,6 +221,7 @@ WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker( DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
int worker_process_id, int worker_process_id,
int worker_route_id) { int worker_route_id) {
DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled());
WorkerId id(worker_process_id, worker_route_id); WorkerId id(worker_process_id, worker_route_id);
AgentHosts::iterator it = g_agent_map.Get().find(id); AgentHosts::iterator it = g_agent_map.Get().find(id);
if (it == g_agent_map.Get().end()) if (it == g_agent_map.Get().end())
......
...@@ -19,6 +19,7 @@ namespace content { ...@@ -19,6 +19,7 @@ namespace content {
class DevToolsAgentHost; class DevToolsAgentHost;
// All methods are supposed to be called on the IO thread. // All methods are supposed to be called on the IO thread.
// This class is not used when "enable-embedded-shared-worker" flag is set.
class WorkerDevToolsManager { class WorkerDevToolsManager {
public: public:
typedef std::pair<int, int> WorkerId; typedef std::pair<int, int> WorkerId;
...@@ -41,9 +42,8 @@ class WorkerDevToolsManager { ...@@ -41,9 +42,8 @@ class WorkerDevToolsManager {
// Called on the IO thread. // Called on the IO thread.
// Returns true when the worker must be paused on start. // Returns true when the worker must be paused on start.
bool WorkerCreated( bool WorkerCreated(WorkerProcessHost* process,
WorkerProcessHost* process, const WorkerProcessHost::WorkerInstance& instance);
const WorkerProcessHost::WorkerInstance& instance);
void WorkerDestroyed(WorkerProcessHost* process, int worker_route_id); void WorkerDestroyed(WorkerProcessHost* process, int worker_route_id);
void WorkerContextStarted(WorkerProcessHost* process, int worker_route_id); void WorkerContextStarted(WorkerProcessHost* process, int worker_route_id);
......
...@@ -5,16 +5,19 @@ ...@@ -5,16 +5,19 @@
#include "content/browser/shared_worker/shared_worker_host.h" #include "content/browser/shared_worker/shared_worker_host.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "content/browser/devtools/shared_worker_devtools_manager.h"
#include "content/browser/frame_host/render_frame_host_delegate.h" #include "content/browser/frame_host/render_frame_host_delegate.h"
#include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/message_port_service.h" #include "content/browser/message_port_service.h"
#include "content/browser/shared_worker/shared_worker_instance.h" #include "content/browser/shared_worker/shared_worker_instance.h"
#include "content/browser/shared_worker/shared_worker_message_filter.h" #include "content/browser/shared_worker/shared_worker_message_filter.h"
#include "content/browser/shared_worker/shared_worker_service_impl.h"
#include "content/browser/worker_host/worker_document_set.h" #include "content/browser/worker_host/worker_document_set.h"
#include "content/common/view_messages.h" #include "content/common/view_messages.h"
#include "content/common/worker_messages.h" #include "content/common/worker_messages.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h" #include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_client.h" #include "content/public/common/content_client.h"
namespace content { namespace content {
...@@ -28,13 +31,27 @@ void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) { ...@@ -28,13 +31,27 @@ void WorkerCrashCallback(int render_process_unique_id, int render_frame_id) {
host->delegate()->WorkerCrashed(host); host->delegate()->WorkerCrashed(host);
} }
void NotifyWorkerScriptLoadedOnUI(int worker_process_id, int worker_route_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
SharedWorkerDevToolsManager::GetInstance()->WorkerContextStarted(
worker_process_id, worker_route_id);
}
void NotifyWorkerDestroyedOnUI(int worker_process_id, int worker_route_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
SharedWorkerDevToolsManager::GetInstance()->WorkerDestroyed(worker_process_id,
worker_route_id);
}
} // namespace } // namespace
SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance* instance) SharedWorkerHost::SharedWorkerHost(SharedWorkerInstance* instance,
SharedWorkerMessageFilter* filter)
: instance_(instance), : instance_(instance),
worker_document_set_(new WorkerDocumentSet()), worker_document_set_(new WorkerDocumentSet()),
container_render_filter_(NULL), container_render_filter_(filter),
worker_route_id_(MSG_ROUTING_NONE), worker_process_id_(filter->render_process_id()),
worker_route_id_(filter->GetNextRoutingID()),
load_failed_(false), load_failed_(false),
closed_(false), closed_(false),
creation_time_(base::TimeTicks::Now()) { creation_time_(base::TimeTicks::Now()) {
...@@ -60,6 +77,13 @@ SharedWorkerHost::~SharedWorkerHost() { ...@@ -60,6 +77,13 @@ SharedWorkerHost::~SharedWorkerHost() {
parent_iter->render_frame_id())); parent_iter->render_frame_id()));
} }
} }
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(
&NotifyWorkerDestroyedOnUI, worker_process_id_, worker_route_id_));
SharedWorkerServiceImpl::GetInstance()->NotifyWorkerDestroyed(
worker_process_id_, worker_route_id_);
} }
bool SharedWorkerHost::Send(IPC::Message* message) { bool SharedWorkerHost::Send(IPC::Message* message) {
...@@ -70,17 +94,13 @@ bool SharedWorkerHost::Send(IPC::Message* message) { ...@@ -70,17 +94,13 @@ bool SharedWorkerHost::Send(IPC::Message* message) {
return container_render_filter_->Send(message); return container_render_filter_->Send(message);
} }
void SharedWorkerHost::Init(SharedWorkerMessageFilter* filter) { void SharedWorkerHost::Start(bool pause_on_start) {
CHECK(instance_);
DCHECK(worker_route_id_ == MSG_ROUTING_NONE);
container_render_filter_ = filter;
worker_route_id_ = filter->GetNextRoutingID();
WorkerProcessMsg_CreateWorker_Params params; WorkerProcessMsg_CreateWorker_Params params;
params.url = instance_->url(); params.url = instance_->url();
params.name = instance_->name(); params.name = instance_->name();
params.content_security_policy = instance_->content_security_policy(); params.content_security_policy = instance_->content_security_policy();
params.security_policy_type = instance_->security_policy_type(); params.security_policy_type = instance_->security_policy_type();
params.pause_on_start = pause_on_start;
params.route_id = worker_route_id_; params.route_id = worker_route_id_;
Send(new WorkerProcessMsg_CreateWorker(params)); Send(new WorkerProcessMsg_CreateWorker(params));
...@@ -143,9 +163,13 @@ void SharedWorkerHost::WorkerContextDestroyed() { ...@@ -143,9 +163,13 @@ void SharedWorkerHost::WorkerContextDestroyed() {
} }
void SharedWorkerHost::WorkerScriptLoaded() { void SharedWorkerHost::WorkerScriptLoaded() {
// TODO(horo): implement this.
UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded", UMA_HISTOGRAM_TIMES("SharedWorker.TimeToScriptLoaded",
base::TimeTicks::Now() - creation_time_); base::TimeTicks::Now() - creation_time_);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(
&NotifyWorkerScriptLoadedOnUI, worker_process_id_, worker_route_id_));
} }
void SharedWorkerHost::WorkerScriptLoadFailed() { void SharedWorkerHost::WorkerScriptLoadFailed() {
......
...@@ -28,15 +28,16 @@ class SharedWorkerInstance; ...@@ -28,15 +28,16 @@ class SharedWorkerInstance;
// the browser <-> worker communication channel. // the browser <-> worker communication channel.
class SharedWorkerHost { class SharedWorkerHost {
public: public:
explicit SharedWorkerHost(SharedWorkerInstance* instance); SharedWorkerHost(SharedWorkerInstance* instance,
SharedWorkerMessageFilter* filter);
~SharedWorkerHost(); ~SharedWorkerHost();
// Sends |message| to the SharedWorker. // Sends |message| to the SharedWorker.
bool Send(IPC::Message* message); bool Send(IPC::Message* message);
// Starts the SharedWorker in the renderer process which is associated with // Starts the SharedWorker in the renderer process which is associated with
// |filter|. // |filter_|.
void Init(SharedWorkerMessageFilter* filter); void Start(bool pause_on_start);
// Returns true iff the given message from a renderer process was forwarded to // Returns true iff the given message from a renderer process was forwarded to
// the worker. // the worker.
...@@ -79,9 +80,7 @@ class SharedWorkerHost { ...@@ -79,9 +80,7 @@ class SharedWorkerHost {
SharedWorkerMessageFilter* container_render_filter() const { SharedWorkerMessageFilter* container_render_filter() const {
return container_render_filter_; return container_render_filter_;
} }
int process_id() const { int process_id() const { return worker_process_id_; }
return container_render_filter_->render_process_id();
}
int worker_route_id() const { return worker_route_id_; } int worker_route_id() const { return worker_route_id_; }
bool load_failed() const { return load_failed_; } bool load_failed() const { return load_failed_; }
bool closed() const { return closed_; } bool closed() const { return closed_; }
...@@ -123,6 +122,7 @@ class SharedWorkerHost { ...@@ -123,6 +122,7 @@ class SharedWorkerHost {
scoped_refptr<WorkerDocumentSet> worker_document_set_; scoped_refptr<WorkerDocumentSet> worker_document_set_;
FilterList filters_; FilterList filters_;
SharedWorkerMessageFilter* container_render_filter_; SharedWorkerMessageFilter* container_render_filter_;
int worker_process_id_;
int worker_route_id_; int worker_route_id_;
bool load_failed_; bool load_failed_;
bool closed_; bool closed_;
......
...@@ -24,8 +24,15 @@ SharedWorkerInstance::SharedWorkerInstance( ...@@ -24,8 +24,15 @@ SharedWorkerInstance::SharedWorkerInstance(
DCHECK(resource_context_); DCHECK(resource_context_);
} }
SharedWorkerInstance::~SharedWorkerInstance() { SharedWorkerInstance::SharedWorkerInstance(const SharedWorkerInstance& other)
} : url_(other.url_),
name_(other.name_),
content_security_policy_(other.content_security_policy_),
security_policy_type_(other.security_policy_type_),
resource_context_(other.resource_context_),
partition_(other.partition_) {}
SharedWorkerInstance::~SharedWorkerInstance() {}
bool SharedWorkerInstance::Matches(const GURL& match_url, bool SharedWorkerInstance::Matches(const GURL& match_url,
const base::string16& match_name, const base::string16& match_name,
...@@ -50,4 +57,9 @@ bool SharedWorkerInstance::Matches(const GURL& match_url, ...@@ -50,4 +57,9 @@ bool SharedWorkerInstance::Matches(const GURL& match_url,
return name_ == match_name; return name_ == match_name;
} }
bool SharedWorkerInstance::Matches(const SharedWorkerInstance& other) const {
return Matches(
other.url(), other.name(), other.partition(), other.resource_context());
}
} // namespace content } // namespace content
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
namespace content { namespace content {
class ResourceContext; class ResourceContext;
// SharedWorkerInstance is copyable value-type data type. It could be passed to
// the UI thread and be used for comparison in SharedWorkerDevToolsManager.
class CONTENT_EXPORT SharedWorkerInstance { class CONTENT_EXPORT SharedWorkerInstance {
public: public:
SharedWorkerInstance(const GURL& url, SharedWorkerInstance(const GURL& url,
...@@ -24,6 +26,7 @@ class CONTENT_EXPORT SharedWorkerInstance { ...@@ -24,6 +26,7 @@ class CONTENT_EXPORT SharedWorkerInstance {
blink::WebContentSecurityPolicyType security_policy_type, blink::WebContentSecurityPolicyType security_policy_type,
ResourceContext* resource_context, ResourceContext* resource_context,
const WorkerStoragePartition& partition); const WorkerStoragePartition& partition);
SharedWorkerInstance(const SharedWorkerInstance& other);
~SharedWorkerInstance(); ~SharedWorkerInstance();
// Checks if this SharedWorkerInstance matches the passed url/name params // Checks if this SharedWorkerInstance matches the passed url/name params
...@@ -37,6 +40,7 @@ class CONTENT_EXPORT SharedWorkerInstance { ...@@ -37,6 +40,7 @@ class CONTENT_EXPORT SharedWorkerInstance {
const base::string16& name, const base::string16& name,
const WorkerStoragePartition& partition, const WorkerStoragePartition& partition,
ResourceContext* resource_context) const; ResourceContext* resource_context) const;
bool Matches(const SharedWorkerInstance& other) const;
// Accessors. // Accessors.
const GURL& url() const { return url_; } const GURL& url() const { return url_; }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <set> #include <set>
#include <vector> #include <vector>
#include "content/browser/devtools/shared_worker_devtools_manager.h"
#include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/shared_worker/shared_worker_host.h" #include "content/browser/shared_worker/shared_worker_host.h"
#include "content/browser/shared_worker/shared_worker_instance.h" #include "content/browser/shared_worker/shared_worker_instance.h"
...@@ -61,6 +62,29 @@ void UpdateWorkerDependency(const std::vector<int>& added_ids, ...@@ -61,6 +62,29 @@ void UpdateWorkerDependency(const std::vector<int>& added_ids,
base::Bind(&UpdateWorkerDependencyOnUI, added_ids, removed_ids)); base::Bind(&UpdateWorkerDependencyOnUI, added_ids, removed_ids));
} }
void WorkerCreatedResultCallbackOnIO(int worker_process_id,
int worker_route_id,
bool pause_on_start) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
SharedWorkerServiceImpl::GetInstance()->WorkerCreatedResultCallback(
worker_process_id, worker_route_id, pause_on_start);
}
void NotifyWorkerCreatedOnUI(int worker_process_id,
int worker_route_id,
const SharedWorkerInstance& instance) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
bool pause_on_start =
SharedWorkerDevToolsManager::GetInstance()->WorkerCreated(
worker_process_id, worker_route_id, instance);
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(&WorkerCreatedResultCallbackOnIO,
worker_process_id,
worker_route_id,
pause_on_start));
}
} // namespace } // namespace
SharedWorkerServiceImpl* SharedWorkerServiceImpl::GetInstance() { SharedWorkerServiceImpl* SharedWorkerServiceImpl::GetInstance() {
...@@ -130,7 +154,7 @@ void SharedWorkerServiceImpl::CreateWorker( ...@@ -130,7 +154,7 @@ void SharedWorkerServiceImpl::CreateWorker(
ScopedWorkerDependencyChecker checker(this); ScopedWorkerDependencyChecker checker(this);
*url_mismatch = false; *url_mismatch = false;
SharedWorkerHost* existing_host = FindSharedWorkerHost( SharedWorkerHost* existing_host = FindSharedWorkerHost(
params.url, params.name, partition, resource_context); worker_hosts_, params.url, params.name, partition, resource_context);
if (existing_host) { if (existing_host) {
if (params.url != existing_host->instance()->url()) { if (params.url != existing_host->instance()->url()) {
*url_mismatch = true; *url_mismatch = true;
...@@ -148,6 +172,23 @@ void SharedWorkerServiceImpl::CreateWorker( ...@@ -148,6 +172,23 @@ void SharedWorkerServiceImpl::CreateWorker(
filter->Send(new ViewMsg_WorkerCreated(route_id)); filter->Send(new ViewMsg_WorkerCreated(route_id));
return; return;
} }
SharedWorkerHost* pending_host = FindSharedWorkerHost(pending_worker_hosts_,
params.url,
params.name,
partition,
resource_context);
if (pending_host) {
if (params.url != pending_host->instance()->url()) {
*url_mismatch = true;
return;
}
pending_host->AddFilter(filter, route_id);
pending_host->worker_document_set()->Add(filter,
params.document_id,
filter->render_process_id(),
params.render_frame_route_id);
return;
}
scoped_ptr<SharedWorkerInstance> instance(new SharedWorkerInstance( scoped_ptr<SharedWorkerInstance> instance(new SharedWorkerInstance(
params.url, params.url,
...@@ -156,25 +197,42 @@ void SharedWorkerServiceImpl::CreateWorker( ...@@ -156,25 +197,42 @@ void SharedWorkerServiceImpl::CreateWorker(
params.security_policy_type, params.security_policy_type,
resource_context, resource_context,
partition)); partition));
scoped_ptr<SharedWorkerHost> host(new SharedWorkerHost(instance.release())); scoped_ptr<SharedWorkerHost> host(
new SharedWorkerHost(instance.release(), filter));
host->AddFilter(filter, route_id); host->AddFilter(filter, route_id);
host->worker_document_set()->Add(filter, host->worker_document_set()->Add(filter,
params.document_id, params.document_id,
filter->render_process_id(), filter->render_process_id(),
params.render_frame_route_id); params.render_frame_route_id);
host->Init(filter);
const int worker_route_id = host->worker_route_id(); const int worker_route_id = host->worker_route_id();
worker_hosts_.set(std::make_pair(filter->render_process_id(), // We need to call SharedWorkerDevToolsManager::WorkerCreated() on UI thread
worker_route_id), // to know whether the worker should be paused on start or not.
host.Pass()); // WorkerCreatedResultCallback() will be called with the result.
BrowserThread::PostTask(BrowserThread::UI,
FROM_HERE,
base::Bind(&NotifyWorkerCreatedOnUI,
filter->render_process_id(),
worker_route_id,
*host->instance()));
pending_worker_hosts_.set(
std::make_pair(filter->render_process_id(), worker_route_id),
host.Pass());
}
void SharedWorkerServiceImpl::WorkerCreatedResultCallback(int worker_process_id,
int worker_route_id,
bool pause_on_start) {
scoped_ptr<SharedWorkerHost> host = pending_worker_hosts_.take_and_erase(
std::make_pair(worker_process_id, worker_route_id));
const GURL url = host->instance()->url();
const base::string16 name = host->instance()->name();
host->Start(pause_on_start);
worker_hosts_.set(std::make_pair(worker_process_id, worker_route_id),
host.Pass());
FOR_EACH_OBSERVER( FOR_EACH_OBSERVER(
WorkerServiceObserver, observers_, WorkerServiceObserver,
WorkerCreated(params.url, observers_,
params.name, WorkerCreated(url, name, worker_process_id, worker_route_id));
filter->render_process_id(),
worker_route_id));
} }
void SharedWorkerServiceImpl::ForwardToWorker( void SharedWorkerServiceImpl::ForwardToWorker(
...@@ -288,8 +346,17 @@ void SharedWorkerServiceImpl::OnSharedWorkerMessageFilterClosing( ...@@ -288,8 +346,17 @@ void SharedWorkerServiceImpl::OnSharedWorkerMessageFilterClosing(
if (iter->first.first == filter->render_process_id()) if (iter->first.first == filter->render_process_id())
remove_list.push_back(iter->first); remove_list.push_back(iter->first);
} }
for (size_t i = 0; i < remove_list.size(); ++i) for (size_t i = 0; i < remove_list.size(); ++i) {
worker_hosts_.erase(remove_list[i]); scoped_ptr<SharedWorkerHost> host =
worker_hosts_.take_and_erase(remove_list[i]);
}
}
void SharedWorkerServiceImpl::NotifyWorkerDestroyed(int worker_process_id,
int worker_route_id) {
FOR_EACH_OBSERVER(WorkerServiceObserver,
observers_,
WorkerDestroyed(worker_process_id, worker_route_id));
} }
SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost( SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost(
...@@ -299,13 +366,14 @@ SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost( ...@@ -299,13 +366,14 @@ SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost(
worker_route_id)); worker_route_id));
} }
// static
SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost( SharedWorkerHost* SharedWorkerServiceImpl::FindSharedWorkerHost(
const WorkerHostMap& hosts,
const GURL& url, const GURL& url,
const base::string16& name, const base::string16& name,
const WorkerStoragePartition& partition, const WorkerStoragePartition& partition,
ResourceContext* resource_context) { ResourceContext* resource_context) {
for (WorkerHostMap::const_iterator iter = worker_hosts_.begin(); for (WorkerHostMap::const_iterator iter = hosts.begin(); iter != hosts.end();
iter != worker_hosts_.end();
++iter) { ++iter) {
SharedWorkerInstance* instance = iter->second->instance(); SharedWorkerInstance* instance = iter->second->instance();
if (instance && !iter->second->closed() && if (instance && !iter->second->closed() &&
......
...@@ -92,12 +92,21 @@ class CONTENT_EXPORT SharedWorkerServiceImpl ...@@ -92,12 +92,21 @@ class CONTENT_EXPORT SharedWorkerServiceImpl
// RenderProcessHostImpl on UI thread if necessary. // RenderProcessHostImpl on UI thread if necessary.
void CheckWorkerDependency(); void CheckWorkerDependency();
void WorkerCreatedResultCallback(int worker_process_id,
int worker_route_id,
bool pause_on_start);
void NotifyWorkerDestroyed(int worker_process_id, int worker_route_id);
private: private:
friend struct DefaultSingletonTraits<SharedWorkerServiceImpl>; friend struct DefaultSingletonTraits<SharedWorkerServiceImpl>;
friend class SharedWorkerServiceImplTest; friend class SharedWorkerServiceImplTest;
typedef void (*UpdateWorkerDependencyFunc)(const std::vector<int>&, typedef void (*UpdateWorkerDependencyFunc)(const std::vector<int>&,
const std::vector<int>&); const std::vector<int>&);
// Pair of render_process_id and worker_route_id.
typedef std::pair<int, int> ProcessRouteIdPair;
typedef base::ScopedPtrHashMap<ProcessRouteIdPair, SharedWorkerHost>
WorkerHostMap;
SharedWorkerServiceImpl(); SharedWorkerServiceImpl();
virtual ~SharedWorkerServiceImpl(); virtual ~SharedWorkerServiceImpl();
...@@ -108,7 +117,8 @@ class CONTENT_EXPORT SharedWorkerServiceImpl ...@@ -108,7 +117,8 @@ class CONTENT_EXPORT SharedWorkerServiceImpl
SharedWorkerMessageFilter* filter, SharedWorkerMessageFilter* filter,
int worker_route_id); int worker_route_id);
SharedWorkerHost* FindSharedWorkerHost( static SharedWorkerHost* FindSharedWorkerHost(
const WorkerHostMap& hosts,
const GURL& url, const GURL& url,
const base::string16& name, const base::string16& name,
const WorkerStoragePartition& worker_partition, const WorkerStoragePartition& worker_partition,
...@@ -124,11 +134,8 @@ class CONTENT_EXPORT SharedWorkerServiceImpl ...@@ -124,11 +134,8 @@ class CONTENT_EXPORT SharedWorkerServiceImpl
std::set<int> last_worker_depended_renderers_; std::set<int> last_worker_depended_renderers_;
UpdateWorkerDependencyFunc update_worker_dependency_; UpdateWorkerDependencyFunc update_worker_dependency_;
// Pair of render_process_id and worker_route_id.
typedef std::pair<int, int> ProcessRouteIdPair;
typedef base::ScopedPtrHashMap<ProcessRouteIdPair,
SharedWorkerHost> WorkerHostMap;
WorkerHostMap worker_hosts_; WorkerHostMap worker_hosts_;
WorkerHostMap pending_worker_hosts_;
ObserverList<WorkerServiceObserver> observers_; ObserverList<WorkerServiceObserver> observers_;
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "content/common/worker_messages.h" #include "content/common/worker_messages.h"
#include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "ipc/ipc_sync_message.h" #include "ipc/ipc_sync_message.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -356,6 +357,9 @@ TEST_F(SharedWorkerServiceImplTest, BasicTest) { ...@@ -356,6 +357,9 @@ TEST_F(SharedWorkerServiceImplTest, BasicTest) {
// SharedWorkerConnector creates two message ports and sends // SharedWorkerConnector creates two message ports and sends
// ViewHostMsg_CreateWorker. // ViewHostMsg_CreateWorker.
connector->Create("http://example.com/w.js", "name", 200, 300); connector->Create("http://example.com/w.js", "name", 200, 300);
// We need to go to UI thread to call
// SharedWorkerDevToolsManager::WorkerCreated().
RunAllPendingInMessageLoop();
EXPECT_EQ(2U, renderer_host->QueuedMessageCount()); EXPECT_EQ(2U, renderer_host->QueuedMessageCount());
// WorkerProcessMsg_CreateWorker should be sent to the renderer in which // WorkerProcessMsg_CreateWorker should be sent to the renderer in which
// SharedWorker will be created. // SharedWorker will be created.
...@@ -441,6 +445,9 @@ TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) { ...@@ -441,6 +445,9 @@ TEST_F(SharedWorkerServiceImplTest, TwoRendererTest) {
// SharedWorkerConnector creates two message ports and sends // SharedWorkerConnector creates two message ports and sends
// ViewHostMsg_CreateWorker. // ViewHostMsg_CreateWorker.
connector1->Create("http://example.com/w.js", "name", 200, 300); connector1->Create("http://example.com/w.js", "name", 200, 300);
// We need to go to UI thread to call
// SharedWorkerDevToolsManager::WorkerCreated().
RunAllPendingInMessageLoop();
EXPECT_EQ(2U, renderer_host1->QueuedMessageCount()); EXPECT_EQ(2U, renderer_host1->QueuedMessageCount());
// WorkerProcessMsg_CreateWorker should be sent to the renderer in which // WorkerProcessMsg_CreateWorker should be sent to the renderer in which
// SharedWorker will be created. // SharedWorker will be created.
......
...@@ -412,6 +412,8 @@ ...@@ -412,6 +412,8 @@
'browser/devtools/render_view_devtools_agent_host.h', 'browser/devtools/render_view_devtools_agent_host.h',
'browser/devtools/renderer_overrides_handler.cc', 'browser/devtools/renderer_overrides_handler.cc',
'browser/devtools/renderer_overrides_handler.h', 'browser/devtools/renderer_overrides_handler.h',
'browser/devtools/shared_worker_devtools_manager.cc',
'browser/devtools/shared_worker_devtools_manager.h',
'browser/devtools/tethering_handler.h', 'browser/devtools/tethering_handler.h',
'browser/devtools/tethering_handler.cc', 'browser/devtools/tethering_handler.cc',
'browser/devtools/worker_devtools_manager.cc', 'browser/devtools/worker_devtools_manager.cc',
......
...@@ -345,6 +345,7 @@ ...@@ -345,6 +345,7 @@
'browser/device_orientation/sensor_manager_android_unittest.cc', 'browser/device_orientation/sensor_manager_android_unittest.cc',
'browser/devtools/devtools_http_handler_unittest.cc', 'browser/devtools/devtools_http_handler_unittest.cc',
'browser/devtools/devtools_manager_unittest.cc', 'browser/devtools/devtools_manager_unittest.cc',
'browser/devtools/shared_worker_devtools_manager_unittest.cc',
'browser/dom_storage/dom_storage_area_unittest.cc', 'browser/dom_storage/dom_storage_area_unittest.cc',
'browser/dom_storage/dom_storage_context_impl_unittest.cc', 'browser/dom_storage/dom_storage_context_impl_unittest.cc',
'browser/dom_storage/dom_storage_database_unittest.cc', 'browser/dom_storage/dom_storage_database_unittest.cc',
......
...@@ -1426,6 +1426,7 @@ void RenderThreadImpl::OnCreateNewSharedWorker( ...@@ -1426,6 +1426,7 @@ void RenderThreadImpl::OnCreateNewSharedWorker(
params.name, params.name,
params.content_security_policy, params.content_security_policy,
params.security_policy_type, params.security_policy_type,
params.pause_on_start,
params.route_id); params.route_id);
} }
......
...@@ -21,13 +21,16 @@ EmbeddedSharedWorkerStub::EmbeddedSharedWorkerStub( ...@@ -21,13 +21,16 @@ EmbeddedSharedWorkerStub::EmbeddedSharedWorkerStub(
const base::string16& name, const base::string16& name,
const base::string16& content_security_policy, const base::string16& content_security_policy,
blink::WebContentSecurityPolicyType security_policy_type, blink::WebContentSecurityPolicyType security_policy_type,
bool pause_on_start,
int route_id) int route_id)
: route_id_(route_id), : route_id_(route_id), name_(name), runing_(false), url_(url) {
name_(name),
runing_(false),
url_(url) {
RenderThreadImpl::current()->AddSharedWorkerRoute(route_id_, this); RenderThreadImpl::current()->AddSharedWorkerRoute(route_id_, this);
impl_ = blink::WebSharedWorker::create(this); impl_ = blink::WebSharedWorker::create(this);
if (pause_on_start) {
// Pause worker context when it starts and wait until either DevTools client
// is attached or explicit resume notification is received.
impl_->pauseWorkerContextOnStart();
}
worker_devtools_agent_.reset( worker_devtools_agent_.reset(
new SharedWorkerDevToolsAgent(route_id, impl_)); new SharedWorkerDevToolsAgent(route_id, impl_));
impl_->startWorkerContext(url, name_, impl_->startWorkerContext(url, name_,
......
...@@ -35,6 +35,7 @@ class EmbeddedSharedWorkerStub : public IPC::Listener, ...@@ -35,6 +35,7 @@ class EmbeddedSharedWorkerStub : public IPC::Listener,
const base::string16& name, const base::string16& name,
const base::string16& content_security_policy, const base::string16& content_security_policy,
blink::WebContentSecurityPolicyType security_policy_type, blink::WebContentSecurityPolicyType security_policy_type,
bool pause_on_start,
int route_id); int route_id);
// IPC::Listener implementation. // IPC::Listener implementation.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment