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/debugger/worker_devtools_manager.h" | |
6 | |
7 #include <list> | |
8 #include <map> | |
9 | |
10 #include "base/bind.h" | |
11 #include "content/browser/debugger/devtools_agent_host.h" | |
12 #include "content/browser/debugger/devtools_manager_impl.h" | |
13 #include "content/browser/debugger/worker_devtools_message_filter.h" | |
14 #include "content/browser/worker_host/worker_service_impl.h" | |
15 #include "content/common/devtools_messages.h" | |
16 #include "content/public/browser/browser_thread.h" | |
17 #include "content/public/browser/child_process_data.h" | |
18 #include "content/public/browser/devtools_agent_host_registry.h" | |
19 #include "content/public/common/process_type.h" | |
20 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h
" | |
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDevToolsAgent.h" | |
22 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" | |
23 | |
24 namespace content { | |
25 | |
26 // Called on the UI thread. | |
27 // static | |
28 DevToolsAgentHost* DevToolsAgentHostRegistry::GetDevToolsAgentHostForWorker( | |
29 int worker_process_id, | |
30 int worker_route_id) { | |
31 return WorkerDevToolsManager::GetDevToolsAgentHostForWorker( | |
32 worker_process_id, | |
33 worker_route_id); | |
34 } | |
35 | |
36 class WorkerDevToolsManager::AgentHosts { | |
37 public: | |
38 static void Add(WorkerId id, WorkerDevToolsAgentHost* host) { | |
39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
40 if (!instance_) | |
41 instance_ = new AgentHosts(); | |
42 instance_->map_[id] = host; | |
43 } | |
44 static void Remove(WorkerId id) { | |
45 DCHECK(instance_); | |
46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
47 Instances& map = instance_->map_; | |
48 map.erase(id); | |
49 if (map.empty()) { | |
50 delete instance_; | |
51 instance_ = NULL; | |
52 } | |
53 } | |
54 | |
55 static WorkerDevToolsAgentHost* GetAgentHost(WorkerId id) { | |
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
57 if (!instance_) | |
58 return NULL; | |
59 Instances& map = instance_->map_; | |
60 Instances::iterator it = map.find(id); | |
61 if (it == map.end()) | |
62 return NULL; | |
63 return it->second; | |
64 } | |
65 | |
66 private: | |
67 AgentHosts() { | |
68 } | |
69 ~AgentHosts() {} | |
70 | |
71 static AgentHosts* instance_; | |
72 typedef std::map<WorkerId, WorkerDevToolsAgentHost*> Instances; | |
73 Instances map_; | |
74 }; | |
75 | |
76 WorkerDevToolsManager::AgentHosts* | |
77 WorkerDevToolsManager::AgentHosts::instance_ = NULL; | |
78 | |
79 | |
80 struct WorkerDevToolsManager::TerminatedInspectedWorker { | |
81 TerminatedInspectedWorker(WorkerId id, const GURL& url, const string16& name) | |
82 : old_worker_id(id), | |
83 worker_url(url), | |
84 worker_name(name) {} | |
85 WorkerId old_worker_id; | |
86 GURL worker_url; | |
87 string16 worker_name; | |
88 }; | |
89 | |
90 | |
91 class WorkerDevToolsManager::WorkerDevToolsAgentHost | |
92 : public DevToolsAgentHost { | |
93 public: | |
94 explicit WorkerDevToolsAgentHost(WorkerId worker_id) | |
95 : worker_id_(worker_id) { | |
96 AgentHosts::Add(worker_id, this); | |
97 BrowserThread::PostTask( | |
98 BrowserThread::IO, | |
99 FROM_HERE, | |
100 base::Bind( | |
101 &RegisterAgent, | |
102 worker_id.first, | |
103 worker_id.second)); | |
104 } | |
105 | |
106 void WorkerDestroyed() { | |
107 NotifyCloseListener(); | |
108 delete this; | |
109 } | |
110 | |
111 private: | |
112 virtual ~WorkerDevToolsAgentHost() { | |
113 AgentHosts::Remove(worker_id_); | |
114 } | |
115 | |
116 static void RegisterAgent( | |
117 int worker_process_id, | |
118 int worker_route_id) { | |
119 WorkerDevToolsManager::GetInstance()->RegisterDevToolsAgentHostForWorker( | |
120 worker_process_id, worker_route_id); | |
121 } | |
122 | |
123 static void ForwardToWorkerDevToolsAgent( | |
124 int worker_process_id, | |
125 int worker_route_id, | |
126 IPC::Message* message) { | |
127 WorkerDevToolsManager::GetInstance()->ForwardToWorkerDevToolsAgent( | |
128 worker_process_id, worker_route_id, *message); | |
129 } | |
130 | |
131 // DevToolsAgentHost implementation. | |
132 virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE { | |
133 BrowserThread::PostTask( | |
134 BrowserThread::IO, FROM_HERE, | |
135 base::Bind( | |
136 &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent, | |
137 worker_id_.first, | |
138 worker_id_.second, | |
139 base::Owned(message))); | |
140 } | |
141 virtual void NotifyClientAttaching() OVERRIDE {} | |
142 virtual void NotifyClientDetaching() OVERRIDE {} | |
143 virtual int GetRenderProcessId() OVERRIDE { return -1; } | |
144 | |
145 WorkerId worker_id_; | |
146 | |
147 DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost); | |
148 }; | |
149 | |
150 | |
151 class WorkerDevToolsManager::DetachedClientHosts { | |
152 public: | |
153 static void WorkerReloaded(WorkerId old_id, WorkerId new_id) { | |
154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
155 if (instance_ && instance_->ReattachClient(old_id, new_id)) | |
156 return; | |
157 RemovePendingWorkerData(old_id); | |
158 } | |
159 | |
160 static void WorkerDestroyed(WorkerId id) { | |
161 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
162 WorkerDevToolsAgentHost* agent = AgentHosts::GetAgentHost(id); | |
163 if (!agent) { | |
164 RemovePendingWorkerData(id); | |
165 return; | |
166 } | |
167 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend( | |
168 agent, | |
169 WebKit::WebDevToolsAgent::workerDisconnectedFromWorkerEvent().utf8()); | |
170 int cookie = DevToolsManagerImpl::GetInstance()->DetachClientHost(agent); | |
171 agent->WorkerDestroyed(); | |
172 if (cookie == -1) { | |
173 RemovePendingWorkerData(id); | |
174 return; | |
175 } | |
176 if (!instance_) | |
177 new DetachedClientHosts(); | |
178 instance_->worker_id_to_cookie_[id] = cookie; | |
179 } | |
180 | |
181 private: | |
182 DetachedClientHosts() { | |
183 instance_ = this; | |
184 } | |
185 ~DetachedClientHosts() { | |
186 instance_ = NULL; | |
187 } | |
188 | |
189 bool ReattachClient(WorkerId old_id, WorkerId new_id) { | |
190 WorkerIdToCookieMap::iterator it = worker_id_to_cookie_.find(old_id); | |
191 if (it == worker_id_to_cookie_.end()) | |
192 return false; | |
193 DevToolsAgentHost* agent = | |
194 WorkerDevToolsManager::GetDevToolsAgentHostForWorker( | |
195 new_id.first, | |
196 new_id.second); | |
197 DevToolsManagerImpl::GetInstance()->AttachClientHost( | |
198 it->second, | |
199 agent); | |
200 worker_id_to_cookie_.erase(it); | |
201 if (worker_id_to_cookie_.empty()) | |
202 delete this; | |
203 return true; | |
204 } | |
205 | |
206 static void RemovePendingWorkerData(WorkerId id) { | |
207 BrowserThread::PostTask( | |
208 BrowserThread::IO, FROM_HERE, | |
209 base::Bind(&RemoveInspectedWorkerDataOnIOThread, id)); | |
210 } | |
211 | |
212 static void RemoveInspectedWorkerDataOnIOThread(WorkerId id) { | |
213 WorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(id); | |
214 } | |
215 | |
216 static DetachedClientHosts* instance_; | |
217 typedef std::map<WorkerId, int> WorkerIdToCookieMap; | |
218 WorkerIdToCookieMap worker_id_to_cookie_; | |
219 }; | |
220 | |
221 WorkerDevToolsManager::DetachedClientHosts* | |
222 WorkerDevToolsManager::DetachedClientHosts::instance_ = NULL; | |
223 | |
224 struct WorkerDevToolsManager::InspectedWorker { | |
225 InspectedWorker(WorkerProcessHost* host, int route_id, const GURL& url, | |
226 const string16& name) | |
227 : host(host), | |
228 route_id(route_id), | |
229 worker_url(url), | |
230 worker_name(name) {} | |
231 WorkerProcessHost* const host; | |
232 int const route_id; | |
233 GURL worker_url; | |
234 string16 worker_name; | |
235 }; | |
236 | |
237 // static | |
238 WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() { | |
239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
240 return Singleton<WorkerDevToolsManager>::get(); | |
241 } | |
242 | |
243 // static | |
244 DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker( | |
245 int worker_process_id, | |
246 int worker_route_id) { | |
247 WorkerId id(worker_process_id, worker_route_id); | |
248 WorkerDevToolsAgentHost* result = AgentHosts::GetAgentHost(id); | |
249 if (!result) | |
250 result = new WorkerDevToolsAgentHost(id); | |
251 return result; | |
252 } | |
253 | |
254 WorkerDevToolsManager::WorkerDevToolsManager() { | |
255 } | |
256 | |
257 WorkerDevToolsManager::~WorkerDevToolsManager() { | |
258 } | |
259 | |
260 void WorkerDevToolsManager::WorkerCreated( | |
261 WorkerProcessHost* worker, | |
262 const WorkerProcessHost::WorkerInstance& instance) { | |
263 for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin(); | |
264 it != terminated_workers_.end(); ++it) { | |
265 if (instance.Matches(it->worker_url, it->worker_name, | |
266 instance.partition(), | |
267 instance.resource_context())) { | |
268 worker->Send(new DevToolsAgentMsg_PauseWorkerContextOnStart( | |
269 instance.worker_route_id())); | |
270 WorkerId new_worker_id(worker->GetData().id, instance.worker_route_id()); | |
271 paused_workers_[new_worker_id] = it->old_worker_id; | |
272 terminated_workers_.erase(it); | |
273 return; | |
274 } | |
275 } | |
276 } | |
277 | |
278 void WorkerDevToolsManager::WorkerDestroyed( | |
279 WorkerProcessHost* worker, | |
280 int worker_route_id) { | |
281 InspectedWorkersList::iterator it = FindInspectedWorker( | |
282 worker->GetData().id, | |
283 worker_route_id); | |
284 if (it == inspected_workers_.end()) | |
285 return; | |
286 | |
287 WorkerId worker_id(worker->GetData().id, worker_route_id); | |
288 terminated_workers_.push_back(TerminatedInspectedWorker( | |
289 worker_id, | |
290 it->worker_url, | |
291 it->worker_name)); | |
292 inspected_workers_.erase(it); | |
293 BrowserThread::PostTask( | |
294 BrowserThread::UI, FROM_HERE, | |
295 base::Bind(&DetachedClientHosts::WorkerDestroyed, worker_id)); | |
296 } | |
297 | |
298 void WorkerDevToolsManager::WorkerContextStarted(WorkerProcessHost* process, | |
299 int worker_route_id) { | |
300 WorkerId new_worker_id(process->GetData().id, worker_route_id); | |
301 PausedWorkers::iterator it = paused_workers_.find(new_worker_id); | |
302 if (it == paused_workers_.end()) | |
303 return; | |
304 | |
305 BrowserThread::PostTask( | |
306 BrowserThread::UI, FROM_HERE, | |
307 base::Bind( | |
308 &DetachedClientHosts::WorkerReloaded, | |
309 it->second, | |
310 new_worker_id)); | |
311 paused_workers_.erase(it); | |
312 } | |
313 | |
314 void WorkerDevToolsManager::RemoveInspectedWorkerData( | |
315 const WorkerId& id) { | |
316 for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin(); | |
317 it != terminated_workers_.end(); ++it) { | |
318 if (it->old_worker_id == id) { | |
319 terminated_workers_.erase(it); | |
320 return; | |
321 } | |
322 } | |
323 | |
324 for (PausedWorkers::iterator it = paused_workers_.begin(); | |
325 it != paused_workers_.end(); ++it) { | |
326 if (it->second == id) { | |
327 SendResumeToWorker(it->first); | |
328 paused_workers_.erase(it); | |
329 return; | |
330 } | |
331 } | |
332 } | |
333 | |
334 WorkerDevToolsManager::InspectedWorkersList::iterator | |
335 WorkerDevToolsManager::FindInspectedWorker( | |
336 int host_id, int route_id) { | |
337 InspectedWorkersList::iterator it = inspected_workers_.begin(); | |
338 while (it != inspected_workers_.end()) { | |
339 if (it->host->GetData().id == host_id && it->route_id == route_id) | |
340 break; | |
341 ++it; | |
342 } | |
343 return it; | |
344 } | |
345 | |
346 static WorkerProcessHost* FindWorkerProcess(int worker_process_id) { | |
347 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
348 if (iter.GetData().id == worker_process_id) | |
349 return *iter; | |
350 } | |
351 return NULL; | |
352 } | |
353 | |
354 void WorkerDevToolsManager::RegisterDevToolsAgentHostForWorker( | |
355 int worker_process_id, | |
356 int worker_route_id) { | |
357 if (WorkerProcessHost* process = FindWorkerProcess(worker_process_id)) { | |
358 const WorkerProcessHost::Instances& instances = process->instances(); | |
359 for (WorkerProcessHost::Instances::const_iterator i = instances.begin(); | |
360 i != instances.end(); ++i) { | |
361 if (i->worker_route_id() == worker_route_id) { | |
362 DCHECK(FindInspectedWorker(worker_process_id, worker_route_id) == | |
363 inspected_workers_.end()); | |
364 inspected_workers_.push_back( | |
365 InspectedWorker(process, worker_route_id, i->url(), i->name())); | |
366 return; | |
367 } | |
368 } | |
369 } | |
370 NotifyWorkerDestroyedOnIOThread(worker_process_id, worker_route_id); | |
371 } | |
372 | |
373 void WorkerDevToolsManager::ForwardToDevToolsClient( | |
374 int worker_process_id, | |
375 int worker_route_id, | |
376 const std::string& message) { | |
377 if (FindInspectedWorker(worker_process_id, worker_route_id) == | |
378 inspected_workers_.end()) { | |
379 NOTREACHED(); | |
380 return; | |
381 } | |
382 BrowserThread::PostTask( | |
383 BrowserThread::UI, FROM_HERE, | |
384 base::Bind( | |
385 &ForwardToDevToolsClientOnUIThread, | |
386 worker_process_id, | |
387 worker_route_id, | |
388 message)); | |
389 } | |
390 | |
391 void WorkerDevToolsManager::SaveAgentRuntimeState(int worker_process_id, | |
392 int worker_route_id, | |
393 const std::string& state) { | |
394 BrowserThread::PostTask( | |
395 BrowserThread::UI, FROM_HERE, | |
396 base::Bind( | |
397 &SaveAgentRuntimeStateOnUIThread, | |
398 worker_process_id, | |
399 worker_route_id, | |
400 state)); | |
401 } | |
402 | |
403 void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent( | |
404 int worker_process_id, | |
405 int worker_route_id, | |
406 const IPC::Message& message) { | |
407 InspectedWorkersList::iterator it = FindInspectedWorker( | |
408 worker_process_id, | |
409 worker_route_id); | |
410 if (it == inspected_workers_.end()) | |
411 return; | |
412 IPC::Message* msg = new IPC::Message(message); | |
413 msg->set_routing_id(worker_route_id); | |
414 it->host->Send(msg); | |
415 } | |
416 | |
417 // static | |
418 void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread( | |
419 int worker_process_id, | |
420 int worker_route_id, | |
421 const std::string& message) { | |
422 WorkerDevToolsAgentHost* agent_host = AgentHosts::GetAgentHost(WorkerId( | |
423 worker_process_id, | |
424 worker_route_id)); | |
425 if (!agent_host) | |
426 return; | |
427 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(agent_host, | |
428 message); | |
429 } | |
430 | |
431 // static | |
432 void WorkerDevToolsManager::SaveAgentRuntimeStateOnUIThread( | |
433 int worker_process_id, | |
434 int worker_route_id, | |
435 const std::string& state) { | |
436 WorkerDevToolsAgentHost* agent_host = AgentHosts::GetAgentHost(WorkerId( | |
437 worker_process_id, | |
438 worker_route_id)); | |
439 if (!agent_host) | |
440 return; | |
441 DevToolsManagerImpl::GetInstance()->SaveAgentRuntimeState(agent_host, state); | |
442 } | |
443 | |
444 // static | |
445 void WorkerDevToolsManager::NotifyWorkerDestroyedOnIOThread( | |
446 int worker_process_id, | |
447 int worker_route_id) { | |
448 BrowserThread::PostTask( | |
449 BrowserThread::UI, FROM_HERE, | |
450 base::Bind( | |
451 &WorkerDevToolsManager::NotifyWorkerDestroyedOnUIThread, | |
452 worker_process_id, | |
453 worker_route_id)); | |
454 } | |
455 | |
456 // static | |
457 void WorkerDevToolsManager::NotifyWorkerDestroyedOnUIThread( | |
458 int worker_process_id, | |
459 int worker_route_id) { | |
460 WorkerDevToolsAgentHost* host = | |
461 AgentHosts::GetAgentHost(WorkerId(worker_process_id, worker_route_id)); | |
462 if (host) | |
463 host->WorkerDestroyed(); | |
464 } | |
465 | |
466 // static | |
467 void WorkerDevToolsManager::SendResumeToWorker(const WorkerId& id) { | |
468 if (WorkerProcessHost* process = FindWorkerProcess(id.first)) | |
469 process->Send(new DevToolsAgentMsg_ResumeWorkerContext(id.second)); | |
470 } | |
471 | |
472 } // namespace content | |
OLD | NEW |