diff options
Diffstat (limited to 'content/browser/debugger/worker_devtools_manager.cc')
-rw-r--r-- | content/browser/debugger/worker_devtools_manager.cc | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/content/browser/debugger/worker_devtools_manager.cc b/content/browser/debugger/worker_devtools_manager.cc new file mode 100644 index 0000000..573ef3f --- /dev/null +++ b/content/browser/debugger/worker_devtools_manager.cc @@ -0,0 +1,337 @@ +// 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/debugger/worker_devtools_manager.h" + +#include <list> +#include <map> + +#include "base/tuple.h" +#include "content/browser/browser_thread.h" +#include "content/browser/debugger/devtools_agent_host.h" +#include "content/browser/debugger/devtools_manager.h" +#include "content/browser/debugger/worker_devtools_message_filter.h" +#include "content/browser/worker_host/worker_process_host.h" +#include "content/common/content_notification_types.h" +#include "content/common/devtools_messages.h" +#include "content/common/notification_observer.h" +#include "content/common/notification_registrar.h" +#include "content/common/notification_service.h" + +namespace { +typedef std::pair<int, int> WorkerId; +} + +class WorkerDevToolsManager::AgentHosts : private NotificationObserver { +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() { + registrar_.Add(this, content::NOTIFICATION_APP_TERMINATING, + NotificationService::AllSources()); + } + ~AgentHosts() {} + + // NotificationObserver implementation. + virtual void Observe(int type, + const NotificationSource&, + const NotificationDetails&) OVERRIDE; + + static AgentHosts* instance_; + typedef std::map<WorkerId, WorkerDevToolsAgentHost*> Instances; + Instances map_; + NotificationRegistrar registrar_; +}; + +WorkerDevToolsManager::AgentHosts* + WorkerDevToolsManager::AgentHosts::instance_ = NULL; + + +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, + NewRunnableFunction( + &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, + const 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, + NewRunnableFunction( + &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent, + worker_id_.first, + worker_id_.second, + *message)); + } + virtual void NotifyClientClosing() OVERRIDE {} + virtual int GetRenderProcessId() OVERRIDE { return -1; } + + WorkerId worker_id_; + + DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost); +}; + +void WorkerDevToolsManager::AgentHosts::Observe(int type, + const NotificationSource&, + const NotificationDetails&) { + DCHECK(type == content::NOTIFICATION_APP_TERMINATING); + Instances copy(map_); + for (Instances::iterator it = copy.begin(); it != copy.end(); ++it) + it->second->WorkerDestroyed(); + DCHECK(!instance_); +} + +class WorkerDevToolsManager::InspectedWorkersList { + public: + InspectedWorkersList() {} + + void AddInstance(WorkerProcessHost* host, int route_id) { + DCHECK(!Contains(host->id(), route_id)); + map_.push_back(Entry(host, route_id)); + } + + bool Contains(int host_id, int route_id) { + return FindHost(host_id, route_id) != NULL; + } + + WorkerProcessHost* FindHost(int host_id, int route_id) { + Entries::iterator it = FindEntry(host_id, route_id); + if (it == map_.end()) + return NULL; + return it->host; + } + + WorkerProcessHost* RemoveInstance(int host_id, int route_id) { + Entries::iterator it = FindEntry(host_id, route_id); + if (it == map_.end()) + return NULL; + WorkerProcessHost* host = it->host; + map_.erase(it); + return host; + } + + void WorkerDevToolsMessageFilterClosing(int worker_process_id) { + Entries::iterator it = map_.begin(); + while (it != map_.end()) { + if (it->host->id() == worker_process_id) { + NotifyWorkerDestroyedOnIOThread( + it->host->id(), + it->route_id); + it = map_.erase(it); + } else + ++it; + } + } + + private: + struct Entry { + Entry(WorkerProcessHost* host, int route_id) + : host(host), + route_id(route_id) {} + WorkerProcessHost* const host; + int const route_id; + }; + typedef std::list<Entry> Entries; + + Entries::iterator FindEntry(int host_id, int route_id) { + Entries::iterator it = map_.begin(); + while (it != map_.end()) { + if (it->host->id() == host_id && it->route_id == route_id) + break; + ++it; + } + return it; + } + + Entries map_; + DISALLOW_COPY_AND_ASSIGN(InspectedWorkersList); +}; + +// 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() + : inspected_workers_(new InspectedWorkersList()) { +} + +WorkerDevToolsManager::~WorkerDevToolsManager() { +} + +static WorkerProcessHost* FindWorkerProcessHostForWorker( + int worker_process_id, + int worker_route_id) { + BrowserChildProcessHost::Iterator iter(ChildProcessInfo::WORKER_PROCESS); + for (; !iter.Done(); ++iter) { + if (iter->id() != worker_process_id) + continue; + WorkerProcessHost* worker = static_cast<WorkerProcessHost*>(*iter); + const WorkerProcessHost::Instances& instances = worker->instances(); + for (WorkerProcessHost::Instances::const_iterator i = instances.begin(); + i != instances.end(); ++i) { + if (i->shared() && i->worker_route_id() == worker_route_id) + return worker; + } + return NULL; + } + return NULL; +} + +void WorkerDevToolsManager::RegisterDevToolsAgentHostForWorker( + int worker_process_id, + int worker_route_id) { + WorkerProcessHost* host = FindWorkerProcessHostForWorker( + worker_process_id, + worker_route_id); + if (host) + inspected_workers_->AddInstance(host, worker_route_id); + else + NotifyWorkerDestroyedOnIOThread(worker_process_id, worker_route_id); +} + +void WorkerDevToolsManager::ForwardToDevToolsClient( + int worker_process_id, + int worker_route_id, + const IPC::Message& message) { + if (!inspected_workers_->Contains(worker_process_id, worker_route_id)) { + NOTREACHED(); + return; + } + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableFunction( + ForwardToDevToolsClientOnUIThread, + worker_process_id, + worker_route_id, + message)); +} + +void WorkerDevToolsManager::WorkerProcessDestroying( + int worker_process_id) { + inspected_workers_->WorkerDevToolsMessageFilterClosing( + worker_process_id); +} + +void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent( + int worker_process_id, + int worker_route_id, + const IPC::Message& message) { + WorkerProcessHost* host = inspected_workers_->FindHost( + worker_process_id, + worker_route_id); + if (!host) + return; + IPC::Message* msg = new IPC::Message(message); + msg->set_routing_id(worker_route_id); + host->Send(msg); +} + +// static +void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread( + int worker_process_id, + int worker_route_id, + const IPC::Message& message) { + WorkerDevToolsAgentHost* agent_host = AgentHosts::GetAgentHost(WorkerId( + worker_process_id, + worker_route_id)); + if (!agent_host) + return; + DevToolsManager::GetInstance()->ForwardToDevToolsClient(agent_host, message); +} + +// static +void WorkerDevToolsManager::NotifyWorkerDestroyedOnIOThread( + int worker_process_id, + int worker_route_id) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableFunction( + &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(); +} |