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.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/protocol/devtools_protocol_handler_impl.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"
27 #include "content/public/browser/web_contents_delegate.h"
29 #if defined(OS_ANDROID)
30 #include "content/browser/power_save_blocker_impl.h"
31 #include "content/public/browser/render_widget_host_view.h"
36 typedef std::vector
<RenderViewDevToolsAgentHost
*> Instances
;
39 base::LazyInstance
<Instances
>::Leaky g_instances
= LAZY_INSTANCE_INITIALIZER
;
41 //Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost
42 //instances associated with |web_contents|
43 static RenderViewDevToolsAgentHost
* FindAgentHost(WebContents
* web_contents
) {
44 if (g_instances
== NULL
)
46 for (Instances::iterator it
= g_instances
.Get().begin();
47 it
!= g_instances
.Get().end(); ++it
) {
48 if ((*it
)->GetWebContents() == web_contents
)
56 scoped_refptr
<DevToolsAgentHost
>
57 DevToolsAgentHost::GetOrCreateFor(WebContents
* web_contents
) {
58 RenderViewDevToolsAgentHost
* result
= FindAgentHost(web_contents
);
60 result
= new RenderViewDevToolsAgentHost(web_contents
->GetRenderViewHost());
65 bool DevToolsAgentHost::HasFor(WebContents
* web_contents
) {
66 return FindAgentHost(web_contents
) != NULL
;
70 bool DevToolsAgentHost::IsDebuggerAttached(WebContents
* web_contents
) {
71 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(web_contents
);
72 return agent_host
&& agent_host
->IsAttached();
76 std::vector
<WebContents
*> DevToolsAgentHostImpl::GetInspectableWebContents() {
77 std::set
<WebContents
*> set
;
78 scoped_ptr
<RenderWidgetHostIterator
> widgets(
79 RenderWidgetHost::GetRenderWidgetHosts());
80 while (RenderWidgetHost
* widget
= widgets
->GetNextHost()) {
81 // Ignore processes that don't have a connection, such as crashed contents.
82 if (!widget
->GetProcess()->HasConnection())
84 if (!widget
->IsRenderView())
87 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
88 WebContents
* web_contents
= WebContents::FromRenderViewHost(rvh
);
90 set
.insert(web_contents
);
92 std::vector
<WebContents
*> result(set
.size());
93 std::copy(set
.begin(), set
.end(), result
.begin());
98 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
99 RenderViewHost
* pending
,
100 RenderViewHost
* current
) {
101 WebContents
* web_contents
= WebContents::FromRenderViewHost(pending
);
102 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(web_contents
);
105 agent_host
->DisconnectRenderViewHost();
106 agent_host
->ConnectRenderViewHost(current
);
109 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost
* rvh
)
110 : render_view_host_(NULL
),
111 dom_handler_(new devtools::dom::DOMHandler()),
112 input_handler_(new devtools::input::InputHandler()),
113 network_handler_(new devtools::network::NetworkHandler()),
114 page_handler_(new devtools::page::PageHandler()),
115 power_handler_(new devtools::power::PowerHandler()),
116 handler_impl_(new DevToolsProtocolHandlerImpl()),
118 new DevToolsTracingHandler(DevToolsTracingHandler::Renderer
)),
119 reattaching_(false) {
120 handler_impl_
->SetDOMHandler(dom_handler_
.get());
121 handler_impl_
->SetInputHandler(input_handler_
.get());
122 handler_impl_
->SetNetworkHandler(network_handler_
.get());
123 handler_impl_
->SetPageHandler(page_handler_
.get());
124 handler_impl_
->SetPowerHandler(power_handler_
.get());
125 SetRenderViewHost(rvh
);
126 DevToolsProtocol::Notifier
notifier(base::Bind(
127 &RenderViewDevToolsAgentHost::DispatchOnInspectorFrontend
,
128 base::Unretained(this)));
129 handler_impl_
->SetNotifier(notifier
);
130 tracing_handler_
->SetNotifier(notifier
);
131 g_instances
.Get().push_back(this);
132 AddRef(); // Balanced in RenderViewHostDestroyed.
133 DevToolsManager::GetInstance()->AgentHostChanged(this);
136 WebContents
* RenderViewDevToolsAgentHost::GetWebContents() {
137 return web_contents();
140 void RenderViewDevToolsAgentHost::DispatchProtocolMessage(
141 const std::string
& message
) {
142 std::string error_message
;
144 scoped_ptr
<base::DictionaryValue
> message_dict(
145 DevToolsProtocol::ParseMessage(message
, &error_message
));
146 scoped_refptr
<DevToolsProtocol::Command
> command
=
147 DevToolsProtocol::ParseCommand(message_dict
.get(), &error_message
);
150 scoped_refptr
<DevToolsProtocol::Response
> overridden_response
;
152 DevToolsManagerDelegate
* delegate
=
153 DevToolsManager::GetInstance()->delegate();
155 scoped_ptr
<base::DictionaryValue
> overridden_response_value(
156 delegate
->HandleCommand(this, message_dict
.get()));
157 if (overridden_response_value
)
158 overridden_response
= DevToolsProtocol::ParseResponse(
159 overridden_response_value
.get());
161 if (!overridden_response
.get())
162 overridden_response
= tracing_handler_
->HandleCommand(command
);
163 if (!overridden_response
.get())
164 overridden_response
= handler_impl_
->HandleCommand(command
);
165 if (overridden_response
.get()) {
166 if (!overridden_response
->is_async_promise())
167 DispatchOnInspectorFrontend(overridden_response
->Serialize());
172 IPCDevToolsAgentHost::DispatchProtocolMessage(message
);
175 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message
* msg
) {
176 if (!render_view_host_
)
178 msg
->set_routing_id(render_view_host_
->GetRoutingID());
179 render_view_host_
->Send(msg
);
182 void RenderViewDevToolsAgentHost::OnClientAttached() {
183 if (!render_view_host_
)
186 InnerOnClientAttached();
188 // TODO(kaznacheev): Move this call back to DevToolsManager when
189 // extensions::ProcessManager no longer relies on this notification.
191 DevToolsAgentHostImpl::NotifyCallbacks(this, true);
194 void RenderViewDevToolsAgentHost::InnerOnClientAttached() {
195 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
196 render_view_host_
->GetProcess()->GetID());
198 #if defined(OS_ANDROID)
199 power_save_blocker_
.reset(
200 static_cast<PowerSaveBlockerImpl
*>(
201 PowerSaveBlocker::Create(
202 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep
,
203 "DevTools").release()));
204 if (render_view_host_
->GetView()) {
205 power_save_blocker_
.get()->
206 InitDisplaySleepBlocker(render_view_host_
->GetView()->GetNativeView());
211 void RenderViewDevToolsAgentHost::OnClientDetached() {
212 #if defined(OS_ANDROID)
213 power_save_blocker_
.reset();
215 tracing_handler_
->OnClientDetached();
216 page_handler_
->Detached();
217 power_handler_
->Detached();
218 ClientDetachedFromRenderer();
220 // TODO(kaznacheev): Move this call back to DevToolsManager when
221 // extensions::ProcessManager no longer relies on this notification.
223 DevToolsAgentHostImpl::NotifyCallbacks(this, false);
226 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
227 if (!render_view_host_
)
230 InnerClientDetachedFromRenderer();
233 void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() {
234 bool process_has_agents
= false;
235 RenderProcessHost
* render_process_host
= render_view_host_
->GetProcess();
236 for (Instances::iterator it
= g_instances
.Get().begin();
237 it
!= g_instances
.Get().end(); ++it
) {
238 if (*it
== this || !(*it
)->IsAttached())
240 RenderViewHost
* rvh
= (*it
)->render_view_host_
;
241 if (rvh
&& rvh
->GetProcess() == render_process_host
)
242 process_has_agents
= true;
245 // We are the last to disconnect from the renderer -> revoke permissions.
246 if (!process_has_agents
) {
247 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
248 render_process_host
->GetID());
252 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
253 Instances::iterator it
= std::find(g_instances
.Get().begin(),
254 g_instances
.Get().end(),
256 if (it
!= g_instances
.Get().end())
257 g_instances
.Get().erase(it
);
260 void RenderViewDevToolsAgentHost::AboutToNavigateRenderView(
261 RenderViewHost
* dest_rvh
) {
262 if (!render_view_host_
)
265 if (render_view_host_
== dest_rvh
&&
266 render_view_host_
->render_view_termination_status() ==
267 base::TERMINATION_STATUS_STILL_RUNNING
)
269 ReattachToRenderViewHost(dest_rvh
);
272 void RenderViewDevToolsAgentHost::RenderViewHostChanged(
273 RenderViewHost
* old_host
,
274 RenderViewHost
* new_host
) {
275 if (new_host
!= render_view_host_
) {
276 // AboutToNavigateRenderView was not called for renderer-initiated
278 ReattachToRenderViewHost(new_host
);
283 RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost
* rvh
) {
284 DCHECK(!reattaching_
);
286 DisconnectRenderViewHost();
287 ConnectRenderViewHost(rvh
);
288 reattaching_
= false;
291 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost
* rvh
) {
292 if (rvh
!= render_view_host_
)
295 DCHECK(render_view_host_
);
296 scoped_refptr
<RenderViewDevToolsAgentHost
> protect(this);
298 ClearRenderViewHost();
299 DevToolsManager::GetInstance()->AgentHostChanged(this);
303 void RenderViewDevToolsAgentHost::RenderProcessGone(
304 base::TerminationStatus status
) {
306 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
307 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
308 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
309 #if defined(OS_ANDROID)
310 case base::TERMINATION_STATUS_OOM_PROTECTED
:
319 bool RenderViewDevToolsAgentHost::OnMessageReceived(
320 const IPC::Message
& message
,
321 RenderFrameHost
* render_frame_host
) {
322 return DispatchIPCMessage(message
);
325 bool RenderViewDevToolsAgentHost::OnMessageReceived(
326 const IPC::Message
& message
) {
327 return DispatchIPCMessage(message
);
330 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
331 page_handler_
->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::DidDetachInterstitialPage() {
346 page_handler_
->DidDetachInterstitialPage();
349 void RenderViewDevToolsAgentHost::TitleWasSet(
350 NavigationEntry
* entry
, bool explicit_set
) {
351 DevToolsManager::GetInstance()->AgentHostChanged(this);
354 void RenderViewDevToolsAgentHost::NavigationEntryCommitted(
355 const LoadCommittedDetails
& load_details
) {
356 DevToolsManager::GetInstance()->AgentHostChanged(this);
359 void RenderViewDevToolsAgentHost::Observe(int type
,
360 const NotificationSource
& source
,
361 const NotificationDetails
& details
) {
362 if (type
== content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
) {
363 bool visible
= *Details
<bool>(details
).ptr();
364 page_handler_
->OnVisibilityChanged(visible
);
368 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost
* rvh
) {
369 DCHECK(!render_view_host_
);
370 render_view_host_
= static_cast<RenderViewHostImpl
*>(rvh
);
372 WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh
));
373 dom_handler_
->SetRenderViewHost(render_view_host_
);
374 input_handler_
->SetRenderViewHost(render_view_host_
);
375 network_handler_
->SetRenderViewHost(render_view_host_
);
376 page_handler_
->SetRenderViewHost(render_view_host_
);
380 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
381 content::Source
<RenderWidgetHost
>(render_view_host_
));
384 void RenderViewDevToolsAgentHost::ClearRenderViewHost() {
385 DCHECK(render_view_host_
);
388 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
389 content::Source
<RenderWidgetHost
>(render_view_host_
));
390 render_view_host_
= nullptr;
391 dom_handler_
->SetRenderViewHost(nullptr);
392 input_handler_
->SetRenderViewHost(nullptr);
393 network_handler_
->SetRenderViewHost(nullptr);
394 page_handler_
->SetRenderViewHost(nullptr);
397 void RenderViewDevToolsAgentHost::DisconnectWebContents() {
398 DisconnectRenderViewHost();
401 void RenderViewDevToolsAgentHost::ConnectWebContents(WebContents
* wc
) {
402 ConnectRenderViewHost(wc
->GetRenderViewHost());
405 DevToolsAgentHost::Type
RenderViewDevToolsAgentHost::GetType() {
406 return TYPE_WEB_CONTENTS
;
409 std::string
RenderViewDevToolsAgentHost::GetTitle() {
410 if (WebContents
* web_contents
= GetWebContents())
411 return base::UTF16ToUTF8(web_contents
->GetTitle());
415 GURL
RenderViewDevToolsAgentHost::GetURL() {
416 if (WebContents
* web_contents
= GetWebContents())
417 return web_contents
->GetVisibleURL();
418 return render_view_host_
?
419 render_view_host_
->GetMainFrame()->GetLastCommittedURL() : GURL();
422 bool RenderViewDevToolsAgentHost::Activate() {
423 if (render_view_host_
) {
424 render_view_host_
->GetDelegate()->Activate();
430 bool RenderViewDevToolsAgentHost::Close() {
431 if (render_view_host_
) {
432 render_view_host_
->ClosePage();
438 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost
* rvh
) {
439 SetRenderViewHost(rvh
);
444 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
445 ClientDetachedFromRenderer();
446 ClearRenderViewHost();
449 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
450 scoped_refptr
<DevToolsProtocol::Notification
> notification
=
451 DevToolsProtocol::CreateNotification(
452 devtools::Inspector::targetCrashed::kName
, NULL
);
453 SendMessageToClient(notification
->Serialize());
456 bool RenderViewDevToolsAgentHost::DispatchIPCMessage(
457 const IPC::Message
& msg
) {
458 if (!render_view_host_
)
462 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost
, msg
)
463 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend
,
464 OnDispatchOnInspectorFrontend
)
465 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState
,
466 OnSaveAgentRuntimeState
)
467 IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_SwapCompositorFrame
,
468 handled
= false; OnSwapCompositorFrame(msg
))
469 IPC_MESSAGE_UNHANDLED(handled
= false)
470 IPC_END_MESSAGE_MAP()
474 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
475 const IPC::Message
& message
) {
476 ViewHostMsg_SwapCompositorFrame::Param param
;
477 if (!ViewHostMsg_SwapCompositorFrame::Read(&message
, ¶m
))
479 page_handler_
->OnSwapCompositorFrame(param
.b
.metadata
);
482 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
483 const cc::CompositorFrameMetadata
& frame_metadata
) {
484 if (!render_view_host_
)
486 page_handler_
->OnSwapCompositorFrame(frame_metadata
);
489 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
490 const std::string
& state
) {
491 if (!render_view_host_
)
496 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
497 const std::string
& message
,
499 if (!IsAttached() || !render_view_host_
)
501 ProcessChunkedMessageFromAgent(message
, total_size
);
504 void RenderViewDevToolsAgentHost::DispatchOnInspectorFrontend(
505 const std::string
& message
) {
506 if (!IsAttached() || !render_view_host_
)
508 SendMessageToClient(message
);
511 } // namespace content