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::ServiceWorkerIdentifier::ServiceWorkerIdentifier(
38 const ServiceWorkerContextCore
* const service_worker_context
,
39 int64 service_worker_version_id
)
40 : service_worker_context_(service_worker_context
),
41 service_worker_version_id_(service_worker_version_id
) {
44 EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier::ServiceWorkerIdentifier(
45 const ServiceWorkerIdentifier
& other
)
46 : service_worker_context_(other
.service_worker_context_
),
47 service_worker_version_id_(other
.service_worker_version_id_
) {
50 bool EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier::Matches(
51 const ServiceWorkerIdentifier
& other
) const {
52 return service_worker_context_
== other
.service_worker_context_
&&
53 service_worker_version_id_
== other
.service_worker_version_id_
;
56 EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo(
57 const SharedWorkerInstance
& instance
)
58 : shared_worker_instance_(new SharedWorkerInstance(instance
)),
59 state_(WORKER_UNINSPECTED
),
63 EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo(
64 const ServiceWorkerIdentifier
& service_worker_id
)
65 : service_worker_id_(new ServiceWorkerIdentifier(service_worker_id
)),
66 state_(WORKER_UNINSPECTED
),
70 bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches(
71 const SharedWorkerInstance
& other
) {
72 if (!shared_worker_instance_
)
74 return shared_worker_instance_
->Matches(other
);
77 bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches(
78 const ServiceWorkerIdentifier
& other
) {
79 if (!service_worker_id_
)
81 return service_worker_id_
->Matches(other
);
84 EmbeddedWorkerDevToolsManager::WorkerInfo::~WorkerInfo() {
87 class EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsAgentHost
88 : public IPCDevToolsAgentHost
,
89 public IPC::Listener
{
91 explicit EmbeddedWorkerDevToolsAgentHost(WorkerId worker_id
)
92 : worker_id_(worker_id
), worker_attached_(false) {
96 // DevToolsAgentHost override.
97 virtual bool IsWorker() const OVERRIDE
{ return true; }
99 // IPCDevToolsAgentHost implementation.
100 virtual void SendMessageToAgent(IPC::Message
* message
) OVERRIDE
{
101 if (worker_attached_
)
102 SendMessageToWorker(worker_id_
, message
);
106 virtual void Attach() OVERRIDE
{
108 IPCDevToolsAgentHost::Attach();
110 virtual void OnClientAttached() OVERRIDE
{}
111 virtual void OnClientDetached() OVERRIDE
{ DetachFromWorker(); }
113 // IPC::Listener implementation.
114 virtual bool OnMessageReceived(const IPC::Message
& msg
) OVERRIDE
{
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
117 IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerDevToolsAgentHost
, msg
)
118 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend
,
119 OnDispatchOnInspectorFrontend
)
120 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState
,
121 OnSaveAgentRuntimeState
)
122 IPC_MESSAGE_UNHANDLED(handled
= false)
123 IPC_END_MESSAGE_MAP()
127 void ReattachToWorker(WorkerId worker_id
) {
128 CHECK(!worker_attached_
);
129 worker_id_
= worker_id
;
136 void DetachFromWorker() {
137 if (!worker_attached_
)
139 worker_attached_
= false;
140 if (RenderProcessHost
* host
= RenderProcessHost::FromID(worker_id_
.first
))
141 host
->RemoveRoute(worker_id_
.second
);
145 WorkerId
worker_id() const { return worker_id_
; }
148 virtual ~EmbeddedWorkerDevToolsAgentHost() {
149 CHECK(!worker_attached_
);
150 EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(
154 void OnDispatchOnInspectorFrontend(const std::string
& message
) {
155 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(this,
159 void OnSaveAgentRuntimeState(const std::string
& state
) { state_
= state
; }
161 void AttachToWorker() {
162 if (worker_attached_
)
164 worker_attached_
= true;
166 if (RenderProcessHost
* host
= RenderProcessHost::FromID(worker_id_
.first
))
167 host
->AddRoute(worker_id_
.second
, this);
171 bool worker_attached_
;
173 DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerDevToolsAgentHost
);
177 EmbeddedWorkerDevToolsManager
* EmbeddedWorkerDevToolsManager::GetInstance() {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
179 return Singleton
<EmbeddedWorkerDevToolsManager
>::get();
182 DevToolsAgentHost
* EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForWorker(
183 int worker_process_id
,
184 int worker_route_id
) {
185 WorkerId
id(worker_process_id
, worker_route_id
);
187 WorkerInfoMap::iterator it
= workers_
.find(id
);
188 if (it
== workers_
.end())
191 WorkerInfo
* info
= it
->second
;
192 if (info
->state() != WORKER_UNINSPECTED
&&
193 info
->state() != WORKER_PAUSED_FOR_DEBUG_ON_START
) {
194 return info
->agent_host();
197 EmbeddedWorkerDevToolsAgentHost
* agent_host
=
198 new EmbeddedWorkerDevToolsAgentHost(id
);
199 info
->set_agent_host(agent_host
);
200 info
->set_state(WORKER_INSPECTED
);
205 EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForServiceWorker(
206 const ServiceWorkerIdentifier
& service_worker_id
) {
207 WorkerInfoMap::iterator it
= FindExistingServiceWorkerInfo(service_worker_id
);
208 if (it
== workers_
.end())
210 return GetDevToolsAgentHostForWorker(it
->first
.first
, it
->first
.second
);
213 EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsManager()
214 : debug_service_worker_on_start_(false) {
217 EmbeddedWorkerDevToolsManager::~EmbeddedWorkerDevToolsManager() {
220 bool EmbeddedWorkerDevToolsManager::SharedWorkerCreated(
221 int worker_process_id
,
223 const SharedWorkerInstance
& instance
) {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
225 const WorkerId
id(worker_process_id
, worker_route_id
);
226 WorkerInfoMap::iterator it
= FindExistingSharedWorkerInfo(instance
);
227 if (it
== workers_
.end()) {
228 scoped_ptr
<WorkerInfo
> info(new WorkerInfo(instance
));
229 workers_
.set(id
, info
.Pass());
232 MoveToPausedState(id
, it
);
236 bool EmbeddedWorkerDevToolsManager::ServiceWorkerCreated(
237 int worker_process_id
,
239 const ServiceWorkerIdentifier
& service_worker_id
) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
241 const WorkerId
id(worker_process_id
, worker_route_id
);
242 WorkerInfoMap::iterator it
= FindExistingServiceWorkerInfo(service_worker_id
);
243 if (it
== workers_
.end()) {
244 scoped_ptr
<WorkerInfo
> info(new WorkerInfo(service_worker_id
));
245 if (debug_service_worker_on_start_
)
246 info
->set_state(WORKER_PAUSED_FOR_DEBUG_ON_START
);
247 workers_
.set(id
, info
.Pass());
248 return debug_service_worker_on_start_
;
250 MoveToPausedState(id
, it
);
254 void EmbeddedWorkerDevToolsManager::WorkerDestroyed(int worker_process_id
,
255 int worker_route_id
) {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
257 const WorkerId
id(worker_process_id
, worker_route_id
);
258 WorkerInfoMap::iterator it
= workers_
.find(id
);
259 DCHECK(it
!= workers_
.end());
260 WorkerInfo
* info
= it
->second
;
261 switch (info
->state()) {
262 case WORKER_UNINSPECTED
:
263 case WORKER_PAUSED_FOR_DEBUG_ON_START
:
266 case WORKER_INSPECTED
: {
267 EmbeddedWorkerDevToolsAgentHost
* agent_host
= info
->agent_host();
268 info
->set_state(WORKER_TERMINATED
);
269 if (!agent_host
->IsAttached()) {
270 agent_host
->DetachFromWorker();
273 // Client host is debugging this worker agent host.
274 std::string notification
=
275 DevToolsProtocol::CreateNotification(
276 devtools::Worker::disconnectedFromWorker::kName
, NULL
)
278 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
279 agent_host
, notification
);
280 agent_host
->DetachFromWorker();
283 case WORKER_TERMINATED
:
286 case WORKER_PAUSED_FOR_REATTACH
: {
287 scoped_ptr
<WorkerInfo
> worker_info
= workers_
.take_and_erase(it
);
288 worker_info
->set_state(WORKER_TERMINATED
);
289 const WorkerId old_id
= worker_info
->agent_host()->worker_id();
290 workers_
.set(old_id
, worker_info
.Pass());
296 void EmbeddedWorkerDevToolsManager::WorkerContextStarted(int worker_process_id
,
297 int worker_route_id
) {
298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
299 const WorkerId
id(worker_process_id
, worker_route_id
);
300 WorkerInfoMap::iterator it
= workers_
.find(id
);
301 DCHECK(it
!= workers_
.end());
302 WorkerInfo
* info
= it
->second
;
303 if (info
->state() == WORKER_PAUSED_FOR_DEBUG_ON_START
) {
304 RenderProcessHost
* rph
= RenderProcessHost::FromID(worker_process_id
);
305 scoped_refptr
<DevToolsAgentHost
> agent_host(
306 GetDevToolsAgentHostForWorker(worker_process_id
, worker_route_id
));
307 DevToolsManagerImpl::GetInstance()->Inspect(rph
->GetBrowserContext(),
309 } else if (info
->state() == WORKER_PAUSED_FOR_REATTACH
) {
310 info
->agent_host()->ReattachToWorker(id
);
311 info
->set_state(WORKER_INSPECTED
);
315 void EmbeddedWorkerDevToolsManager::RemoveInspectedWorkerData(
316 EmbeddedWorkerDevToolsAgentHost
* agent_host
) {
317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
318 const WorkerId
id(agent_host
->worker_id());
319 scoped_ptr
<WorkerInfo
> worker_info
= workers_
.take_and_erase(id
);
321 DCHECK_EQ(worker_info
->agent_host(), agent_host
);
322 if (worker_info
->state() == WORKER_TERMINATED
)
324 DCHECK_EQ(worker_info
->state(), WORKER_INSPECTED
);
325 worker_info
->set_agent_host(NULL
);
326 worker_info
->set_state(WORKER_UNINSPECTED
);
327 workers_
.set(id
, worker_info
.Pass());
330 for (WorkerInfoMap::iterator it
= workers_
.begin(); it
!= workers_
.end();
332 if (it
->second
->agent_host() == agent_host
) {
333 DCHECK_EQ(WORKER_PAUSED_FOR_REATTACH
, it
->second
->state());
336 new DevToolsAgentMsg_ResumeWorkerContext(it
->first
.second
));
337 it
->second
->set_agent_host(NULL
);
338 it
->second
->set_state(WORKER_UNINSPECTED
);
344 EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator
345 EmbeddedWorkerDevToolsManager::FindExistingSharedWorkerInfo(
346 const SharedWorkerInstance
& instance
) {
347 WorkerInfoMap::iterator it
= workers_
.begin();
348 for (; it
!= workers_
.end(); ++it
) {
349 if (it
->second
->Matches(instance
))
355 EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator
356 EmbeddedWorkerDevToolsManager::FindExistingServiceWorkerInfo(
357 const ServiceWorkerIdentifier
& service_worker_id
) {
358 WorkerInfoMap::iterator it
= workers_
.begin();
359 for (; it
!= workers_
.end(); ++it
) {
360 if (it
->second
->Matches(service_worker_id
))
366 void EmbeddedWorkerDevToolsManager::MoveToPausedState(
368 const WorkerInfoMap::iterator
& it
) {
369 DCHECK_EQ(WORKER_TERMINATED
, it
->second
->state());
370 scoped_ptr
<WorkerInfo
> info
= workers_
.take_and_erase(it
);
371 info
->set_state(WORKER_PAUSED_FOR_REATTACH
);
372 workers_
.set(id
, info
.Pass());
375 void EmbeddedWorkerDevToolsManager::ResetForTesting() {
379 } // namespace content