Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / devtools / embedded_worker_devtools_manager.cc
1 // Copyright 2014 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/embedded_worker_devtools_manager.h"
6
7 #include "content/browser/devtools/devtools_manager_impl.h"
8 #include "content/browser/devtools/devtools_protocol.h"
9 #include "content/browser/devtools/devtools_protocol_constants.h"
10 #include "content/browser/devtools/ipc_devtools_agent_host.h"
11 #include "content/browser/shared_worker/shared_worker_instance.h"
12 #include "content/common/devtools_messages.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/worker_service.h"
16 #include "ipc/ipc_listener.h"
17
18 namespace content {
19
20 namespace {
21
22 bool SendMessageToWorker(
23     const EmbeddedWorkerDevToolsManager::WorkerId& worker_id,
24     IPC::Message* message) {
25   RenderProcessHost* host = RenderProcessHost::FromID(worker_id.first);
26   if (!host) {
27     delete message;
28     return false;
29   }
30   message->set_routing_id(worker_id.second);
31   host->Send(message);
32   return true;
33 }
34
35 }  // namespace
36
37 EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo(
38     const SharedWorkerInstance& instance)
39     : shared_worker_instance_(new SharedWorkerInstance(instance)),
40       state_(WORKER_UNINSPECTED),
41       agent_host_(NULL) {
42 }
43
44 EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo(
45     const base::FilePath& storage_partition_path,
46     const GURL& service_worker_scope)
47     : storage_partition_path_(new base::FilePath(storage_partition_path)),
48       service_worker_scope_(new GURL(service_worker_scope)),
49       state_(WORKER_UNINSPECTED),
50       agent_host_(NULL) {
51 }
52
53 bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches(
54     const SharedWorkerInstance& other) {
55   if (!shared_worker_instance_)
56     return false;
57   return shared_worker_instance_->Matches(other);
58 }
59
60 bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches(
61     const base::FilePath& other_storage_partition_path,
62     const GURL& other_service_worker_scope) {
63   if (!storage_partition_path_ || !service_worker_scope_)
64     return false;
65   return *storage_partition_path_ == other_storage_partition_path &&
66          *service_worker_scope_ == other_service_worker_scope;
67 }
68
69 EmbeddedWorkerDevToolsManager::WorkerInfo::~WorkerInfo() {
70 }
71
72 class EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsAgentHost
73     : public IPCDevToolsAgentHost,
74       public IPC::Listener {
75  public:
76   explicit EmbeddedWorkerDevToolsAgentHost(WorkerId worker_id)
77       : worker_id_(worker_id), worker_attached_(true) {
78     AddRef();
79     if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
80       host->AddRoute(worker_id_.second, this);
81   }
82
83   // IPCDevToolsAgentHost implementation.
84   virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE {
85     if (worker_attached_)
86       SendMessageToWorker(worker_id_, message);
87     else
88       delete message;
89   }
90   virtual void OnClientAttached() OVERRIDE {}
91   virtual void OnClientDetached() OVERRIDE {}
92
93   // IPC::Listener implementation.
94   virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE {
95     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
96     bool handled = true;
97     IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerDevToolsAgentHost, msg)
98     IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
99                         OnDispatchOnInspectorFrontend)
100     IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
101                         OnSaveAgentRuntimeState)
102     IPC_MESSAGE_UNHANDLED(handled = false)
103     IPC_END_MESSAGE_MAP()
104     return handled;
105   }
106
107   void ReattachToWorker(WorkerId worker_id) {
108     CHECK(!worker_attached_);
109     worker_attached_ = true;
110     worker_id_ = worker_id;
111     AddRef();
112     if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
113       host->AddRoute(worker_id_.second, this);
114     Reattach(state_);
115   }
116
117   void DetachFromWorker() {
118     CHECK(worker_attached_);
119     worker_attached_ = false;
120     if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
121       host->RemoveRoute(worker_id_.second);
122     Release();
123   }
124
125   WorkerId worker_id() const { return worker_id_; }
126
127  private:
128   virtual ~EmbeddedWorkerDevToolsAgentHost() {
129     CHECK(!worker_attached_);
130     EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(
131         this);
132   }
133
134   void OnDispatchOnInspectorFrontend(const std::string& message) {
135     DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(this,
136                                                                     message);
137   }
138
139   void OnSaveAgentRuntimeState(const std::string& state) { state_ = state; }
140
141   WorkerId worker_id_;
142   bool worker_attached_;
143   std::string state_;
144   DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerDevToolsAgentHost);
145 };
146
147 // static
148 EmbeddedWorkerDevToolsManager* EmbeddedWorkerDevToolsManager::GetInstance() {
149   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
150   return Singleton<EmbeddedWorkerDevToolsManager>::get();
151 }
152
153 DevToolsAgentHost* EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForWorker(
154     int worker_process_id,
155     int worker_route_id) {
156   WorkerId id(worker_process_id, worker_route_id);
157
158   WorkerInfoMap::iterator it = workers_.find(id);
159   if (it == workers_.end())
160     return NULL;
161
162   WorkerInfo* info = it->second;
163   if (info->state() != WORKER_UNINSPECTED)
164     return info->agent_host();
165
166   EmbeddedWorkerDevToolsAgentHost* agent_host =
167       new EmbeddedWorkerDevToolsAgentHost(id);
168   info->set_agent_host(agent_host);
169   info->set_state(WORKER_INSPECTED);
170   return agent_host;
171 }
172
173 DevToolsAgentHost*
174 EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForServiceWorker(
175     const base::FilePath& storage_partition_path,
176     const GURL& service_worker_scope) {
177   WorkerInfoMap::iterator it = FindExistingServiceWorkerInfo(
178       storage_partition_path, service_worker_scope);
179   if (it == workers_.end())
180     return NULL;
181   return GetDevToolsAgentHostForWorker(it->first.first, it->first.second);
182 }
183
184 EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsManager() {
185 }
186
187 EmbeddedWorkerDevToolsManager::~EmbeddedWorkerDevToolsManager() {
188 }
189
190 bool EmbeddedWorkerDevToolsManager::SharedWorkerCreated(
191     int worker_process_id,
192     int worker_route_id,
193     const SharedWorkerInstance& instance) {
194   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195   const WorkerId id(worker_process_id, worker_route_id);
196   WorkerInfoMap::iterator it = FindExistingSharedWorkerInfo(instance);
197   if (it == workers_.end()) {
198     scoped_ptr<WorkerInfo> info(new WorkerInfo(instance));
199     workers_.set(id, info.Pass());
200     return false;
201   }
202   MoveToPausedState(id, it);
203   return true;
204 }
205
206 bool EmbeddedWorkerDevToolsManager::ServiceWorkerCreated(
207     int worker_process_id,
208     int worker_route_id,
209     const base::FilePath& storage_partition_path,
210     const GURL& service_worker_scope) {
211   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
212   const WorkerId id(worker_process_id, worker_route_id);
213   WorkerInfoMap::iterator it = FindExistingServiceWorkerInfo(
214       storage_partition_path, service_worker_scope);
215   if (it == workers_.end()) {
216     scoped_ptr<WorkerInfo> info(
217         new WorkerInfo(storage_partition_path, service_worker_scope));
218     workers_.set(id, info.Pass());
219     return false;
220   }
221   MoveToPausedState(id, it);
222   return true;
223 }
224
225 void EmbeddedWorkerDevToolsManager::WorkerDestroyed(int worker_process_id,
226                                                     int worker_route_id) {
227   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
228   const WorkerId id(worker_process_id, worker_route_id);
229   WorkerInfoMap::iterator it = workers_.find(id);
230   DCHECK(it != workers_.end());
231   WorkerInfo* info = it->second;
232   switch (info->state()) {
233     case WORKER_UNINSPECTED:
234       workers_.erase(it);
235       break;
236     case WORKER_INSPECTED: {
237       EmbeddedWorkerDevToolsAgentHost* agent_host = info->agent_host();
238       if (!agent_host->IsAttached()) {
239         scoped_ptr<WorkerInfo> worker_info = workers_.take_and_erase(it);
240         agent_host->DetachFromWorker();
241         return;
242       }
243       info->set_state(WORKER_TERMINATED);
244       // Client host is debugging this worker agent host.
245       std::string notification =
246           DevToolsProtocol::CreateNotification(
247               devtools::Worker::disconnectedFromWorker::kName, NULL)
248               ->Serialize();
249       DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
250           agent_host, notification);
251       agent_host->DetachFromWorker();
252       break;
253     }
254     case WORKER_TERMINATED:
255       NOTREACHED();
256       break;
257     case WORKER_PAUSED: {
258       scoped_ptr<WorkerInfo> worker_info = workers_.take_and_erase(it);
259       worker_info->set_state(WORKER_TERMINATED);
260       const WorkerId old_id = worker_info->agent_host()->worker_id();
261       workers_.set(old_id, worker_info.Pass());
262       break;
263     }
264   }
265 }
266
267 void EmbeddedWorkerDevToolsManager::WorkerContextStarted(int worker_process_id,
268                                                          int worker_route_id) {
269   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
270   const WorkerId id(worker_process_id, worker_route_id);
271   WorkerInfoMap::iterator it = workers_.find(id);
272   DCHECK(it != workers_.end());
273   WorkerInfo* info = it->second;
274   if (info->state() != WORKER_PAUSED)
275     return;
276   info->agent_host()->ReattachToWorker(id);
277   info->set_state(WORKER_INSPECTED);
278 }
279
280 void EmbeddedWorkerDevToolsManager::RemoveInspectedWorkerData(
281     EmbeddedWorkerDevToolsAgentHost* agent_host) {
282   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283   const WorkerId id(agent_host->worker_id());
284   scoped_ptr<WorkerInfo> worker_info = workers_.take_and_erase(id);
285   if (worker_info) {
286     DCHECK_EQ(WORKER_TERMINATED, worker_info->state());
287     return;
288   }
289   for (WorkerInfoMap::iterator it = workers_.begin(); it != workers_.end();
290        ++it) {
291     if (it->second->agent_host() == agent_host) {
292       DCHECK_EQ(WORKER_PAUSED, it->second->state());
293       SendMessageToWorker(
294           it->first,
295           new DevToolsAgentMsg_ResumeWorkerContext(it->first.second));
296       it->second->set_agent_host(NULL);
297       it->second->set_state(WORKER_UNINSPECTED);
298       return;
299     }
300   }
301 }
302
303 EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator
304 EmbeddedWorkerDevToolsManager::FindExistingSharedWorkerInfo(
305     const SharedWorkerInstance& instance) {
306   WorkerInfoMap::iterator it = workers_.begin();
307   for (; it != workers_.end(); ++it) {
308     if (it->second->Matches(instance))
309       break;
310   }
311   return it;
312 }
313
314 EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator
315 EmbeddedWorkerDevToolsManager::FindExistingServiceWorkerInfo(
316     const base::FilePath& storage_partition_path,
317     const GURL& service_worker_scope) {
318   WorkerInfoMap::iterator it = workers_.begin();
319   for (; it != workers_.end(); ++it) {
320     if (it->second->Matches(storage_partition_path, service_worker_scope))
321       break;
322   }
323   return it;
324 }
325
326 void EmbeddedWorkerDevToolsManager::MoveToPausedState(
327     const WorkerId& id,
328     const WorkerInfoMap::iterator& it) {
329   DCHECK_EQ(WORKER_TERMINATED, it->second->state());
330   scoped_ptr<WorkerInfo> info = workers_.take_and_erase(it);
331   info->set_state(WORKER_PAUSED);
332   workers_.set(id, info.Pass());
333 }
334
335 void EmbeddedWorkerDevToolsManager::ResetForTesting() {
336   workers_.clear();
337 }
338
339 }  // namespace content