Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / devtools / embedded_worker_devtools_manager.cc
blobb7f2be6d0d2bce99836044ec6e6a4c77a8b86b62
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"
18 namespace content {
20 namespace {
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;
30 message->set_routing_id(worker_id.second);
31 host->Send(message);
32 return true;
35 } // namespace
37 EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo(
38 const SharedWorkerInstance& instance)
39 : shared_worker_instance_(new SharedWorkerInstance(instance)),
40 state_(WORKER_UNINSPECTED),
41 agent_host_(NULL) {
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) {
53 bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches(
54 const SharedWorkerInstance& other) {
55 if (!shared_worker_instance_)
56 return false;
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_)
64 return false;
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 {
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);
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;
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));
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;
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_);
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();
125 WorkerId worker_id() const { return worker_id_; }
127 private:
128 virtual ~EmbeddedWorkerDevToolsAgentHost() {
129 CHECK(!worker_attached_);
130 EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(
131 this);
134 void OnDispatchOnInspectorFrontend(const std::string& message) {
135 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(this,
136 message);
139 void OnSaveAgentRuntimeState(const std::string& state) { state_ = state; }
141 WorkerId worker_id_;
142 bool worker_attached_;
143 std::string state_;
144 DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerDevToolsAgentHost);
147 // static
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())
160 return NULL;
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);
170 return agent_host;
173 EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsManager() {
176 EmbeddedWorkerDevToolsManager::~EmbeddedWorkerDevToolsManager() {
179 bool EmbeddedWorkerDevToolsManager::SharedWorkerCreated(
180 int worker_process_id,
181 int worker_route_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());
189 return false;
191 MoveToPausedState(id, it);
192 return true;
195 bool EmbeddedWorkerDevToolsManager::ServiceWorkerCreated(
196 int worker_process_id,
197 int worker_route_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());
208 return false;
210 MoveToPausedState(id, it);
211 return true;
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:
223 workers_.erase(it);
224 break;
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();
230 return;
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)
237 ->Serialize();
238 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
239 agent_host, notification);
240 agent_host->DetachFromWorker();
241 break;
243 case WORKER_TERMINATED:
244 NOTREACHED();
245 break;
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());
251 break;
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)
264 return;
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);
274 if (worker_info) {
275 DCHECK_EQ(WORKER_TERMINATED, worker_info->state());
276 return;
278 for (WorkerInfoMap::iterator it = workers_.begin(); it != workers_.end();
279 ++it) {
280 if (it->second->agent_host() == agent_host) {
281 DCHECK_EQ(WORKER_PAUSED, it->second->state());
282 SendMessageToWorker(
283 it->first,
284 new DevToolsAgentMsg_ResumeWorkerContext(it->first.second));
285 it->second->set_agent_host(NULL);
286 it->second->set_state(WORKER_UNINSPECTED);
287 return;
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))
298 break;
300 return it;
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))
310 break;
312 return it;
315 void EmbeddedWorkerDevToolsManager::MoveToPausedState(
316 const WorkerId& id,
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() {
325 workers_.clear();
328 } // namespace content