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