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 "base/strings/utf_string_conversions.h"
10 #include "content/browser/child_process_security_policy_impl.h"
11 #include "content/browser/devtools/devtools_manager_impl.h"
12 #include "content/browser/devtools/devtools_power_handler.h"
13 #include "content/browser/devtools/devtools_protocol.h"
14 #include "content/browser/devtools/devtools_protocol_constants.h"
15 #include "content/browser/devtools/devtools_tracing_handler.h"
16 #include "content/browser/devtools/renderer_overrides_handler.h"
17 #include "content/browser/renderer_host/render_process_host_impl.h"
18 #include "content/browser/renderer_host/render_view_host_impl.h"
19 #include "content/browser/site_instance_impl.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/common/devtools_messages.h"
22 #include "content/common/view_messages.h"
23 #include "content/public/browser/content_browser_client.h"
24 #include "content/public/browser/devtools_manager_delegate.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/browser/render_widget_host_iterator.h"
28 #include "content/public/browser/web_contents_delegate.h"
30 #if defined(OS_ANDROID)
31 #include "content/browser/power_save_blocker_impl.h"
32 #include "content/public/browser/render_widget_host_view.h"
37 typedef std::vector
<RenderViewDevToolsAgentHost
*> Instances
;
40 base::LazyInstance
<Instances
>::Leaky g_instances
= LAZY_INSTANCE_INITIALIZER
;
42 //Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost
43 //instances associated with |web_contents|
44 static RenderViewDevToolsAgentHost
* FindAgentHost(WebContents
* web_contents
) {
45 if (g_instances
== NULL
)
47 for (Instances::iterator it
= g_instances
.Get().begin();
48 it
!= g_instances
.Get().end(); ++it
) {
49 if ((*it
)->GetWebContents() == web_contents
)
57 scoped_refptr
<DevToolsAgentHost
>
58 DevToolsAgentHost::GetOrCreateFor(WebContents
* web_contents
) {
59 RenderViewDevToolsAgentHost
* result
= FindAgentHost(web_contents
);
61 result
= new RenderViewDevToolsAgentHost(web_contents
->GetRenderViewHost());
66 bool DevToolsAgentHost::HasFor(WebContents
* web_contents
) {
67 return FindAgentHost(web_contents
) != NULL
;
71 bool DevToolsAgentHost::IsDebuggerAttached(WebContents
* web_contents
) {
72 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(web_contents
);
73 return agent_host
&& agent_host
->IsAttached();
77 std::vector
<WebContents
*> DevToolsAgentHostImpl::GetInspectableWebContents() {
78 std::set
<WebContents
*> set
;
79 scoped_ptr
<RenderWidgetHostIterator
> widgets(
80 RenderWidgetHost::GetRenderWidgetHosts());
81 while (RenderWidgetHost
* widget
= widgets
->GetNextHost()) {
82 // Ignore processes that don't have a connection, such as crashed contents.
83 if (!widget
->GetProcess()->HasConnection())
85 if (!widget
->IsRenderView())
88 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
89 WebContents
* web_contents
= WebContents::FromRenderViewHost(rvh
);
91 set
.insert(web_contents
);
93 std::vector
<WebContents
*> result(set
.size());
94 std::copy(set
.begin(), set
.end(), result
.begin());
99 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
100 RenderViewHost
* pending
,
101 RenderViewHost
* current
) {
102 WebContents
* web_contents
= WebContents::FromRenderViewHost(pending
);
103 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(web_contents
);
106 agent_host
->DisconnectRenderViewHost();
107 agent_host
->ConnectRenderViewHost(current
);
110 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost
* rvh
)
111 : render_view_host_(NULL
),
112 overrides_handler_(new RendererOverridesHandler()),
114 new DevToolsTracingHandler(DevToolsTracingHandler::Renderer
)),
115 power_handler_(new DevToolsPowerHandler()),
116 reattaching_(false) {
117 SetRenderViewHost(rvh
);
118 DevToolsProtocol::Notifier
notifier(base::Bind(
119 &RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend
,
120 base::Unretained(this)));
121 overrides_handler_
->SetNotifier(notifier
);
122 tracing_handler_
->SetNotifier(notifier
);
123 power_handler_
->SetNotifier(notifier
);
124 g_instances
.Get().push_back(this);
125 AddRef(); // Balanced in RenderViewHostDestroyed.
128 WebContents
* RenderViewDevToolsAgentHost::GetWebContents() {
129 return web_contents();
132 void RenderViewDevToolsAgentHost::DispatchProtocolMessage(
133 const std::string
& message
) {
134 std::string error_message
;
136 scoped_ptr
<base::DictionaryValue
> message_dict(
137 DevToolsProtocol::ParseMessage(message
, &error_message
));
138 scoped_refptr
<DevToolsProtocol::Command
> command
=
139 DevToolsProtocol::ParseCommand(message_dict
.get(), &error_message
);
142 scoped_refptr
<DevToolsProtocol::Response
> overridden_response
;
144 DevToolsManagerDelegate
* delegate
=
145 DevToolsManagerImpl::GetInstance()->delegate();
147 scoped_ptr
<base::DictionaryValue
> overridden_response_value(
148 delegate
->HandleCommand(this, message_dict
.get()));
149 if (overridden_response_value
)
150 overridden_response
= DevToolsProtocol::ParseResponse(
151 overridden_response_value
.get());
153 if (!overridden_response
.get())
154 overridden_response
= overrides_handler_
->HandleCommand(command
);
155 if (!overridden_response
.get())
156 overridden_response
= tracing_handler_
->HandleCommand(command
);
157 if (!overridden_response
.get())
158 overridden_response
= power_handler_
->HandleCommand(command
);
159 if (overridden_response
.get()) {
160 if (!overridden_response
->is_async_promise())
161 OnDispatchOnInspectorFrontend(overridden_response
->Serialize());
166 IPCDevToolsAgentHost::DispatchProtocolMessage(message
);
169 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message
* msg
) {
170 if (!render_view_host_
)
172 msg
->set_routing_id(render_view_host_
->GetRoutingID());
173 render_view_host_
->Send(msg
);
176 void RenderViewDevToolsAgentHost::OnClientAttached() {
177 if (!render_view_host_
)
180 InnerOnClientAttached();
182 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
183 // extensions::ProcessManager no longer relies on this notification.
185 DevToolsAgentHostImpl::NotifyCallbacks(this, true);
188 void RenderViewDevToolsAgentHost::InnerOnClientAttached() {
189 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
190 render_view_host_
->GetProcess()->GetID());
192 #if defined(OS_ANDROID)
193 power_save_blocker_
.reset(
194 static_cast<PowerSaveBlockerImpl
*>(
195 PowerSaveBlocker::Create(
196 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep
,
197 "DevTools").release()));
198 if (render_view_host_
->GetView()) {
199 power_save_blocker_
.get()->
200 InitDisplaySleepBlocker(render_view_host_
->GetView()->GetNativeView());
205 void RenderViewDevToolsAgentHost::OnClientDetached() {
206 #if defined(OS_ANDROID)
207 power_save_blocker_
.reset();
209 overrides_handler_
->OnClientDetached();
210 tracing_handler_
->OnClientDetached();
211 power_handler_
->OnClientDetached();
212 ClientDetachedFromRenderer();
214 // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when
215 // extensions::ProcessManager no longer relies on this notification.
217 DevToolsAgentHostImpl::NotifyCallbacks(this, false);
220 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
221 if (!render_view_host_
)
224 InnerClientDetachedFromRenderer();
227 void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() {
228 bool process_has_agents
= false;
229 RenderProcessHost
* render_process_host
= render_view_host_
->GetProcess();
230 for (Instances::iterator it
= g_instances
.Get().begin();
231 it
!= g_instances
.Get().end(); ++it
) {
232 if (*it
== this || !(*it
)->IsAttached())
234 RenderViewHost
* rvh
= (*it
)->render_view_host_
;
235 if (rvh
&& rvh
->GetProcess() == render_process_host
)
236 process_has_agents
= true;
239 // We are the last to disconnect from the renderer -> revoke permissions.
240 if (!process_has_agents
) {
241 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
242 render_process_host
->GetID());
246 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
247 Instances::iterator it
= std::find(g_instances
.Get().begin(),
248 g_instances
.Get().end(),
250 if (it
!= g_instances
.Get().end())
251 g_instances
.Get().erase(it
);
254 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
255 RenderViewHost
* dest_rvh
) {
256 if (!render_view_host_
)
259 if (render_view_host_
== dest_rvh
&&
260 render_view_host_
->render_view_termination_status() ==
261 base::TERMINATION_STATUS_STILL_RUNNING
)
263 ReattachToRenderViewHost(dest_rvh
);
266 void RenderViewDevToolsAgentHost::RenderViewHostChanged(
267 RenderViewHost
* old_host
,
268 RenderViewHost
* new_host
) {
269 if (new_host
!= render_view_host_
) {
270 // AboutToNavigateRenderView was not called for renderer-initiated
272 ReattachToRenderViewHost(new_host
);
277 RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost
* rvh
) {
278 DCHECK(!reattaching_
);
280 DisconnectRenderViewHost();
281 ConnectRenderViewHost(rvh
);
282 reattaching_
= false;
285 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost
* rvh
) {
286 if (rvh
!= render_view_host_
)
289 DCHECK(render_view_host_
);
290 scoped_refptr
<RenderViewDevToolsAgentHost
> protect(this);
292 ClearRenderViewHost();
296 void RenderViewDevToolsAgentHost::RenderProcessGone(
297 base::TerminationStatus status
) {
299 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
300 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
301 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
302 #if defined(OS_ANDROID)
303 case base::TERMINATION_STATUS_OOM_PROTECTED
:
312 bool RenderViewDevToolsAgentHost::OnMessageReceived(
313 const IPC::Message
& message
,
314 RenderFrameHost
* render_frame_host
) {
315 return DispatchIPCMessage(message
);
318 bool RenderViewDevToolsAgentHost::OnMessageReceived(
319 const IPC::Message
& message
) {
320 return DispatchIPCMessage(message
);
323 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
324 overrides_handler_
->DidAttachInterstitialPage();
326 if (!render_view_host_
)
328 // The rvh set in AboutToNavigateRenderView turned out to be interstitial.
329 // Connect back to the real one.
330 WebContents
* web_contents
=
331 WebContents::FromRenderViewHost(render_view_host_
);
334 DisconnectRenderViewHost();
335 ConnectRenderViewHost(web_contents
->GetRenderViewHost());
338 void RenderViewDevToolsAgentHost::DidDetachInterstitialPage() {
339 overrides_handler_
->DidDetachInterstitialPage();
342 void RenderViewDevToolsAgentHost::Observe(int type
,
343 const NotificationSource
& source
,
344 const NotificationDetails
& details
) {
345 if (type
== content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
) {
346 bool visible
= *Details
<bool>(details
).ptr();
347 overrides_handler_
->OnVisibilityChanged(visible
);
351 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost
* rvh
) {
352 DCHECK(!render_view_host_
);
353 render_view_host_
= static_cast<RenderViewHostImpl
*>(rvh
);
355 WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh
));
356 overrides_handler_
->SetRenderViewHost(render_view_host_
);
360 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
361 content::Source
<RenderWidgetHost
>(render_view_host_
));
364 void RenderViewDevToolsAgentHost::ClearRenderViewHost() {
365 DCHECK(render_view_host_
);
368 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
369 content::Source
<RenderWidgetHost
>(render_view_host_
));
370 render_view_host_
= NULL
;
371 overrides_handler_
->ClearRenderViewHost();
374 void RenderViewDevToolsAgentHost::DisconnectWebContents() {
375 DisconnectRenderViewHost();
378 void RenderViewDevToolsAgentHost::ConnectWebContents(WebContents
* wc
) {
379 ConnectRenderViewHost(wc
->GetRenderViewHost());
382 DevToolsAgentHost::Type
RenderViewDevToolsAgentHost::GetType() {
383 return TYPE_WEB_CONTENTS
;
386 std::string
RenderViewDevToolsAgentHost::GetTitle() {
387 if (WebContents
* web_contents
= GetWebContents())
388 return base::UTF16ToUTF8(web_contents
->GetTitle());
392 GURL
RenderViewDevToolsAgentHost::GetURL() {
393 if (WebContents
* web_contents
= GetWebContents())
394 return web_contents
->GetVisibleURL();
395 return render_view_host_
?
396 render_view_host_
->GetMainFrame()->GetLastCommittedURL() : GURL();
399 bool RenderViewDevToolsAgentHost::Activate() {
400 if (render_view_host_
) {
401 render_view_host_
->GetDelegate()->Activate();
407 bool RenderViewDevToolsAgentHost::Close() {
408 if (render_view_host_
) {
409 render_view_host_
->ClosePage();
415 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost
* rvh
) {
416 SetRenderViewHost(rvh
);
421 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
422 ClientDetachedFromRenderer();
423 ClearRenderViewHost();
426 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
427 scoped_refptr
<DevToolsProtocol::Notification
> notification
=
428 DevToolsProtocol::CreateNotification(
429 devtools::Inspector::targetCrashed::kName
, NULL
);
430 SendMessageToClient(notification
->Serialize());
433 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
434 const IPC::Message
& msg
) {
435 if (!render_view_host_
)
439 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost
, msg
)
440 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend
,
441 OnDispatchOnInspectorFrontend
)
442 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState
,
443 OnSaveAgentRuntimeState
)
444 IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame
,
445 handled
= false; OnSwapCompositorFrame(msg
))
446 IPC_MESSAGE_UNHANDLED(handled
= false)
447 IPC_END_MESSAGE_MAP()
451 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
452 const IPC::Message
& message
) {
453 ViewHostMsg_SwapCompositorFrame::Param param
;
454 if (!ViewHostMsg_SwapCompositorFrame::Read(&message
, ¶m
))
456 overrides_handler_
->OnSwapCompositorFrame(param
.b
.metadata
);
459 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
460 const cc::CompositorFrameMetadata
& frame_metadata
) {
461 if (!render_view_host_
)
463 overrides_handler_
->OnSwapCompositorFrame(frame_metadata
);
466 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
467 const std::string
& state
) {
468 if (!render_view_host_
)
473 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
474 const std::string
& message
) {
475 if (!render_view_host_
)
478 scoped_refptr
<DevToolsProtocol::Notification
> notification
=
479 DevToolsProtocol::ParseNotification(message
);
481 if (notification
.get()) {
482 tracing_handler_
->HandleNotification(notification
);
484 SendMessageToClient(message
);
487 } // namespace content