devtools_manager.cc revision dc0f95d653279beabeb9817299e2902918ba123e
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 "chrome/browser/debugger/devtools_manager.h" 6 7#include <vector> 8 9#include "base/auto_reset.h" 10#include "base/message_loop.h" 11#include "chrome/browser/browser_process.h" 12#include "chrome/browser/debugger/devtools_client_host.h" 13#include "chrome/browser/debugger/devtools_netlog_observer.h" 14#include "chrome/browser/debugger/devtools_window.h" 15#include "chrome/browser/io_thread.h" 16#include "chrome/browser/prefs/pref_service.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 19#include "chrome/common/devtools_messages.h" 20#include "chrome/common/notification_service.h" 21#include "chrome/common/pref_names.h" 22#include "content/browser/browsing_instance.h" 23#include "content/browser/child_process_security_policy.h" 24#include "content/browser/renderer_host/render_view_host.h" 25#include "content/browser/site_instance.h" 26#include "googleurl/src/gurl.h" 27 28// static 29DevToolsManager* DevToolsManager::GetInstance() { 30 // http://crbug.com/47806 this method may be called when BrowserProcess 31 // has already been destroyed. 32 if (!g_browser_process) 33 return NULL; 34 return g_browser_process->devtools_manager(); 35} 36 37// static 38void DevToolsManager::RegisterUserPrefs(PrefService* prefs) { 39 prefs->RegisterBooleanPref(prefs::kDevToolsOpenDocked, true); 40} 41 42DevToolsManager::DevToolsManager() 43 : inspected_rvh_for_reopen_(NULL), 44 in_initial_show_(false), 45 last_orphan_cookie_(0) { 46} 47 48DevToolsManager::~DevToolsManager() { 49 DCHECK(inspected_rvh_to_client_host_.empty()); 50 DCHECK(client_host_to_inspected_rvh_.empty()); 51 // By the time we destroy devtools manager, all orphan client hosts should 52 // have been delelted, no need to notify them upon tab closing. 53 DCHECK(orphan_client_hosts_.empty()); 54} 55 56DevToolsClientHost* DevToolsManager::GetDevToolsClientHostFor( 57 RenderViewHost* inspected_rvh) { 58 InspectedRvhToClientHostMap::iterator it = 59 inspected_rvh_to_client_host_.find(inspected_rvh); 60 if (it != inspected_rvh_to_client_host_.end()) 61 return it->second; 62 return NULL; 63} 64 65void DevToolsManager::RegisterDevToolsClientHostFor( 66 RenderViewHost* inspected_rvh, 67 DevToolsClientHost* client_host) { 68 DCHECK(!GetDevToolsClientHostFor(inspected_rvh)); 69 70 DevToolsRuntimeProperties initial_properties; 71 BindClientHost(inspected_rvh, client_host, initial_properties); 72 client_host->set_close_listener(this); 73 SendAttachToAgent(inspected_rvh); 74} 75 76void DevToolsManager::ForwardToDevToolsAgent( 77 RenderViewHost* client_rvh, 78 const IPC::Message& message) { 79 DevToolsClientHost* client_host = FindOwnerDevToolsClientHost(client_rvh); 80 if (client_host) 81 ForwardToDevToolsAgent(client_host, message); 82} 83 84void DevToolsManager::ForwardToDevToolsAgent(DevToolsClientHost* from, 85 const IPC::Message& message) { 86 RenderViewHost* inspected_rvh = GetInspectedRenderViewHost(from); 87 if (!inspected_rvh) { 88 // TODO(yurys): notify client that the agent is no longer available 89 NOTREACHED(); 90 return; 91 } 92 93 IPC::Message* m = new IPC::Message(message); 94 m->set_routing_id(inspected_rvh->routing_id()); 95 inspected_rvh->Send(m); 96} 97 98void DevToolsManager::ForwardToDevToolsClient(RenderViewHost* inspected_rvh, 99 const IPC::Message& message) { 100 DevToolsClientHost* client_host = GetDevToolsClientHostFor(inspected_rvh); 101 if (!client_host) { 102 // Client window was closed while there were messages 103 // being sent to it. 104 return; 105 } 106 client_host->SendMessageToClient(message); 107} 108 109void DevToolsManager::ActivateWindow(RenderViewHost* client_rvh) { 110 DevToolsClientHost* client_host = FindOwnerDevToolsClientHost(client_rvh); 111 if (!client_host) 112 return; 113 114 DevToolsWindow* window = client_host->AsDevToolsWindow(); 115 DCHECK(window); 116 window->Activate(); 117} 118 119void DevToolsManager::CloseWindow(RenderViewHost* client_rvh) { 120 DevToolsClientHost* client_host = FindOwnerDevToolsClientHost(client_rvh); 121 if (client_host) { 122 RenderViewHost* inspected_rvh = GetInspectedRenderViewHost(client_host); 123 DCHECK(inspected_rvh); 124 UnregisterDevToolsClientHostFor(inspected_rvh); 125 } 126} 127 128void DevToolsManager::RequestDockWindow(RenderViewHost* client_rvh) { 129 ReopenWindow(client_rvh, true); 130} 131 132void DevToolsManager::RequestUndockWindow(RenderViewHost* client_rvh) { 133 ReopenWindow(client_rvh, false); 134} 135 136void DevToolsManager::OpenDevToolsWindow(RenderViewHost* inspected_rvh) { 137 ToggleDevToolsWindow( 138 inspected_rvh, 139 true, 140 DEVTOOLS_TOGGLE_ACTION_NONE); 141} 142 143void DevToolsManager::ToggleDevToolsWindow( 144 RenderViewHost* inspected_rvh, 145 DevToolsToggleAction action) { 146 ToggleDevToolsWindow(inspected_rvh, false, action); 147} 148 149void DevToolsManager::RuntimePropertyChanged(RenderViewHost* inspected_rvh, 150 const std::string& name, 151 const std::string& value) { 152 RuntimePropertiesMap::iterator it = 153 runtime_properties_map_.find(inspected_rvh); 154 if (it == runtime_properties_map_.end()) { 155 std::pair<RenderViewHost*, DevToolsRuntimeProperties> value( 156 inspected_rvh, 157 DevToolsRuntimeProperties()); 158 it = runtime_properties_map_.insert(value).first; 159 } 160 it->second[name] = value; 161} 162 163void DevToolsManager::InspectElement(RenderViewHost* inspected_rvh, 164 int x, 165 int y) { 166 IPC::Message* m = new DevToolsAgentMsg_InspectElement(x, y); 167 m->set_routing_id(inspected_rvh->routing_id()); 168 inspected_rvh->Send(m); 169 // TODO(loislo): we should initiate DevTools window opening from within 170 // renderer. Otherwise, we still can hit a race condition here. 171 OpenDevToolsWindow(inspected_rvh); 172} 173 174void DevToolsManager::ClientHostClosing(DevToolsClientHost* host) { 175 RenderViewHost* inspected_rvh = GetInspectedRenderViewHost(host); 176 if (!inspected_rvh) { 177 // It might be in the list of orphan client hosts, remove it from there. 178 for (OrphanClientHosts::iterator it = orphan_client_hosts_.begin(); 179 it != orphan_client_hosts_.end(); ++it) { 180 if (it->second.first == host) { 181 orphan_client_hosts_.erase(it->first); 182 return; 183 } 184 } 185 return; 186 } 187 188 NotificationService::current()->Notify( 189 NotificationType::DEVTOOLS_WINDOW_CLOSING, 190 Source<Profile>(inspected_rvh->site_instance()->GetProcess()->profile()), 191 Details<RenderViewHost>(inspected_rvh)); 192 193 UnbindClientHost(inspected_rvh, host); 194} 195 196RenderViewHost* DevToolsManager::GetInspectedRenderViewHost( 197 DevToolsClientHost* client_host) { 198 ClientHostToInspectedRvhMap::iterator it = 199 client_host_to_inspected_rvh_.find(client_host); 200 if (it != client_host_to_inspected_rvh_.end()) 201 return it->second; 202 return NULL; 203} 204 205void DevToolsManager::UnregisterDevToolsClientHostFor( 206 RenderViewHost* inspected_rvh) { 207 DevToolsClientHost* host = GetDevToolsClientHostFor(inspected_rvh); 208 if (!host) 209 return; 210 UnbindClientHost(inspected_rvh, host); 211 host->InspectedTabClosing(); 212} 213 214void DevToolsManager::OnNavigatingToPendingEntry(RenderViewHost* rvh, 215 RenderViewHost* dest_rvh, 216 const GURL& gurl) { 217 if (in_initial_show_) { 218 // Mute this even in case it is caused by the initial show routines. 219 return; 220 } 221 222 int cookie = DetachClientHost(rvh); 223 if (cookie != -1) { 224 // Navigating to URL in the inspected window. 225 AttachClientHost(cookie, dest_rvh); 226 227 DevToolsClientHost* client_host = GetDevToolsClientHostFor(dest_rvh); 228 client_host->FrameNavigating(gurl.spec()); 229 230 return; 231 } 232 233 // Iterate over client hosts and if there is one that has render view host 234 // changing, reopen entire client window (this must be caused by the user 235 // manually refreshing its content). 236 for (ClientHostToInspectedRvhMap::iterator it = 237 client_host_to_inspected_rvh_.begin(); 238 it != client_host_to_inspected_rvh_.end(); ++it) { 239 DevToolsWindow* window = it->first->AsDevToolsWindow(); 240 if (window && window->GetRenderViewHost() == rvh) { 241 inspected_rvh_for_reopen_ = it->second; 242 MessageLoop::current()->PostTask(FROM_HERE, 243 NewRunnableMethod(this, 244 &DevToolsManager::ForceReopenWindow)); 245 return; 246 } 247 } 248} 249 250void DevToolsManager::TabReplaced(TabContentsWrapper* old_tab, 251 TabContentsWrapper* new_tab) { 252 RenderViewHost* old_rvh = old_tab->tab_contents()->render_view_host(); 253 DevToolsClientHost* client_host = GetDevToolsClientHostFor(old_rvh); 254 if (!client_host) 255 return; // Didn't know about old_tab. 256 int cookie = DetachClientHost(old_rvh); 257 if (cookie == -1) 258 return; // Didn't know about old_tab. 259 260 client_host->TabReplaced(new_tab); 261 AttachClientHost(cookie, new_tab->tab_contents()->render_view_host()); 262} 263 264int DevToolsManager::DetachClientHost(RenderViewHost* from_rvh) { 265 DevToolsClientHost* client_host = GetDevToolsClientHostFor(from_rvh); 266 if (!client_host) 267 return -1; 268 269 int cookie = last_orphan_cookie_++; 270 orphan_client_hosts_[cookie] = 271 std::pair<DevToolsClientHost*, DevToolsRuntimeProperties>( 272 client_host, runtime_properties_map_[from_rvh]); 273 274 UnbindClientHost(from_rvh, client_host); 275 return cookie; 276} 277 278void DevToolsManager::AttachClientHost(int client_host_cookie, 279 RenderViewHost* to_rvh) { 280 OrphanClientHosts::iterator it = orphan_client_hosts_.find( 281 client_host_cookie); 282 if (it == orphan_client_hosts_.end()) 283 return; 284 285 DevToolsClientHost* client_host = (*it).second.first; 286 BindClientHost(to_rvh, client_host, (*it).second.second); 287 SendAttachToAgent(to_rvh); 288 289 orphan_client_hosts_.erase(client_host_cookie); 290} 291 292void DevToolsManager::SendAttachToAgent(RenderViewHost* inspected_rvh) { 293 if (inspected_rvh) { 294 ChildProcessSecurityPolicy::GetInstance()->GrantReadRawCookies( 295 inspected_rvh->process()->id()); 296 297 DevToolsRuntimeProperties properties; 298 RuntimePropertiesMap::iterator it = 299 runtime_properties_map_.find(inspected_rvh); 300 if (it != runtime_properties_map_.end()) { 301 properties = DevToolsRuntimeProperties(it->second.begin(), 302 it->second.end()); 303 } 304 IPC::Message* m = new DevToolsAgentMsg_Attach(properties); 305 m->set_routing_id(inspected_rvh->routing_id()); 306 inspected_rvh->Send(m); 307 } 308} 309 310void DevToolsManager::SendDetachToAgent(RenderViewHost* inspected_rvh) { 311 if (inspected_rvh) { 312 IPC::Message* m = new DevToolsAgentMsg_Detach(); 313 m->set_routing_id(inspected_rvh->routing_id()); 314 inspected_rvh->Send(m); 315 } 316} 317 318void DevToolsManager::ForceReopenWindow() { 319 if (inspected_rvh_for_reopen_) { 320 RenderViewHost* inspected_rvn = inspected_rvh_for_reopen_; 321 UnregisterDevToolsClientHostFor(inspected_rvn); 322 OpenDevToolsWindow(inspected_rvn); 323 } 324} 325 326DevToolsClientHost* DevToolsManager::FindOwnerDevToolsClientHost( 327 RenderViewHost* client_rvh) { 328 for (InspectedRvhToClientHostMap::iterator it = 329 inspected_rvh_to_client_host_.begin(); 330 it != inspected_rvh_to_client_host_.end(); 331 ++it) { 332 DevToolsWindow* win = it->second->AsDevToolsWindow(); 333 if (!win) 334 continue; 335 if (client_rvh == win->GetRenderViewHost()) 336 return it->second; 337 } 338 return NULL; 339} 340 341void DevToolsManager::ReopenWindow(RenderViewHost* client_rvh, bool docked) { 342 DevToolsClientHost* client_host = FindOwnerDevToolsClientHost(client_rvh); 343 if (!client_host) 344 return; 345 RenderViewHost* inspected_rvh = GetInspectedRenderViewHost(client_host); 346 DCHECK(inspected_rvh); 347 inspected_rvh->process()->profile()->GetPrefs()->SetBoolean( 348 prefs::kDevToolsOpenDocked, docked); 349 350 DevToolsWindow* window = client_host->AsDevToolsWindow(); 351 DCHECK(window); 352 window->SetDocked(docked); 353} 354 355void DevToolsManager::ToggleDevToolsWindow( 356 RenderViewHost* inspected_rvh, 357 bool force_open, 358 DevToolsToggleAction action) { 359 bool do_open = force_open; 360 DevToolsClientHost* host = GetDevToolsClientHostFor(inspected_rvh); 361 if (!host) { 362 bool docked = inspected_rvh->process()->profile()->GetPrefs()-> 363 GetBoolean(prefs::kDevToolsOpenDocked); 364 host = new DevToolsWindow( 365 inspected_rvh->site_instance()->browsing_instance()->profile(), 366 inspected_rvh, 367 docked); 368 RegisterDevToolsClientHostFor(inspected_rvh, host); 369 do_open = true; 370 } 371 DevToolsWindow* window = host->AsDevToolsWindow(); 372 if (!window) 373 return; 374 375 // If window is docked and visible, we hide it on toggle. If window is 376 // undocked, we show (activate) it. 377 if (!window->is_docked() || do_open) { 378 AutoReset<bool> auto_reset_in_initial_show(&in_initial_show_, true); 379 window->Show(action); 380 } else { 381 UnregisterDevToolsClientHostFor(inspected_rvh); 382 } 383} 384 385void DevToolsManager::BindClientHost( 386 RenderViewHost* inspected_rvh, 387 DevToolsClientHost* client_host, 388 const DevToolsRuntimeProperties& runtime_properties) { 389 DCHECK(inspected_rvh_to_client_host_.find(inspected_rvh) == 390 inspected_rvh_to_client_host_.end()); 391 DCHECK(client_host_to_inspected_rvh_.find(client_host) == 392 client_host_to_inspected_rvh_.end()); 393 394 if (client_host_to_inspected_rvh_.empty()) { 395 BrowserThread::PostTask( 396 BrowserThread::IO, 397 FROM_HERE, 398 NewRunnableFunction(&DevToolsNetLogObserver::Attach, 399 g_browser_process->io_thread())); 400 } 401 inspected_rvh_to_client_host_[inspected_rvh] = client_host; 402 client_host_to_inspected_rvh_[client_host] = inspected_rvh; 403 runtime_properties_map_[inspected_rvh] = runtime_properties; 404} 405 406void DevToolsManager::UnbindClientHost(RenderViewHost* inspected_rvh, 407 DevToolsClientHost* client_host) { 408 DCHECK(inspected_rvh_to_client_host_.find(inspected_rvh)->second == 409 client_host); 410 DCHECK(client_host_to_inspected_rvh_.find(client_host)->second == 411 inspected_rvh); 412 413 inspected_rvh_to_client_host_.erase(inspected_rvh); 414 client_host_to_inspected_rvh_.erase(client_host); 415 runtime_properties_map_.erase(inspected_rvh); 416 417 if (client_host_to_inspected_rvh_.empty()) { 418 BrowserThread::PostTask( 419 BrowserThread::IO, 420 FROM_HERE, 421 NewRunnableFunction(&DevToolsNetLogObserver::Detach)); 422 } 423 SendDetachToAgent(inspected_rvh); 424 if (inspected_rvh_for_reopen_ == inspected_rvh) 425 inspected_rvh_for_reopen_ = NULL; 426 427 int process_id = inspected_rvh->process()->id(); 428 for (InspectedRvhToClientHostMap::iterator it = 429 inspected_rvh_to_client_host_.begin(); 430 it != inspected_rvh_to_client_host_.end(); 431 ++it) { 432 if (it->first->process()->id() == process_id) 433 return; 434 } 435 // We've disconnected from the last renderer -> revoke cookie permissions. 436 ChildProcessSecurityPolicy::GetInstance()->RevokeReadRawCookies(process_id); 437} 438 439void DevToolsManager::CloseAllClientHosts() { 440 std::vector<RenderViewHost*> rhvs; 441 for (InspectedRvhToClientHostMap::iterator it = 442 inspected_rvh_to_client_host_.begin(); 443 it != inspected_rvh_to_client_host_.end(); ++it) { 444 rhvs.push_back(it->first); 445 } 446 for (std::vector<RenderViewHost*>::iterator it = rhvs.begin(); 447 it != rhvs.end(); ++it) { 448 UnregisterDevToolsClientHostFor(*it); 449 } 450} 451