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/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_frame_trace_recorder.h"
12 #include "content/browser/devtools/devtools_protocol_handler.h"
13 #include "content/browser/devtools/protocol/dom_handler.h"
14 #include "content/browser/devtools/protocol/emulation_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/security_handler.h"
21 #include "content/browser/devtools/protocol/service_worker_handler.h"
22 #include "content/browser/devtools/protocol/tracing_handler.h"
23 #include "content/browser/frame_host/render_frame_host_impl.h"
24 #include "content/browser/renderer_host/render_process_host_impl.h"
25 #include "content/browser/renderer_host/render_view_host_impl.h"
26 #include "content/browser/site_instance_impl.h"
27 #include "content/browser/web_contents/web_contents_impl.h"
28 #include "content/common/view_messages.h"
29 #include "content/public/browser/browser_context.h"
30 #include "content/public/browser/content_browser_client.h"
31 #include "content/public/browser/render_widget_host_iterator.h"
32 #include "content/public/browser/web_contents_delegate.h"
34 #if defined(OS_ANDROID)
35 #include "content/browser/power_save_blocker_impl.h"
36 #include "content/public/browser/render_widget_host_view.h"
41 typedef std::vector
<RenderFrameDevToolsAgentHost
*> Instances
;
44 base::LazyInstance
<Instances
>::Leaky g_instances
= LAZY_INSTANCE_INITIALIZER
;
46 static RenderFrameDevToolsAgentHost
* FindAgentHost(RenderFrameHost
* host
) {
47 if (g_instances
== NULL
)
49 for (Instances::iterator it
= g_instances
.Get().begin();
50 it
!= g_instances
.Get().end(); ++it
) {
51 if ((*it
)->HasRenderFrameHost(host
))
57 // Returns RenderFrameDevToolsAgentHost attached to any of RenderFrameHost
58 // instances associated with |web_contents|
59 static RenderFrameDevToolsAgentHost
* FindAgentHost(WebContents
* web_contents
) {
60 if (g_instances
== NULL
)
62 for (Instances::iterator it
= g_instances
.Get().begin();
63 it
!= g_instances
.Get().end(); ++it
) {
64 if ((*it
)->GetWebContents() == web_contents
)
70 bool ShouldCreateDevToolsFor(RenderFrameHost
* rfh
) {
71 return rfh
->IsCrossProcessSubframe() || !rfh
->GetParent();
76 // RenderFrameDevToolsAgentHost::FrameHostHolder -------------------------------
78 class RenderFrameDevToolsAgentHost::FrameHostHolder
{
81 RenderFrameDevToolsAgentHost
* agent
, RenderFrameHostImpl
* host
);
84 RenderFrameHostImpl
* host() const { return host_
; }
87 void Reattach(FrameHostHolder
* old
);
89 void DispatchProtocolMessage(int call_id
, const std::string
& message
);
90 void InspectElement(int x
, int y
);
91 void ProcessChunkedMessageFromAgent(const DevToolsMessageChunk
& chunk
);
98 void SendMessageToClient(const std::string
& message
);
100 RenderFrameDevToolsAgentHost
* agent_
;
101 RenderFrameHostImpl
* host_
;
104 DevToolsMessageChunkProcessor chunk_processor_
;
105 std::vector
<std::string
> pending_messages_
;
106 std::map
<int, std::string
> sent_messages_
;
109 RenderFrameDevToolsAgentHost::FrameHostHolder::FrameHostHolder(
110 RenderFrameDevToolsAgentHost
* agent
, RenderFrameHostImpl
* host
)
115 chunk_processor_(base::Bind(
116 &RenderFrameDevToolsAgentHost::FrameHostHolder::SendMessageToClient
,
117 base::Unretained(this))) {
122 RenderFrameDevToolsAgentHost::FrameHostHolder::~FrameHostHolder() {
127 void RenderFrameDevToolsAgentHost::FrameHostHolder::Attach() {
128 host_
->Send(new DevToolsAgentMsg_Attach(
129 host_
->GetRoutingID(), agent_
->GetId()));
134 void RenderFrameDevToolsAgentHost::FrameHostHolder::Reattach(
135 FrameHostHolder
* old
) {
137 chunk_processor_
.set_state_cookie(old
->chunk_processor_
.state_cookie());
138 host_
->Send(new DevToolsAgentMsg_Reattach(
139 host_
->GetRoutingID(), agent_
->GetId(), chunk_processor_
.state_cookie()));
141 for (const auto& pair
: old
->sent_messages_
)
142 DispatchProtocolMessage(pair
.first
, pair
.second
);
148 void RenderFrameDevToolsAgentHost::FrameHostHolder::Detach() {
149 host_
->Send(new DevToolsAgentMsg_Detach(host_
->GetRoutingID()));
154 void RenderFrameDevToolsAgentHost::FrameHostHolder::GrantPolicy() {
155 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
156 host_
->GetProcess()->GetID());
159 void RenderFrameDevToolsAgentHost::FrameHostHolder::RevokePolicy() {
160 bool process_has_agents
= false;
161 RenderProcessHost
* process_host
= host_
->GetProcess();
162 for (RenderFrameDevToolsAgentHost
* agent
: g_instances
.Get()) {
163 if (!agent
->IsAttached())
165 if (agent
->current_
&& agent
->current_
->host() != host_
&&
166 agent
->current_
->host()->GetProcess() == process_host
) {
167 process_has_agents
= true;
169 if (agent
->pending_
&& agent
->pending_
->host() != host_
&&
170 agent
->pending_
->host()->GetProcess() == process_host
) {
171 process_has_agents
= true;
175 // We are the last to disconnect from the renderer -> revoke permissions.
176 if (!process_has_agents
) {
177 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
178 process_host
->GetID());
181 void RenderFrameDevToolsAgentHost::FrameHostHolder::DispatchProtocolMessage(
182 int call_id
, const std::string
& message
) {
183 host_
->Send(new DevToolsAgentMsg_DispatchOnInspectorBackend(
184 host_
->GetRoutingID(), message
));
185 sent_messages_
[call_id
] = message
;
188 void RenderFrameDevToolsAgentHost::FrameHostHolder::InspectElement(
190 host_
->Send(new DevToolsAgentMsg_InspectElement(
191 host_
->GetRoutingID(), agent_
->GetId(), x
, y
));
195 RenderFrameDevToolsAgentHost::FrameHostHolder::ProcessChunkedMessageFromAgent(
196 const DevToolsMessageChunk
& chunk
) {
197 chunk_processor_
.ProcessChunkedMessageFromAgent(chunk
);
200 void RenderFrameDevToolsAgentHost::FrameHostHolder::SendMessageToClient(
201 const std::string
& message
) {
202 sent_messages_
.erase(chunk_processor_
.last_call_id());
204 pending_messages_
.push_back(message
);
206 agent_
->SendMessageToClient(message
);
209 void RenderFrameDevToolsAgentHost::FrameHostHolder::Suspend() {
213 void RenderFrameDevToolsAgentHost::FrameHostHolder::Resume() {
215 for (const std::string
& message
: pending_messages_
)
216 agent_
->SendMessageToClient(message
);
217 std::vector
<std::string
> empty
;
218 pending_messages_
.swap(empty
);
221 // RenderFrameDevToolsAgentHost ------------------------------------------------
223 scoped_refptr
<DevToolsAgentHost
>
224 DevToolsAgentHost::GetOrCreateFor(WebContents
* web_contents
) {
225 RenderFrameDevToolsAgentHost
* result
= FindAgentHost(web_contents
);
227 // TODO(dgozman): this check should not be necessary. See
228 // http://crbug.com/489664.
229 if (!web_contents
->GetMainFrame())
231 result
= new RenderFrameDevToolsAgentHost(
232 static_cast<RenderFrameHostImpl
*>(web_contents
->GetMainFrame()));
238 scoped_refptr
<DevToolsAgentHost
> RenderFrameDevToolsAgentHost::GetOrCreateFor(
239 RenderFrameHostImpl
* host
) {
240 RenderFrameDevToolsAgentHost
* result
= FindAgentHost(host
);
242 result
= new RenderFrameDevToolsAgentHost(host
);
247 void RenderFrameDevToolsAgentHost::AppendAgentHostForFrameIfApplicable(
248 DevToolsAgentHost::List
* result
,
249 RenderFrameHost
* host
) {
250 RenderFrameHostImpl
* rfh
= static_cast<RenderFrameHostImpl
*>(host
);
251 if (!rfh
->IsRenderFrameLive())
253 if (ShouldCreateDevToolsFor(rfh
))
254 result
->push_back(RenderFrameDevToolsAgentHost::GetOrCreateFor(rfh
));
258 bool DevToolsAgentHost::HasFor(WebContents
* web_contents
) {
259 return FindAgentHost(web_contents
) != NULL
;
263 bool DevToolsAgentHost::IsDebuggerAttached(WebContents
* web_contents
) {
264 RenderFrameDevToolsAgentHost
* agent_host
= FindAgentHost(web_contents
);
265 return agent_host
&& agent_host
->IsAttached();
269 void RenderFrameDevToolsAgentHost::AddAllAgentHosts(
270 DevToolsAgentHost::List
* result
) {
271 base::Callback
<void(RenderFrameHost
*)> callback
= base::Bind(
272 RenderFrameDevToolsAgentHost::AppendAgentHostForFrameIfApplicable
,
273 base::Unretained(result
));
274 for (const auto& wc
: WebContentsImpl::GetAllWebContents())
275 wc
->ForEachFrame(callback
);
279 void RenderFrameDevToolsAgentHost::OnCancelPendingNavigation(
280 RenderFrameHost
* pending
,
281 RenderFrameHost
* current
) {
282 RenderFrameDevToolsAgentHost
* agent_host
= FindAgentHost(pending
);
285 if (agent_host
->pending_
&& agent_host
->pending_
->host() == pending
) {
286 DCHECK(agent_host
->current_
&& agent_host
->current_
->host() == current
);
287 agent_host
->DiscardPending();
291 RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost(
292 RenderFrameHostImpl
* host
)
293 : dom_handler_(new devtools::dom::DOMHandler()),
294 input_handler_(new devtools::input::InputHandler()),
295 inspector_handler_(new devtools::inspector::InspectorHandler()),
296 network_handler_(new devtools::network::NetworkHandler()),
297 page_handler_(nullptr),
298 power_handler_(new devtools::power::PowerHandler()),
299 security_handler_(nullptr),
300 service_worker_handler_(
301 new devtools::service_worker::ServiceWorkerHandler()),
302 tracing_handler_(new devtools::tracing::TracingHandler(
303 devtools::tracing::TracingHandler::Renderer
)),
304 emulation_handler_(nullptr),
305 frame_trace_recorder_(nullptr),
306 protocol_handler_(new DevToolsProtocolHandler(
308 base::Bind(&RenderFrameDevToolsAgentHost::SendMessageToClient
,
309 base::Unretained(this)))),
310 current_frame_crashed_(false) {
311 DevToolsProtocolDispatcher
* dispatcher
= protocol_handler_
->dispatcher();
312 dispatcher
->SetDOMHandler(dom_handler_
.get());
313 dispatcher
->SetInputHandler(input_handler_
.get());
314 dispatcher
->SetInspectorHandler(inspector_handler_
.get());
315 dispatcher
->SetNetworkHandler(network_handler_
.get());
316 dispatcher
->SetPowerHandler(power_handler_
.get());
317 dispatcher
->SetServiceWorkerHandler(service_worker_handler_
.get());
318 dispatcher
->SetTracingHandler(tracing_handler_
.get());
320 if (!host
->GetParent()) {
321 security_handler_
.reset(new devtools::security::SecurityHandler());
322 page_handler_
.reset(new devtools::page::PageHandler());
323 emulation_handler_
.reset(
324 new devtools::emulation::EmulationHandler(page_handler_
.get()));
325 dispatcher
->SetSecurityHandler(security_handler_
.get());
326 dispatcher
->SetPageHandler(page_handler_
.get());
327 dispatcher
->SetEmulationHandler(emulation_handler_
.get());
332 WebContentsObserver::Observe(WebContents::FromRenderFrameHost(host
));
334 g_instances
.Get().push_back(this);
335 AddRef(); // Balanced in RenderFrameHostDestroyed.
338 void RenderFrameDevToolsAgentHost::SetPending(RenderFrameHostImpl
* host
) {
340 current_frame_crashed_
= false;
341 pending_
.reset(new FrameHostHolder(this, host
));
343 pending_
->Reattach(current_
.get());
345 // Can only be null in constructor.
350 UpdateProtocolHandlers(host
);
353 void RenderFrameDevToolsAgentHost::CommitPending() {
355 current_frame_crashed_
= false;
357 if (!ShouldCreateDevToolsFor(pending_
->host())) {
358 DestroyOnRenderFrameGone();
359 // |this| may be deleted at this point.
363 current_
= pending_
.Pass();
364 UpdateProtocolHandlers(current_
->host());
368 void RenderFrameDevToolsAgentHost::DiscardPending() {
372 UpdateProtocolHandlers(current_
->host());
376 BrowserContext
* RenderFrameDevToolsAgentHost::GetBrowserContext() {
377 WebContents
* contents
= web_contents();
378 return contents
? contents
->GetBrowserContext() : nullptr;
381 WebContents
* RenderFrameDevToolsAgentHost::GetWebContents() {
382 return web_contents();
385 void RenderFrameDevToolsAgentHost::Attach() {
393 void RenderFrameDevToolsAgentHost::Detach() {
401 bool RenderFrameDevToolsAgentHost::DispatchProtocolMessage(
402 const std::string
& message
) {
404 if (protocol_handler_
->HandleOptionalMessage(message
, &call_id
))
408 current_
->DispatchProtocolMessage(call_id
, message
);
410 pending_
->DispatchProtocolMessage(call_id
, message
);
414 void RenderFrameDevToolsAgentHost::InspectElement(int x
, int y
) {
416 current_
->InspectElement(x
, y
);
418 pending_
->InspectElement(x
, y
);
421 void RenderFrameDevToolsAgentHost::OnClientAttached() {
425 frame_trace_recorder_
.reset(new DevToolsFrameTraceRecorder());
427 #if defined(OS_ANDROID)
428 power_save_blocker_
.reset(static_cast<PowerSaveBlockerImpl
*>(
429 PowerSaveBlocker::Create(
430 PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep
,
431 PowerSaveBlocker::kReasonOther
, "DevTools").release()));
432 power_save_blocker_
->InitDisplaySleepBlocker(web_contents());
435 // TODO(kaznacheev): Move this call back to DevToolsManager when
436 // extensions::ProcessManager no longer relies on this notification.
437 DevToolsAgentHostImpl::NotifyCallbacks(this, true);
440 void RenderFrameDevToolsAgentHost::OnClientDetached() {
441 #if defined(OS_ANDROID)
442 power_save_blocker_
.reset();
444 if (emulation_handler_
)
445 emulation_handler_
->Detached();
447 page_handler_
->Detached();
448 power_handler_
->Detached();
449 service_worker_handler_
->Detached();
450 tracing_handler_
->Detached();
451 frame_trace_recorder_
.reset();
453 // TODO(kaznacheev): Move this call back to DevToolsManager when
454 // extensions::ProcessManager no longer relies on this notification.
455 DevToolsAgentHostImpl::NotifyCallbacks(this, false);
458 RenderFrameDevToolsAgentHost::~RenderFrameDevToolsAgentHost() {
459 Instances::iterator it
= std::find(g_instances
.Get().begin(),
460 g_instances
.Get().end(),
462 if (it
!= g_instances
.Get().end())
463 g_instances
.Get().erase(it
);
466 // TODO(creis): Consider removing this in favor of RenderFrameHostChanged.
467 void RenderFrameDevToolsAgentHost::AboutToNavigateRenderFrame(
468 RenderFrameHost
* old_host
,
469 RenderFrameHost
* new_host
) {
470 DCHECK(!pending_
|| pending_
->host() != old_host
);
471 if (!current_
|| current_
->host() != old_host
)
473 if (old_host
== new_host
&& !current_frame_crashed_
)
476 SetPending(static_cast<RenderFrameHostImpl
*>(new_host
));
479 void RenderFrameDevToolsAgentHost::RenderFrameHostChanged(
480 RenderFrameHost
* old_host
,
481 RenderFrameHost
* new_host
) {
482 DCHECK(!pending_
|| pending_
->host() != old_host
);
483 if (!current_
|| current_
->host() != old_host
)
486 // AboutToNavigateRenderFrame was not called for renderer-initiated
489 SetPending(static_cast<RenderFrameHostImpl
*>(new_host
));
494 void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost
* rfh
) {
495 if (pending_
&& pending_
->host() == rfh
) {
500 if (current_
&& current_
->host() == rfh
)
501 DestroyOnRenderFrameGone(); // |this| may be deleted at this point.
504 void RenderFrameDevToolsAgentHost::RenderFrameDeleted(RenderFrameHost
* rfh
) {
505 if (!current_frame_crashed_
)
509 void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() {
511 scoped_refptr
<RenderFrameDevToolsAgentHost
> protect(this);
512 UpdateProtocolHandlers(nullptr);
521 void RenderFrameDevToolsAgentHost::RenderProcessGone(
522 base::TerminationStatus status
) {
524 case base::TERMINATION_STATUS_ABNORMAL_TERMINATION
:
525 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED
:
526 #if defined(OS_CHROMEOS)
527 case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM
:
529 case base::TERMINATION_STATUS_PROCESS_CRASHED
:
530 #if defined(OS_ANDROID)
531 case base::TERMINATION_STATUS_OOM_PROTECTED
:
533 inspector_handler_
->TargetCrashed();
534 current_frame_crashed_
= true;
541 bool RenderFrameDevToolsAgentHost::OnMessageReceived(
542 const IPC::Message
& message
) {
545 if (message
.type() == ViewHostMsg_SwapCompositorFrame::ID
)
546 OnSwapCompositorFrame(message
);
550 bool RenderFrameDevToolsAgentHost::OnMessageReceived(
551 const IPC::Message
& message
,
552 RenderFrameHost
* render_frame_host
) {
553 if (message
.type() != DevToolsClientMsg_DispatchOnInspectorFrontend::ID
)
558 FrameHostHolder
* holder
= nullptr;
559 if (current_
&& current_
->host() == render_frame_host
)
560 holder
= current_
.get();
561 if (pending_
&& pending_
->host() == render_frame_host
)
562 holder
= pending_
.get();
566 DevToolsClientMsg_DispatchOnInspectorFrontend::Param param
;
567 if (!DevToolsClientMsg_DispatchOnInspectorFrontend::Read(&message
, ¶m
))
569 holder
->ProcessChunkedMessageFromAgent(base::get
<0>(param
));
573 void RenderFrameDevToolsAgentHost::DidAttachInterstitialPage() {
575 page_handler_
->DidAttachInterstitialPage();
577 // TODO(dgozman): this may break for cross-process subframes.
580 // Pending set in AboutToNavigateRenderFrame turned out to be interstitial.
581 // Connect back to the real one.
585 void RenderFrameDevToolsAgentHost::DidDetachInterstitialPage() {
587 page_handler_
->DidDetachInterstitialPage();
590 void RenderFrameDevToolsAgentHost::DidCommitProvisionalLoadForFrame(
591 RenderFrameHost
* render_frame_host
,
593 ui::PageTransition transition_type
) {
594 if (pending_
&& pending_
->host() == render_frame_host
)
596 service_worker_handler_
->UpdateHosts();
599 void RenderFrameDevToolsAgentHost::DidFailProvisionalLoad(
600 RenderFrameHost
* render_frame_host
,
601 const GURL
& validated_url
,
603 const base::string16
& error_description
,
604 bool was_ignored_by_handler
) {
605 if (pending_
&& pending_
->host() == render_frame_host
)
609 void RenderFrameDevToolsAgentHost::UpdateProtocolHandlers(
610 RenderFrameHostImpl
* host
) {
611 dom_handler_
->SetRenderFrameHost(host
);
612 if (emulation_handler_
)
613 emulation_handler_
->SetRenderFrameHost(host
);
614 input_handler_
->SetRenderWidgetHost(
615 host
? host
->GetRenderWidgetHost() : nullptr);
616 inspector_handler_
->SetRenderFrameHost(host
);
617 network_handler_
->SetRenderFrameHost(host
);
619 page_handler_
->SetRenderFrameHost(host
);
620 service_worker_handler_
->SetRenderFrameHost(host
);
621 if (security_handler_
)
622 security_handler_
->SetRenderFrameHost(host
);
625 void RenderFrameDevToolsAgentHost::DisconnectWebContents() {
628 UpdateProtocolHandlers(nullptr);
630 WebContentsObserver::Observe(nullptr);
633 void RenderFrameDevToolsAgentHost::ConnectWebContents(WebContents
* wc
) {
636 RenderFrameHostImpl
* host
=
637 static_cast<RenderFrameHostImpl
*>(wc
->GetMainFrame());
641 WebContentsObserver::Observe(WebContents::FromRenderFrameHost(host
));
644 DevToolsAgentHost::Type
RenderFrameDevToolsAgentHost::GetType() {
645 return IsChildFrame() ? TYPE_FRAME
: TYPE_WEB_CONTENTS
;
648 std::string
RenderFrameDevToolsAgentHost::GetTitle() {
650 return GetURL().spec();
651 if (WebContents
* web_contents
= GetWebContents())
652 return base::UTF16ToUTF8(web_contents
->GetTitle());
656 GURL
RenderFrameDevToolsAgentHost::GetURL() {
657 // Order is important here.
658 WebContents
* web_contents
= GetWebContents();
659 if (web_contents
&& !IsChildFrame())
660 return web_contents
->GetVisibleURL();
662 return pending_
->host()->GetLastCommittedURL();
664 return current_
->host()->GetLastCommittedURL();
668 bool RenderFrameDevToolsAgentHost::Activate() {
669 WebContentsImpl
* wc
= static_cast<WebContentsImpl
*>(web_contents());
677 bool RenderFrameDevToolsAgentHost::Close() {
678 if (web_contents()) {
679 web_contents()->ClosePage();
685 void RenderFrameDevToolsAgentHost::OnSwapCompositorFrame(
686 const IPC::Message
& message
) {
687 ViewHostMsg_SwapCompositorFrame::Param param
;
688 if (!ViewHostMsg_SwapCompositorFrame::Read(&message
, ¶m
))
691 page_handler_
->OnSwapCompositorFrame(base::get
<1>(param
).metadata
);
693 input_handler_
->OnSwapCompositorFrame(base::get
<1>(param
).metadata
);
694 if (frame_trace_recorder_
) {
695 frame_trace_recorder_
->OnSwapCompositorFrame(
696 current_
? current_
->host() : nullptr,
697 base::get
<1>(param
).metadata
,
698 tracing_handler_
->did_initiate_recording());
702 void RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame(
703 const cc::CompositorFrameMetadata
& frame_metadata
) {
705 page_handler_
->OnSwapCompositorFrame(frame_metadata
);
707 input_handler_
->OnSwapCompositorFrame(frame_metadata
);
708 if (frame_trace_recorder_
) {
709 frame_trace_recorder_
->OnSwapCompositorFrame(
710 current_
? current_
->host() : nullptr,
712 tracing_handler_
->did_initiate_recording());
716 bool RenderFrameDevToolsAgentHost::HasRenderFrameHost(
717 RenderFrameHost
* host
) {
718 return (current_
&& current_
->host() == host
) ||
719 (pending_
&& pending_
->host() == host
);
722 bool RenderFrameDevToolsAgentHost::IsChildFrame() {
723 return current_
&& current_
->host()->GetParent();
726 } // namespace content