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