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_frame_devtools_agent_host.h"
7 #include "base/basictypes.h"
8 #include "base/json/json_writer.h"
9 #include "base/lazy_instance.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/browser/child_process_security_policy_impl.h"
12 #include "content/browser/devtools/devtools_frame_trace_recorder.h"
13 #include "content/browser/devtools/devtools_manager.h"
14 #include "content/browser/devtools/protocol/devtools_protocol_handler.h"
15 #include "content/browser/devtools/protocol/dom_handler.h"
16 #include "content/browser/devtools/protocol/input_handler.h"
17 #include "content/browser/devtools/protocol/inspector_handler.h"
18 #include "content/browser/devtools/protocol/network_handler.h"
19 #include "content/browser/devtools/protocol/page_handler.h"
20 #include "content/browser/devtools/protocol/power_handler.h"
21 #include "content/browser/devtools/protocol/tracing_handler.h"
22 #include "content/browser/frame_host/render_frame_host_impl.h"
23 #include "content/browser/renderer_host/render_process_host_impl.h"
24 #include "content/browser/renderer_host/render_view_host_impl.h"
25 #include "content/browser/site_instance_impl.h"
26 #include "content/browser/web_contents/web_contents_impl.h"
27 #include "content/common/view_messages.h"
28 #include "content/public/browser/browser_context.h"
29 #include "content/public/browser/content_browser_client.h"
30 #include "content/public/browser/devtools_manager_delegate.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_types.h"
33 #include "content/public/browser/render_widget_host_iterator.h"
34 #include "content/public/browser/web_contents_delegate.h"
36 #if defined(OS_ANDROID)
37 #include "content/browser/power_save_blocker_impl.h"
38 #include "content/public/browser/render_widget_host_view.h"
43 typedef std::vector
<RenderFrameDevToolsAgentHost
*> Instances
;
46 base::LazyInstance
<Instances
>::Leaky g_instances
= LAZY_INSTANCE_INITIALIZER
;
48 static RenderFrameDevToolsAgentHost
* FindAgentHost(RenderFrameHost
* host
) {
49 if (g_instances
== NULL
)
51 for (Instances::iterator it
= g_instances
.Get().begin();
52 it
!= g_instances
.Get().end(); ++it
) {
53 if ((*it
)->HasRenderFrameHost(host
))
59 // Returns RenderFrameDevToolsAgentHost attached to any of RenderFrameHost
60 // instances associated with |web_contents|
61 static RenderFrameDevToolsAgentHost
* FindAgentHost(WebContents
* web_contents
) {
62 if (g_instances
== NULL
)
64 for (Instances::iterator it
= g_instances
.Get().begin();
65 it
!= g_instances
.Get().end(); ++it
) {
66 if ((*it
)->GetWebContents() == web_contents
)
74 scoped_refptr
<DevToolsAgentHost
>
75 DevToolsAgentHost::GetOrCreateFor(WebContents
* web_contents
) {
76 RenderFrameDevToolsAgentHost
* result
= FindAgentHost(web_contents
);
78 result
= new RenderFrameDevToolsAgentHost(web_contents
->GetMainFrame());
83 bool DevToolsAgentHost::HasFor(WebContents
* web_contents
) {
84 return FindAgentHost(web_contents
) != NULL
;
88 bool DevToolsAgentHost::IsDebuggerAttached(WebContents
* web_contents
) {
89 RenderFrameDevToolsAgentHost
* agent_host
= FindAgentHost(web_contents
);
90 return agent_host
&& agent_host
->IsAttached();
94 std::vector
<WebContents
*> DevToolsAgentHostImpl::GetInspectableWebContents() {
95 std::set
<WebContents
*> set
;
96 scoped_ptr
<RenderWidgetHostIterator
> widgets(
97 RenderWidgetHost::GetRenderWidgetHosts());
98 while (RenderWidgetHost
* widget
= widgets
->GetNextHost()) {
99 // Ignore processes that don't have a connection, such as crashed contents.
100 if (!widget
->GetProcess()->HasConnection())
102 if (!widget
->IsRenderView())
105 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
106 WebContents
* web_contents
= WebContents::FromRenderViewHost(rvh
);
108 set
.insert(web_contents
);
110 std::vector
<WebContents
*> result(set
.size());
111 std::copy(set
.begin(), set
.end(), result
.begin());
116 void RenderFrameDevToolsAgentHost::OnCancelPendingNavigation(
117 RenderFrameHost
* pending
,
118 RenderFrameHost
* current
) {
119 RenderFrameDevToolsAgentHost
* agent_host
= FindAgentHost(pending
);
122 agent_host
->DisconnectRenderFrameHost();
123 agent_host
->ConnectRenderFrameHost(current
);
126 RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost(RenderFrameHost
* rfh
)
127 : render_frame_host_(NULL
),
128 dom_handler_(new devtools::dom::DOMHandler()),
129 input_handler_(new devtools::input::InputHandler()),
130 inspector_handler_(new devtools::inspector::InspectorHandler()),
131 network_handler_(new devtools::network::NetworkHandler()),
132 page_handler_(new devtools::page::PageHandler()),
133 power_handler_(new devtools::power::PowerHandler()),
134 tracing_handler_(new devtools::tracing::TracingHandler(
135 devtools::tracing::TracingHandler::Renderer
)),
136 protocol_handler_(new DevToolsProtocolHandler(
137 base::Bind(&RenderFrameDevToolsAgentHost::DispatchOnInspectorFrontend
,
138 base::Unretained(this)))),
139 frame_trace_recorder_(new DevToolsFrameTraceRecorder()),
140 reattaching_(false) {
141 DevToolsProtocolDispatcher
* dispatcher
= protocol_handler_
->dispatcher();
142 dispatcher
->SetDOMHandler(dom_handler_
.get());
143 dispatcher
->SetInputHandler(input_handler_
.get());
144 dispatcher
->SetInspectorHandler(inspector_handler_
.get());
145 dispatcher
->SetNetworkHandler(network_handler_
.get());
146 dispatcher
->SetPageHandler(page_handler_
.get());
147 dispatcher
->SetPowerHandler(power_handler_
.get());
148 dispatcher
->SetTracingHandler(tracing_handler_
.get());
149 SetRenderFrameHost(rfh
);
150 g_instances
.Get().push_back(this);
151 AddRef(); // Balanced in RenderFrameHostDestroyed.
152 DevToolsManager::GetInstance()->AgentHostChanged(this);
155 BrowserContext
* RenderFrameDevToolsAgentHost::GetBrowserContext() {
156 WebContents
* contents
= web_contents();
157 return contents
? contents
->GetBrowserContext() : nullptr;
160 WebContents
* RenderFrameDevToolsAgentHost::GetWebContents() {
161 return web_contents();
164 void RenderFrameDevToolsAgentHost::DispatchProtocolMessage(
165 const std::string
& message
) {
166 scoped_ptr
<base::DictionaryValue
> command
=
167 protocol_handler_
->ParseCommand(message
);
171 DevToolsManagerDelegate
* delegate
=
172 DevToolsManager::GetInstance()->delegate();
174 scoped_ptr
<base::DictionaryValue
> response(
175 delegate
->HandleCommand(this, command
.get()));
177 std::string json_response
;
178 base::JSONWriter::Write(response
.get(), &json_response
);
179 DispatchOnInspectorFrontend(json_response
);
184 if (protocol_handler_
->HandleOptionalCommand(command
.Pass()))
187 IPCDevToolsAgentHost::DispatchProtocolMessage(message
);
190 void RenderFrameDevToolsAgentHost::SendMessageToAgent(IPC::Message
* msg
) {
191 if (!render_frame_host_
)
193 msg
->set_routing_id(render_frame_host_
->GetRoutingID());
194 render_frame_host_
->Send(msg
);
197 void RenderFrameDevToolsAgentHost::OnClientAttached() {
198 if (!render_frame_host_
)
201 InnerOnClientAttached();
203 // TODO(kaznacheev): Move this call back to DevToolsManager when
204 // extensions::ProcessManager no longer relies on this notification.
206 DevToolsAgentHostImpl::NotifyCallbacks(this, true);
209 void RenderFrameDevToolsAgentHost::InnerOnClientAttached() {
210 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
211 render_frame_host_
->GetProcess()->GetID());
213 #if defined(OS_ANDROID)
214 power_save_blocker_
.reset(static_cast<PowerSaveBlockerImpl
*>(
215 PowerSaveBlocker::Create(
216 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep
,
217 PowerSaveBlocker::kReasonOther
, "DevTools").release()));
218 RenderViewHostImpl
* rvh
= static_cast<RenderViewHostImpl
*>(
219 render_frame_host_
->GetRenderViewHost());
220 if (rvh
->GetView()) {
221 power_save_blocker_
.get()->
222 InitDisplaySleepBlocker(rvh
->GetView()->GetNativeView());
227 void RenderFrameDevToolsAgentHost::OnClientDetached() {
228 #if defined(OS_ANDROID)
229 power_save_blocker_
.reset();
231 page_handler_
->Detached();
232 power_handler_
->Detached();
233 tracing_handler_
->Detached();
234 ClientDetachedFromRenderer();
236 // TODO(kaznacheev): Move this call back to DevToolsManager when
237 // extensions::ProcessManager no longer relies on this notification.
239 DevToolsAgentHostImpl::NotifyCallbacks(this, false);
242 void RenderFrameDevToolsAgentHost::ClientDetachedFromRenderer() {
243 if (!render_frame_host_
)
246 InnerClientDetachedFromRenderer();
249 void RenderFrameDevToolsAgentHost::InnerClientDetachedFromRenderer() {
250 bool process_has_agents
= false;
251 RenderProcessHost
* render_process_host
= render_frame_host_
->GetProcess();
252 for (Instances::iterator it
= g_instances
.Get().begin();
253 it
!= g_instances
.Get().end(); ++it
) {
254 if (*it
== this || !(*it
)->IsAttached())
256 RenderFrameHost
* rfh
= (*it
)->render_frame_host_
;
257 if (rfh
&& rfh
->GetProcess() == render_process_host
)
258 process_has_agents
= true;
261 // We are the last to disconnect from the renderer -> revoke permissions.
262 if (!process_has_agents
) {
263 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
264 render_process_host
->GetID());
268 RenderFrameDevToolsAgentHost::~RenderFrameDevToolsAgentHost() {
269 Instances::iterator it
= std::find(g_instances
.Get().begin(),
270 g_instances
.Get().end(),
272 if (it
!= g_instances
.Get().end())
273 g_instances
.Get().erase(it
);
276 // TODO(creis): Consider removing this in favor of RenderFrameHostChanged.
277 void RenderFrameDevToolsAgentHost::AboutToNavigateRenderFrame(
278 RenderFrameHost
* old_host
,
279 RenderFrameHost
* new_host
) {
280 if (render_frame_host_
!= old_host
)
283 // TODO(creis): This will need to be updated for --site-per-process, since
284 // RenderViewHost is going away and navigations could happen in any frame.
285 if (render_frame_host_
== new_host
) {
286 RenderViewHostImpl
* rvh
= static_cast<RenderViewHostImpl
*>(
287 render_frame_host_
->GetRenderViewHost());
288 if (rvh
->render_view_termination_status() ==
289 base::TERMINATION_STATUS_STILL_RUNNING
)
292 ReattachToRenderFrameHost(new_host
);
295 void RenderFrameDevToolsAgentHost::RenderFrameHostChanged(
296 RenderFrameHost
* old_host
,
297 RenderFrameHost
* new_host
) {
298 if (old_host
== render_frame_host_
&& new_host
!= render_frame_host_
) {
299 // AboutToNavigateRenderFrame was not called for renderer-initiated
301 ReattachToRenderFrameHost(new_host
);
306 RenderFrameDevToolsAgentHost::ReattachToRenderFrameHost(RenderFrameHost
* rfh
) {
307 DCHECK(!reattaching_
);
309 DisconnectRenderFrameHost();
310 ConnectRenderFrameHost(rfh
);
311 reattaching_
= false;
314 void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost
* rfh
) {
315 if (rfh
!= render_frame_host_
)
318 DCHECK(render_frame_host_
);
319 scoped_refptr
<RenderFrameDevToolsAgentHost
> protect(this);
321 ClearRenderFrameHost();
322 DevToolsManager::GetInstance()->AgentHostChanged(this);
326 void RenderFrameDevToolsAgentHost::RenderProcessGone(
327 base::TerminationStatus status
) {
329 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
330 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
331 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
332 #if defined(OS_ANDROID)
333 case base::TERMINATION_STATUS_OOM_PROTECTED
:
335 RenderFrameCrashed();
342 bool RenderFrameDevToolsAgentHost::OnMessageReceived(
343 const IPC::Message
& message
) {
344 if (!render_frame_host_
)
346 if (message
.type() == ViewHostMsg_SwapCompositorFrame::ID
)
347 OnSwapCompositorFrame(message
);
351 bool RenderFrameDevToolsAgentHost::OnMessageReceived(
352 const IPC::Message
& message
,
353 RenderFrameHost
* render_frame_host
) {
354 if (!render_frame_host_
|| render_frame_host
!= render_frame_host_
)
358 IPC_BEGIN_MESSAGE_MAP(RenderFrameDevToolsAgentHost
, message
)
359 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend
,
360 OnDispatchOnInspectorFrontend
)
361 IPC_MESSAGE_UNHANDLED(handled
= false)
362 IPC_END_MESSAGE_MAP()
366 void RenderFrameDevToolsAgentHost::DidAttachInterstitialPage() {
367 page_handler_
->DidAttachInterstitialPage();
369 if (!render_frame_host_
)
371 // The rvh set in AboutToNavigateRenderFrame turned out to be interstitial.
372 // Connect back to the real one.
373 WebContents
* web_contents
=
374 WebContents::FromRenderFrameHost(render_frame_host_
);
377 DisconnectRenderFrameHost();
378 ConnectRenderFrameHost(web_contents
->GetMainFrame());
381 void RenderFrameDevToolsAgentHost::DidDetachInterstitialPage() {
382 page_handler_
->DidDetachInterstitialPage();
385 void RenderFrameDevToolsAgentHost::TitleWasSet(
386 NavigationEntry
* entry
, bool explicit_set
) {
387 DevToolsManager::GetInstance()->AgentHostChanged(this);
390 void RenderFrameDevToolsAgentHost::NavigationEntryCommitted(
391 const LoadCommittedDetails
& load_details
) {
392 DevToolsManager::GetInstance()->AgentHostChanged(this);
395 void RenderFrameDevToolsAgentHost::Observe(int type
,
396 const NotificationSource
& source
,
397 const NotificationDetails
& details
) {
398 if (type
== content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
) {
399 bool visible
= *Details
<bool>(details
).ptr();
400 page_handler_
->OnVisibilityChanged(visible
);
404 void RenderFrameDevToolsAgentHost::SetRenderFrameHost(RenderFrameHost
* rfh
) {
405 DCHECK(!render_frame_host_
);
406 render_frame_host_
= static_cast<RenderFrameHostImpl
*>(rfh
);
408 WebContentsObserver::Observe(WebContents::FromRenderFrameHost(rfh
));
409 RenderViewHostImpl
* rvh
= static_cast<RenderViewHostImpl
*>(
410 rfh
->GetRenderViewHost());
411 dom_handler_
->SetRenderViewHost(rvh
);
412 input_handler_
->SetRenderViewHost(rvh
);
413 network_handler_
->SetRenderViewHost(rvh
);
414 page_handler_
->SetRenderViewHost(rvh
);
418 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
419 content::Source
<RenderWidgetHost
>(rvh
));
422 void RenderFrameDevToolsAgentHost::ClearRenderFrameHost() {
423 DCHECK(render_frame_host_
);
424 RenderViewHostImpl
* rvh
= static_cast<RenderViewHostImpl
*>(
425 render_frame_host_
->GetRenderViewHost());
428 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
429 content::Source
<RenderWidgetHost
>(rvh
));
430 render_frame_host_
= nullptr;
431 dom_handler_
->SetRenderViewHost(nullptr);
432 input_handler_
->SetRenderViewHost(nullptr);
433 network_handler_
->SetRenderViewHost(nullptr);
434 page_handler_
->SetRenderViewHost(nullptr);
437 void RenderFrameDevToolsAgentHost::DisconnectWebContents() {
438 DisconnectRenderFrameHost();
441 void RenderFrameDevToolsAgentHost::ConnectWebContents(WebContents
* wc
) {
442 ConnectRenderFrameHost(wc
->GetMainFrame());
445 DevToolsAgentHost::Type
RenderFrameDevToolsAgentHost::GetType() {
446 return TYPE_WEB_CONTENTS
;
449 std::string
RenderFrameDevToolsAgentHost::GetTitle() {
450 if (WebContents
* web_contents
= GetWebContents())
451 return base::UTF16ToUTF8(web_contents
->GetTitle());
455 GURL
RenderFrameDevToolsAgentHost::GetURL() {
456 if (WebContents
* web_contents
= GetWebContents())
457 return web_contents
->GetVisibleURL();
458 return render_frame_host_
?
459 render_frame_host_
->GetLastCommittedURL() : GURL();
462 bool RenderFrameDevToolsAgentHost::Activate() {
463 if (render_frame_host_
) {
464 render_frame_host_
->GetRenderViewHost()->GetDelegate()->Activate();
470 bool RenderFrameDevToolsAgentHost::Close() {
471 if (render_frame_host_
) {
472 render_frame_host_
->GetRenderViewHost()->ClosePage();
478 void RenderFrameDevToolsAgentHost::ConnectRenderFrameHost(
479 RenderFrameHost
* rfh
) {
480 SetRenderFrameHost(rfh
);
485 void RenderFrameDevToolsAgentHost::DisconnectRenderFrameHost() {
486 ClientDetachedFromRenderer();
487 ClearRenderFrameHost();
490 void RenderFrameDevToolsAgentHost::RenderFrameCrashed() {
491 inspector_handler_
->TargetCrashed();
494 void RenderFrameDevToolsAgentHost::OnSwapCompositorFrame(
495 const IPC::Message
& message
) {
496 ViewHostMsg_SwapCompositorFrame::Param param
;
497 if (!ViewHostMsg_SwapCompositorFrame::Read(&message
, ¶m
))
499 page_handler_
->OnSwapCompositorFrame(get
<1>(param
).metadata
);
500 frame_trace_recorder_
->OnSwapCompositorFrame(
501 render_frame_host_
, get
<1>(param
).metadata
);
504 void RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame(
505 const cc::CompositorFrameMetadata
& frame_metadata
) {
506 if (!render_frame_host_
)
508 page_handler_
->OnSwapCompositorFrame(frame_metadata
);
509 frame_trace_recorder_
->OnSwapCompositorFrame(
510 render_frame_host_
, frame_metadata
);
513 bool RenderFrameDevToolsAgentHost::HasRenderFrameHost(
514 RenderFrameHost
* host
) {
515 return host
== render_frame_host_
;
518 void RenderFrameDevToolsAgentHost::OnDispatchOnInspectorFrontend(
519 const DevToolsMessageChunk
& message
) {
520 if (!IsAttached() || !render_frame_host_
)
522 ProcessChunkedMessageFromAgent(message
);
525 void RenderFrameDevToolsAgentHost::DispatchOnInspectorFrontend(
526 const std::string
& message
) {
527 if (!IsAttached() || !render_frame_host_
)
529 SendMessageToClient(message
);
532 } // namespace content