Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / devtools / worker_devtools_manager.cc
blob0658c353c64ce4b2461be4affbf83b8f2614dc5b
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/embedded_worker_devtools_manager.h"
16 #include "content/browser/devtools/ipc_devtools_agent_host.h"
17 #include "content/browser/devtools/worker_devtools_message_filter.h"
18 #include "content/browser/worker_host/worker_service_impl.h"
19 #include "content/common/devtools_messages.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/child_process_data.h"
22 #include "content/public/common/process_type.h"
24 namespace content {
26 // Called on the UI thread.
27 // static
28 scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForWorker(
29 int worker_process_id,
30 int worker_route_id) {
31 if (WorkerService::EmbeddedSharedWorkerEnabled()) {
32 return EmbeddedWorkerDevToolsManager::GetInstance()
33 ->GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id);
34 } else {
35 return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
36 worker_process_id, worker_route_id);
40 namespace {
42 typedef std::map<WorkerDevToolsManager::WorkerId,
43 WorkerDevToolsManager::WorkerDevToolsAgentHost*> AgentHosts;
44 base::LazyInstance<AgentHosts>::Leaky g_agent_map = LAZY_INSTANCE_INITIALIZER;
45 base::LazyInstance<AgentHosts>::Leaky g_orphan_map = LAZY_INSTANCE_INITIALIZER;
47 } // namespace
49 struct WorkerDevToolsManager::TerminatedInspectedWorker {
50 TerminatedInspectedWorker(WorkerId id,
51 const GURL& url,
52 const base::string16& name)
53 : old_worker_id(id),
54 worker_url(url),
55 worker_name(name) {}
56 WorkerId old_worker_id;
57 GURL worker_url;
58 base::string16 worker_name;
62 class WorkerDevToolsManager::WorkerDevToolsAgentHost
63 : public IPCDevToolsAgentHost {
64 public:
65 explicit WorkerDevToolsAgentHost(WorkerId worker_id)
66 : has_worker_id_(false) {
67 SetWorkerId(worker_id, false);
70 void SetWorkerId(WorkerId worker_id, bool reattach) {
71 worker_id_ = worker_id;
72 if (!has_worker_id_)
73 AddRef(); // Balanced in ResetWorkerId.
74 has_worker_id_ = true;
75 g_agent_map.Get()[worker_id_] = this;
77 BrowserThread::PostTask(
78 BrowserThread::IO,
79 FROM_HERE,
80 base::Bind(
81 &ConnectToWorker,
82 worker_id.first,
83 worker_id.second));
85 if (reattach)
86 Reattach(state_);
89 void ResetWorkerId() {
90 g_agent_map.Get().erase(worker_id_);
91 has_worker_id_ = false;
92 Release(); // Balanced in SetWorkerId.
95 void SaveAgentRuntimeState(const std::string& state) {
96 state_ = state;
99 void ConnectionFailed() {
100 NotifyCloseListener();
101 // Object can be deleted here.
104 private:
105 virtual ~WorkerDevToolsAgentHost();
107 static void ConnectToWorker(
108 int worker_process_id,
109 int worker_route_id) {
110 WorkerDevToolsManager::GetInstance()->ConnectDevToolsAgentHostToWorker(
111 worker_process_id, worker_route_id);
114 static void ForwardToWorkerDevToolsAgent(
115 int worker_process_id,
116 int worker_route_id,
117 IPC::Message* message) {
118 WorkerDevToolsManager::GetInstance()->ForwardToWorkerDevToolsAgent(
119 worker_process_id, worker_route_id, *message);
122 // IPCDevToolsAgentHost implementation.
123 virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE {
124 if (!has_worker_id_) {
125 delete message;
126 return;
128 BrowserThread::PostTask(
129 BrowserThread::IO, FROM_HERE,
130 base::Bind(
131 &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent,
132 worker_id_.first,
133 worker_id_.second,
134 base::Owned(message)));
137 virtual void OnClientAttached() OVERRIDE {}
138 virtual void OnClientDetached() OVERRIDE {}
140 bool has_worker_id_;
141 WorkerId worker_id_;
142 std::string state_;
144 DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost);
148 class WorkerDevToolsManager::DetachedClientHosts {
149 public:
150 static void WorkerReloaded(WorkerId old_id, WorkerId new_id) {
151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
152 AgentHosts::iterator it = g_orphan_map.Get().find(old_id);
153 if (it != g_orphan_map.Get().end()) {
154 it->second->SetWorkerId(new_id, true);
155 g_orphan_map.Get().erase(old_id);
156 return;
158 RemovePendingWorkerData(old_id);
161 static void WorkerDestroyed(WorkerId id) {
162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
163 AgentHosts::iterator it = g_agent_map.Get().find(id);
164 if (it == g_agent_map.Get().end()) {
165 RemovePendingWorkerData(id);
166 return;
169 WorkerDevToolsAgentHost* agent = it->second;
170 DevToolsManagerImpl* devtools_manager = DevToolsManagerImpl::GetInstance();
171 if (!agent->IsAttached()) {
172 // Agent has no client hosts -> delete it.
173 RemovePendingWorkerData(id);
174 return;
177 // Client host is debugging this worker agent host.
178 std::string notification = DevToolsProtocol::CreateNotification(
179 devtools::Worker::disconnectedFromWorker::kName, NULL)->Serialize();
180 devtools_manager->DispatchOnInspectorFrontend(agent, notification);
181 g_orphan_map.Get()[id] = agent;
182 agent->ResetWorkerId();
185 static void RemovePendingWorkerData(WorkerId id) {
186 BrowserThread::PostTask(
187 BrowserThread::IO, FROM_HERE,
188 base::Bind(&RemoveInspectedWorkerDataOnIOThread, id));
191 private:
192 DetachedClientHosts() {}
193 ~DetachedClientHosts() {}
195 static void RemoveInspectedWorkerDataOnIOThread(WorkerId id) {
196 WorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(id);
200 struct WorkerDevToolsManager::InspectedWorker {
201 InspectedWorker(WorkerProcessHost* host, int route_id, const GURL& url,
202 const base::string16& name)
203 : host(host),
204 route_id(route_id),
205 worker_url(url),
206 worker_name(name) {}
207 WorkerProcessHost* const host;
208 int const route_id;
209 GURL worker_url;
210 base::string16 worker_name;
213 // static
214 WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
215 DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled());
216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
217 return Singleton<WorkerDevToolsManager>::get();
220 // static
221 DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
222 int worker_process_id,
223 int worker_route_id) {
224 DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled());
225 WorkerId id(worker_process_id, worker_route_id);
226 AgentHosts::iterator it = g_agent_map.Get().find(id);
227 if (it == g_agent_map.Get().end())
228 return new WorkerDevToolsAgentHost(id);
229 return it->second;
232 WorkerDevToolsManager::WorkerDevToolsManager() {
235 WorkerDevToolsManager::~WorkerDevToolsManager() {
238 bool WorkerDevToolsManager::WorkerCreated(
239 WorkerProcessHost* worker,
240 const WorkerProcessHost::WorkerInstance& instance) {
241 for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
242 it != terminated_workers_.end(); ++it) {
243 if (instance.Matches(it->worker_url, it->worker_name,
244 instance.partition(),
245 instance.resource_context())) {
246 WorkerId new_worker_id(worker->GetData().id, instance.worker_route_id());
247 paused_workers_[new_worker_id] = it->old_worker_id;
248 terminated_workers_.erase(it);
249 return true;
252 return false;
255 void WorkerDevToolsManager::WorkerDestroyed(
256 WorkerProcessHost* worker,
257 int worker_route_id) {
258 InspectedWorkersList::iterator it = FindInspectedWorker(
259 worker->GetData().id,
260 worker_route_id);
261 if (it == inspected_workers_.end())
262 return;
264 WorkerId worker_id(worker->GetData().id, worker_route_id);
265 terminated_workers_.push_back(TerminatedInspectedWorker(
266 worker_id,
267 it->worker_url,
268 it->worker_name));
269 inspected_workers_.erase(it);
270 BrowserThread::PostTask(
271 BrowserThread::UI, FROM_HERE,
272 base::Bind(&DetachedClientHosts::WorkerDestroyed, worker_id));
275 void WorkerDevToolsManager::WorkerContextStarted(WorkerProcessHost* process,
276 int worker_route_id) {
277 WorkerId new_worker_id(process->GetData().id, worker_route_id);
278 PausedWorkers::iterator it = paused_workers_.find(new_worker_id);
279 if (it == paused_workers_.end())
280 return;
282 BrowserThread::PostTask(
283 BrowserThread::UI, FROM_HERE,
284 base::Bind(
285 &DetachedClientHosts::WorkerReloaded,
286 it->second,
287 new_worker_id));
288 paused_workers_.erase(it);
291 void WorkerDevToolsManager::RemoveInspectedWorkerData(
292 const WorkerId& id) {
293 for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin();
294 it != terminated_workers_.end(); ++it) {
295 if (it->old_worker_id == id) {
296 terminated_workers_.erase(it);
297 return;
301 for (PausedWorkers::iterator it = paused_workers_.begin();
302 it != paused_workers_.end(); ++it) {
303 if (it->second == id) {
304 SendResumeToWorker(it->first);
305 paused_workers_.erase(it);
306 return;
311 WorkerDevToolsManager::InspectedWorkersList::iterator
312 WorkerDevToolsManager::FindInspectedWorker(
313 int host_id, int route_id) {
314 InspectedWorkersList::iterator it = inspected_workers_.begin();
315 while (it != inspected_workers_.end()) {
316 if (it->host->GetData().id == host_id && it->route_id == route_id)
317 break;
318 ++it;
320 return it;
323 static WorkerProcessHost* FindWorkerProcess(int worker_process_id) {
324 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) {
325 if (iter.GetData().id == worker_process_id)
326 return *iter;
328 return NULL;
331 void WorkerDevToolsManager::ConnectDevToolsAgentHostToWorker(
332 int worker_process_id,
333 int worker_route_id) {
334 if (WorkerProcessHost* process = FindWorkerProcess(worker_process_id)) {
335 const WorkerProcessHost::Instances& instances = process->instances();
336 for (WorkerProcessHost::Instances::const_iterator i = instances.begin();
337 i != instances.end(); ++i) {
338 if (i->worker_route_id() == worker_route_id) {
339 DCHECK(FindInspectedWorker(worker_process_id, worker_route_id) ==
340 inspected_workers_.end());
341 inspected_workers_.push_back(
342 InspectedWorker(process, worker_route_id, i->url(), i->name()));
343 return;
347 NotifyConnectionFailedOnIOThread(worker_process_id, worker_route_id);
350 void WorkerDevToolsManager::ForwardToDevToolsClient(
351 int worker_process_id,
352 int worker_route_id,
353 const std::string& message) {
354 if (FindInspectedWorker(worker_process_id, worker_route_id) ==
355 inspected_workers_.end()) {
356 NOTREACHED();
357 return;
359 BrowserThread::PostTask(
360 BrowserThread::UI, FROM_HERE,
361 base::Bind(
362 &ForwardToDevToolsClientOnUIThread,
363 worker_process_id,
364 worker_route_id,
365 message));
368 void WorkerDevToolsManager::SaveAgentRuntimeState(int worker_process_id,
369 int worker_route_id,
370 const std::string& state) {
371 BrowserThread::PostTask(
372 BrowserThread::UI, FROM_HERE,
373 base::Bind(
374 &SaveAgentRuntimeStateOnUIThread,
375 worker_process_id,
376 worker_route_id,
377 state));
380 void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent(
381 int worker_process_id,
382 int worker_route_id,
383 const IPC::Message& message) {
384 InspectedWorkersList::iterator it = FindInspectedWorker(
385 worker_process_id,
386 worker_route_id);
387 if (it == inspected_workers_.end())
388 return;
389 IPC::Message* msg = new IPC::Message(message);
390 msg->set_routing_id(worker_route_id);
391 it->host->Send(msg);
394 // static
395 void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread(
396 int worker_process_id,
397 int worker_route_id,
398 const std::string& message) {
399 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id,
400 worker_route_id));
401 if (it == g_agent_map.Get().end())
402 return;
403 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(it->second,
404 message);
407 // static
408 void WorkerDevToolsManager::SaveAgentRuntimeStateOnUIThread(
409 int worker_process_id,
410 int worker_route_id,
411 const std::string& state) {
412 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id,
413 worker_route_id));
414 if (it == g_agent_map.Get().end())
415 return;
416 it->second->SaveAgentRuntimeState(state);
419 // static
420 void WorkerDevToolsManager::NotifyConnectionFailedOnIOThread(
421 int worker_process_id,
422 int worker_route_id) {
423 BrowserThread::PostTask(
424 BrowserThread::UI, FROM_HERE,
425 base::Bind(
426 &WorkerDevToolsManager::NotifyConnectionFailedOnUIThread,
427 worker_process_id,
428 worker_route_id));
431 // static
432 void WorkerDevToolsManager::NotifyConnectionFailedOnUIThread(
433 int worker_process_id,
434 int worker_route_id) {
435 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id,
436 worker_route_id));
437 if (it != g_agent_map.Get().end())
438 it->second->ConnectionFailed();
441 // static
442 void WorkerDevToolsManager::SendResumeToWorker(const WorkerId& id) {
443 if (WorkerProcessHost* process = FindWorkerProcess(id.first))
444 process->Send(new DevToolsAgentMsg_ResumeWorkerContext(id.second));
447 WorkerDevToolsManager::WorkerDevToolsAgentHost::~WorkerDevToolsAgentHost() {
448 DetachedClientHosts::RemovePendingWorkerData(worker_id_);
449 g_agent_map.Get().erase(worker_id_);
450 g_orphan_map.Get().erase(worker_id_);
453 } // namespace content