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"
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"
25 // Called on the UI thread.
27 scoped_refptr
<DevToolsAgentHost
> DevToolsAgentHost::GetForWorker(
28 int worker_process_id
,
29 int worker_route_id
) {
30 return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
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
;
44 struct WorkerDevToolsManager::TerminatedInspectedWorker
{
45 TerminatedInspectedWorker(WorkerId id
, const GURL
& url
, const string16
& name
)
49 WorkerId old_worker_id
;
55 class WorkerDevToolsManager::WorkerDevToolsAgentHost
56 : public IPCDevToolsAgentHost
{
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
;
66 AddRef(); // Balanced in ResetWorkerId.
67 has_worker_id_
= true;
68 g_agent_map
.Get()[worker_id_
] = this;
70 BrowserThread::PostTask(
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
) {
92 void ConnectionFailed() {
93 NotifyCloseListener();
94 // Object can be deleted here.
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
,
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_
) {
121 BrowserThread::PostTask(
122 BrowserThread::IO
, FROM_HERE
,
124 &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent
,
127 base::Owned(message
)));
130 virtual void OnClientAttached() OVERRIDE
{}
131 virtual void OnClientDetached() OVERRIDE
{}
137 DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost
);
141 class WorkerDevToolsManager::DetachedClientHosts
{
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
);
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
);
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
);
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
));
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
)
200 WorkerProcessHost
* const host
;
203 string16 worker_name
;
207 WorkerDevToolsManager
* WorkerDevToolsManager::GetInstance() {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
209 return Singleton
<WorkerDevToolsManager
>::get();
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
);
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
);
247 void WorkerDevToolsManager::WorkerDestroyed(
248 WorkerProcessHost
* worker
,
249 int worker_route_id
) {
250 InspectedWorkersList::iterator it
= FindInspectedWorker(
251 worker
->GetData().id
,
253 if (it
== inspected_workers_
.end())
256 WorkerId
worker_id(worker
->GetData().id
, worker_route_id
);
257 terminated_workers_
.push_back(TerminatedInspectedWorker(
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())
274 BrowserThread::PostTask(
275 BrowserThread::UI
, FROM_HERE
,
277 &DetachedClientHosts::WorkerReloaded
,
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
);
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
);
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
)
315 static WorkerProcessHost
* FindWorkerProcess(int worker_process_id
) {
316 for (WorkerProcessHostIterator iter
; !iter
.Done(); ++iter
) {
317 if (iter
.GetData().id
== worker_process_id
)
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()));
339 NotifyConnectionFailedOnIOThread(worker_process_id
, worker_route_id
);
342 void WorkerDevToolsManager::ForwardToDevToolsClient(
343 int worker_process_id
,
345 const std::string
& message
) {
346 if (FindInspectedWorker(worker_process_id
, worker_route_id
) ==
347 inspected_workers_
.end()) {
351 BrowserThread::PostTask(
352 BrowserThread::UI
, FROM_HERE
,
354 &ForwardToDevToolsClientOnUIThread
,
360 void WorkerDevToolsManager::SaveAgentRuntimeState(int worker_process_id
,
362 const std::string
& state
) {
363 BrowserThread::PostTask(
364 BrowserThread::UI
, FROM_HERE
,
366 &SaveAgentRuntimeStateOnUIThread
,
372 void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent(
373 int worker_process_id
,
375 const IPC::Message
& message
) {
376 InspectedWorkersList::iterator it
= FindInspectedWorker(
379 if (it
== inspected_workers_
.end())
381 IPC::Message
* msg
= new IPC::Message(message
);
382 msg
->set_routing_id(worker_route_id
);
387 void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread(
388 int worker_process_id
,
390 const std::string
& message
) {
391 AgentHosts::iterator it
= g_agent_map
.Get().find(WorkerId(worker_process_id
,
393 if (it
== g_agent_map
.Get().end())
395 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(it
->second
,
400 void WorkerDevToolsManager::SaveAgentRuntimeStateOnUIThread(
401 int worker_process_id
,
403 const std::string
& state
) {
404 AgentHosts::iterator it
= g_agent_map
.Get().find(WorkerId(worker_process_id
,
406 if (it
== g_agent_map
.Get().end())
408 it
->second
->SaveAgentRuntimeState(state
);
412 void WorkerDevToolsManager::NotifyConnectionFailedOnIOThread(
413 int worker_process_id
,
414 int worker_route_id
) {
415 BrowserThread::PostTask(
416 BrowserThread::UI
, FROM_HERE
,
418 &WorkerDevToolsManager::NotifyConnectionFailedOnUIThread
,
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
,
429 if (it
!= g_agent_map
.Get().end())
430 it
->second
->ConnectionFailed();
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