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