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/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"
26 // Called on the UI thread.
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
);
35 return WorkerDevToolsManager::GetDevToolsAgentHostForWorker(
36 worker_process_id
, worker_route_id
);
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
;
49 struct WorkerDevToolsManager::TerminatedInspectedWorker
{
50 TerminatedInspectedWorker(WorkerId id
,
52 const base::string16
& name
)
56 WorkerId old_worker_id
;
58 base::string16 worker_name
;
62 class WorkerDevToolsManager::WorkerDevToolsAgentHost
63 : public IPCDevToolsAgentHost
{
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
;
73 AddRef(); // Balanced in ResetWorkerId.
74 has_worker_id_
= true;
75 g_agent_map
.Get()[worker_id_
] = this;
77 BrowserThread::PostTask(
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
) {
99 void ConnectionFailed() {
100 NotifyCloseListener();
101 // Object can be deleted here.
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
,
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_
) {
128 BrowserThread::PostTask(
129 BrowserThread::IO
, FROM_HERE
,
131 &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent
,
134 base::Owned(message
)));
137 virtual void OnClientAttached() OVERRIDE
{}
138 virtual void OnClientDetached() OVERRIDE
{}
144 DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost
);
148 class WorkerDevToolsManager::DetachedClientHosts
{
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
);
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
);
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
);
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
));
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
)
207 WorkerProcessHost
* const host
;
210 base::string16 worker_name
;
214 WorkerDevToolsManager
* WorkerDevToolsManager::GetInstance() {
215 DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled());
216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
217 return Singleton
<WorkerDevToolsManager
>::get();
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
);
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
);
255 void WorkerDevToolsManager::WorkerDestroyed(
256 WorkerProcessHost
* worker
,
257 int worker_route_id
) {
258 InspectedWorkersList::iterator it
= FindInspectedWorker(
259 worker
->GetData().id
,
261 if (it
== inspected_workers_
.end())
264 WorkerId
worker_id(worker
->GetData().id
, worker_route_id
);
265 terminated_workers_
.push_back(TerminatedInspectedWorker(
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())
282 BrowserThread::PostTask(
283 BrowserThread::UI
, FROM_HERE
,
285 &DetachedClientHosts::WorkerReloaded
,
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
);
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
);
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
)
323 static WorkerProcessHost
* FindWorkerProcess(int worker_process_id
) {
324 for (WorkerProcessHostIterator iter
; !iter
.Done(); ++iter
) {
325 if (iter
.GetData().id
== worker_process_id
)
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()));
347 NotifyConnectionFailedOnIOThread(worker_process_id
, worker_route_id
);
350 void WorkerDevToolsManager::ForwardToDevToolsClient(
351 int worker_process_id
,
353 const std::string
& message
) {
354 if (FindInspectedWorker(worker_process_id
, worker_route_id
) ==
355 inspected_workers_
.end()) {
359 BrowserThread::PostTask(
360 BrowserThread::UI
, FROM_HERE
,
362 &ForwardToDevToolsClientOnUIThread
,
368 void WorkerDevToolsManager::SaveAgentRuntimeState(int worker_process_id
,
370 const std::string
& state
) {
371 BrowserThread::PostTask(
372 BrowserThread::UI
, FROM_HERE
,
374 &SaveAgentRuntimeStateOnUIThread
,
380 void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent(
381 int worker_process_id
,
383 const IPC::Message
& message
) {
384 InspectedWorkersList::iterator it
= FindInspectedWorker(
387 if (it
== inspected_workers_
.end())
389 IPC::Message
* msg
= new IPC::Message(message
);
390 msg
->set_routing_id(worker_route_id
);
395 void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread(
396 int worker_process_id
,
398 const std::string
& message
) {
399 AgentHosts::iterator it
= g_agent_map
.Get().find(WorkerId(worker_process_id
,
401 if (it
== g_agent_map
.Get().end())
403 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(it
->second
,
408 void WorkerDevToolsManager::SaveAgentRuntimeStateOnUIThread(
409 int worker_process_id
,
411 const std::string
& state
) {
412 AgentHosts::iterator it
= g_agent_map
.Get().find(WorkerId(worker_process_id
,
414 if (it
== g_agent_map
.Get().end())
416 it
->second
->SaveAgentRuntimeState(state
);
420 void WorkerDevToolsManager::NotifyConnectionFailedOnIOThread(
421 int worker_process_id
,
422 int worker_route_id
) {
423 BrowserThread::PostTask(
424 BrowserThread::UI
, FROM_HERE
,
426 &WorkerDevToolsManager::NotifyConnectionFailedOnUIThread
,
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
,
437 if (it
!= g_agent_map
.Get().end())
438 it
->second
->ConnectionFailed();
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