• 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/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
GetForWorker(int worker_process_id,int worker_route_id)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 {
TerminatedInspectedWorkercontent::WorkerDevToolsManager::TerminatedInspectedWorker50   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:
WorkerDevToolsAgentHost(WorkerId worker_id)65   explicit WorkerDevToolsAgentHost(WorkerId worker_id)
66       : has_worker_id_(false) {
67     SetWorkerId(worker_id, false);
68   }
69 
SetWorkerId(WorkerId worker_id,bool reattach)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 
ResetWorkerId()89   void ResetWorkerId() {
90     g_agent_map.Get().erase(worker_id_);
91     has_worker_id_ = false;
92     Release();  //  Balanced in SetWorkerId.
93   }
94 
SaveAgentRuntimeState(const std::string & state)95   void SaveAgentRuntimeState(const std::string& state) {
96     state_ = state;
97   }
98 
ConnectionFailed()99   void ConnectionFailed() {
100     NotifyCloseListener();
101     // Object can be deleted here.
102   }
103 
104  private:
105   virtual ~WorkerDevToolsAgentHost();
106 
ConnectToWorker(int worker_process_id,int worker_route_id)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 
ForwardToWorkerDevToolsAgent(int worker_process_id,int worker_route_id,IPC::Message * message)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.
SendMessageToAgent(IPC::Message * message)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 
OnClientAttached()137   virtual void OnClientAttached() OVERRIDE {}
OnClientDetached()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:
WorkerReloaded(WorkerId old_id,WorkerId new_id)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 
WorkerDestroyed(WorkerId 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);
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 
RemovePendingWorkerData(WorkerId id)185   static void RemovePendingWorkerData(WorkerId id) {
186     BrowserThread::PostTask(
187         BrowserThread::IO, FROM_HERE,
188         base::Bind(&RemoveInspectedWorkerDataOnIOThread, id));
189   }
190 
191  private:
DetachedClientHosts()192   DetachedClientHosts() {}
~DetachedClientHosts()193   ~DetachedClientHosts() {}
194 
RemoveInspectedWorkerDataOnIOThread(WorkerId id)195   static void RemoveInspectedWorkerDataOnIOThread(WorkerId id) {
196     WorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(id);
197   }
198 };
199 
200 struct WorkerDevToolsManager::InspectedWorker {
InspectedWorkercontent::WorkerDevToolsManager::InspectedWorker201   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
GetInstance()214 WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() {
215   DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled());
216   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
217   return Singleton<WorkerDevToolsManager>::get();
218 }
219 
220 // static
GetDevToolsAgentHostForWorker(int worker_process_id,int worker_route_id)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 
WorkerDevToolsManager()232 WorkerDevToolsManager::WorkerDevToolsManager() {
233 }
234 
~WorkerDevToolsManager()235 WorkerDevToolsManager::~WorkerDevToolsManager() {
236 }
237 
WorkerCreated(WorkerProcessHost * worker,const WorkerProcessHost::WorkerInstance & instance)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 
WorkerDestroyed(WorkerProcessHost * worker,int worker_route_id)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 
WorkerContextStarted(WorkerProcessHost * process,int worker_route_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())
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 
RemoveInspectedWorkerData(const WorkerId & id)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
FindInspectedWorker(int host_id,int route_id)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 
FindWorkerProcess(int worker_process_id)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 
ConnectDevToolsAgentHostToWorker(int worker_process_id,int worker_route_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()));
343         return;
344       }
345     }
346   }
347   NotifyConnectionFailedOnIOThread(worker_process_id, worker_route_id);
348 }
349 
ForwardToDevToolsClient(int worker_process_id,int worker_route_id,const std::string & message)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 
SaveAgentRuntimeState(int worker_process_id,int worker_route_id,const std::string & state)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 
ForwardToWorkerDevToolsAgent(int worker_process_id,int worker_route_id,const IPC::Message & message)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
ForwardToDevToolsClientOnUIThread(int worker_process_id,int worker_route_id,const std::string & message)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
SaveAgentRuntimeStateOnUIThread(int worker_process_id,int worker_route_id,const std::string & state)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
NotifyConnectionFailedOnIOThread(int worker_process_id,int worker_route_id)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
NotifyConnectionFailedOnUIThread(int worker_process_id,int worker_route_id)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
SendResumeToWorker(const WorkerId & id)442 void WorkerDevToolsManager::SendResumeToWorker(const WorkerId& id) {
443   if (WorkerProcessHost* process = FindWorkerProcess(id.first))
444     process->Send(new DevToolsAgentMsg_ResumeWorkerContext(id.second));
445 }
446 
~WorkerDevToolsAgentHost()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
454