1 // Copyright (c) 2012 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/render_view_devtools_agent_host.h" 6 7 #include "base/basictypes.h" 8 #include "base/lazy_instance.h" 9 #include "content/browser/child_process_security_policy_impl.h" 10 #include "content/browser/devtools/devtools_manager_impl.h" 11 #include "content/browser/devtools/devtools_power_handler.h" 12 #include "content/browser/devtools/devtools_protocol.h" 13 #include "content/browser/devtools/devtools_protocol_constants.h" 14 #include "content/browser/devtools/devtools_tracing_handler.h" 15 #include "content/browser/devtools/renderer_overrides_handler.h" 16 #include "content/browser/renderer_host/render_process_host_impl.h" 17 #include "content/browser/renderer_host/render_view_host_impl.h" 18 #include "content/browser/site_instance_impl.h" 19 #include "content/browser/web_contents/web_contents_impl.h" 20 #include "content/common/devtools_messages.h" 21 #include "content/common/view_messages.h" 22 #include "content/public/browser/content_browser_client.h" 23 #include "content/public/browser/devtools_manager_delegate.h" 24 #include "content/public/browser/notification_service.h" 25 #include "content/public/browser/notification_types.h" 26 #include "content/public/browser/render_widget_host_iterator.h" 27 28 #if defined(OS_ANDROID) 29 #include "content/browser/power_save_blocker_impl.h" 30 #include "content/public/browser/render_widget_host_view.h" 31 #endif 32 33 namespace content { 34 35 typedef std::vector<RenderViewDevToolsAgentHost*> Instances; 36 37 namespace { 38 base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER; 39 40 //Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost 41 //instances associated with |web_contents| 42 static RenderViewDevToolsAgentHost* FindAgentHost(WebContents* web_contents) { 43 if (g_instances == NULL) 44 return NULL; 45 RenderViewHostDelegate* delegate = 46 static_cast<WebContentsImpl*>(web_contents); 47 for (Instances::iterator it = g_instances.Get().begin(); 48 it != g_instances.Get().end(); ++it) { 49 RenderViewHost* rvh = (*it)->render_view_host(); 50 if (rvh && rvh->GetDelegate() == delegate) 51 return *it; 52 } 53 return NULL; 54 } 55 56 static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) { 57 if (g_instances == NULL) 58 return NULL; 59 for (Instances::iterator it = g_instances.Get().begin(); 60 it != g_instances.Get().end(); ++it) { 61 if (rvh == (*it)->render_view_host()) 62 return *it; 63 } 64 return NULL; 65 } 66 67 } // namespace 68 69 scoped_refptr<DevToolsAgentHost> 70 DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) { 71 RenderViewDevToolsAgentHost* result = FindAgentHost(web_contents); 72 if (!result) 73 result = new RenderViewDevToolsAgentHost(web_contents->GetRenderViewHost()); 74 return result; 75 } 76 77 // static 78 scoped_refptr<DevToolsAgentHost> 79 DevToolsAgentHost::GetOrCreateFor(RenderViewHost* rvh) { 80 RenderViewDevToolsAgentHost* result = FindAgentHost(rvh); 81 if (!result) 82 result = new RenderViewDevToolsAgentHost(rvh); 83 return result; 84 } 85 86 // static 87 bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) { 88 return FindAgentHost(rvh) != NULL; 89 } 90 91 // static 92 bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) { 93 if (g_instances == NULL) 94 return false; 95 DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); 96 if (!devtools_manager) 97 return false; 98 RenderViewHostDelegate* delegate = 99 static_cast<WebContentsImpl*>(web_contents); 100 for (Instances::iterator it = g_instances.Get().begin(); 101 it != g_instances.Get().end(); ++it) { 102 RenderViewHost* rvh = (*it)->render_view_host_; 103 if (rvh && rvh->GetDelegate() != delegate) 104 continue; 105 if ((*it)->IsAttached()) 106 return true; 107 } 108 return false; 109 } 110 111 //static 112 std::vector<RenderViewHost*> DevToolsAgentHost::GetValidRenderViewHosts() { 113 std::vector<RenderViewHost*> result; 114 scoped_ptr<RenderWidgetHostIterator> widgets( 115 RenderWidgetHost::GetRenderWidgetHosts()); 116 while (RenderWidgetHost* widget = widgets->GetNextHost()) { 117 // Ignore processes that don't have a connection, such as crashed contents. 118 if (!widget->GetProcess()->HasConnection()) 119 continue; 120 if (!widget->IsRenderView()) 121 continue; 122 123 RenderViewHost* rvh = RenderViewHost::From(widget); 124 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 125 if (!web_contents) 126 continue; 127 128 // Don't report a RenderViewHost if it is not the current RenderViewHost 129 // for some WebContents (this filters out pre-render RVHs and similar). 130 // However report a RenderViewHost created for an out of process iframe. 131 // TODO (kaznacheev): Revisit this when it is clear how OOP iframes 132 // interact with pre-rendering. 133 // TODO (kaznacheev): GetMainFrame() call is a temporary hack. Iterate over 134 // all RenderFrameHost instances when multiple OOP frames are supported. 135 if (rvh != web_contents->GetRenderViewHost() && 136 !rvh->GetMainFrame()->IsCrossProcessSubframe()) { 137 continue; 138 } 139 140 result.push_back(rvh); 141 } 142 return result; 143 } 144 145 // static 146 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation( 147 RenderViewHost* pending, 148 RenderViewHost* current) { 149 RenderViewDevToolsAgentHost* agent_host = FindAgentHost(pending); 150 if (!agent_host) 151 return; 152 agent_host->DisconnectRenderViewHost(); 153 agent_host->ConnectRenderViewHost(current); 154 } 155 156 // static 157 bool RenderViewDevToolsAgentHost::DispatchIPCMessage( 158 RenderViewHost* source, 159 const IPC::Message& message) { 160 RenderViewDevToolsAgentHost* agent_host = FindAgentHost(source); 161 return agent_host && agent_host->DispatchIPCMessage(message); 162 } 163 164 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost* rvh) 165 : render_view_host_(NULL), 166 overrides_handler_(new RendererOverridesHandler(this)), 167 tracing_handler_( 168 new DevToolsTracingHandler(DevToolsTracingHandler::Renderer)), 169 power_handler_(new DevToolsPowerHandler()), 170 reattaching_(false) { 171 SetRenderViewHost(rvh); 172 DevToolsProtocol::Notifier notifier(base::Bind( 173 &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend, 174 base::Unretained(this))); 175 overrides_handler_->SetNotifier(notifier); 176 tracing_handler_->SetNotifier(notifier); 177 power_handler_->SetNotifier(notifier); 178 g_instances.Get().push_back(this); 179 AddRef(); // Balanced in RenderViewHostDestroyed. 180 } 181 182 RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() { 183 return render_view_host_; 184 } 185 186 void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend( 187 const std::string& message) { 188 std::string error_message; 189 190 scoped_ptr<base::DictionaryValue> message_dict( 191 DevToolsProtocol::ParseMessage(message, &error_message)); 192 scoped_refptr<DevToolsProtocol::Command> command = 193 DevToolsProtocol::ParseCommand(message_dict.get(), &error_message); 194 195 if (command) { 196 scoped_refptr<DevToolsProtocol::Response> overridden_response; 197 198 DevToolsManagerDelegate* delegate = 199 DevToolsManagerImpl::GetInstance()->delegate(); 200 if (delegate) { 201 scoped_ptr<base::DictionaryValue> overridden_response_value( 202 delegate->HandleCommand(this, message_dict.get())); 203 if (overridden_response_value) 204 overridden_response = DevToolsProtocol::ParseResponse( 205 overridden_response_value.get()); 206 } 207 if (!overridden_response) 208 overridden_response = overrides_handler_->HandleCommand(command); 209 if (!overridden_response) 210 overridden_response = tracing_handler_->HandleCommand(command); 211 if (!overridden_response) 212 overridden_response = power_handler_->HandleCommand(command); 213 if (overridden_response) { 214 if (!overridden_response->is_async_promise()) 215 OnDispatchOnInspectorFrontend(overridden_response->Serialize()); 216 return; 217 } 218 } 219 220 IPCDevToolsAgentHost::DispatchOnInspectorBackend(message); 221 } 222 223 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) { 224 if (!render_view_host_) 225 return; 226 msg->set_routing_id(render_view_host_->GetRoutingID()); 227 render_view_host_->Send(msg); 228 } 229 230 void RenderViewDevToolsAgentHost::OnClientAttached() { 231 if (!render_view_host_) 232 return; 233 234 InnerOnClientAttached(); 235 236 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when 237 // extensions::ProcessManager no longer relies on this notification. 238 if (!reattaching_) 239 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true); 240 } 241 242 void RenderViewDevToolsAgentHost::InnerOnClientAttached() { 243 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies( 244 render_view_host_->GetProcess()->GetID()); 245 246 #if defined(OS_ANDROID) 247 power_save_blocker_.reset( 248 static_cast<PowerSaveBlockerImpl*>( 249 PowerSaveBlocker::Create( 250 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, 251 "DevTools").release())); 252 if (render_view_host_->GetView()) { 253 power_save_blocker_.get()-> 254 InitDisplaySleepBlocker(render_view_host_->GetView()->GetNativeView()); 255 } 256 #endif 257 } 258 259 void RenderViewDevToolsAgentHost::OnClientDetached() { 260 #if defined(OS_ANDROID) 261 power_save_blocker_.reset(); 262 #endif 263 overrides_handler_->OnClientDetached(); 264 tracing_handler_->OnClientDetached(); 265 ClientDetachedFromRenderer(); 266 } 267 268 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() { 269 if (!render_view_host_) 270 return; 271 272 InnerClientDetachedFromRenderer(); 273 274 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when 275 // extensions::ProcessManager no longer relies on this notification. 276 if (!reattaching_) 277 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false); 278 } 279 280 void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() { 281 bool process_has_agents = false; 282 RenderProcessHost* render_process_host = render_view_host_->GetProcess(); 283 for (Instances::iterator it = g_instances.Get().begin(); 284 it != g_instances.Get().end(); ++it) { 285 if (*it == this || !(*it)->IsAttached()) 286 continue; 287 RenderViewHost* rvh = (*it)->render_view_host(); 288 if (rvh && rvh->GetProcess() == render_process_host) 289 process_has_agents = true; 290 } 291 292 // We are the last to disconnect from the renderer -> revoke permissions. 293 if (!process_has_agents) { 294 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies( 295 render_process_host->GetID()); 296 } 297 } 298 299 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() { 300 Instances::iterator it = std::find(g_instances.Get().begin(), 301 g_instances.Get().end(), 302 this); 303 if (it != g_instances.Get().end()) 304 g_instances.Get().erase(it); 305 } 306 307 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView( 308 RenderViewHost* dest_rvh) { 309 if (!render_view_host_) 310 return; 311 312 if (render_view_host_ == dest_rvh && static_cast<RenderViewHostImpl*>( 313 render_view_host_)->render_view_termination_status() == 314 base::TERMINATION_STATUS_STILL_RUNNING) 315 return; 316 ReattachToRenderViewHost(dest_rvh); 317 } 318 319 void RenderViewDevToolsAgentHost::RenderViewHostChanged( 320 RenderViewHost* old_host, 321 RenderViewHost* new_host) { 322 if (new_host != render_view_host_) { 323 // AboutToNavigateRenderView was not called for renderer-initiated 324 // navigation. 325 ReattachToRenderViewHost(new_host); 326 } 327 } 328 329 void 330 RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost* rvh) { 331 DCHECK(!reattaching_); 332 reattaching_ = true; 333 DisconnectRenderViewHost(); 334 ConnectRenderViewHost(rvh); 335 reattaching_ = false; 336 } 337 338 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost* rvh) { 339 if (rvh != render_view_host_) 340 return; 341 342 DCHECK(render_view_host_); 343 scoped_refptr<RenderViewDevToolsAgentHost> protect(this); 344 NotifyCloseListener(); 345 ClearRenderViewHost(); 346 Release(); 347 } 348 349 void RenderViewDevToolsAgentHost::RenderProcessGone( 350 base::TerminationStatus status) { 351 switch(status) { 352 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: 353 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: 354 case base::TERMINATION_STATUS_PROCESS_CRASHED: 355 #if defined(OS_ANDROID) 356 case base::TERMINATION_STATUS_OOM_PROTECTED: 357 #endif 358 RenderViewCrashed(); 359 break; 360 default: 361 break; 362 } 363 } 364 365 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() { 366 if (!render_view_host_) 367 return; 368 // The rvh set in AboutToNavigateRenderView turned out to be interstitial. 369 // Connect back to the real one. 370 WebContents* web_contents = 371 WebContents::FromRenderViewHost(render_view_host_); 372 if (!web_contents) 373 return; 374 DisconnectRenderViewHost(); 375 ConnectRenderViewHost(web_contents->GetRenderViewHost()); 376 } 377 378 void RenderViewDevToolsAgentHost::Observe(int type, 379 const NotificationSource& source, 380 const NotificationDetails& details) { 381 if (type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) { 382 bool visible = *Details<bool>(details).ptr(); 383 overrides_handler_->OnVisibilityChanged(visible); 384 } 385 } 386 387 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) { 388 DCHECK(!render_view_host_); 389 render_view_host_ = rvh; 390 391 WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh)); 392 393 registrar_.Add( 394 this, 395 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, 396 content::Source<RenderWidgetHost>(render_view_host_)); 397 } 398 399 void RenderViewDevToolsAgentHost::ClearRenderViewHost() { 400 DCHECK(render_view_host_); 401 registrar_.Remove( 402 this, 403 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, 404 content::Source<RenderWidgetHost>(render_view_host_)); 405 render_view_host_ = NULL; 406 } 407 408 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) { 409 SetRenderViewHost(rvh); 410 if (IsAttached()) 411 Reattach(state_); 412 } 413 414 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() { 415 ClientDetachedFromRenderer(); 416 ClearRenderViewHost(); 417 } 418 419 void RenderViewDevToolsAgentHost::RenderViewCrashed() { 420 scoped_refptr<DevToolsProtocol::Notification> notification = 421 DevToolsProtocol::CreateNotification( 422 devtools::Inspector::targetCrashed::kName, NULL); 423 DevToolsManagerImpl::GetInstance()-> 424 DispatchOnInspectorFrontend(this, notification->Serialize()); 425 } 426 427 bool RenderViewDevToolsAgentHost::DispatchIPCMessage( 428 const IPC::Message& msg) { 429 if (!render_view_host_) 430 return false; 431 432 bool handled = true; 433 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, msg) 434 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, 435 OnDispatchOnInspectorFrontend) 436 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState, 437 OnSaveAgentRuntimeState) 438 IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame, 439 handled = false; OnSwapCompositorFrame(msg)) 440 IPC_MESSAGE_UNHANDLED(handled = false) 441 IPC_END_MESSAGE_MAP() 442 return handled; 443 } 444 445 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame( 446 const IPC::Message& message) { 447 ViewHostMsg_SwapCompositorFrame::Param param; 448 if (!ViewHostMsg_SwapCompositorFrame::Read(&message, ¶m)) 449 return; 450 overrides_handler_->OnSwapCompositorFrame(param.b.metadata); 451 } 452 453 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame( 454 const cc::CompositorFrameMetadata& frame_metadata) { 455 if (!render_view_host_) 456 return; 457 overrides_handler_->OnSwapCompositorFrame(frame_metadata); 458 } 459 460 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState( 461 const std::string& state) { 462 if (!render_view_host_) 463 return; 464 state_ = state; 465 } 466 467 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend( 468 const std::string& message) { 469 if (!render_view_host_) 470 return; 471 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend( 472 this, message); 473 } 474 475 } // namespace content 476