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.
5 #include "content/browser/devtools/embedded_worker_devtools_manager.h"
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"
22 bool SendMessageToWorker(
23 const EmbeddedWorkerDevToolsManager::WorkerId
& worker_id
,
24 IPC::Message
* message
) {
25 RenderProcessHost
* host
= RenderProcessHost::FromID(worker_id
.first
);
30 message
->set_routing_id(worker_id
.second
);
37 EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo(
38 const SharedWorkerInstance
& instance
)
39 : shared_worker_instance_(new SharedWorkerInstance(instance
)),
40 state_(WORKER_UNINSPECTED
),
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
),
53 bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches(
54 const SharedWorkerInstance
& other
) {
55 if (!shared_worker_instance_
)
57 return shared_worker_instance_
->Matches(other
);
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_
)
65 return *storage_partition_path_
== other_storage_partition_path
&&
66 *service_worker_scope_
== other_service_worker_scope
;
69 EmbeddedWorkerDevToolsManager::WorkerInfo::~WorkerInfo() {
72 class EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsAgentHost
73 : public IPCDevToolsAgentHost
,
74 public IPC::Listener
{
76 explicit EmbeddedWorkerDevToolsAgentHost(WorkerId worker_id
)
77 : worker_id_(worker_id
), worker_attached_(true) {
79 if (RenderProcessHost
* host
= RenderProcessHost::FromID(worker_id_
.first
))
80 host
->AddRoute(worker_id_
.second
, this);
83 // IPCDevToolsAgentHost implementation.
84 virtual void SendMessageToAgent(IPC::Message
* message
) OVERRIDE
{
86 SendMessageToWorker(worker_id_
, message
);
90 virtual void OnClientAttached() OVERRIDE
{}
91 virtual void OnClientDetached() OVERRIDE
{}
93 // IPC::Listener implementation.
94 virtual bool OnMessageReceived(const IPC::Message
& msg
) OVERRIDE
{
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
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()
107 void ReattachToWorker(WorkerId worker_id
) {
108 CHECK(!worker_attached_
);
109 worker_attached_
= true;
110 worker_id_
= worker_id
;
112 if (RenderProcessHost
* host
= RenderProcessHost::FromID(worker_id_
.first
))
113 host
->AddRoute(worker_id_
.second
, this);
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
);
125 WorkerId
worker_id() const { return worker_id_
; }
128 virtual ~EmbeddedWorkerDevToolsAgentHost() {
129 CHECK(!worker_attached_
);
130 EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(
134 void OnDispatchOnInspectorFrontend(const std::string
& message
) {
135 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(this,
139 void OnSaveAgentRuntimeState(const std::string
& state
) { state_
= state
; }
142 bool worker_attached_
;
144 DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerDevToolsAgentHost
);
148 EmbeddedWorkerDevToolsManager
* EmbeddedWorkerDevToolsManager::GetInstance() {
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
150 return Singleton
<EmbeddedWorkerDevToolsManager
>::get();
153 DevToolsAgentHost
* EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForWorker(
154 int worker_process_id
,
155 int worker_route_id
) {
156 WorkerId
id(worker_process_id
, worker_route_id
);
158 WorkerInfoMap::iterator it
= workers_
.find(id
);
159 if (it
== workers_
.end())
162 WorkerInfo
* info
= it
->second
;
163 if (info
->state() != WORKER_UNINSPECTED
)
164 return info
->agent_host();
166 EmbeddedWorkerDevToolsAgentHost
* agent_host
=
167 new EmbeddedWorkerDevToolsAgentHost(id
);
168 info
->set_agent_host(agent_host
);
169 info
->set_state(WORKER_INSPECTED
);
173 EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsManager() {
176 EmbeddedWorkerDevToolsManager::~EmbeddedWorkerDevToolsManager() {
179 bool EmbeddedWorkerDevToolsManager::SharedWorkerCreated(
180 int worker_process_id
,
182 const SharedWorkerInstance
& instance
) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
184 const WorkerId
id(worker_process_id
, worker_route_id
);
185 WorkerInfoMap::iterator it
= FindExistingSharedWorkerInfo(instance
);
186 if (it
== workers_
.end()) {
187 scoped_ptr
<WorkerInfo
> info(new WorkerInfo(instance
));
188 workers_
.set(id
, info
.Pass());
191 MoveToPausedState(id
, it
);
195 bool EmbeddedWorkerDevToolsManager::ServiceWorkerCreated(
196 int worker_process_id
,
198 const base::FilePath
& storage_partition_path
,
199 const GURL
& service_worker_scope
) {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
201 const WorkerId
id(worker_process_id
, worker_route_id
);
202 WorkerInfoMap::iterator it
= FindExistingServiceWorkerInfo(
203 storage_partition_path
, service_worker_scope
);
204 if (it
== workers_
.end()) {
205 scoped_ptr
<WorkerInfo
> info(
206 new WorkerInfo(storage_partition_path
, service_worker_scope
));
207 workers_
.set(id
, info
.Pass());
210 MoveToPausedState(id
, it
);
214 void EmbeddedWorkerDevToolsManager::WorkerDestroyed(int worker_process_id
,
215 int worker_route_id
) {
216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
217 const WorkerId
id(worker_process_id
, worker_route_id
);
218 WorkerInfoMap::iterator it
= workers_
.find(id
);
219 DCHECK(it
!= workers_
.end());
220 WorkerInfo
* info
= it
->second
;
221 switch (info
->state()) {
222 case WORKER_UNINSPECTED
:
225 case WORKER_INSPECTED
: {
226 EmbeddedWorkerDevToolsAgentHost
* agent_host
= info
->agent_host();
227 if (!agent_host
->IsAttached()) {
228 scoped_ptr
<WorkerInfo
> worker_info
= workers_
.take_and_erase(it
);
229 agent_host
->DetachFromWorker();
232 info
->set_state(WORKER_TERMINATED
);
233 // Client host is debugging this worker agent host.
234 std::string notification
=
235 DevToolsProtocol::CreateNotification(
236 devtools::Worker::disconnectedFromWorker::kName
, NULL
)
238 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
239 agent_host
, notification
);
240 agent_host
->DetachFromWorker();
243 case WORKER_TERMINATED
:
246 case WORKER_PAUSED
: {
247 scoped_ptr
<WorkerInfo
> worker_info
= workers_
.take_and_erase(it
);
248 worker_info
->set_state(WORKER_TERMINATED
);
249 const WorkerId old_id
= worker_info
->agent_host()->worker_id();
250 workers_
.set(old_id
, worker_info
.Pass());
256 void EmbeddedWorkerDevToolsManager::WorkerContextStarted(int worker_process_id
,
257 int worker_route_id
) {
258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
259 const WorkerId
id(worker_process_id
, worker_route_id
);
260 WorkerInfoMap::iterator it
= workers_
.find(id
);
261 DCHECK(it
!= workers_
.end());
262 WorkerInfo
* info
= it
->second
;
263 if (info
->state() != WORKER_PAUSED
)
265 info
->agent_host()->ReattachToWorker(id
);
266 info
->set_state(WORKER_INSPECTED
);
269 void EmbeddedWorkerDevToolsManager::RemoveInspectedWorkerData(
270 EmbeddedWorkerDevToolsAgentHost
* agent_host
) {
271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
272 const WorkerId
id(agent_host
->worker_id());
273 scoped_ptr
<WorkerInfo
> worker_info
= workers_
.take_and_erase(id
);
275 DCHECK_EQ(WORKER_TERMINATED
, worker_info
->state());
278 for (WorkerInfoMap::iterator it
= workers_
.begin(); it
!= workers_
.end();
280 if (it
->second
->agent_host() == agent_host
) {
281 DCHECK_EQ(WORKER_PAUSED
, it
->second
->state());
284 new DevToolsAgentMsg_ResumeWorkerContext(it
->first
.second
));
285 it
->second
->set_agent_host(NULL
);
286 it
->second
->set_state(WORKER_UNINSPECTED
);
292 EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator
293 EmbeddedWorkerDevToolsManager::FindExistingSharedWorkerInfo(
294 const SharedWorkerInstance
& instance
) {
295 WorkerInfoMap::iterator it
= workers_
.begin();
296 for (; it
!= workers_
.end(); ++it
) {
297 if (it
->second
->Matches(instance
))
303 EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator
304 EmbeddedWorkerDevToolsManager::FindExistingServiceWorkerInfo(
305 const base::FilePath
& storage_partition_path
,
306 const GURL
& service_worker_scope
) {
307 WorkerInfoMap::iterator it
= workers_
.begin();
308 for (; it
!= workers_
.end(); ++it
) {
309 if (it
->second
->Matches(storage_partition_path
, service_worker_scope
))
315 void EmbeddedWorkerDevToolsManager::MoveToPausedState(
317 const WorkerInfoMap::iterator
& it
) {
318 DCHECK_EQ(WORKER_TERMINATED
, it
->second
->state());
319 scoped_ptr
<WorkerInfo
> info
= workers_
.take_and_erase(it
);
320 info
->set_state(WORKER_PAUSED
);
321 workers_
.set(id
, info
.Pass());
324 void EmbeddedWorkerDevToolsManager::ResetForTesting() {
328 } // namespace content