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_agent_host.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "content/browser/devtools/devtools_protocol.h"
9#include "content/browser/devtools/devtools_protocol_constants.h"
10#include "content/browser/service_worker/service_worker_context_core.h"
11#include "content/browser/service_worker/service_worker_version.h"
12#include "content/browser/shared_worker/shared_worker_service_impl.h"
13#include "content/common/devtools_messages.h"
14#include "content/public/browser/browser_thread.h"
15#include "content/public/browser/render_process_host.h"
16
17namespace content {
18
19namespace {
20
21void TerminateSharedWorkerOnIO(
22    EmbeddedWorkerDevToolsAgentHost::WorkerId worker_id) {
23  SharedWorkerServiceImpl::GetInstance()->TerminateWorker(
24      worker_id.first, worker_id.second);
25}
26
27void StatusNoOp(ServiceWorkerStatusCode status) {
28}
29
30void TerminateServiceWorkerOnIO(
31    base::WeakPtr<ServiceWorkerContextCore> context_weak,
32    int64 version_id) {
33  if (ServiceWorkerContextCore* context = context_weak.get()) {
34    if (ServiceWorkerVersion* version = context->GetLiveVersion(version_id))
35      version->StopWorker(base::Bind(&StatusNoOp));
36  }
37}
38
39}
40
41EmbeddedWorkerDevToolsAgentHost::EmbeddedWorkerDevToolsAgentHost(
42    WorkerId worker_id,
43    const SharedWorkerInstance& shared_worker)
44    : shared_worker_(new SharedWorkerInstance(shared_worker)),
45      state_(WORKER_UNINSPECTED),
46      worker_id_(worker_id) {
47  WorkerCreated();
48}
49
50EmbeddedWorkerDevToolsAgentHost::EmbeddedWorkerDevToolsAgentHost(
51    WorkerId worker_id,
52    const ServiceWorkerIdentifier& service_worker,
53    bool debug_service_worker_on_start)
54    : service_worker_(new ServiceWorkerIdentifier(service_worker)),
55      state_(WORKER_UNINSPECTED),
56      worker_id_(worker_id) {
57  if (debug_service_worker_on_start)
58    state_ = WORKER_PAUSED_FOR_DEBUG_ON_START;
59  WorkerCreated();
60}
61
62bool EmbeddedWorkerDevToolsAgentHost::IsWorker() const {
63  return true;
64}
65
66DevToolsAgentHost::Type EmbeddedWorkerDevToolsAgentHost::GetType() {
67  return shared_worker_ ? TYPE_SHARED_WORKER : TYPE_SERVICE_WORKER;
68}
69
70std::string EmbeddedWorkerDevToolsAgentHost::GetTitle() {
71  if (shared_worker_ && shared_worker_->name().length())
72    return base::UTF16ToUTF8(shared_worker_->name());
73  if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) {
74    return base::StringPrintf("Worker pid:%d",
75                              base::GetProcId(host->GetHandle()));
76  }
77  return "";
78}
79
80GURL EmbeddedWorkerDevToolsAgentHost::GetURL() {
81  if (shared_worker_)
82    return shared_worker_->url();
83  if (service_worker_)
84    return service_worker_->url();
85  return GURL();
86}
87
88bool EmbeddedWorkerDevToolsAgentHost::Activate() {
89  return false;
90}
91
92bool EmbeddedWorkerDevToolsAgentHost::Close() {
93  if (shared_worker_) {
94    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
95        base::Bind(&TerminateSharedWorkerOnIO, worker_id_));
96    return true;
97  }
98  if (service_worker_) {
99    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
100        base::Bind(&TerminateServiceWorkerOnIO,
101                   service_worker_->context_weak(),
102                   service_worker_->version_id()));
103    return true;
104  }
105  return false;
106}
107
108void EmbeddedWorkerDevToolsAgentHost::SendMessageToAgent(
109    IPC::Message* message_raw) {
110  scoped_ptr<IPC::Message> message(message_raw);
111  if (state_ != WORKER_INSPECTED)
112    return;
113  if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) {
114    message->set_routing_id(worker_id_.second);
115    host->Send(message.release());
116  }
117}
118
119void EmbeddedWorkerDevToolsAgentHost::Attach() {
120  if (state_ != WORKER_INSPECTED) {
121    state_ = WORKER_INSPECTED;
122    AttachToWorker();
123  }
124  IPCDevToolsAgentHost::Attach();
125}
126
127void EmbeddedWorkerDevToolsAgentHost::OnClientDetached() {
128  if (state_ == WORKER_INSPECTED) {
129    state_ = WORKER_UNINSPECTED;
130    DetachFromWorker();
131  } else if (state_ == WORKER_PAUSED_FOR_REATTACH) {
132    state_ = WORKER_UNINSPECTED;
133  }
134}
135
136bool EmbeddedWorkerDevToolsAgentHost::OnMessageReceived(
137    const IPC::Message& msg) {
138  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
139  bool handled = true;
140  IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerDevToolsAgentHost, msg)
141  IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
142                      OnDispatchOnInspectorFrontend)
143  IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
144                      OnSaveAgentRuntimeState)
145  IPC_MESSAGE_UNHANDLED(handled = false)
146  IPC_END_MESSAGE_MAP()
147  return handled;
148}
149
150void EmbeddedWorkerDevToolsAgentHost::WorkerReadyForInspection() {
151  if (state_ == WORKER_PAUSED_FOR_DEBUG_ON_START) {
152    RenderProcessHost* rph = RenderProcessHost::FromID(worker_id_.first);
153    Inspect(rph->GetBrowserContext());
154  } else if (state_ == WORKER_PAUSED_FOR_REATTACH) {
155    DCHECK(IsAttached());
156    state_ = WORKER_INSPECTED;
157    AttachToWorker();
158    Reattach(saved_agent_state_);
159  }
160}
161
162void EmbeddedWorkerDevToolsAgentHost::WorkerContextStarted() {
163}
164
165void EmbeddedWorkerDevToolsAgentHost::WorkerRestarted(WorkerId worker_id) {
166  DCHECK_EQ(WORKER_TERMINATED, state_);
167  state_ = IsAttached() ? WORKER_PAUSED_FOR_REATTACH : WORKER_UNINSPECTED;
168  worker_id_ = worker_id;
169  WorkerCreated();
170}
171
172void EmbeddedWorkerDevToolsAgentHost::WorkerDestroyed() {
173  DCHECK_NE(WORKER_TERMINATED, state_);
174  if (state_ == WORKER_INSPECTED) {
175    DCHECK(IsAttached());
176    // Client host is debugging this worker agent host.
177    std::string notification =
178        DevToolsProtocol::CreateNotification(
179            devtools::Worker::disconnectedFromWorker::kName, NULL)->Serialize();
180    SendMessageToClient(notification);
181    DetachFromWorker();
182  }
183  state_ = WORKER_TERMINATED;
184  Release();  // Balanced in WorkerCreated()
185}
186
187bool EmbeddedWorkerDevToolsAgentHost::Matches(
188    const SharedWorkerInstance& other) {
189  return shared_worker_ && shared_worker_->Matches(other);
190}
191
192bool EmbeddedWorkerDevToolsAgentHost::Matches(
193    const ServiceWorkerIdentifier& other) {
194  return service_worker_ && service_worker_->Matches(other);
195}
196
197bool EmbeddedWorkerDevToolsAgentHost::IsTerminated() {
198  return state_ == WORKER_TERMINATED;
199}
200
201EmbeddedWorkerDevToolsAgentHost::~EmbeddedWorkerDevToolsAgentHost() {
202  DCHECK_EQ(WORKER_TERMINATED, state_);
203  EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(
204      worker_id_);
205}
206
207void EmbeddedWorkerDevToolsAgentHost::AttachToWorker() {
208  if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
209    host->AddRoute(worker_id_.second, this);
210}
211
212void EmbeddedWorkerDevToolsAgentHost::DetachFromWorker() {
213  if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first))
214    host->RemoveRoute(worker_id_.second);
215}
216
217void EmbeddedWorkerDevToolsAgentHost::WorkerCreated() {
218  AddRef();  // Balanced in WorkerDestroyed()
219}
220
221void EmbeddedWorkerDevToolsAgentHost::OnDispatchOnInspectorFrontend(
222    const std::string& message) {
223  SendMessageToClient(message);
224}
225
226void EmbeddedWorkerDevToolsAgentHost::OnSaveAgentRuntimeState(
227    const std::string& state) {
228  saved_agent_state_ = state;
229}
230
231}  // namespace content
232