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