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.
5 #include "content/browser/devtools/render_view_devtools_agent_host.h"
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"
28 #if defined(OS_ANDROID)
29 #include "content/browser/power_save_blocker_impl.h"
30 #include "content/public/browser/render_widget_host_view.h"
35 typedef std::vector<RenderViewDevToolsAgentHost*> Instances;
38 base::LazyInstance<Instances>::Leaky g_instances = LAZY_INSTANCE_INITIALIZER;
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)
45 for (Instances::iterator it = g_instances.Get().begin();
46 it != g_instances.Get().end(); ++it) {
47 if ((*it)->GetWebContents() == web_contents)
55 scoped_refptr<DevToolsAgentHost>
56 DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) {
57 RenderViewDevToolsAgentHost* result = FindAgentHost(web_contents);
59 result = new RenderViewDevToolsAgentHost(web_contents->GetRenderViewHost());
64 bool DevToolsAgentHost::HasFor(WebContents* web_contents) {
65 return FindAgentHost(web_contents) != NULL;
69 bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
70 RenderViewDevToolsAgentHost* agent_host = FindAgentHost(web_contents);
71 return agent_host && agent_host->IsAttached();
75 std::vector<WebContents*> DevToolsAgentHost::GetInspectableWebContents() {
76 std::set<WebContents*> set;
77 scoped_ptr<RenderWidgetHostIterator> widgets(
78 RenderWidgetHost::GetRenderWidgetHosts());
79 while (RenderWidgetHost* widget = widgets->GetNextHost()) {
80 // Ignore processes that don't have a connection, such as crashed contents.
81 if (!widget->GetProcess()->HasConnection())
83 if (!widget->IsRenderView())
86 RenderViewHost* rvh = RenderViewHost::From(widget);
87 WebContents* web_contents = WebContents::FromRenderViewHost(rvh);
89 set.insert(web_contents);
91 std::vector<WebContents*> result(set.size());
92 std::copy(set.begin(), set.end(), result.begin());
97 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
98 RenderViewHost* pending,
99 RenderViewHost* current) {
100 WebContents* web_contents = WebContents::FromRenderViewHost(pending);
101 RenderViewDevToolsAgentHost* agent_host = FindAgentHost(web_contents);
104 agent_host->DisconnectRenderViewHost();
105 agent_host->ConnectRenderViewHost(current);
108 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost* rvh)
109 : render_view_host_(NULL),
110 overrides_handler_(new RendererOverridesHandler()),
112 new DevToolsTracingHandler(DevToolsTracingHandler::Renderer)),
113 power_handler_(new DevToolsPowerHandler()),
114 reattaching_(false) {
115 SetRenderViewHost(rvh);
116 DevToolsProtocol::Notifier notifier(base::Bind(
117 &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend,
118 base::Unretained(this)));
119 overrides_handler_->SetNotifier(notifier);
120 tracing_handler_->SetNotifier(notifier);
121 power_handler_->SetNotifier(notifier);
122 g_instances.Get().push_back(this);
123 AddRef(); // Balanced in RenderViewHostDestroyed.
126 WebContents* RenderViewDevToolsAgentHost::GetWebContents() {
127 return web_contents();
130 void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend(
131 const std::string& message) {
132 std::string error_message;
134 scoped_ptr<base::DictionaryValue> message_dict(
135 DevToolsProtocol::ParseMessage(message, &error_message));
136 scoped_refptr<DevToolsProtocol::Command> command =
137 DevToolsProtocol::ParseCommand(message_dict.get(), &error_message);
140 scoped_refptr<DevToolsProtocol::Response> overridden_response;
142 DevToolsManagerDelegate* delegate =
143 DevToolsManagerImpl::GetInstance()->delegate();
145 scoped_ptr<base::DictionaryValue> overridden_response_value(
146 delegate->HandleCommand(this, message_dict.get()));
147 if (overridden_response_value)
148 overridden_response = DevToolsProtocol::ParseResponse(
149 overridden_response_value.get());
151 if (!overridden_response)
152 overridden_response = overrides_handler_->HandleCommand(command);
153 if (!overridden_response)
154 overridden_response = tracing_handler_->HandleCommand(command);
155 if (!overridden_response)
156 overridden_response = power_handler_->HandleCommand(command);
157 if (overridden_response) {
158 if (!overridden_response->is_async_promise())
159 OnDispatchOnInspectorFrontend(overridden_response->Serialize());
164 IPCDevToolsAgentHost::DispatchOnInspectorBackend(message);
167 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message* msg) {
168 if (!render_view_host_)
170 msg->set_routing_id(render_view_host_->GetRoutingID());
171 render_view_host_->Send(msg);
174 void RenderViewDevToolsAgentHost::OnClientAttached() {
175 if (!render_view_host_)
178 InnerOnClientAttached();
180 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
181 // extensions::ProcessManager no longer relies on this notification.
183 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
186 void RenderViewDevToolsAgentHost::InnerOnClientAttached() {
187 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
188 render_view_host_->GetProcess()->GetID());
190 #if defined(OS_ANDROID)
191 power_save_blocker_.reset(
192 static_cast<PowerSaveBlockerImpl*>(
193 PowerSaveBlocker::Create(
194 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep,
195 "DevTools").release()));
196 if (render_view_host_->GetView()) {
197 power_save_blocker_.get()->
198 InitDisplaySleepBlocker(render_view_host_->GetView()->GetNativeView());
203 void RenderViewDevToolsAgentHost::OnClientDetached() {
204 #if defined(OS_ANDROID)
205 power_save_blocker_.reset();
207 overrides_handler_->OnClientDetached();
208 tracing_handler_->OnClientDetached();
209 power_handler_->OnClientDetached();
210 ClientDetachedFromRenderer();
212 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
213 // extensions::ProcessManager no longer relies on this notification.
215 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
218 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
219 if (!render_view_host_)
222 InnerClientDetachedFromRenderer();
225 void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() {
226 bool process_has_agents = false;
227 RenderProcessHost* render_process_host = render_view_host_->GetProcess();
228 for (Instances::iterator it = g_instances.Get().begin();
229 it != g_instances.Get().end(); ++it) {
230 if (*it == this || !(*it)->IsAttached())
232 RenderViewHost* rvh = (*it)->render_view_host_;
233 if (rvh && rvh->GetProcess() == render_process_host)
234 process_has_agents = true;
237 // We are the last to disconnect from the renderer -> revoke permissions.
238 if (!process_has_agents) {
239 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
240 render_process_host->GetID());
244 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
245 Instances::iterator it = std::find(g_instances.Get().begin(),
246 g_instances.Get().end(),
248 if (it != g_instances.Get().end())
249 g_instances.Get().erase(it);
252 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
253 RenderViewHost* dest_rvh) {
254 if (!render_view_host_)
257 if (render_view_host_ == dest_rvh &&
258 render_view_host_->render_view_termination_status() ==
259 base::TERMINATION_STATUS_STILL_RUNNING)
261 ReattachToRenderViewHost(dest_rvh);
264 void RenderViewDevToolsAgentHost::RenderViewHostChanged(
265 RenderViewHost* old_host,
266 RenderViewHost* new_host) {
267 if (new_host != render_view_host_) {
268 // AboutToNavigateRenderView was not called for renderer-initiated
270 ReattachToRenderViewHost(new_host);
275 RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost* rvh) {
276 DCHECK(!reattaching_);
278 DisconnectRenderViewHost();
279 ConnectRenderViewHost(rvh);
280 reattaching_ = false;
283 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost* rvh) {
284 if (rvh != render_view_host_)
287 DCHECK(render_view_host_);
288 scoped_refptr<RenderViewDevToolsAgentHost> protect(this);
289 NotifyCloseListener();
290 ClearRenderViewHost();
294 void RenderViewDevToolsAgentHost::RenderProcessGone(
295 base::TerminationStatus status) {
297 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
298 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
299 case base::TERMINATION_STATUS_PROCESS_CRASHED:
300 #if defined(OS_ANDROID)
301 case base::TERMINATION_STATUS_OOM_PROTECTED:
310 bool RenderViewDevToolsAgentHost::OnMessageReceived(
311 const IPC::Message& message,
312 RenderFrameHost* render_frame_host) {
313 return DispatchIPCMessage(message);
316 bool RenderViewDevToolsAgentHost::OnMessageReceived(
317 const IPC::Message& message) {
318 return DispatchIPCMessage(message);
321 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
322 if (!render_view_host_)
324 // The rvh set in AboutToNavigateRenderView turned out to be interstitial.
325 // Connect back to the real one.
326 WebContents* web_contents =
327 WebContents::FromRenderViewHost(render_view_host_);
330 DisconnectRenderViewHost();
331 ConnectRenderViewHost(web_contents->GetRenderViewHost());
334 void RenderViewDevToolsAgentHost::Observe(int type,
335 const NotificationSource& source,
336 const NotificationDetails& details) {
337 if (type == content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED) {
338 bool visible = *Details<bool>(details).ptr();
339 overrides_handler_->OnVisibilityChanged(visible);
343 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) {
344 DCHECK(!render_view_host_);
345 render_view_host_ = static_cast<RenderViewHostImpl*>(rvh);
347 WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh));
348 overrides_handler_->SetRenderViewHost(render_view_host_);
352 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
353 content::Source<RenderWidgetHost>(render_view_host_));
356 void RenderViewDevToolsAgentHost::ClearRenderViewHost() {
357 DCHECK(render_view_host_);
360 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
361 content::Source<RenderWidgetHost>(render_view_host_));
362 render_view_host_ = NULL;
363 overrides_handler_->ClearRenderViewHost();
366 void RenderViewDevToolsAgentHost::DisconnectWebContents() {
367 DisconnectRenderViewHost();
370 void RenderViewDevToolsAgentHost::ConnectWebContents(WebContents* wc) {
371 ConnectRenderViewHost(wc->GetRenderViewHost());
374 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) {
375 SetRenderViewHost(rvh);
380 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
381 ClientDetachedFromRenderer();
382 ClearRenderViewHost();
385 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
386 scoped_refptr<DevToolsProtocol::Notification> notification =
387 DevToolsProtocol::CreateNotification(
388 devtools::Inspector::targetCrashed::kName, NULL);
389 DevToolsManagerImpl::GetInstance()->
390 DispatchOnInspectorFrontend(this, notification->Serialize());
393 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
394 const IPC::Message& msg) {
395 if (!render_view_host_)
399 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost, msg)
400 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend,
401 OnDispatchOnInspectorFrontend)
402 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState,
403 OnSaveAgentRuntimeState)
404 IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame,
405 handled = false; OnSwapCompositorFrame(msg))
406 IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SetTouchEventEmulationEnabled,
407 handled = OnSetTouchEventEmulationEnabled(msg))
408 IPC_MESSAGE_UNHANDLED(handled = false)
409 IPC_END_MESSAGE_MAP()
413 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
414 const IPC::Message& message) {
415 ViewHostMsg_SwapCompositorFrame::Param param;
416 if (!ViewHostMsg_SwapCompositorFrame::Read(&message, ¶m))
418 overrides_handler_->OnSwapCompositorFrame(param.b.metadata);
421 bool RenderViewDevToolsAgentHost::OnSetTouchEventEmulationEnabled(
422 const IPC::Message& message) {
423 return overrides_handler_->OnSetTouchEventEmulationEnabled();
426 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
427 const cc::CompositorFrameMetadata& frame_metadata) {
428 if (!render_view_host_)
430 overrides_handler_->OnSwapCompositorFrame(frame_metadata);
433 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
434 const std::string& state) {
435 if (!render_view_host_)
440 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
441 const std::string& message) {
442 if (!render_view_host_)
445 scoped_refptr<DevToolsProtocol::Notification> notification =
446 DevToolsProtocol::ParseNotification(message);
449 tracing_handler_->HandleNotification(notification);
451 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
455 } // namespace content