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 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
)
56 static RenderViewDevToolsAgentHost
* FindAgentHost(RenderViewHost
* rvh
) {
57 if (g_instances
== NULL
)
59 for (Instances::iterator it
= g_instances
.Get().begin();
60 it
!= g_instances
.Get().end(); ++it
) {
61 if (rvh
== (*it
)->render_view_host())
69 scoped_refptr
<DevToolsAgentHost
>
70 DevToolsAgentHost::GetOrCreateFor(WebContents
* web_contents
) {
71 RenderViewDevToolsAgentHost
* result
= FindAgentHost(web_contents
);
73 result
= new RenderViewDevToolsAgentHost(web_contents
->GetRenderViewHost());
78 scoped_refptr
<DevToolsAgentHost
>
79 DevToolsAgentHost::GetOrCreateFor(RenderViewHost
* rvh
) {
80 RenderViewDevToolsAgentHost
* result
= FindAgentHost(rvh
);
82 result
= new RenderViewDevToolsAgentHost(rvh
);
87 bool DevToolsAgentHost::HasFor(RenderViewHost
* rvh
) {
88 return FindAgentHost(rvh
) != NULL
;
92 bool DevToolsAgentHost::IsDebuggerAttached(WebContents
* web_contents
) {
93 if (g_instances
== NULL
)
95 DevToolsManager
* devtools_manager
= DevToolsManager::GetInstance();
96 if (!devtools_manager
)
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
)
105 if ((*it
)->IsAttached())
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())
120 if (!widget
->IsRenderView())
123 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
124 WebContents
* web_contents
= WebContents::FromRenderViewHost(rvh
);
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()) {
140 result
.push_back(rvh
);
146 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
147 RenderViewHost
* pending
,
148 RenderViewHost
* current
) {
149 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(pending
);
152 agent_host
->DisconnectRenderViewHost();
153 agent_host
->ConnectRenderViewHost(current
);
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
);
164 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost
* rvh
)
165 : render_view_host_(NULL
),
166 overrides_handler_(new RendererOverridesHandler(this)),
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.
182 RenderViewHost
* RenderViewDevToolsAgentHost::GetRenderViewHost() {
183 return render_view_host_
;
186 void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend(
187 const std::string
& message
) {
188 std::string error_message
;
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
);
196 scoped_refptr
<DevToolsProtocol::Response
> overridden_response
;
198 DevToolsManagerDelegate
* delegate
=
199 DevToolsManagerImpl::GetInstance()->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());
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());
220 IPCDevToolsAgentHost::DispatchOnInspectorBackend(message
);
223 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message
* msg
) {
224 if (!render_view_host_
)
226 msg
->set_routing_id(render_view_host_
->GetRoutingID());
227 render_view_host_
->Send(msg
);
230 void RenderViewDevToolsAgentHost::OnClientAttached() {
231 if (!render_view_host_
)
234 InnerOnClientAttached();
236 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
237 // extensions::ProcessManager no longer relies on this notification.
239 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
242 void RenderViewDevToolsAgentHost::InnerOnClientAttached() {
243 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
244 render_view_host_
->GetProcess()->GetID());
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());
259 void RenderViewDevToolsAgentHost::OnClientDetached() {
260 #if defined(OS_ANDROID)
261 power_save_blocker_
.reset();
263 overrides_handler_
->OnClientDetached();
264 tracing_handler_
->OnClientDetached();
265 power_handler_
->OnClientDetached();
266 ClientDetachedFromRenderer();
269 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
270 if (!render_view_host_
)
273 InnerClientDetachedFromRenderer();
275 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
276 // extensions::ProcessManager no longer relies on this notification.
278 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
281 void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() {
282 bool process_has_agents
= false;
283 RenderProcessHost
* render_process_host
= render_view_host_
->GetProcess();
284 for (Instances::iterator it
= g_instances
.Get().begin();
285 it
!= g_instances
.Get().end(); ++it
) {
286 if (*it
== this || !(*it
)->IsAttached())
288 RenderViewHost
* rvh
= (*it
)->render_view_host();
289 if (rvh
&& rvh
->GetProcess() == render_process_host
)
290 process_has_agents
= true;
293 // We are the last to disconnect from the renderer -> revoke permissions.
294 if (!process_has_agents
) {
295 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
296 render_process_host
->GetID());
300 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
301 Instances::iterator it
= std::find(g_instances
.Get().begin(),
302 g_instances
.Get().end(),
304 if (it
!= g_instances
.Get().end())
305 g_instances
.Get().erase(it
);
308 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
309 RenderViewHost
* dest_rvh
) {
310 if (!render_view_host_
)
313 if (render_view_host_
== dest_rvh
&& static_cast<RenderViewHostImpl
*>(
314 render_view_host_
)->render_view_termination_status() ==
315 base::TERMINATION_STATUS_STILL_RUNNING
)
317 ReattachToRenderViewHost(dest_rvh
);
320 void RenderViewDevToolsAgentHost::RenderViewHostChanged(
321 RenderViewHost
* old_host
,
322 RenderViewHost
* new_host
) {
323 if (new_host
!= render_view_host_
) {
324 // AboutToNavigateRenderView was not called for renderer-initiated
326 ReattachToRenderViewHost(new_host
);
331 RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost
* rvh
) {
332 DCHECK(!reattaching_
);
334 DisconnectRenderViewHost();
335 ConnectRenderViewHost(rvh
);
336 reattaching_
= false;
339 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost
* rvh
) {
340 if (rvh
!= render_view_host_
)
343 DCHECK(render_view_host_
);
344 scoped_refptr
<RenderViewDevToolsAgentHost
> protect(this);
345 NotifyCloseListener();
346 ClearRenderViewHost();
350 void RenderViewDevToolsAgentHost::RenderProcessGone(
351 base::TerminationStatus status
) {
353 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
354 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
355 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
356 #if defined(OS_ANDROID)
357 case base::TERMINATION_STATUS_OOM_PROTECTED
:
366 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
367 if (!render_view_host_
)
369 // The rvh set in AboutToNavigateRenderView turned out to be interstitial.
370 // Connect back to the real one.
371 WebContents
* web_contents
=
372 WebContents::FromRenderViewHost(render_view_host_
);
375 DisconnectRenderViewHost();
376 ConnectRenderViewHost(web_contents
->GetRenderViewHost());
379 void RenderViewDevToolsAgentHost::Observe(int type
,
380 const NotificationSource
& source
,
381 const NotificationDetails
& details
) {
382 if (type
== content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
) {
383 bool visible
= *Details
<bool>(details
).ptr();
384 overrides_handler_
->OnVisibilityChanged(visible
);
388 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost
* rvh
) {
389 DCHECK(!render_view_host_
);
390 render_view_host_
= rvh
;
392 WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh
));
396 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
397 content::Source
<RenderWidgetHost
>(render_view_host_
));
400 void RenderViewDevToolsAgentHost::ClearRenderViewHost() {
401 DCHECK(render_view_host_
);
404 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
405 content::Source
<RenderWidgetHost
>(render_view_host_
));
406 render_view_host_
= NULL
;
409 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost
* rvh
) {
410 SetRenderViewHost(rvh
);
415 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
416 ClientDetachedFromRenderer();
417 ClearRenderViewHost();
420 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
421 scoped_refptr
<DevToolsProtocol::Notification
> notification
=
422 DevToolsProtocol::CreateNotification(
423 devtools::Inspector::targetCrashed::kName
, NULL
);
424 DevToolsManagerImpl::GetInstance()->
425 DispatchOnInspectorFrontend(this, notification
->Serialize());
428 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
429 const IPC::Message
& msg
) {
430 if (!render_view_host_
)
434 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost
, msg
)
435 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend
,
436 OnDispatchOnInspectorFrontend
)
437 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState
,
438 OnSaveAgentRuntimeState
)
439 IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame
,
440 handled
= false; OnSwapCompositorFrame(msg
))
441 IPC_MESSAGE_UNHANDLED(handled
= false)
442 IPC_END_MESSAGE_MAP()
446 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
447 const IPC::Message
& message
) {
448 ViewHostMsg_SwapCompositorFrame::Param param
;
449 if (!ViewHostMsg_SwapCompositorFrame::Read(&message
, ¶m
))
451 overrides_handler_
->OnSwapCompositorFrame(param
.b
.metadata
);
454 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
455 const cc::CompositorFrameMetadata
& frame_metadata
) {
456 if (!render_view_host_
)
458 overrides_handler_
->OnSwapCompositorFrame(frame_metadata
);
461 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
462 const std::string
& state
) {
463 if (!render_view_host_
)
468 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
469 const std::string
& message
) {
470 if (!render_view_host_
)
472 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
476 } // namespace content