| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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/worker_devtools_manager.h" | |
| 6 | |
| 7 #include <list> | |
| 8 #include <map> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/lazy_instance.h" | |
| 12 #include "content/browser/devtools/devtools_manager_impl.h" | |
| 13 #include "content/browser/devtools/devtools_protocol.h" | |
| 14 #include "content/browser/devtools/devtools_protocol_constants.h" | |
| 15 #include "content/browser/devtools/embedded_worker_devtools_manager.h" | |
| 16 #include "content/browser/devtools/ipc_devtools_agent_host.h" | |
| 17 #include "content/browser/devtools/worker_devtools_message_filter.h" | |
| 18 #include "content/browser/worker_host/worker_service_impl.h" | |
| 19 #include "content/common/devtools_messages.h" | |
| 20 #include "content/public/browser/browser_thread.h" | |
| 21 #include "content/public/browser/child_process_data.h" | |
| 22 #include "content/public/common/process_type.h" | |
| 23 | |
| 24 namespace content { | |
| 25 | |
| 26 // Called on the UI thread. | |
| 27 // static | |
| 28 scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetForWorker( | |
| 29 int worker_process_id, | |
| 30 int worker_route_id) { | |
| 31 if (WorkerService::EmbeddedSharedWorkerEnabled()) { | |
| 32 return EmbeddedWorkerDevToolsManager::GetInstance() | |
| 33 ->GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id); | |
| 34 } else { | |
| 35 return WorkerDevToolsManager::GetDevToolsAgentHostForWorker( | |
| 36 worker_process_id, worker_route_id); | |
| 37 } | |
| 38 } | |
| 39 | |
| 40 namespace { | |
| 41 | |
| 42 typedef std::map<WorkerDevToolsManager::WorkerId, | |
| 43 WorkerDevToolsManager::WorkerDevToolsAgentHost*> AgentHosts; | |
| 44 base::LazyInstance<AgentHosts>::Leaky g_agent_map = LAZY_INSTANCE_INITIALIZER; | |
| 45 base::LazyInstance<AgentHosts>::Leaky g_orphan_map = LAZY_INSTANCE_INITIALIZER; | |
| 46 | |
| 47 } // namespace | |
| 48 | |
| 49 struct WorkerDevToolsManager::TerminatedInspectedWorker { | |
| 50 TerminatedInspectedWorker(WorkerId id, | |
| 51 const GURL& url, | |
| 52 const base::string16& name) | |
| 53 : old_worker_id(id), | |
| 54 worker_url(url), | |
| 55 worker_name(name) {} | |
| 56 WorkerId old_worker_id; | |
| 57 GURL worker_url; | |
| 58 base::string16 worker_name; | |
| 59 }; | |
| 60 | |
| 61 | |
| 62 class WorkerDevToolsManager::WorkerDevToolsAgentHost | |
| 63 : public IPCDevToolsAgentHost { | |
| 64 public: | |
| 65 explicit WorkerDevToolsAgentHost(WorkerId worker_id) | |
| 66 : has_worker_id_(false) { | |
| 67 SetWorkerId(worker_id, false); | |
| 68 } | |
| 69 | |
| 70 void SetWorkerId(WorkerId worker_id, bool reattach) { | |
| 71 worker_id_ = worker_id; | |
| 72 if (!has_worker_id_) | |
| 73 AddRef(); // Balanced in ResetWorkerId. | |
| 74 has_worker_id_ = true; | |
| 75 g_agent_map.Get()[worker_id_] = this; | |
| 76 | |
| 77 BrowserThread::PostTask( | |
| 78 BrowserThread::IO, | |
| 79 FROM_HERE, | |
| 80 base::Bind( | |
| 81 &ConnectToWorker, | |
| 82 worker_id.first, | |
| 83 worker_id.second)); | |
| 84 | |
| 85 if (reattach) | |
| 86 Reattach(state_); | |
| 87 } | |
| 88 | |
| 89 void ResetWorkerId() { | |
| 90 g_agent_map.Get().erase(worker_id_); | |
| 91 has_worker_id_ = false; | |
| 92 Release(); // Balanced in SetWorkerId. | |
| 93 } | |
| 94 | |
| 95 void SaveAgentRuntimeState(const std::string& state) { | |
| 96 state_ = state; | |
| 97 } | |
| 98 | |
| 99 void ConnectionFailed() { | |
| 100 NotifyCloseListener(); | |
| 101 // Object can be deleted here. | |
| 102 } | |
| 103 | |
| 104 private: | |
| 105 virtual ~WorkerDevToolsAgentHost(); | |
| 106 | |
| 107 static void ConnectToWorker( | |
| 108 int worker_process_id, | |
| 109 int worker_route_id) { | |
| 110 WorkerDevToolsManager::GetInstance()->ConnectDevToolsAgentHostToWorker( | |
| 111 worker_process_id, worker_route_id); | |
| 112 } | |
| 113 | |
| 114 static void ForwardToWorkerDevToolsAgent( | |
| 115 int worker_process_id, | |
| 116 int worker_route_id, | |
| 117 IPC::Message* message) { | |
| 118 WorkerDevToolsManager::GetInstance()->ForwardToWorkerDevToolsAgent( | |
| 119 worker_process_id, worker_route_id, *message); | |
| 120 } | |
| 121 | |
| 122 // IPCDevToolsAgentHost implementation. | |
| 123 virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE { | |
| 124 if (!has_worker_id_) { | |
| 125 delete message; | |
| 126 return; | |
| 127 } | |
| 128 BrowserThread::PostTask( | |
| 129 BrowserThread::IO, FROM_HERE, | |
| 130 base::Bind( | |
| 131 &WorkerDevToolsAgentHost::ForwardToWorkerDevToolsAgent, | |
| 132 worker_id_.first, | |
| 133 worker_id_.second, | |
| 134 base::Owned(message))); | |
| 135 } | |
| 136 | |
| 137 virtual void OnClientAttached() OVERRIDE {} | |
| 138 virtual void OnClientDetached() OVERRIDE {} | |
| 139 | |
| 140 bool has_worker_id_; | |
| 141 WorkerId worker_id_; | |
| 142 std::string state_; | |
| 143 | |
| 144 DISALLOW_COPY_AND_ASSIGN(WorkerDevToolsAgentHost); | |
| 145 }; | |
| 146 | |
| 147 | |
| 148 class WorkerDevToolsManager::DetachedClientHosts { | |
| 149 public: | |
| 150 static void WorkerReloaded(WorkerId old_id, WorkerId new_id) { | |
| 151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 152 AgentHosts::iterator it = g_orphan_map.Get().find(old_id); | |
| 153 if (it != g_orphan_map.Get().end()) { | |
| 154 it->second->SetWorkerId(new_id, true); | |
| 155 g_orphan_map.Get().erase(old_id); | |
| 156 return; | |
| 157 } | |
| 158 RemovePendingWorkerData(old_id); | |
| 159 } | |
| 160 | |
| 161 static void WorkerDestroyed(WorkerId id) { | |
| 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 163 AgentHosts::iterator it = g_agent_map.Get().find(id); | |
| 164 if (it == g_agent_map.Get().end()) { | |
| 165 RemovePendingWorkerData(id); | |
| 166 return; | |
| 167 } | |
| 168 | |
| 169 WorkerDevToolsAgentHost* agent = it->second; | |
| 170 DevToolsManagerImpl* devtools_manager = DevToolsManagerImpl::GetInstance(); | |
| 171 if (!agent->IsAttached()) { | |
| 172 // Agent has no client hosts -> delete it. | |
| 173 RemovePendingWorkerData(id); | |
| 174 return; | |
| 175 } | |
| 176 | |
| 177 // Client host is debugging this worker agent host. | |
| 178 std::string notification = DevToolsProtocol::CreateNotification( | |
| 179 devtools::Worker::disconnectedFromWorker::kName, NULL)->Serialize(); | |
| 180 devtools_manager->DispatchOnInspectorFrontend(agent, notification); | |
| 181 g_orphan_map.Get()[id] = agent; | |
| 182 agent->ResetWorkerId(); | |
| 183 } | |
| 184 | |
| 185 static void RemovePendingWorkerData(WorkerId id) { | |
| 186 BrowserThread::PostTask( | |
| 187 BrowserThread::IO, FROM_HERE, | |
| 188 base::Bind(&RemoveInspectedWorkerDataOnIOThread, id)); | |
| 189 } | |
| 190 | |
| 191 private: | |
| 192 DetachedClientHosts() {} | |
| 193 ~DetachedClientHosts() {} | |
| 194 | |
| 195 static void RemoveInspectedWorkerDataOnIOThread(WorkerId id) { | |
| 196 WorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData(id); | |
| 197 } | |
| 198 }; | |
| 199 | |
| 200 struct WorkerDevToolsManager::InspectedWorker { | |
| 201 InspectedWorker(WorkerProcessHost* host, int route_id, const GURL& url, | |
| 202 const base::string16& name) | |
| 203 : host(host), | |
| 204 route_id(route_id), | |
| 205 worker_url(url), | |
| 206 worker_name(name) {} | |
| 207 WorkerProcessHost* const host; | |
| 208 int const route_id; | |
| 209 GURL worker_url; | |
| 210 base::string16 worker_name; | |
| 211 }; | |
| 212 | |
| 213 // static | |
| 214 WorkerDevToolsManager* WorkerDevToolsManager::GetInstance() { | |
| 215 DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled()); | |
| 216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 217 return Singleton<WorkerDevToolsManager>::get(); | |
| 218 } | |
| 219 | |
| 220 // static | |
| 221 DevToolsAgentHost* WorkerDevToolsManager::GetDevToolsAgentHostForWorker( | |
| 222 int worker_process_id, | |
| 223 int worker_route_id) { | |
| 224 DCHECK(!WorkerService::EmbeddedSharedWorkerEnabled()); | |
| 225 WorkerId id(worker_process_id, worker_route_id); | |
| 226 AgentHosts::iterator it = g_agent_map.Get().find(id); | |
| 227 if (it == g_agent_map.Get().end()) | |
| 228 return new WorkerDevToolsAgentHost(id); | |
| 229 return it->second; | |
| 230 } | |
| 231 | |
| 232 WorkerDevToolsManager::WorkerDevToolsManager() { | |
| 233 } | |
| 234 | |
| 235 WorkerDevToolsManager::~WorkerDevToolsManager() { | |
| 236 } | |
| 237 | |
| 238 bool WorkerDevToolsManager::WorkerCreated( | |
| 239 WorkerProcessHost* worker, | |
| 240 const WorkerProcessHost::WorkerInstance& instance) { | |
| 241 for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin(); | |
| 242 it != terminated_workers_.end(); ++it) { | |
| 243 if (instance.Matches(it->worker_url, it->worker_name, | |
| 244 instance.partition(), | |
| 245 instance.resource_context())) { | |
| 246 WorkerId new_worker_id(worker->GetData().id, instance.worker_route_id()); | |
| 247 paused_workers_[new_worker_id] = it->old_worker_id; | |
| 248 terminated_workers_.erase(it); | |
| 249 return true; | |
| 250 } | |
| 251 } | |
| 252 return false; | |
| 253 } | |
| 254 | |
| 255 void WorkerDevToolsManager::WorkerDestroyed( | |
| 256 WorkerProcessHost* worker, | |
| 257 int worker_route_id) { | |
| 258 InspectedWorkersList::iterator it = FindInspectedWorker( | |
| 259 worker->GetData().id, | |
| 260 worker_route_id); | |
| 261 if (it == inspected_workers_.end()) | |
| 262 return; | |
| 263 | |
| 264 WorkerId worker_id(worker->GetData().id, worker_route_id); | |
| 265 terminated_workers_.push_back(TerminatedInspectedWorker( | |
| 266 worker_id, | |
| 267 it->worker_url, | |
| 268 it->worker_name)); | |
| 269 inspected_workers_.erase(it); | |
| 270 BrowserThread::PostTask( | |
| 271 BrowserThread::UI, FROM_HERE, | |
| 272 base::Bind(&DetachedClientHosts::WorkerDestroyed, worker_id)); | |
| 273 } | |
| 274 | |
| 275 void WorkerDevToolsManager::WorkerContextStarted(WorkerProcessHost* process, | |
| 276 int worker_route_id) { | |
| 277 WorkerId new_worker_id(process->GetData().id, worker_route_id); | |
| 278 PausedWorkers::iterator it = paused_workers_.find(new_worker_id); | |
| 279 if (it == paused_workers_.end()) | |
| 280 return; | |
| 281 | |
| 282 BrowserThread::PostTask( | |
| 283 BrowserThread::UI, FROM_HERE, | |
| 284 base::Bind( | |
| 285 &DetachedClientHosts::WorkerReloaded, | |
| 286 it->second, | |
| 287 new_worker_id)); | |
| 288 paused_workers_.erase(it); | |
| 289 } | |
| 290 | |
| 291 void WorkerDevToolsManager::RemoveInspectedWorkerData( | |
| 292 const WorkerId& id) { | |
| 293 for (TerminatedInspectedWorkers::iterator it = terminated_workers_.begin(); | |
| 294 it != terminated_workers_.end(); ++it) { | |
| 295 if (it->old_worker_id == id) { | |
| 296 terminated_workers_.erase(it); | |
| 297 return; | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 for (PausedWorkers::iterator it = paused_workers_.begin(); | |
| 302 it != paused_workers_.end(); ++it) { | |
| 303 if (it->second == id) { | |
| 304 SendResumeToWorker(it->first); | |
| 305 paused_workers_.erase(it); | |
| 306 return; | |
| 307 } | |
| 308 } | |
| 309 } | |
| 310 | |
| 311 WorkerDevToolsManager::InspectedWorkersList::iterator | |
| 312 WorkerDevToolsManager::FindInspectedWorker( | |
| 313 int host_id, int route_id) { | |
| 314 InspectedWorkersList::iterator it = inspected_workers_.begin(); | |
| 315 while (it != inspected_workers_.end()) { | |
| 316 if (it->host->GetData().id == host_id && it->route_id == route_id) | |
| 317 break; | |
| 318 ++it; | |
| 319 } | |
| 320 return it; | |
| 321 } | |
| 322 | |
| 323 static WorkerProcessHost* FindWorkerProcess(int worker_process_id) { | |
| 324 for (WorkerProcessHostIterator iter; !iter.Done(); ++iter) { | |
| 325 if (iter.GetData().id == worker_process_id) | |
| 326 return *iter; | |
| 327 } | |
| 328 return NULL; | |
| 329 } | |
| 330 | |
| 331 void WorkerDevToolsManager::ConnectDevToolsAgentHostToWorker( | |
| 332 int worker_process_id, | |
| 333 int worker_route_id) { | |
| 334 if (WorkerProcessHost* process = FindWorkerProcess(worker_process_id)) { | |
| 335 const WorkerProcessHost::Instances& instances = process->instances(); | |
| 336 for (WorkerProcessHost::Instances::const_iterator i = instances.begin(); | |
| 337 i != instances.end(); ++i) { | |
| 338 if (i->worker_route_id() == worker_route_id) { | |
| 339 DCHECK(FindInspectedWorker(worker_process_id, worker_route_id) == | |
| 340 inspected_workers_.end()); | |
| 341 inspected_workers_.push_back( | |
| 342 InspectedWorker(process, worker_route_id, i->url(), i->name())); | |
| 343 return; | |
| 344 } | |
| 345 } | |
| 346 } | |
| 347 NotifyConnectionFailedOnIOThread(worker_process_id, worker_route_id); | |
| 348 } | |
| 349 | |
| 350 void WorkerDevToolsManager::ForwardToDevToolsClient( | |
| 351 int worker_process_id, | |
| 352 int worker_route_id, | |
| 353 const std::string& message) { | |
| 354 if (FindInspectedWorker(worker_process_id, worker_route_id) == | |
| 355 inspected_workers_.end()) { | |
| 356 NOTREACHED(); | |
| 357 return; | |
| 358 } | |
| 359 BrowserThread::PostTask( | |
| 360 BrowserThread::UI, FROM_HERE, | |
| 361 base::Bind( | |
| 362 &ForwardToDevToolsClientOnUIThread, | |
| 363 worker_process_id, | |
| 364 worker_route_id, | |
| 365 message)); | |
| 366 } | |
| 367 | |
| 368 void WorkerDevToolsManager::SaveAgentRuntimeState(int worker_process_id, | |
| 369 int worker_route_id, | |
| 370 const std::string& state) { | |
| 371 BrowserThread::PostTask( | |
| 372 BrowserThread::UI, FROM_HERE, | |
| 373 base::Bind( | |
| 374 &SaveAgentRuntimeStateOnUIThread, | |
| 375 worker_process_id, | |
| 376 worker_route_id, | |
| 377 state)); | |
| 378 } | |
| 379 | |
| 380 void WorkerDevToolsManager::ForwardToWorkerDevToolsAgent( | |
| 381 int worker_process_id, | |
| 382 int worker_route_id, | |
| 383 const IPC::Message& message) { | |
| 384 InspectedWorkersList::iterator it = FindInspectedWorker( | |
| 385 worker_process_id, | |
| 386 worker_route_id); | |
| 387 if (it == inspected_workers_.end()) | |
| 388 return; | |
| 389 IPC::Message* msg = new IPC::Message(message); | |
| 390 msg->set_routing_id(worker_route_id); | |
| 391 it->host->Send(msg); | |
| 392 } | |
| 393 | |
| 394 // static | |
| 395 void WorkerDevToolsManager::ForwardToDevToolsClientOnUIThread( | |
| 396 int worker_process_id, | |
| 397 int worker_route_id, | |
| 398 const std::string& message) { | |
| 399 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id, | |
| 400 worker_route_id)); | |
| 401 if (it == g_agent_map.Get().end()) | |
| 402 return; | |
| 403 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(it->second, | |
| 404 message); | |
| 405 } | |
| 406 | |
| 407 // static | |
| 408 void WorkerDevToolsManager::SaveAgentRuntimeStateOnUIThread( | |
| 409 int worker_process_id, | |
| 410 int worker_route_id, | |
| 411 const std::string& state) { | |
| 412 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id, | |
| 413 worker_route_id)); | |
| 414 if (it == g_agent_map.Get().end()) | |
| 415 return; | |
| 416 it->second->SaveAgentRuntimeState(state); | |
| 417 } | |
| 418 | |
| 419 // static | |
| 420 void WorkerDevToolsManager::NotifyConnectionFailedOnIOThread( | |
| 421 int worker_process_id, | |
| 422 int worker_route_id) { | |
| 423 BrowserThread::PostTask( | |
| 424 BrowserThread::UI, FROM_HERE, | |
| 425 base::Bind( | |
| 426 &WorkerDevToolsManager::NotifyConnectionFailedOnUIThread, | |
| 427 worker_process_id, | |
| 428 worker_route_id)); | |
| 429 } | |
| 430 | |
| 431 // static | |
| 432 void WorkerDevToolsManager::NotifyConnectionFailedOnUIThread( | |
| 433 int worker_process_id, | |
| 434 int worker_route_id) { | |
| 435 AgentHosts::iterator it = g_agent_map.Get().find(WorkerId(worker_process_id, | |
| 436 worker_route_id)); | |
| 437 if (it != g_agent_map.Get().end()) | |
| 438 it->second->ConnectionFailed(); | |
| 439 } | |
| 440 | |
| 441 // static | |
| 442 void WorkerDevToolsManager::SendResumeToWorker(const WorkerId& id) { | |
| 443 if (WorkerProcessHost* process = FindWorkerProcess(id.first)) | |
| 444 process->Send(new DevToolsAgentMsg_ResumeWorkerContext(id.second)); | |
| 445 } | |
| 446 | |
| 447 WorkerDevToolsManager::WorkerDevToolsAgentHost::~WorkerDevToolsAgentHost() { | |
| 448 DetachedClientHosts::RemovePendingWorkerData(worker_id_); | |
| 449 g_agent_map.Get().erase(worker_id_); | |
| 450 g_orphan_map.Get().erase(worker_id_); | |
| 451 } | |
| 452 | |
| 453 } // namespace content | |
| OLD | NEW |