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/public/browser/content_browser_client.h" 21 #include "content/public/browser/notification_service.h" 22 #include "content/public/browser/notification_types.h" 23 24 namespace content { 25 26 typedef std::vector<RenderViewDevToolsAgentHost*> Instances; 27 28 namespace { 29 base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER; 30 31 static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) { 32 if (g_instances == NULL) 33 return NULL; 34 for (Instances::iterator it = g_instances.Get().begin(); 35 it != g_instances.Get().end(); ++it) { 36 if (rvh == (*it)->render_view_host()) 37 return *it; 38 } 39 return NULL; 40 } 41 42 } // namespace 43 44 class RenderViewDevToolsAgentHost::DevToolsAgentHostRvhObserver 45 : public RenderViewHostObserver { 46 public: 47 DevToolsAgentHostRvhObserver(RenderViewHost* rvh, 48 RenderViewDevToolsAgentHost* agent_host) 49 : RenderViewHostObserver(rvh), 50 agent_host_(agent_host) { 51 } 52 virtual ~DevToolsAgentHostRvhObserver() {} 53 54 // RenderViewHostObserver overrides. 55 virtual void RenderViewHostDestroyed(RenderViewHost* rvh) OVERRIDE { 56 agent_host_->RenderViewHostDestroyed(rvh); 57 } 58 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 59 return agent_host_->OnRvhMessageReceived(message); 60 } 61 private: 62 RenderViewDevToolsAgentHost* agent_host_; 63 DISALLOW_COPY_AND_ASSIGN(DevToolsAgentHostRvhObserver); 64 }; 65 66 // static 67 scoped_refptr<DevToolsAgentHost> 68 DevToolsAgentHost::GetOrCreateFor(RenderViewHost* rvh) { 69 RenderViewDevToolsAgentHost* result = FindAgentHost(rvh); 70 if (!result) 71 result = new RenderViewDevToolsAgentHost(rvh); 72 return result; 73 } 74 75 // static 76 bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) { 77 return FindAgentHost(rvh) != NULL; 78 } 79 80 // static 81 bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) { 82 if (g_instances == NULL) 83 return false; 84 DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); 85 if (!devtools_manager) 86 return false; 87 RenderViewHostDelegate* delegate = 88 static_cast<WebContentsImpl*>(web_contents); 89 for (Instances::iterator it = g_instances.Get().begin(); 90 it != g_instances.Get().end(); ++it) { 91 RenderViewHost* rvh = (*it)->render_view_host_; 92 if (rvh && rvh->GetDelegate() != delegate) 93 continue; 94 if ((*it)->IsAttached()) 95 return true; 96 } 97 return false; 98 } 99 100 //static 101 std::vector<RenderViewHost*> DevToolsAgentHost::GetValidRenderViewHosts() { 102 std::vector<RenderViewHost*> result; 103 RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); 104 for (size_t i = 0; i < widgets.size(); ++i) { 105 // Ignore processes that don't have a connection, such as crashed contents. 106 if (!widgets[i]->GetProcess()->HasConnection()) 107 continue; 108 if (!widgets[i]->IsRenderView()) 109 continue; 110 111 RenderViewHost* rvh = RenderViewHost::From(widgets[i]); 112 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 113 // Don't report a RenderViewHost if it is not the current RenderViewHost 114 // for some WebContents. 115 if (!web_contents || rvh != web_contents->GetRenderViewHost()) 116 continue; 117 118 result.push_back(rvh); 119 } 120 return result; 121 } 122 123 // static 124 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation( 125 RenderViewHost* pending, 126 RenderViewHost* current) { 127 RenderViewDevToolsAgentHost* agent_host = FindAgentHost(pending); 128 if (!agent_host) 129 return; 130 agent_host->DisconnectRenderViewHost(); 131 agent_host->ConnectRenderViewHost(current); 132 } 133 134 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost( 135 RenderViewHost* rvh) 136 : overrides_handler_(new RendererOverridesHandler(this)), 137 tracing_handler_(new DevToolsTracingHandler()) 138 { 139 SetRenderViewHost(rvh); 140 DevToolsProtocol::Notifier notifier(base::Bind( 141 &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend, 142 base::Unretained(this))); 143 overrides_handler_->SetNotifier(notifier); 144 tracing_handler_->SetNotifier(notifier); 145 g_instances.Get().push_back(this); 146 RenderViewHostDelegate* delegate = render_view_host_->GetDelegate(); 147 if (delegate && delegate->GetAsWebContents()) 148 Observe(delegate->GetAsWebContents()); 149 AddRef(); // Balanced in RenderViewHostDestroyed. 150 } 151 152 RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() { 153 return render_view_host_; 154 } 155 156 void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend( 157 const std::string& message) { 158 std::string error_message; 159 scoped_refptr<DevToolsProtocol::Command> command = 160 DevToolsProtocol::ParseCommand(message, &error_message); 161 162 if (command) { 163 scoped_refptr<DevToolsProtocol::Response> overridden_response = 164 overrides_handler_->HandleCommand(command); 165 if (!overridden_response) 166 overridden_response = tracing_handler_->HandleCommand(command); 167 if (overridden_response) { 168 if (!overridden_response->is_async_promise()) 169 OnDispatchOnInspectorFrontend(overridden_response->Serialize()); 170 return; 171 } 172 } 173 174 IPCDevToolsAgentHost::DispatchOnInspectorBackend(message); 175 } 176 177 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) { 178 if (!render_view_host_) 179 return; 180 msg->set_routing_id(render_view_host_->GetRoutingID()); 181 render_view_host_->Send(msg); 182 } 183 184 void RenderViewDevToolsAgentHost::OnClientAttached() { 185 if (!render_view_host_) 186 return; 187 188 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies( 189 render_view_host_->GetProcess()->GetID()); 190 191 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when 192 // ExtensionProcessManager no longer relies on this notification. 193 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true); 194 } 195 196 void RenderViewDevToolsAgentHost::OnClientDetached() { 197 if (!render_view_host_) 198 return; 199 200 bool process_has_agents = false; 201 RenderProcessHost* render_process_host = render_view_host_->GetProcess(); 202 for (Instances::iterator it = g_instances.Get().begin(); 203 it != g_instances.Get().end(); ++it) { 204 if (*it == this || !(*it)->IsAttached()) 205 continue; 206 RenderViewHost* rvh = (*it)->render_view_host(); 207 if (rvh && rvh->GetProcess() == render_process_host) 208 process_has_agents = true; 209 } 210 211 // We are the last to disconnect from the renderer -> revoke permissions. 212 if (!process_has_agents) { 213 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies( 214 render_process_host->GetID()); 215 } 216 217 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when 218 // ExtensionProcessManager no longer relies on this notification. 219 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false); 220 } 221 222 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() { 223 Instances::iterator it = std::find(g_instances.Get().begin(), 224 g_instances.Get().end(), 225 this); 226 if (it != g_instances.Get().end()) 227 g_instances.Get().erase(it); 228 } 229 230 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView( 231 RenderViewHost* dest_rvh) { 232 if (!render_view_host_) 233 return; 234 235 if (render_view_host_ == dest_rvh && static_cast<RenderViewHostImpl*>( 236 render_view_host_)->render_view_termination_status() == 237 base::TERMINATION_STATUS_STILL_RUNNING) 238 return; 239 DisconnectRenderViewHost(); 240 ConnectRenderViewHost(dest_rvh); 241 } 242 243 void RenderViewDevToolsAgentHost::RenderProcessGone( 244 base::TerminationStatus status) { 245 switch(status) { 246 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: 247 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: 248 case base::TERMINATION_STATUS_PROCESS_CRASHED: 249 RenderViewCrashed(); 250 break; 251 default: 252 break; 253 } 254 } 255 256 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() { 257 if (!render_view_host_) 258 return; 259 // The rvh set in AboutToNavigateRenderView turned out to be interstitial. 260 // Connect back to the real one. 261 WebContents* web_contents = 262 WebContents::FromRenderViewHost(render_view_host_); 263 if (!web_contents) 264 return; 265 DisconnectRenderViewHost(); 266 ConnectRenderViewHost(web_contents->GetRenderViewHost()); 267 } 268 269 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) { 270 render_view_host_ = rvh; 271 rvh_observer_.reset(new DevToolsAgentHostRvhObserver(rvh, this)); 272 } 273 274 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) { 275 SetRenderViewHost(rvh); 276 if (IsAttached()) 277 Reattach(state_); 278 } 279 280 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() { 281 OnClientDetached(); 282 rvh_observer_.reset(); 283 render_view_host_ = NULL; 284 } 285 286 void RenderViewDevToolsAgentHost::RenderViewHostDestroyed( 287 RenderViewHost* rvh) { 288 DCHECK(render_view_host_); 289 scoped_refptr<RenderViewDevToolsAgentHost> protect(this); 290 NotifyCloseListener(); 291 render_view_host_ = NULL; 292 Release(); 293 } 294 295 void RenderViewDevToolsAgentHost::RenderViewCrashed() { 296 scoped_refptr<DevToolsProtocol::Notification> notification = 297 DevToolsProtocol::CreateNotification( 298 devtools::Inspector::targetCrashed::kName, NULL); 299 DevToolsManagerImpl::GetInstance()-> 300 DispatchOnInspectorFrontend(this, notification->Serialize()); 301 } 302 303 bool RenderViewDevToolsAgentHost::OnRvhMessageReceived( 304 const IPC::Message& message) { 305 bool handled = true; 306 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, message) 307 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, 308 OnDispatchOnInspectorFrontend) 309 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState, 310 OnSaveAgentRuntimeState) 311 IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCache, OnClearBrowserCache) 312 IPC_MESSAGE_HANDLER(DevToolsHostMsg_ClearBrowserCookies, 313 OnClearBrowserCookies) 314 IPC_MESSAGE_UNHANDLED(handled = false) 315 IPC_END_MESSAGE_MAP() 316 return handled; 317 } 318 319 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState( 320 const std::string& state) { 321 if (!render_view_host_) 322 return; 323 state_ = state; 324 } 325 326 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend( 327 const std::string& message) { 328 if (!render_view_host_) 329 return; 330 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend( 331 this, message); 332 } 333 334 void RenderViewDevToolsAgentHost::OnClearBrowserCache() { 335 if (render_view_host_) 336 GetContentClient()->browser()->ClearCache(render_view_host_); 337 } 338 339 void RenderViewDevToolsAgentHost::OnClearBrowserCookies() { 340 if (render_view_host_) 341 GetContentClient()->browser()->ClearCookies(render_view_host_); 342 } 343 344 } // namespace content 345