OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/devtools/worker_devtools_manager.h" | |
6 | |
7 #include <list> | |
8 #include <map> | |
9 | |
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" | |
23 | |
24 namespace content { | |
25 | |
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); | |
37 } | |
38 } | |
39 | |
40 namespace { | |
41 | |
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; | |
46 | |
47 } // namespace | |
48 | |
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; | |
59 }; | |
60 | |
61 | |
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); | |
68 } | |
69 | |
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; | |
76 | |
77 BrowserThread::PostTask( | |
78 BrowserThread::IO, | |
79 FROM_HERE, | |
80 base::Bind( | |
81 &ConnectToWorker, | |
82 worker_id.first, | |
83 worker_id.second)); | |
84 | |
85 if (reattach) | |
86 Reattach(state_); | |
87 } | |
88 | |
89 void ResetWorkerId() { | |
90 g_agent_map.Get().erase(worker_id_); | |
91 has_worker_id_ = false; | |
92 Release(); // Balanced in SetWorkerId. | |
93 } | |
94 | |
95 void SaveAgentRuntimeState(const std::string& state) { | |
96 state_ = state; | |
97 } | |
98 | |
99 void ConnectionFailed() { | |
100 NotifyCloseListener(); | |
101 // Object can be deleted here. | |
102 } | |
103 | |
104 private: | |
105 virtual ~WorkerDevToolsAgentHost(); | |
106 | |
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); | |
112 } | |
113 | |
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); | |
120 } | |
121 | |
122 // IPCDevToolsAgentHost implementation. | |
123 virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE { | |
124 if (!has_worker_id_) { | |
125 delete message; | |
126 return; | |
127 } | |
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))); | |
135 } | |
136 | |
137 virtual void OnClientAttached() OVERRIDE {} | |
138 virtual void OnClientDetached() OVERRIDE {} | |
139 | |
140 bool has_worker_id_; | |
141 WorkerId worker_id_; | |
142 std::string state_; | |
143 | |
144 DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost); | |
145 }; | |
146 | |
147 | |
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; | |
157 } | |
158 RemovePendingWorkerData(old_id); | |
159 } | |
160 | |
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; | |
167 } | |
168 | |
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; | |
175 } | |
176 | |
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(); | |
183 } | |
184 | |
185 static void RemovePendingWorkerData(WorkerId id) { | |
186 BrowserThread::PostTask( | |
187 BrowserThread::IO, FROM_HERE, | |
188 base::Bind(&RemoveInspectedWorkerDataOnIOThread, id)); | |
189 } | |
190 | |
191 private: | |
192 DetachedClientHosts() {} | |
193 ~DetachedClientHosts() {} | |
194 | |
195 static void RemoveInspectedWorkerDataOnIOThread(WorkerId id) { | |
196 WorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(id); | |
197 } | |
198 }; | |
199 | |
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; | |
211 }; | |
212 | |
213 // static | |
214 WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() { | |
215 DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled()); | |
216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
217 return Singleton<WorkerDevToolsManager>::get(); | |
218 } | |
219 | |
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; | |
230 } | |
231 | |
232 WorkerDevToolsManager::WorkerDevToolsManager() { | |
233 } | |
234 | |
235 WorkerDevToolsManager::~WorkerDevToolsManager() { | |
236 } | |
237 | |
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; | |
250 } | |
251 } | |
252 return false; | |
253 } | |
254 | |
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; | |
263 | |
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)); | |
273 } | |
274 | |
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; | |
281 | |
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); | |
289 } | |
290 | |
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; | |
298 } | |
299 } | |
300 | |
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; | |
307 } | |
308 } | |
309 } | |
310 | |
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; | |
319 } | |
320 return it; | |
321 } | |
322 | |
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; | |
327 } | |
328 return NULL; | |
329 } | |
330 | |
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; | |
344 } | |
345 } | |
346 } | |
347 NotifyConnectionFailedOnIOThread(worker_process_id, worker_route_id); | |
348 } | |
349 | |
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; | |
358 } | |
359 BrowserThread::PostTask( | |
360 BrowserThread::UI, FROM_HERE, | |
361 base::Bind( | |
362 &ForwardToDevToolsClientOnUIThread, | |
363 worker_process_id, | |
364 worker_route_id, | |
365 message)); | |
366 } | |
367 | |
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)); | |
378 } | |
379 | |
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); | |
392 } | |
393 | |
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); | |
405 } | |
406 | |
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); | |
417 } | |
418 | |
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)); | |
429 } | |
430 | |
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(); | |
439 } | |
440 | |
441 // static | |
442 void WorkerDevToolsManager::SendResumeToWorker(const WorkerId& id) { | |
443 if (WorkerProcessHost* process = FindWorkerProcess(id.first)) | |
444 process->Send(new DevToolsAgentMsg_ResumeWorkerContext(id.second)); | |
445 } | |
446 | |
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_); | |
451 } | |
452 | |
453 } // namespace content | |
OLD | NEW |