diff options
Diffstat (limited to 'content/browser/devtools/worker_devtools_manager.cc')
-rw-r--r-- | content/browser/devtools/worker_devtools_manager.cc | 472 |
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 |