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/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_manager.h"
13 #include "content/browser/devtools/protocol/devtools_protocol_handler.h"
14 #include "content/browser/devtools/protocol/dom_handler.h"
15 #include "content/browser/devtools/protocol/input_handler.h"
16 #include "content/browser/devtools/protocol/inspector_handler.h"
17 #include "content/browser/devtools/protocol/network_handler.h"
18 #include "content/browser/devtools/protocol/page_handler.h"
19 #include "content/browser/devtools/protocol/power_handler.h"
20 #include "content/browser/devtools/protocol/tracing_handler.h"
21 #include "content/browser/renderer_host/render_process_host_impl.h"
22 #include "content/browser/renderer_host/render_view_host_impl.h"
23 #include "content/browser/site_instance_impl.h"
24 #include "content/browser/web_contents/web_contents_impl.h"
25 #include "content/common/devtools_messages.h"
26 #include "content/common/view_messages.h"
27 #include "content/public/browser/browser_context.h"
28 #include "content/public/browser/content_browser_client.h"
29 #include "content/public/browser/devtools_manager_delegate.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/notification_types.h"
32 #include "content/public/browser/render_widget_host_iterator.h"
33 #include "content/public/browser/web_contents_delegate.h"
35 #if defined(OS_ANDROID)
36 #include "content/browser/power_save_blocker_impl.h"
37 #include "content/public/browser/render_widget_host_view.h"
42 typedef std::vector
<RenderViewDevToolsAgentHost
*> Instances
;
45 base::LazyInstance
<Instances
>::Leaky g_instances
= LAZY_INSTANCE_INITIALIZER
;
47 //Returns RenderViewDevToolsAgentHost attached to any of RenderViewHost
48 //instances associated with |web_contents|
49 static RenderViewDevToolsAgentHost
* FindAgentHost(WebContents
* web_contents
) {
50 if (g_instances
== NULL
)
52 for (Instances::iterator it
= g_instances
.Get().begin();
53 it
!= g_instances
.Get().end(); ++it
) {
54 if ((*it
)->GetWebContents() == web_contents
)
62 scoped_refptr
<DevToolsAgentHost
>
63 DevToolsAgentHost::GetOrCreateFor(WebContents
* web_contents
) {
64 RenderViewDevToolsAgentHost
* result
= FindAgentHost(web_contents
);
66 result
= new RenderViewDevToolsAgentHost(web_contents
->GetRenderViewHost());
71 bool DevToolsAgentHost::HasFor(WebContents
* web_contents
) {
72 return FindAgentHost(web_contents
) != NULL
;
76 bool DevToolsAgentHost::IsDebuggerAttached(WebContents
* web_contents
) {
77 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(web_contents
);
78 return agent_host
&& agent_host
->IsAttached();
82 std::vector
<WebContents
*> DevToolsAgentHostImpl::GetInspectableWebContents() {
83 std::set
<WebContents
*> set
;
84 scoped_ptr
<RenderWidgetHostIterator
> widgets(
85 RenderWidgetHost::GetRenderWidgetHosts());
86 while (RenderWidgetHost
* widget
= widgets
->GetNextHost()) {
87 // Ignore processes that don't have a connection, such as crashed contents.
88 if (!widget
->GetProcess()->HasConnection())
90 if (!widget
->IsRenderView())
93 RenderViewHost
* rvh
= RenderViewHost::From(widget
);
94 WebContents
* web_contents
= WebContents::FromRenderViewHost(rvh
);
96 set
.insert(web_contents
);
98 std::vector
<WebContents
*> result(set
.size());
99 std::copy(set
.begin(), set
.end(), result
.begin());
104 void RenderViewDevToolsAgentHost::OnCancelPendingNavigation(
105 RenderViewHost
* pending
,
106 RenderViewHost
* current
) {
107 WebContents
* web_contents
= WebContents::FromRenderViewHost(pending
);
108 RenderViewDevToolsAgentHost
* agent_host
= FindAgentHost(web_contents
);
111 agent_host
->DisconnectRenderViewHost();
112 agent_host
->ConnectRenderViewHost(current
);
115 RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost
* rvh
)
116 : render_view_host_(NULL
),
117 dom_handler_(new devtools::dom::DOMHandler()),
118 input_handler_(new devtools::input::InputHandler()),
119 inspector_handler_(new devtools::inspector::InspectorHandler()),
120 network_handler_(new devtools::network::NetworkHandler()),
121 page_handler_(new devtools::page::PageHandler()),
122 power_handler_(new devtools::power::PowerHandler()),
123 tracing_handler_(new devtools::tracing::TracingHandler(
124 devtools::tracing::TracingHandler::Renderer
)),
125 protocol_handler_(new DevToolsProtocolHandler(
126 false /* handle_generic_errors */,
127 base::Bind(&RenderViewDevToolsAgentHost::DispatchOnInspectorFrontend
,
128 base::Unretained(this)))),
129 reattaching_(false) {
130 DevToolsProtocolDispatcher
* dispatcher
= protocol_handler_
->dispatcher();
131 dispatcher
->SetDOMHandler(dom_handler_
.get());
132 dispatcher
->SetInputHandler(input_handler_
.get());
133 dispatcher
->SetInspectorHandler(inspector_handler_
.get());
134 dispatcher
->SetNetworkHandler(network_handler_
.get());
135 dispatcher
->SetPageHandler(page_handler_
.get());
136 dispatcher
->SetPowerHandler(power_handler_
.get());
137 dispatcher
->SetTracingHandler(tracing_handler_
.get());
138 SetRenderViewHost(rvh
);
139 g_instances
.Get().push_back(this);
140 AddRef(); // Balanced in RenderViewHostDestroyed.
141 DevToolsManager::GetInstance()->AgentHostChanged(this);
144 BrowserContext
* RenderViewDevToolsAgentHost::GetBrowserContext() {
145 WebContents
* contents
= web_contents();
146 return contents
? contents
->GetBrowserContext() : nullptr;
149 WebContents
* RenderViewDevToolsAgentHost::GetWebContents() {
150 return web_contents();
153 void RenderViewDevToolsAgentHost::DispatchProtocolMessage(
154 const std::string
& message
) {
156 scoped_ptr
<base::DictionaryValue
> command
=
157 protocol_handler_
->ParseCommand(message
);
159 DevToolsManagerDelegate
* delegate
=
160 DevToolsManager::GetInstance()->delegate();
162 scoped_ptr
<base::DictionaryValue
> response(
163 delegate
->HandleCommand(this, command
.get()));
165 std::string json_response
;
166 base::JSONWriter::Write(response
.get(), &json_response
);
167 DispatchOnInspectorFrontend(json_response
);
171 if (protocol_handler_
->HandleCommand(command
.Pass()))
175 IPCDevToolsAgentHost::DispatchProtocolMessage(message
);
178 void RenderViewDevToolsAgentHost::SendMessageToAgent(IPC::Message
* msg
) {
179 if (!render_view_host_
)
181 RenderFrameHost
* main_frame_host
= render_view_host_
->GetMainFrame();
182 msg
->set_routing_id(main_frame_host
->GetRoutingID());
183 main_frame_host
->Send(msg
);
186 void RenderViewDevToolsAgentHost::OnClientAttached() {
187 if (!render_view_host_
)
190 InnerOnClientAttached();
192 // TODO(kaznacheev): Move this call back to DevToolsManager when
193 // extensions::ProcessManager no longer relies on this notification.
195 DevToolsAgentHostImpl::NotifyCallbacks(this, true);
198 void RenderViewDevToolsAgentHost::InnerOnClientAttached() {
199 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
200 render_view_host_
->GetProcess()->GetID());
202 #if defined(OS_ANDROID)
203 power_save_blocker_
.reset(
204 static_cast<PowerSaveBlockerImpl
*>(
205 PowerSaveBlocker::Create(
206 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep
,
207 "DevTools").release()));
208 if (render_view_host_
->GetView()) {
209 power_save_blocker_
.get()->
210 InitDisplaySleepBlocker(render_view_host_
->GetView()->GetNativeView());
215 void RenderViewDevToolsAgentHost::OnClientDetached() {
216 #if defined(OS_ANDROID)
217 power_save_blocker_
.reset();
219 page_handler_
->Detached();
220 power_handler_
->Detached();
221 tracing_handler_
->Detached();
222 ClientDetachedFromRenderer();
224 // TODO(kaznacheev): Move this call back to DevToolsManager when
225 // extensions::ProcessManager no longer relies on this notification.
227 DevToolsAgentHostImpl::NotifyCallbacks(this, false);
230 void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() {
231 if (!render_view_host_
)
234 InnerClientDetachedFromRenderer();
237 void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() {
238 bool process_has_agents
= false;
239 RenderProcessHost
* render_process_host
= render_view_host_
->GetProcess();
240 for (Instances::iterator it
= g_instances
.Get().begin();
241 it
!= g_instances
.Get().end(); ++it
) {
242 if (*it
== this || !(*it
)->IsAttached())
244 RenderViewHost
* rvh
= (*it
)->render_view_host_
;
245 if (rvh
&& rvh
->GetProcess() == render_process_host
)
246 process_has_agents
= true;
249 // We are the last to disconnect from the renderer -> revoke permissions.
250 if (!process_has_agents
) {
251 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
252 render_process_host
->GetID());
256 RenderViewDevToolsAgentHost::~RenderViewDevToolsAgentHost() {
257 Instances::iterator it
= std::find(g_instances
.Get().begin(),
258 g_instances
.Get().end(),
260 if (it
!= g_instances
.Get().end())
261 g_instances
.Get().erase(it
);
264 // TODO(creis): Consider removing this in favor of RenderFrameHostChanged.
265 void RenderViewDevToolsAgentHost::AboutToNavigateRenderFrame(
266 RenderFrameHost
* render_frame_host
) {
267 if (!render_view_host_
)
270 // TODO(creis): This will need to be updated for --site-per-process, since
271 // RenderViewHost is going away and navigations could happen in any frame.
272 if (render_view_host_
== render_frame_host
->GetRenderViewHost() &&
273 render_view_host_
->render_view_termination_status() ==
274 base::TERMINATION_STATUS_STILL_RUNNING
)
276 ReattachToRenderViewHost(render_frame_host
->GetRenderViewHost());
279 // TODO(creis): Move to RenderFrameHostChanged.
280 void RenderViewDevToolsAgentHost::RenderViewHostChanged(
281 RenderViewHost
* old_host
,
282 RenderViewHost
* new_host
) {
283 if (new_host
!= render_view_host_
) {
284 // AboutToNavigateRenderFrame was not called for renderer-initiated
286 ReattachToRenderViewHost(new_host
);
291 RenderViewDevToolsAgentHost::ReattachToRenderViewHost(RenderViewHost
* rvh
) {
292 DCHECK(!reattaching_
);
294 DisconnectRenderViewHost();
295 ConnectRenderViewHost(rvh
);
296 reattaching_
= false;
299 void RenderViewDevToolsAgentHost::RenderViewDeleted(RenderViewHost
* rvh
) {
300 if (rvh
!= render_view_host_
)
303 DCHECK(render_view_host_
);
304 scoped_refptr
<RenderViewDevToolsAgentHost
> protect(this);
306 ClearRenderViewHost();
307 DevToolsManager::GetInstance()->AgentHostChanged(this);
311 void RenderViewDevToolsAgentHost::RenderProcessGone(
312 base::TerminationStatus status
) {
314 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
315 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
316 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
317 #if defined(OS_ANDROID)
318 case base::TERMINATION_STATUS_OOM_PROTECTED
:
327 bool RenderViewDevToolsAgentHost::OnMessageReceived(
328 const IPC::Message
& message
) {
329 if (!render_view_host_
)
331 if (message
.type() == ViewHostMsg_SwapCompositorFrame::ID
)
332 OnSwapCompositorFrame(message
);
336 bool RenderViewDevToolsAgentHost::OnMessageReceived(
337 const IPC::Message
& message
,
338 RenderFrameHost
* render_frame_host
) {
339 if (!render_view_host_
)
341 if (render_frame_host
!= render_view_host_
->GetMainFrame())
345 IPC_BEGIN_MESSAGE_MAP(RenderViewDevToolsAgentHost
, message
)
346 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend
,
347 OnDispatchOnInspectorFrontend
)
348 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState
,
349 OnSaveAgentRuntimeState
)
350 IPC_MESSAGE_UNHANDLED(handled
= false)
351 IPC_END_MESSAGE_MAP()
355 void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() {
356 page_handler_
->DidAttachInterstitialPage();
358 if (!render_view_host_
)
360 // The rvh set in AboutToNavigateRenderFrame turned out to be interstitial.
361 // Connect back to the real one.
362 WebContents
* web_contents
=
363 WebContents::FromRenderViewHost(render_view_host_
);
366 DisconnectRenderViewHost();
367 ConnectRenderViewHost(web_contents
->GetRenderViewHost());
370 void RenderViewDevToolsAgentHost::DidDetachInterstitialPage() {
371 page_handler_
->DidDetachInterstitialPage();
374 void RenderViewDevToolsAgentHost::TitleWasSet(
375 NavigationEntry
* entry
, bool explicit_set
) {
376 DevToolsManager::GetInstance()->AgentHostChanged(this);
379 void RenderViewDevToolsAgentHost::NavigationEntryCommitted(
380 const LoadCommittedDetails
& load_details
) {
381 DevToolsManager::GetInstance()->AgentHostChanged(this);
384 void RenderViewDevToolsAgentHost::Observe(int type
,
385 const NotificationSource
& source
,
386 const NotificationDetails
& details
) {
387 if (type
== content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
) {
388 bool visible
= *Details
<bool>(details
).ptr();
389 page_handler_
->OnVisibilityChanged(visible
);
393 void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost
* rvh
) {
394 DCHECK(!render_view_host_
);
395 render_view_host_
= static_cast<RenderViewHostImpl
*>(rvh
);
397 WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh
));
398 dom_handler_
->SetRenderViewHost(render_view_host_
);
399 input_handler_
->SetRenderViewHost(render_view_host_
);
400 network_handler_
->SetRenderViewHost(render_view_host_
);
401 page_handler_
->SetRenderViewHost(render_view_host_
);
405 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
406 content::Source
<RenderWidgetHost
>(render_view_host_
));
409 void RenderViewDevToolsAgentHost::ClearRenderViewHost() {
410 DCHECK(render_view_host_
);
413 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED
,
414 content::Source
<RenderWidgetHost
>(render_view_host_
));
415 render_view_host_
= nullptr;
416 dom_handler_
->SetRenderViewHost(nullptr);
417 input_handler_
->SetRenderViewHost(nullptr);
418 network_handler_
->SetRenderViewHost(nullptr);
419 page_handler_
->SetRenderViewHost(nullptr);
422 void RenderViewDevToolsAgentHost::DisconnectWebContents() {
423 DisconnectRenderViewHost();
426 void RenderViewDevToolsAgentHost::ConnectWebContents(WebContents
* wc
) {
427 ConnectRenderViewHost(wc
->GetRenderViewHost());
430 DevToolsAgentHost::Type
RenderViewDevToolsAgentHost::GetType() {
431 return TYPE_WEB_CONTENTS
;
434 std::string
RenderViewDevToolsAgentHost::GetTitle() {
435 if (WebContents
* web_contents
= GetWebContents())
436 return base::UTF16ToUTF8(web_contents
->GetTitle());
440 GURL
RenderViewDevToolsAgentHost::GetURL() {
441 if (WebContents
* web_contents
= GetWebContents())
442 return web_contents
->GetVisibleURL();
443 return render_view_host_
?
444 render_view_host_
->GetMainFrame()->GetLastCommittedURL() : GURL();
447 bool RenderViewDevToolsAgentHost::Activate() {
448 if (render_view_host_
) {
449 render_view_host_
->GetDelegate()->Activate();
455 bool RenderViewDevToolsAgentHost::Close() {
456 if (render_view_host_
) {
457 render_view_host_
->ClosePage();
463 void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost
* rvh
) {
464 SetRenderViewHost(rvh
);
469 void RenderViewDevToolsAgentHost::DisconnectRenderViewHost() {
470 ClientDetachedFromRenderer();
471 ClearRenderViewHost();
474 void RenderViewDevToolsAgentHost::RenderViewCrashed() {
475 inspector_handler_
->TargetCrashed();
478 void RenderViewDevToolsAgentHost::OnSwapCompositorFrame(
479 const IPC::Message
& message
) {
480 ViewHostMsg_SwapCompositorFrame::Param param
;
481 if (!ViewHostMsg_SwapCompositorFrame::Read(&message
, ¶m
))
483 page_handler_
->OnSwapCompositorFrame(param
.b
.metadata
);
486 void RenderViewDevToolsAgentHost::SynchronousSwapCompositorFrame(
487 const cc::CompositorFrameMetadata
& frame_metadata
) {
488 if (!render_view_host_
)
490 page_handler_
->OnSwapCompositorFrame(frame_metadata
);
493 void RenderViewDevToolsAgentHost::OnSaveAgentRuntimeState(
494 const std::string
& state
) {
495 if (!render_view_host_
)
500 void RenderViewDevToolsAgentHost::OnDispatchOnInspectorFrontend(
501 const std::string
& message
,
503 if (!IsAttached() || !render_view_host_
)
505 ProcessChunkedMessageFromAgent(message
, total_size
);
508 void RenderViewDevToolsAgentHost::DispatchOnInspectorFrontend(
509 const std::string
& message
) {
510 if (!IsAttached() || !render_view_host_
)
512 SendMessageToClient(message
);
515 } // namespace content