Remove dependency with third_party/webkit for worker_devtools_manager.cc
[chromium-blink-merge.git] / content / browser / devtools / worker_devtools_manager.cc
blobdd54f8088f5c287d2fbfab1d8adb6862140a0a08
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/devtools/worker_devtools_manager.h"
7 #include <list>
8 #include <map>
10 #include "base/bind.h"
11 #include "base/lazy_instance.h"
12 #include "content/browser/devtools/devtools_manager_impl.h"
13 #include "content/browser/devtools/devtools_protocol.h"
14 #include "content/browser/devtools/devtools_protocol_constants.h"
15 #include "content/browser/devtools/ipc_devtools_agent_host.h"
16 #include "content/browser/devtools/worker_devtools_message_filter.h"
17 #include "content/browser/worker_host/worker_service_impl.h"
18 #include "content/common/devtools_messages.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/child_process_data.h"
21 #include "content/public/common/process_type.h"
23 namespace content {
25 // Called on the UI thread.
26 // static
27 scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForWorker(
28 int worker_process_id,
29 int worker_route_id) {
30 return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
31 worker_process_id,
32 worker_route_id);
35 namespace {
37 typedef std::map<WorkerDevToolsManager::WorkerId,
38 WorkerDevToolsManager::WorkerDevToolsAgentHost*> AgentHosts;
39 base::LazyInstance<AgentHosts>::Leaky g_agent_map = LAZY_INSTANCE_INITIALIZER;
40 base::LazyInstance<AgentHosts>::Leaky g_orphan_map = LAZY_INSTANCE_INITIALIZER;
42 } // namespace
44 struct WorkerDevToolsManager::TerminatedInspectedWorker {
45 TerminatedInspectedWorker(WorkerId id, const GURL& url, const string16& name)
46 : old_worker_id(id),
47 worker_url(url),
48 worker_name(name) {}
49 WorkerId old_worker_id;
50 GURL worker_url;
51 string16 worker_name;
55 class WorkerDevToolsManager::WorkerDevToolsAgentHost
56 : public IPCDevToolsAgentHost {
57 public:
58 explicit WorkerDevToolsAgentHost(WorkerId worker_id)
59 : has_worker_id_(false) {
60 SetWorkerId(worker_id, false);
63 void SetWorkerId(WorkerId worker_id, bool reattach) {
64 worker_id_ = worker_id;
65 if (!has_worker_id_)
66 AddRef(); // Balanced in ResetWorkerId.
67 has_worker_id_ = true;
68 g_agent_map.Get()[worker_id_] = this;
70 BrowserThread::PostTask(
71 BrowserThread::IO,
72 FROM_HERE,
73 base::Bind(
74 &ConnectToWorker,
75 worker_id.first,
76 worker_id.second));
78 if (reattach)
79 Reattach(state_);
82 void ResetWorkerId() {
83 g_agent_map.Get().erase(worker_id_);
84 has_worker_id_ = false;
85 Release(); // Balanced in SetWorkerId.
88 void SaveAgentRuntimeState(const std::string& state) {
89 state_ = state;
92 void ConnectionFailed() {
93 NotifyCloseListener();
94 // Object can be deleted here.
97 private:
98 virtual ~WorkerDevToolsAgentHost();
100 static void ConnectToWorker(
101 int worker_process_id,
102 int worker_route_id) {
103 WorkerDevToolsManager::GetInstance()->ConnectDevToolsAgentHostToWorker(
104 worker_process_id, worker_route_id);
107 static void ForwardToWorkerDevToolsAgent(
108 int worker_process_id,
109 int worker_route_id,
110 IPC::Message* message) {
111 WorkerDevToolsManager::GetInstance()->ForwardToWorkerDevToolsAgent(
112 worker_process_id, worker_route_id, *message);
115 // IPCDevToolsAgentHost implementation.
116 virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE {
117 if (!has_worker_id_) {
118 delete message;
119 return;
121 BrowserThread::PostTask(
122 BrowserThread::IO, FROM_HERE,
123 base::Bind(
124 &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent,
125 worker_id_.first,
126 worker_id_.second,
127 base::Owned(message)));
130 virtual void OnClientAttached() OVERRIDE {}
131 virtual void OnClientDetached() OVERRIDE {}
133 bool has_worker_id_;
134 WorkerId worker_id_;
135 std::string state_;
137 DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost);
141 class WorkerDevToolsManager::DetachedClientHosts {
142 public:
143 static void WorkerReloaded(WorkerId old_id, WorkerId new_id) {
144 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
145 AgentHosts::iterator it = g_orphan_map.Get().find(old_id);
146 if (it != g_orphan_map.Get().end()) {
147 it->second->SetWorkerId(new_id, true);
148 g_orphan_map.Get().erase(old_id);
149 return;
151 RemovePendingWorkerData(old_id);
154 static void WorkerDestroyed(WorkerId id) {
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
156 AgentHosts::iterator it = g_agent_map.Get().find(id);
157 if (it == g_agent_map.Get().end()) {
158 RemovePendingWorkerData(id);
159 return;
162 WorkerDevToolsAgentHost* agent = it->second;
163 DevToolsManagerImpl* devtools_manager = DevToolsManagerImpl::GetInstance();
164 if (!agent->IsAttached()) {
165 // Agent has no client hosts -> delete it.
166 RemovePendingWorkerData(id);
167 return;
170 // Client host is debugging this worker agent host.
171 std::string notification = DevToolsProtocol::CreateNotification(
172 devtools::Worker::disconnectedFromWorker::kName, NULL)->Serialize();
173 devtools_manager->DispatchOnInspectorFrontend(agent, notification);
174 g_orphan_map.Get()[id] = agent;
175 agent->ResetWorkerId();
178 static void RemovePendingWorkerData(WorkerId id) {
179 BrowserThread::PostTask(
180 BrowserThread::IO, FROM_HERE,
181 base::Bind(&RemoveInspectedWorkerDataOnIOThread, id));
184 private:
185 DetachedClientHosts() {}
186 ~DetachedClientHosts() {}
188 static void RemoveInspectedWorkerDataOnIOThread(WorkerId id) {
189 WorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(id);
193 struct WorkerDevToolsManager::InspectedWorker {
194 InspectedWorker(WorkerProcessHost* host, int route_id, const GURL& url,
195 const string16& name)
196 : host(host),
197 route_id(route_id),
198 worker_url(url),
199 worker_name(name) {}
200 WorkerProcessHost* const host;
201 int const route_id;
202 GURL worker_url;
203 string16 worker_name;
206 // static
207 WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
209 return Singleton<WorkerDevToolsManager>::get();
212 // static
213 DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
214 int worker_process_id,
215 int worker_route_id) {
216 WorkerId id(worker_process_id, worker_route_id);
217 AgentHosts::iterator it = g_agent_map.Get().find(id);
218 if (it == g_agent_map.Get().end())
219 return new WorkerDevToolsAgentHost(id);
220 return it->second;
223 WorkerDevToolsManager::WorkerDevToolsManager() {
226 WorkerDevToolsManager::~WorkerDevToolsManager() {
229 void WorkerDevToolsManager::WorkerCreated(
230 WorkerProcessHost* worker,
231 const WorkerProcessHost::WorkerInstance& instance) {
232 for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
233 it != terminated_workers_.end(); ++it) {
234 if (instance.Matches(it->worker_url, it->worker_name,
235 instance.partition(),
236 instance.resource_context())) {
237 worker->Send(new DevToolsAgentMsg_PauseWorkerContextOnStart(
238 instance.worker_route_id()));
239 WorkerId new_worker_id(worker->GetData().id, instance.worker_route_id());
240 paused_workers_[new_worker_id] = it->old_worker_id;
241 terminated_workers_.erase(it);
242 return;
247 void WorkerDevToolsManager::WorkerDestroyed(
248 WorkerProcessHost* worker,
249 int worker_route_id) {
250 InspectedWorkersList::iterator it = FindInspectedWorker(
251 worker->GetData().id,
252 worker_route_id);
253 if (it == inspected_workers_.end())
254 return;
256 WorkerId worker_id(worker->GetData().id, worker_route_id);
257 terminated_workers_.push_back(TerminatedInspectedWorker(
258 worker_id,
259 it->worker_url,
260 it->worker_name));
261 inspected_workers_.erase(it);
262 BrowserThread::PostTask(
263 BrowserThread::UI, FROM_HERE,
264 base::Bind(&DetachedClientHosts::WorkerDestroyed, worker_id));
267 void WorkerDevToolsManager::WorkerContextStarted(WorkerProcessHost* process,
268 int worker_route_id) {
269 WorkerId new_worker_id(process->GetData().id, worker_route_id);
270 PausedWorkers::iterator it = paused_workers_.find(new_worker_id);
271 if (it == paused_workers_.end())
272 return;
274 BrowserThread::PostTask(
275 BrowserThread::UI, FROM_HERE,
276 base::Bind(
277 &DetachedClientHosts::WorkerReloaded,
278 it->second,
279 new_worker_id));
280 paused_workers_.erase(it);
283 void WorkerDevToolsManager::RemoveInspectedWorkerData(
284 const WorkerId& id) {
285 for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
286 it != terminated_workers_.end(); ++it) {
287 if (it->old_worker_id == id) {
288 terminated_workers_.erase(it);
289 return;
293 for (PausedWorkers::iterator it = paused_workers_.begin();
294 it != paused_workers_.end(); ++it) {
295 if (it->second == id) {
296 SendResumeToWorker(it->first);
297 paused_workers_.erase(it);
298 return;
303 WorkerDevToolsManager::InspectedWorkersList::iterator
304 WorkerDevToolsManager::FindInspectedWorker(
305 int host_id, int route_id) {
306 InspectedWorkersList::iterator it = inspected_workers_.begin();
307 while (it != inspected_workers_.end()) {
308 if (it->host->GetData().id == host_id && it->route_id == route_id)
309 break;
310 ++it;
312 return it;
315 static WorkerProcessHost* FindWorkerProcess(int worker_process_id) {
316 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
317 if (iter.GetData().id == worker_process_id)
318 return *iter;
320 return NULL;
323 void WorkerDevToolsManager::ConnectDevToolsAgentHostToWorker(
324 int worker_process_id,
325 int worker_route_id) {
326 if (WorkerProcessHost* process = FindWorkerProcess(worker_process_id)) {
327 const WorkerProcessHost::Instances& instances = process->instances();
328 for (WorkerProcessHost::Instances::const_iterator i = instances.begin();
329 i != instances.end(); ++i) {
330 if (i->worker_route_id() == worker_route_id) {
331 DCHECK(FindInspectedWorker(worker_process_id, worker_route_id) ==
332 inspected_workers_.end());
333 inspected_workers_.push_back(
334 InspectedWorker(process, worker_route_id, i->url(), i->name()));
335 return;
339 NotifyConnectionFailedOnIOThread(worker_process_id, worker_route_id);
342 void WorkerDevToolsManager::ForwardToDevToolsClient(
343 int worker_process_id,
344 int worker_route_id,
345 const std::string& message) {
346 if (FindInspectedWorker(worker_process_id, worker_route_id) ==
347 inspected_workers_.end()) {
348 NOTREACHED();
349 return;
351 BrowserThread::PostTask(
352 BrowserThread::UI, FROM_HERE,
353 base::Bind(
354 &ForwardToDevToolsClientOnUIThread,
355 worker_process_id,
356 worker_route_id,
357 message));
360 void WorkerDevToolsManager::SaveAgentRuntimeState(int worker_process_id,
361 int worker_route_id,
362 const std::string& state) {
363 BrowserThread::PostTask(
364 BrowserThread::UI, FROM_HERE,
365 base::Bind(
366 &SaveAgentRuntimeStateOnUIThread,
367 worker_process_id,
368 worker_route_id,
369 state));
372 void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent(
373 int worker_process_id,
374 int worker_route_id,
375 const IPC::Message& message) {
376 InspectedWorkersList::iterator it = FindInspectedWorker(
377 worker_process_id,
378 worker_route_id);
379 if (it == inspected_workers_.end())
380 return;
381 IPC::Message* msg = new IPC::Message(message);
382 msg->set_routing_id(worker_route_id);
383 it->host->Send(msg);
386 // static
387 void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread(
388 int worker_process_id,
389 int worker_route_id,
390 const std::string& message) {
391 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id,
392 worker_route_id));
393 if (it == g_agent_map.Get().end())
394 return;
395 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(it->second,
396 message);
399 // static
400 void WorkerDevToolsManager::SaveAgentRuntimeStateOnUIThread(
401 int worker_process_id,
402 int worker_route_id,
403 const std::string& state) {
404 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id,
405 worker_route_id));
406 if (it == g_agent_map.Get().end())
407 return;
408 it->second->SaveAgentRuntimeState(state);
411 // static
412 void WorkerDevToolsManager::NotifyConnectionFailedOnIOThread(
413 int worker_process_id,
414 int worker_route_id) {
415 BrowserThread::PostTask(
416 BrowserThread::UI, FROM_HERE,
417 base::Bind(
418 &WorkerDevToolsManager::NotifyConnectionFailedOnUIThread,
419 worker_process_id,
420 worker_route_id));
423 // static
424 void WorkerDevToolsManager::NotifyConnectionFailedOnUIThread(
425 int worker_process_id,
426 int worker_route_id) {
427 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id,
428 worker_route_id));
429 if (it != g_agent_map.Get().end())
430 it->second->ConnectionFailed();
433 // static
434 void WorkerDevToolsManager::SendResumeToWorker(const WorkerId& id) {
435 if (WorkerProcessHost* process = FindWorkerProcess(id.first))
436 process->Send(new DevToolsAgentMsg_ResumeWorkerContext(id.second));
439 WorkerDevToolsManager::WorkerDevToolsAgentHost::~WorkerDevToolsAgentHost() {
440 DetachedClientHosts::RemovePendingWorkerData(worker_id_);
441 g_agent_map.Get().erase(worker_id_);
442 g_orphan_map.Get().erase(worker_id_);
445 } // namespace content