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/notification_service.h"
24 #include "content/public/browser/notification_types.h"
25 #include "content/public/browser/render_widget_host_iterator.h"
27 #if defined(OS_ANDROID)
28 #include "content/browser/power_save_blocker_impl.h"
29 #include "content/public/browser/render_widget_host_view.h"
34 typedef std::vector
<RenderViewDevToolsAgentHost
*> Instances
;
37 base::LazyInstance
<Instances
>::Leaky g_instances
= LAZY_INSTANCE_INITIALIZER
;
39 //Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost
40 //instances associated with |web_contents|
41 static RenderViewDevToolsAgentHost
* FindAgentHost(WebContents
* web_contents
) {
42 if (g_instances
== NULL
)
44 RenderViewHostDelegate
* delegate
=
45 static_cast<WebContentsImpl
*>(web_contents
);
46 for (Instances::iterator it
= g_instances
.Get().begin();
47 it
!= g_instances
.Get().end(); ++it
) {
48 RenderViewHost
* rvh
= (*it
)->render_view_host();
49 if (rvh
&& rvh
->GetDelegate() == delegate
)
55 static RenderViewDevToolsAgentHost
* FindAgentHost(RenderViewHost
* rvh
) {
56 if (g_instances
== NULL
)
58 for (Instances::iterator it
= g_instances
.Get().begin();
59 it
!= g_instances
.Get().end(); ++it
) {
60 if (rvh
== (*it
)->render_view_host())
68 scoped_refptr
<DevToolsAgentHost
>
69 DevToolsAgentHost::GetOrCreateFor(WebContents
* web_contents
) {
70 RenderViewDevToolsAgentHost
* result
= FindAgentHost(web_contents
);
72 result
= new RenderViewDevToolsAgentHost(web_contents
->GetRenderViewHost());
77 scoped_refptr
<DevToolsAgentHost
>
78 DevToolsAgentHost::GetOrCreateFor(RenderViewHost
* rvh
) {
79 RenderViewDevToolsAgentHost
* result
= FindAgentHost(rvh
);
81 result
= new RenderViewDevToolsAgentHost(rvh
);
86 bool DevToolsAgentHost::HasFor(RenderViewHost
* rvh
) {
87 return FindAgentHost(rvh
) != NULL
;
91 bool DevToolsAgentHost::IsDebuggerAttached(WebContents
* web_contents
) {
92 if (g_instances
== NULL
)
94 DevToolsManager
* devtools_manager
= DevToolsManager::GetInstance();
95 if (!devtools_manager
)
97 RenderViewHostDelegate
* delegate
=
98 static_cast<WebContentsImpl
*>(web_contents
);
99 for (Instances::iterator it
= g_instances
.Get().begin();
100 it
!= g_instances
.Get().end(); ++it
) {
101 RenderViewHost
* rvh
= (*it
)->render_view_host_
;
102 if (rvh
&& rvh
->GetDelegate() != delegate
)
104 if ((*it
)->IsAttached())
111 std::vector
<RenderViewHost
*> DevToolsAgentHost::GetValidRenderViewHosts() {
112 std::vector
<RenderViewHost
*> result
;
113 scoped_ptr
<RenderWidgetHostIterator
> widgets(
114 RenderWidgetHost::GetRenderWidgetHosts());
115 while (RenderWidgetHost
* widget
= widgets
->GetNextHost()) {
116 // Ignore processes that don't have a connection, such as crashed contents.
117 if (!widget
->GetProcess()->HasConnection())
119 if (!widget
->IsRenderView())
122 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
123 WebContents
* web_contents
= WebContents::FromRenderViewHost(rvh
);
127 // Don't report a RenderViewHost if it is not the current RenderViewHost
128 // for some WebContents (this filters out pre-render RVHs and similar).
129 // However report a RenderViewHost created for an out of process iframe.
130 // TODO (kaznacheev): Revisit this when it is clear how OOP iframes
131 // interact with pre-rendering.
132 // TODO (kaznacheev): GetMainFrame() call is a temporary hack. Iterate over
133 // all RenderFrameHost instances when multiple OOP frames are supported.
134 if (rvh
!= web_contents
->GetRenderViewHost() &&
135 !rvh
->GetMainFrame()->IsCrossProcessSubframe()) {
139 result
.push_back(rvh
);
145 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
146 RenderViewHost
* pending
,
147 RenderViewHost
* current
) {
148 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(pending
);
151 agent_host
->DisconnectRenderViewHost();
152 agent_host
->ConnectRenderViewHost(current
);
156 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
157 RenderViewHost
* source
,
158 const IPC::Message
& message
) {
159 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(source
);
160 return agent_host
&& agent_host
->DispatchIPCMessage(message
);
163 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(
165 : render_view_host_(NULL
),
166 overrides_handler_(new RendererOverridesHandler(this)),
167 tracing_handler_(new DevToolsTracingHandler()),
168 power_handler_(new DevToolsPowerHandler())
170 SetRenderViewHost(rvh
);
171 DevToolsProtocol::Notifier
notifier(base::Bind(
172 &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend
,
173 base::Unretained(this)));
174 overrides_handler_
->SetNotifier(notifier
);
175 tracing_handler_
->SetNotifier(notifier
);
176 power_handler_
->SetNotifier(notifier
);
177 g_instances
.Get().push_back(this);
178 AddRef(); // Balanced in RenderViewHostDestroyed.
181 RenderViewHost
* RenderViewDevToolsAgentHost::GetRenderViewHost() {
182 return render_view_host_
;
185 void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend(
186 const std::string
& message
) {
187 std::string error_message
;
188 scoped_refptr
<DevToolsProtocol::Command
> command
=
189 DevToolsProtocol::ParseCommand(message
, &error_message
);
192 scoped_refptr
<DevToolsProtocol::Response
> overridden_response
=
193 overrides_handler_
->HandleCommand(command
);
194 if (!overridden_response
)
195 overridden_response
= tracing_handler_
->HandleCommand(command
);
196 if (!overridden_response
)
197 overridden_response
= power_handler_
->HandleCommand(command
);
198 if (overridden_response
) {
199 if (!overridden_response
->is_async_promise())
200 OnDispatchOnInspectorFrontend(overridden_response
->Serialize());
205 IPCDevToolsAgentHost::DispatchOnInspectorBackend(message
);
208 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message
* msg
) {
209 if (!render_view_host_
)
211 msg
->set_routing_id(render_view_host_
->GetRoutingID());
212 render_view_host_
->Send(msg
);
215 void RenderViewDevToolsAgentHost::OnClientAttached() {
216 if (!render_view_host_
)
219 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
220 render_view_host_
->GetProcess()->GetID());
222 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
223 // extensions::ProcessManager no longer relies on this notification.
224 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, true);
226 #if defined(OS_ANDROID)
227 power_save_blocker_
.reset(
228 static_cast<PowerSaveBlockerImpl
*>(
229 PowerSaveBlocker::Create(
230 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep
,
231 "DevTools").release()));
232 if (render_view_host_
->GetView()) {
233 power_save_blocker_
.get()->
234 InitDisplaySleepBlocker(render_view_host_
->GetView()->GetNativeView());
239 void RenderViewDevToolsAgentHost::OnClientDetached() {
240 #if defined(OS_ANDROID)
241 power_save_blocker_
.reset();
243 overrides_handler_
->OnClientDetached();
244 ClientDetachedFromRenderer();
247 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
248 if (!render_view_host_
)
251 bool process_has_agents
= false;
252 RenderProcessHost
* render_process_host
= render_view_host_
->GetProcess();
253 for (Instances::iterator it
= g_instances
.Get().begin();
254 it
!= g_instances
.Get().end(); ++it
) {
255 if (*it
== this || !(*it
)->IsAttached())
257 RenderViewHost
* rvh
= (*it
)->render_view_host();
258 if (rvh
&& rvh
->GetProcess() == render_process_host
)
259 process_has_agents
= true;
262 // We are the last to disconnect from the renderer -> revoke permissions.
263 if (!process_has_agents
) {
264 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
265 render_process_host
->GetID());
268 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
269 // extensions::ProcessManager no longer relies on this notification.
270 DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false);
273 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
274 Instances::iterator it
= std::find(g_instances
.Get().begin(),
275 g_instances
.Get().end(),
277 if (it
!= g_instances
.Get().end())
278 g_instances
.Get().erase(it
);
281 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
282 RenderViewHost
* dest_rvh
) {
283 if (!render_view_host_
)
286 if (render_view_host_
== dest_rvh
&& static_cast<RenderViewHostImpl
*>(
287 render_view_host_
)->render_view_termination_status() ==
288 base::TERMINATION_STATUS_STILL_RUNNING
)
290 DisconnectRenderViewHost();
291 ConnectRenderViewHost(dest_rvh
);
294 void RenderViewDevToolsAgentHost::RenderViewHostChanged(
295 RenderViewHost
* old_host
,
296 RenderViewHost
* new_host
) {
297 if (new_host
!= render_view_host_
) {
298 // AboutToNavigateRenderView was not called for renderer-initiated
300 DisconnectRenderViewHost();
301 ConnectRenderViewHost(new_host
);
305 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost
* rvh
) {
306 if (rvh
!= render_view_host_
)
309 DCHECK(render_view_host_
);
310 scoped_refptr
<RenderViewDevToolsAgentHost
> protect(this);
311 NotifyCloseListener();
312 ClearRenderViewHost();
316 void RenderViewDevToolsAgentHost::RenderProcessGone(
317 base::TerminationStatus status
) {
319 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
320 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
321 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
322 #if defined(OS_ANDROID)
323 case base::TERMINATION_STATUS_OOM_PROTECTED
:
332 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
333 if (!render_view_host_
)
335 // The rvh set in AboutToNavigateRenderView turned out to be interstitial.
336 // Connect back to the real one.
337 WebContents
* web_contents
=
338 WebContents::FromRenderViewHost(render_view_host_
);
341 DisconnectRenderViewHost();
342 ConnectRenderViewHost(web_contents
->GetRenderViewHost());
345 void RenderViewDevToolsAgentHost::Observe(int type
,
346 const NotificationSource
& source
,
347 const NotificationDetails
& details
) {
348 if (type
== content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
) {
349 bool visible
= *Details
<bool>(details
).ptr();
350 overrides_handler_
->OnVisibilityChanged(visible
);
354 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost
* rvh
) {
355 DCHECK(!render_view_host_
);
356 render_view_host_
= rvh
;
358 WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh
));
362 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
363 content::Source
<RenderWidgetHost
>(render_view_host_
));
366 void RenderViewDevToolsAgentHost::ClearRenderViewHost() {
367 DCHECK(render_view_host_
);
370 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
371 content::Source
<RenderWidgetHost
>(render_view_host_
));
372 render_view_host_
= NULL
;
375 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost
* rvh
) {
376 SetRenderViewHost(rvh
);
381 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
382 ClientDetachedFromRenderer();
383 ClearRenderViewHost();
386 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
387 scoped_refptr
<DevToolsProtocol::Notification
> notification
=
388 DevToolsProtocol::CreateNotification(
389 devtools::Inspector::targetCrashed::kName
, NULL
);
390 DevToolsManagerImpl::GetInstance()->
391 DispatchOnInspectorFrontend(this, notification
->Serialize());
394 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
395 const IPC::Message
& msg
) {
396 if (!render_view_host_
)
400 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost
, msg
)
401 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend
,
402 OnDispatchOnInspectorFrontend
)
403 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState
,
404 OnSaveAgentRuntimeState
)
405 IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame
,
406 handled
= false; OnSwapCompositorFrame(msg
))
407 IPC_MESSAGE_UNHANDLED(handled
= false)
408 IPC_END_MESSAGE_MAP()
412 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
413 const IPC::Message
& message
) {
414 ViewHostMsg_SwapCompositorFrame::Param param
;
415 if (!ViewHostMsg_SwapCompositorFrame::Read(&message
, ¶m
))
417 overrides_handler_
->OnSwapCompositorFrame(param
.b
.metadata
);
420 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
421 const cc::CompositorFrameMetadata
& frame_metadata
) {
422 if (!render_view_host_
)
424 overrides_handler_
->OnSwapCompositorFrame(frame_metadata
);
427 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
428 const std::string
& state
) {
429 if (!render_view_host_
)
434 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
435 const std::string
& message
) {
436 if (!render_view_host_
)
438 DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(
442 } // namespace content