summaryrefslogtreecommitdiffstats
path: root/content/browser/devtools/worker_devtools_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'content/browser/devtools/worker_devtools_manager.cc')
-rw-r--r--content/browser/devtools/worker_devtools_manager.cc472
1 files changed, 472 insertions, 0 deletions
diff --git a/content/browser/devtools/worker_devtools_manager.cc b/content/browser/devtools/worker_devtools_manager.cc
new file mode 100644
index 0000000..f341c82
--- /dev/null
+++ b/content/browser/devtools/worker_devtools_manager.cc
@@ -0,0 +1,472 @@
+// 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/devtools/worker_devtools_manager.h"
+
+#include <list>
+#include <map>
+
+#include "base/bind.h"
+#include "content/browser/devtools/devtools_agent_host.h"
+#include "content/browser/devtools/devtools_manager_impl.h"
+#include "content/browser/devtools/worker_devtools_message_filter.h"
+#include "content/browser/worker_host/worker_service_impl.h"
+#include "content/common/devtools_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/devtools_agent_host_registry.h"
+#include "content/public/common/process_type.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDevToolsAgent.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
+
+namespace content {
+
+// Called on the UI thread.
+// static
+DevToolsAgentHost* DevToolsAgentHostRegistry::GetDevToolsAgentHostForWorker(
+ int worker_process_id,
+ int worker_route_id) {
+ return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
+ worker_process_id,
+ worker_route_id);
+}
+
+class WorkerDevToolsManager::AgentHosts {
+public:
+ static void Add(WorkerId id, WorkerDevToolsAgentHost* host) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!instance_)
+ instance_ = new AgentHosts();
+ instance_->map_[id] = host;
+ }
+ static void Remove(WorkerId id) {
+ DCHECK(instance_);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ Instances& map = instance_->map_;
+ map.erase(id);
+ if (map.empty()) {
+ delete instance_;
+ instance_ = NULL;
+ }
+ }
+
+ static WorkerDevToolsAgentHost* GetAgentHost(WorkerId id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (!instance_)
+ return NULL;
+ Instances& map = instance_->map_;
+ Instances::iterator it = map.find(id);
+ if (it == map.end())
+ return NULL;
+ return it->second;
+ }
+
+private:
+ AgentHosts() {
+ }
+ ~AgentHosts() {}
+
+ static AgentHosts* instance_;
+ typedef std::map<WorkerId, WorkerDevToolsAgentHost*> Instances;
+ Instances map_;
+};
+
+WorkerDevToolsManager::AgentHosts*
+ WorkerDevToolsManager::AgentHosts::instance_ = NULL;
+
+
+struct WorkerDevToolsManager::TerminatedInspectedWorker {
+ TerminatedInspectedWorker(WorkerId id, const GURL& url, const string16& name)
+ : old_worker_id(id),
+ worker_url(url),
+ worker_name(name) {}
+ WorkerId old_worker_id;
+ GURL worker_url;
+ string16 worker_name;
+};
+
+
+class WorkerDevToolsManager::WorkerDevToolsAgentHost
+ : public DevToolsAgentHost {
+ public:
+ explicit WorkerDevToolsAgentHost(WorkerId worker_id)
+ : worker_id_(worker_id) {
+ AgentHosts::Add(worker_id, this);
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(
+ &RegisterAgent,
+ worker_id.first,
+ worker_id.second));
+ }
+
+ void WorkerDestroyed() {
+ NotifyCloseListener();
+ delete this;
+ }
+
+ private:
+ virtual ~WorkerDevToolsAgentHost() {
+ AgentHosts::Remove(worker_id_);
+ }
+
+ static void RegisterAgent(
+ int worker_process_id,
+ int worker_route_id) {
+ WorkerDevToolsManager::GetInstance()->RegisterDevToolsAgentHostForWorker(
+ worker_process_id, worker_route_id);
+ }
+
+ static void ForwardToWorkerDevToolsAgent(
+ int worker_process_id,
+ int worker_route_id,
+ IPC::Message* message) {
+ WorkerDevToolsManager::GetInstance()->ForwardToWorkerDevToolsAgent(
+ worker_process_id, worker_route_id, *message);
+ }
+
+ // DevToolsAgentHost implementation.
+ virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(
+ &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent,
+ worker_id_.first,
+ worker_id_.second,
+ base::Owned(message)));
+ }
+ virtual void NotifyClientAttaching() OVERRIDE {}
+ virtual void NotifyClientDetaching() OVERRIDE {}
+ virtual int GetRenderProcessId() OVERRIDE { return -1; }
+
+ WorkerId worker_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost);
+};
+
+
+class WorkerDevToolsManager::DetachedClientHosts {
+ public:
+ static void WorkerReloaded(WorkerId old_id, WorkerId new_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (instance_ && instance_->ReattachClient(old_id, new_id))
+ return;
+ RemovePendingWorkerData(old_id);
+ }
+
+ static void WorkerDestroyed(WorkerId id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ WorkerDevToolsAgentHost* agent = AgentHosts::GetAgentHost(id);
+ if (!agent) {
+ RemovePendingWorkerData(id);
+ return;
+ }
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
+ agent,
+ WebKit::WebDevToolsAgent::workerDisconnectedFromWorkerEvent().utf8());
+ int cookie = DevToolsManagerImpl::GetInstance()->DetachClientHost(agent);
+ agent->WorkerDestroyed();
+ if (cookie == -1) {
+ RemovePendingWorkerData(id);
+ return;
+ }
+ if (!instance_)
+ new DetachedClientHosts();
+ instance_->worker_id_to_cookie_[id] = cookie;
+ }
+
+ private:
+ DetachedClientHosts() {
+ instance_ = this;
+ }
+ ~DetachedClientHosts() {
+ instance_ = NULL;
+ }
+
+ bool ReattachClient(WorkerId old_id, WorkerId new_id) {
+ WorkerIdToCookieMap::iterator it = worker_id_to_cookie_.find(old_id);
+ if (it == worker_id_to_cookie_.end())
+ return false;
+ DevToolsAgentHost* agent =
+ WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
+ new_id.first,
+ new_id.second);
+ DevToolsManagerImpl::GetInstance()->AttachClientHost(
+ it->second,
+ agent);
+ worker_id_to_cookie_.erase(it);
+ if (worker_id_to_cookie_.empty())
+ delete this;
+ return true;
+ }
+
+ static void RemovePendingWorkerData(WorkerId id) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ base::Bind(&RemoveInspectedWorkerDataOnIOThread, id));
+ }
+
+ static void RemoveInspectedWorkerDataOnIOThread(WorkerId id) {
+ WorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(id);
+ }
+
+ static DetachedClientHosts* instance_;
+ typedef std::map<WorkerId, int> WorkerIdToCookieMap;
+ WorkerIdToCookieMap worker_id_to_cookie_;
+};
+
+WorkerDevToolsManager::DetachedClientHosts*
+ WorkerDevToolsManager::DetachedClientHosts::instance_ = NULL;
+
+struct WorkerDevToolsManager::InspectedWorker {
+ InspectedWorker(WorkerProcessHost* host, int route_id, const GURL& url,
+ const string16& name)
+ : host(host),
+ route_id(route_id),
+ worker_url(url),
+ worker_name(name) {}
+ WorkerProcessHost* const host;
+ int const route_id;
+ GURL worker_url;
+ string16 worker_name;
+};
+
+// static
+WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ return Singleton<WorkerDevToolsManager>::get();
+}
+
+// static
+DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
+ int worker_process_id,
+ int worker_route_id) {
+ WorkerId id(worker_process_id, worker_route_id);
+ WorkerDevToolsAgentHost* result = AgentHosts::GetAgentHost(id);
+ if (!result)
+ result = new WorkerDevToolsAgentHost(id);
+ return result;
+}
+
+WorkerDevToolsManager::WorkerDevToolsManager() {
+}
+
+WorkerDevToolsManager::~WorkerDevToolsManager() {
+}
+
+void WorkerDevToolsManager::WorkerCreated(
+ WorkerProcessHost* worker,
+ const WorkerProcessHost::WorkerInstance& instance) {
+ for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
+ it != terminated_workers_.end(); ++it) {
+ if (instance.Matches(it->worker_url, it->worker_name,
+ instance.partition(),
+ instance.resource_context())) {
+ worker->Send(new DevToolsAgentMsg_PauseWorkerContextOnStart(
+ instance.worker_route_id()));
+ WorkerId new_worker_id(worker->GetData().id, instance.worker_route_id());
+ paused_workers_[new_worker_id] = it->old_worker_id;
+ terminated_workers_.erase(it);
+ return;
+ }
+ }
+}
+
+void WorkerDevToolsManager::WorkerDestroyed(
+ WorkerProcessHost* worker,
+ int worker_route_id) {
+ InspectedWorkersList::iterator it = FindInspectedWorker(
+ worker->GetData().id,
+ worker_route_id);
+ if (it == inspected_workers_.end())
+ return;
+
+ WorkerId worker_id(worker->GetData().id, worker_route_id);
+ terminated_workers_.push_back(TerminatedInspectedWorker(
+ worker_id,
+ it->worker_url,
+ it->worker_name));
+ inspected_workers_.erase(it);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DetachedClientHosts::WorkerDestroyed, worker_id));
+}
+
+void WorkerDevToolsManager::WorkerContextStarted(WorkerProcessHost* process,
+ int worker_route_id) {
+ WorkerId new_worker_id(process->GetData().id, worker_route_id);
+ PausedWorkers::iterator it = paused_workers_.find(new_worker_id);
+ if (it == paused_workers_.end())
+ return;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &DetachedClientHosts::WorkerReloaded,
+ it->second,
+ new_worker_id));
+ paused_workers_.erase(it);
+}
+
+void WorkerDevToolsManager::RemoveInspectedWorkerData(
+ const WorkerId& id) {
+ for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
+ it != terminated_workers_.end(); ++it) {
+ if (it->old_worker_id == id) {
+ terminated_workers_.erase(it);
+ return;
+ }
+ }
+
+ for (PausedWorkers::iterator it = paused_workers_.begin();
+ it != paused_workers_.end(); ++it) {
+ if (it->second == id) {
+ SendResumeToWorker(it->first);
+ paused_workers_.erase(it);
+ return;
+ }
+ }
+}
+
+WorkerDevToolsManager::InspectedWorkersList::iterator
+WorkerDevToolsManager::FindInspectedWorker(
+ int host_id, int route_id) {
+ InspectedWorkersList::iterator it = inspected_workers_.begin();
+ while (it != inspected_workers_.end()) {
+ if (it->host->GetData().id == host_id && it->route_id == route_id)
+ break;
+ ++it;
+ }
+ return it;
+}
+
+static WorkerProcessHost* FindWorkerProcess(int worker_process_id) {
+ for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
+ if (iter.GetData().id == worker_process_id)
+ return *iter;
+ }
+ return NULL;
+}
+
+void WorkerDevToolsManager::RegisterDevToolsAgentHostForWorker(
+ int worker_process_id,
+ int worker_route_id) {
+ if (WorkerProcessHost* process = FindWorkerProcess(worker_process_id)) {
+ const WorkerProcessHost::Instances& instances = process->instances();
+ for (WorkerProcessHost::Instances::const_iterator i = instances.begin();
+ i != instances.end(); ++i) {
+ if (i->worker_route_id() == worker_route_id) {
+ DCHECK(FindInspectedWorker(worker_process_id, worker_route_id) ==
+ inspected_workers_.end());
+ inspected_workers_.push_back(
+ InspectedWorker(process, worker_route_id, i->url(), i->name()));
+ return;
+ }
+ }
+ }
+ NotifyWorkerDestroyedOnIOThread(worker_process_id, worker_route_id);
+}
+
+void WorkerDevToolsManager::ForwardToDevToolsClient(
+ int worker_process_id,
+ int worker_route_id,
+ const std::string& message) {
+ if (FindInspectedWorker(worker_process_id, worker_route_id) ==
+ inspected_workers_.end()) {
+ NOTREACHED();
+ return;
+ }
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &ForwardToDevToolsClientOnUIThread,
+ worker_process_id,
+ worker_route_id,
+ message));
+}
+
+void WorkerDevToolsManager::SaveAgentRuntimeState(int worker_process_id,
+ int worker_route_id,
+ const std::string& state) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &SaveAgentRuntimeStateOnUIThread,
+ worker_process_id,
+ worker_route_id,
+ state));
+}
+
+void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent(
+ int worker_process_id,
+ int worker_route_id,
+ const IPC::Message& message) {
+ InspectedWorkersList::iterator it = FindInspectedWorker(
+ worker_process_id,
+ worker_route_id);
+ if (it == inspected_workers_.end())
+ return;
+ IPC::Message* msg = new IPC::Message(message);
+ msg->set_routing_id(worker_route_id);
+ it->host->Send(msg);
+}
+
+// static
+void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread(
+ int worker_process_id,
+ int worker_route_id,
+ const std::string& message) {
+ WorkerDevToolsAgentHost* agent_host = AgentHosts::GetAgentHost(WorkerId(
+ worker_process_id,
+ worker_route_id));
+ if (!agent_host)
+ return;
+ DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(agent_host,
+ message);
+}
+
+// static
+void WorkerDevToolsManager::SaveAgentRuntimeStateOnUIThread(
+ int worker_process_id,
+ int worker_route_id,
+ const std::string& state) {
+ WorkerDevToolsAgentHost* agent_host = AgentHosts::GetAgentHost(WorkerId(
+ worker_process_id,
+ worker_route_id));
+ if (!agent_host)
+ return;
+ DevToolsManagerImpl::GetInstance()->SaveAgentRuntimeState(agent_host, state);
+}
+
+// static
+void WorkerDevToolsManager::NotifyWorkerDestroyedOnIOThread(
+ int worker_process_id,
+ int worker_route_id) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(
+ &WorkerDevToolsManager::NotifyWorkerDestroyedOnUIThread,
+ worker_process_id,
+ worker_route_id));
+}
+
+// static
+void WorkerDevToolsManager::NotifyWorkerDestroyedOnUIThread(
+ int worker_process_id,
+ int worker_route_id) {
+ WorkerDevToolsAgentHost* host =
+ AgentHosts::GetAgentHost(WorkerId(worker_process_id, worker_route_id));
+ if (host)
+ host->WorkerDestroyed();
+}
+
+// static
+void WorkerDevToolsManager::SendResumeToWorker(const WorkerId& id) {
+ if (WorkerProcessHost* process = FindWorkerProcess(id.first))
+ process->Send(new DevToolsAgentMsg_ResumeWorkerContext(id.second));
+}
+
+} // namespace content