blob: f2171cb30f414a8e30cd567732a61b290bc68e54 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/devtools/render_frame_devtools_agent_host.h"
#include <tuple>
#include <utility>
#include "base/json/json_reader.h"
#include "base/lazy_instance.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/viz/common/features.h"
#include "content/browser/bad_message.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/devtools/devtools_frame_trace_recorder.h"
#include "content/browser/devtools/devtools_manager.h"
#include "content/browser/devtools/devtools_session.h"
#include "content/browser/devtools/protocol/browser_handler.h"
#include "content/browser/devtools/protocol/dom_handler.h"
#include "content/browser/devtools/protocol/emulation_handler.h"
#include "content/browser/devtools/protocol/input_handler.h"
#include "content/browser/devtools/protocol/inspector_handler.h"
#include "content/browser/devtools/protocol/io_handler.h"
#include "content/browser/devtools/protocol/memory_handler.h"
#include "content/browser/devtools/protocol/network_handler.h"
#include "content/browser/devtools/protocol/page_handler.h"
#include "content/browser/devtools/protocol/protocol.h"
#include "content/browser/devtools/protocol/schema_handler.h"
#include "content/browser/devtools/protocol/security_handler.h"
#include "content/browser/devtools/protocol/service_worker_handler.h"
#include "content/browser/devtools/protocol/storage_handler.h"
#include "content/browser/devtools/protocol/target_handler.h"
#include "content/browser/devtools/protocol/tracing_handler.h"
#include "content/browser/frame_host/navigation_handle_impl.h"
#include "content/browser/frame_host/navigation_request.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/render_widget_host_iterator.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/browser_side_navigation_policy.h"
#include "content/public/common/content_features.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/features.h"
#include "third_party/WebKit/public/common/associated_interfaces/associated_interface_provider.h"
#if defined(OS_ANDROID)
#include "content/public/browser/render_widget_host_view.h"
#include "services/device/public/mojom/wake_lock_context.mojom.h"
#endif
namespace content {
namespace {
using RenderFrameDevToolsMap =
std::map<FrameTreeNode*, RenderFrameDevToolsAgentHost*>;
base::LazyInstance<RenderFrameDevToolsMap>::Leaky g_agent_host_instances =
LAZY_INSTANCE_INITIALIZER;
RenderFrameDevToolsAgentHost* FindAgentHost(FrameTreeNode* frame_tree_node) {
if (!g_agent_host_instances.IsCreated())
return nullptr;
auto it = g_agent_host_instances.Get().find(frame_tree_node);
return it == g_agent_host_instances.Get().end() ? nullptr : it->second;
}
bool ShouldCreateDevToolsForHost(RenderFrameHost* rfh) {
return rfh->IsCrossProcessSubframe() || !rfh->GetParent();
}
bool ShouldCreateDevToolsForNode(FrameTreeNode* ftn) {
return !ftn->parent() || ftn->current_frame_host()->IsCrossProcessSubframe();
}
FrameTreeNode* GetFrameTreeNodeAncestor(FrameTreeNode* frame_tree_node) {
while (frame_tree_node && !ShouldCreateDevToolsForNode(frame_tree_node))
frame_tree_node = frame_tree_node->parent();
DCHECK(frame_tree_node);
return frame_tree_node;
}
} // namespace
template <typename Handler, typename... MethodArgs, typename... Args>
void DispatchToAgents(FrameTreeNode* frame_tree_node,
void (Handler::*method)(MethodArgs...),
Args&&... args) {
RenderFrameDevToolsAgentHost* agent_host =
FindAgentHost(GetFrameTreeNodeAncestor(frame_tree_node));
if (!agent_host)
return;
for (auto* h : Handler::ForAgentHost(agent_host))
(h->*method)(std::forward<Args>(args)...);
}
template <typename Handler, typename... MethodArgs, typename... Args>
void DispatchToAgents(int frame_tree_node_id,
void (Handler::*method)(MethodArgs...),
Args&&... args) {
FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (ftn)
DispatchToAgents(ftn, method, std::forward<Args>(args)...);
}
// static
scoped_refptr<DevToolsAgentHost>
DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) {
FrameTreeNode* node =
static_cast<WebContentsImpl*>(web_contents)->GetFrameTree()->root();
// TODO(dgozman): this check should not be necessary. See
// http://crbug.com/489664.
if (!node)
return nullptr;
return RenderFrameDevToolsAgentHost::GetOrCreateFor(node);
}
// static
scoped_refptr<DevToolsAgentHost> RenderFrameDevToolsAgentHost::GetOrCreateFor(
FrameTreeNode* frame_tree_node) {
frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node);
RenderFrameDevToolsAgentHost* result = FindAgentHost(frame_tree_node);
if (!result)
result = new RenderFrameDevToolsAgentHost(frame_tree_node);
return result;
}
// static
scoped_refptr<DevToolsAgentHost>
RenderFrameDevToolsAgentHost::GetOrCreateForDangling(
FrameTreeNode* frame_tree_node) {
// Note that this method does not use FrameTreeNode::current_frame_host(),
// since it is used while the frame host may not be set as current yet,
// for example right before commit time.
// So the caller must be sure that passed frame will indeed be a correct
// devtools target (see ShouldCreateDevToolsForNode above).
RenderFrameDevToolsAgentHost* result = FindAgentHost(frame_tree_node);
if (!result)
result = new RenderFrameDevToolsAgentHost(frame_tree_node);
return result;
}
// static
scoped_refptr<DevToolsAgentHost> RenderFrameDevToolsAgentHost::FindForDangling(
FrameTreeNode* frame_tree_node) {
return FindAgentHost(frame_tree_node);
}
// static
bool DevToolsAgentHost::HasFor(WebContents* web_contents) {
FrameTreeNode* node =
static_cast<WebContentsImpl*>(web_contents)->GetFrameTree()->root();
return node ? FindAgentHost(node) != nullptr : false;
}
// static
bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) {
FrameTreeNode* node =
static_cast<WebContentsImpl*>(web_contents)->GetFrameTree()->root();
RenderFrameDevToolsAgentHost* host = node ? FindAgentHost(node) : nullptr;
return host && host->IsAttached();
}
// static
void RenderFrameDevToolsAgentHost::AddAllAgentHosts(
DevToolsAgentHost::List* result) {
for (WebContentsImpl* wc : WebContentsImpl::GetAllWebContents()) {
for (FrameTreeNode* node : wc->GetFrameTree()->Nodes()) {
if (!node->current_frame_host() || !ShouldCreateDevToolsForNode(node))
continue;
if (!node->current_frame_host()->IsRenderFrameLive())
continue;
result->push_back(RenderFrameDevToolsAgentHost::GetOrCreateFor(node));
}
}
}
// static
void RenderFrameDevToolsAgentHost::OnResetNavigationRequest(
NavigationRequest* navigation_request) {
// Traverse frame chain all the way to the top and report to all
// page handlers that the navigation completed.
for (FrameTreeNode* node = navigation_request->frame_tree_node(); node;
node = node->parent()) {
DispatchToAgents(node, &protocol::PageHandler::NavigationReset,
navigation_request);
}
}
// static
void RenderFrameDevToolsAgentHost::OnNavigationResponseReceived(
const NavigationRequest& nav_request,
const network::ResourceResponse& response) {
FrameTreeNode* ftn = nav_request.frame_tree_node();
std::string id = nav_request.devtools_navigation_token().ToString();
std::string frame_id = ftn->devtools_frame_token().ToString();
GURL url = nav_request.common_params().url;
DispatchToAgents(ftn, &protocol::NetworkHandler::ResponseReceived, id, id,
url, protocol::Page::ResourceTypeEnum::Document,
response.head, frame_id);
}
// static
void RenderFrameDevToolsAgentHost::OnNavigationRequestFailed(
const NavigationRequest& nav_request,
int error_code) {
FrameTreeNode* ftn = nav_request.frame_tree_node();
std::string id = nav_request.devtools_navigation_token().ToString();
DispatchToAgents(ftn, &protocol::NetworkHandler::LoadingComplete, id,
protocol::Page::ResourceTypeEnum::Document,
network::URLLoaderCompletionStatus(error_code));
}
// static
std::vector<std::unique_ptr<NavigationThrottle>>
RenderFrameDevToolsAgentHost::CreateNavigationThrottles(
NavigationHandleImpl* navigation_handle) {
std::vector<std::unique_ptr<NavigationThrottle>> result;
FrameTreeNode* frame_tree_node = navigation_handle->frame_tree_node();
// Interception might throttle navigations in inspected frames.
RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(frame_tree_node);
if (agent_host) {
for (auto* network_handler :
protocol::NetworkHandler::ForAgentHost(agent_host)) {
std::unique_ptr<NavigationThrottle> throttle =
network_handler->CreateThrottleForNavigation(navigation_handle);
if (throttle)
result.push_back(std::move(throttle));
}
}
agent_host = nullptr;
if (frame_tree_node->parent()) {
// Target domain of the parent frame's DevTools may want to pause
// this frame to do some setup.
agent_host =
FindAgentHost(GetFrameTreeNodeAncestor(frame_tree_node->parent()));
}
if (agent_host) {
for (auto* target_handler :
protocol::TargetHandler::ForAgentHost(agent_host)) {
std::unique_ptr<NavigationThrottle> throttle =
target_handler->CreateThrottleForNavigation(navigation_handle);
if (throttle)
result.push_back(std::move(throttle));
}
}
return result;
}
// static
void RenderFrameDevToolsAgentHost::ApplyOverrides(
FrameTreeNode* frame_tree_node,
mojom::BeginNavigationParams* begin_params,
bool* report_raw_headers) {
bool disable_cache = false;
frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node);
RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(frame_tree_node);
if (!agent_host)
return;
net::HttpRequestHeaders headers;
headers.AddHeadersFromString(begin_params->headers);
for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) {
// TODO(caseq): consider chaining intercepting proxies from multiple agents.
if (!network->enabled())
continue;
*report_raw_headers = true;
network->ApplyOverrides(&headers, &begin_params->skip_service_worker,
&disable_cache);
}
if (disable_cache) {
begin_params->load_flags &=
~(net::LOAD_VALIDATE_CACHE | net::LOAD_SKIP_CACHE_VALIDATION |
net::LOAD_ONLY_FROM_CACHE | net::LOAD_DISABLE_CACHE);
begin_params->load_flags |= net::LOAD_BYPASS_CACHE;
}
begin_params->headers = headers.ToString();
}
// static
bool RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
RenderFrameHostImpl* rfh,
bool is_navigation,
network::mojom::URLLoaderFactoryRequest* target_factory_request) {
FrameTreeNode* frame_tree_node = rfh->frame_tree_node();
base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token();
frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node);
RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(frame_tree_node);
if (!agent_host)
return false;
int process_id = is_navigation ? 0 : rfh->GetProcess()->GetID();
for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) {
if (network->MaybeCreateProxyForInterception(frame_token, process_id,
target_factory_request)) {
return true;
}
}
return false;
}
// static
void RenderFrameDevToolsAgentHost::OnNavigationRequestWillBeSent(
const NavigationRequest& navigation_request) {
DispatchToAgents(navigation_request.frame_tree_node(),
&protocol::NetworkHandler::NavigationRequestWillBeSent,
navigation_request);
}
// static
void RenderFrameDevToolsAgentHost::WebContentsCreated(
WebContents* web_contents) {
if (ShouldForceCreation()) {
// Force agent host.
DevToolsAgentHost::GetOrCreateFor(web_contents);
}
}
RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost(
FrameTreeNode* frame_tree_node)
: DevToolsAgentHostImpl(frame_tree_node->devtools_frame_token().ToString()),
frame_tree_node_(nullptr) {
SetFrameTreeNode(frame_tree_node);
frame_host_ = frame_tree_node->current_frame_host();
render_frame_alive_ = frame_host_ && frame_host_->IsRenderFrameLive();
AddRef(); // Balanced in DestroyOnRenderFrameGone.
NotifyCreated();
}
void RenderFrameDevToolsAgentHost::SetFrameTreeNode(
FrameTreeNode* frame_tree_node) {
if (frame_tree_node_)
g_agent_host_instances.Get().erase(frame_tree_node_);
frame_tree_node_ = frame_tree_node;
if (frame_tree_node_) {
// TODO(dgozman): with ConnectWebContents/DisconnectWebContents,
// we may get two agent hosts for the same FrameTreeNode.
// That is definitely a bug, and we should fix that, and DCHECK
// here that there is no other agent host.
g_agent_host_instances.Get()[frame_tree_node] = this;
}
WebContentsObserver::Observe(
frame_tree_node_ ? WebContentsImpl::FromFrameTreeNode(frame_tree_node_)
: nullptr);
}
BrowserContext* RenderFrameDevToolsAgentHost::GetBrowserContext() {
WebContents* contents = web_contents();
return contents ? contents->GetBrowserContext() : nullptr;
}
WebContents* RenderFrameDevToolsAgentHost::GetWebContents() {
return web_contents();
}
bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) {
if (session->restricted() && !IsFrameHostAllowedForRestrictedSessions())
return false;
session->SetRenderer(frame_host_ ? frame_host_->GetProcess()->GetID()
: ChildProcessHost::kInvalidUniqueID,
frame_host_);
protocol::EmulationHandler* emulation_handler =
new protocol::EmulationHandler();
session->AddHandler(base::WrapUnique(new protocol::BrowserHandler()));
session->AddHandler(base::WrapUnique(new protocol::DOMHandler()));
session->AddHandler(base::WrapUnique(emulation_handler));
session->AddHandler(base::WrapUnique(new protocol::InputHandler()));
session->AddHandler(base::WrapUnique(new protocol::InspectorHandler()));
session->AddHandler(base::WrapUnique(new protocol::IOHandler(
GetIOContext())));
session->AddHandler(base::WrapUnique(new protocol::MemoryHandler()));
session->AddHandler(base::WrapUnique(new protocol::NetworkHandler(GetId())));
session->AddHandler(base::WrapUnique(new protocol::SchemaHandler()));
session->AddHandler(base::WrapUnique(new protocol::ServiceWorkerHandler()));
session->AddHandler(base::WrapUnique(new protocol::StorageHandler()));
if (!session->restricted()) {
session->AddHandler(base::WrapUnique(
new protocol::TargetHandler(false /* browser_only */)));
}
session->AddHandler(base::WrapUnique(new protocol::TracingHandler(
protocol::TracingHandler::Renderer,
frame_tree_node_ ? frame_tree_node_->frame_tree_node_id() : 0,
GetIOContext())));
session->AddHandler(
base::WrapUnique(new protocol::PageHandler(emulation_handler)));
session->AddHandler(base::WrapUnique(new protocol::SecurityHandler()));
if (EnsureAgent())
session->AttachToAgent(agent_ptr_);
if (sessions().size() == 1) {
// Taking screenshots using the video capture API is done in TracingHandler.
if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor) &&
!base::FeatureList::IsEnabled(
features::kUseVideoCaptureApiForDevToolsSnapshots)) {
frame_trace_recorder_.reset(new DevToolsFrameTraceRecorder());
}
GrantPolicy();
#if defined(OS_ANDROID)
GetWakeLock()->RequestWakeLock();
#endif
}
return true;
}
void RenderFrameDevToolsAgentHost::DetachSession(DevToolsSession* session) {
// Destroying session automatically detaches in renderer.
if (sessions().empty()) {
frame_trace_recorder_.reset();
RevokePolicy();
#if defined(OS_ANDROID)
GetWakeLock()->CancelWakeLock();
#endif
}
}
void RenderFrameDevToolsAgentHost::DispatchProtocolMessage(
DevToolsSession* session,
const std::string& message) {
session->DispatchProtocolMessage(message);
}
void RenderFrameDevToolsAgentHost::InspectElement(RenderFrameHost* frame_host,
int x,
int y) {
FrameTreeNode* ftn =
static_cast<RenderFrameHostImpl*>(frame_host)->frame_tree_node();
RenderFrameDevToolsAgentHost* host =
static_cast<RenderFrameDevToolsAgentHost*>(GetOrCreateFor(ftn).get());
gfx::Point point(x, y);
// The renderer expects coordinates relative to the local frame root,
// so we need to transform the coordinates from the root space
// to the local frame root widget's space.
if (host->frame_host_) {
if (RenderWidgetHostView* view = host->frame_host_->GetView()) {
point = gfx::ToRoundedPoint(
view->TransformRootPointToViewCoordSpace(gfx::PointF(point)));
}
}
if (host->EnsureAgent())
host->agent_ptr_->InspectElement(point);
}
RenderFrameDevToolsAgentHost::~RenderFrameDevToolsAgentHost() {
SetFrameTreeNode(nullptr);
}
void RenderFrameDevToolsAgentHost::ReadyToCommitNavigation(
NavigationHandle* navigation_handle) {
NavigationHandleImpl* handle =
static_cast<NavigationHandleImpl*>(navigation_handle);
if (handle->frame_tree_node() != frame_tree_node_) {
if (ShouldForceCreation() && handle->GetRenderFrameHost() &&
handle->GetRenderFrameHost()->IsCrossProcessSubframe()) {
RenderFrameDevToolsAgentHost::GetOrCreateForDangling(
handle->frame_tree_node());
}
return;
}
UpdateFrameHost(handle->GetRenderFrameHost());
// UpdateFrameHost may destruct |this|.
}
void RenderFrameDevToolsAgentHost::DidFinishNavigation(
NavigationHandle* navigation_handle) {
NavigationHandleImpl* handle =
static_cast<NavigationHandleImpl*>(navigation_handle);
if (handle->frame_tree_node() != frame_tree_node_)
return;
navigation_handles_.erase(handle);
NotifyNavigated();
// UpdateFrameHost may destruct |this|.
scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
UpdateFrameHost(frame_tree_node_->current_frame_host());
if (navigation_handles_.empty()) {
for (DevToolsSession* session : sessions())
session->ResumeSendingMessagesToAgent();
}
if (handle->HasCommitted()) {
for (auto* target : protocol::TargetHandler::ForAgentHost(this))
target->DidCommitNavigation();
}
}
void RenderFrameDevToolsAgentHost::UpdateFrameHost(
RenderFrameHostImpl* frame_host) {
if (frame_host == frame_host_) {
if (frame_host && !render_frame_alive_) {
render_frame_alive_ = true;
for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
inspector->TargetReloadedAfterCrash();
MaybeReattachToRenderFrame();
}
return;
}
if (frame_host && !ShouldCreateDevToolsForHost(frame_host)) {
DestroyOnRenderFrameGone();
// |this| may be deleted at this point.
return;
}
if (IsAttached())
RevokePolicy();
frame_host_ = frame_host;
agent_ptr_.reset();
if (!IsFrameHostAllowedForRestrictedSessions())
ForceDetachRestrictedSessions();
if (!render_frame_alive_) {
render_frame_alive_ = true;
for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
inspector->TargetReloadedAfterCrash();
}
if (IsAttached()) {
GrantPolicy();
for (DevToolsSession* session : sessions()) {
session->SetRenderer(frame_host ? frame_host->GetProcess()->GetID() : -1,
frame_host);
}
MaybeReattachToRenderFrame();
}
}
void RenderFrameDevToolsAgentHost::MaybeReattachToRenderFrame() {
if (!EnsureAgent())
return;
for (DevToolsSession* session : sessions())
session->AttachToAgent(agent_ptr_);
}
void RenderFrameDevToolsAgentHost::GrantPolicy() {
if (!frame_host_)
return;
uint32_t process_id = frame_host_->GetProcess()->GetID();
if (base::FeatureList::IsEnabled(network::features::kNetworkService))
GetNetworkService()->SetRawHeadersAccess(process_id, true);
ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadRawCookies(
process_id);
}
void RenderFrameDevToolsAgentHost::RevokePolicy() {
if (!frame_host_)
return;
bool process_has_agents = false;
RenderProcessHost* process_host = frame_host_->GetProcess();
for (const auto& ftn_agent : g_agent_host_instances.Get()) {
RenderFrameDevToolsAgentHost* agent = ftn_agent.second;
if (!agent->IsAttached())
continue;
if (agent->frame_host_ && agent->frame_host_ != frame_host_ &&
agent->frame_host_->GetProcess() == process_host) {
process_has_agents = true;
}
}
// We are the last to disconnect from the renderer -> revoke permissions.
if (!process_has_agents) {
if (base::FeatureList::IsEnabled(network::features::kNetworkService))
GetNetworkService()->SetRawHeadersAccess(process_host->GetID(), false);
ChildProcessSecurityPolicyImpl::GetInstance()->RevokeReadRawCookies(
process_host->GetID());
}
}
void RenderFrameDevToolsAgentHost::DidStartNavigation(
NavigationHandle* navigation_handle) {
NavigationHandleImpl* handle =
static_cast<NavigationHandleImpl*>(navigation_handle);
if (handle->frame_tree_node() != frame_tree_node_)
return;
if (navigation_handles_.empty()) {
for (DevToolsSession* session : sessions())
session->SuspendSendingMessagesToAgent();
}
navigation_handles_.insert(handle);
}
void RenderFrameDevToolsAgentHost::RenderFrameHostChanged(
RenderFrameHost* old_host,
RenderFrameHost* new_host) {
if (old_host != frame_host_)
return;
UpdateFrameHost(nullptr);
// UpdateFrameHost may destruct |this|.
}
void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost* rfh) {
if (static_cast<RenderFrameHostImpl*>(rfh)->frame_tree_node() ==
frame_tree_node_) {
DestroyOnRenderFrameGone(); // |this| may be deleted at this point.
}
}
void RenderFrameDevToolsAgentHost::RenderFrameDeleted(RenderFrameHost* rfh) {
if (rfh == frame_host_) {
render_frame_alive_ = false;
agent_ptr_.reset();
}
}
void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() {
scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
if (IsAttached())
RevokePolicy();
ForceDetachAllSessions();
frame_host_ = nullptr;
agent_ptr_.reset();
SetFrameTreeNode(nullptr);
Release();
}
#if defined(OS_ANDROID)
device::mojom::WakeLock* RenderFrameDevToolsAgentHost::GetWakeLock() {
// Here is a lazy binding, and will not reconnect after connection error.
if (!wake_lock_) {
device::mojom::WakeLockRequest request = mojo::MakeRequest(&wake_lock_);
device::mojom::WakeLockContext* wake_lock_context =
web_contents()->GetWakeLockContext();
if (wake_lock_context) {
wake_lock_context->GetWakeLock(
device::mojom::WakeLockType::kPreventDisplaySleep,
device::mojom::WakeLockReason::kOther, "DevTools",
std::move(request));
}
}
return wake_lock_.get();
}
#endif
void RenderFrameDevToolsAgentHost::RenderProcessGone(
base::TerminationStatus status) {
switch(status) {
case base::TERMINATION_STATUS_ABNORMAL_TERMINATION:
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED:
#if defined(OS_CHROMEOS)
case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM:
#endif
case base::TERMINATION_STATUS_PROCESS_CRASHED:
#if defined(OS_ANDROID)
case base::TERMINATION_STATUS_OOM_PROTECTED:
#endif
case base::TERMINATION_STATUS_LAUNCH_FAILED:
for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
inspector->TargetCrashed();
break;
default:
for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this))
inspector->TargetDetached("Render process gone.");
break;
}
}
void RenderFrameDevToolsAgentHost::DidAttachInterstitialPage() {
for (auto* page : protocol::PageHandler::ForAgentHost(this))
page->DidAttachInterstitialPage();
}
void RenderFrameDevToolsAgentHost::DidDetachInterstitialPage() {
for (auto* page : protocol::PageHandler::ForAgentHost(this))
page->DidDetachInterstitialPage();
}
void RenderFrameDevToolsAgentHost::OnVisibilityChanged(
content::Visibility visibility) {
#if defined(OS_ANDROID)
if (visibility == content::Visibility::HIDDEN)
GetWakeLock()->CancelWakeLock();
else
GetWakeLock()->RequestWakeLock();
#endif
}
void RenderFrameDevToolsAgentHost::DidReceiveCompositorFrame() {
const viz::CompositorFrameMetadata& metadata =
RenderWidgetHostImpl::From(
web_contents()->GetRenderViewHost()->GetWidget())
->last_frame_metadata();
for (auto* page : protocol::PageHandler::ForAgentHost(this))
page->OnSwapCompositorFrame(metadata.Clone());
if (!frame_trace_recorder_)
return;
bool did_initiate_recording = false;
for (auto* tracing : protocol::TracingHandler::ForAgentHost(this))
did_initiate_recording |= tracing->did_initiate_recording();
if (did_initiate_recording)
frame_trace_recorder_->OnSwapCompositorFrame(frame_host_, metadata);
}
void RenderFrameDevToolsAgentHost::OnPageScaleFactorChanged(
float page_scale_factor) {
for (auto* input : protocol::InputHandler::ForAgentHost(this))
input->OnPageScaleFactorChanged(page_scale_factor);
}
void RenderFrameDevToolsAgentHost::DisconnectWebContents() {
navigation_handles_.clear();
SetFrameTreeNode(nullptr);
// UpdateFrameHost may destruct |this|.
scoped_refptr<RenderFrameDevToolsAgentHost> protect(this);
UpdateFrameHost(nullptr);
for (DevToolsSession* session : sessions())
session->ResumeSendingMessagesToAgent();
}
void RenderFrameDevToolsAgentHost::ConnectWebContents(WebContents* wc) {
RenderFrameHostImpl* host =
static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
DCHECK(host);
SetFrameTreeNode(host->frame_tree_node());
UpdateFrameHost(host);
// UpdateFrameHost may destruct |this|.
}
std::string RenderFrameDevToolsAgentHost::GetParentId() {
if (IsChildFrame()) {
FrameTreeNode* frame_tree_node =
GetFrameTreeNodeAncestor(frame_tree_node_->parent());
return RenderFrameDevToolsAgentHost::GetOrCreateFor(frame_tree_node)
->GetId();
}
WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
if (!contents)
return "";
contents = contents->GetOuterWebContents();
if (contents)
return DevToolsAgentHost::GetOrCreateFor(contents)->GetId();
return "";
}
std::string RenderFrameDevToolsAgentHost::GetOpenerId() {
if (!frame_tree_node_)
return std::string();
FrameTreeNode* opener = frame_tree_node_->original_opener();
return opener ? opener->devtools_frame_token().ToString() : std::string();
}
std::string RenderFrameDevToolsAgentHost::GetType() {
if (web_contents() &&
static_cast<WebContentsImpl*>(web_contents())->GetOuterWebContents()) {
return kTypeGuest;
}
if (IsChildFrame())
return kTypeFrame;
DevToolsManager* manager = DevToolsManager::GetInstance();
if (manager->delegate() && web_contents()) {
std::string type = manager->delegate()->GetTargetType(web_contents());
if (!type.empty())
return type;
}
return kTypePage;
}
std::string RenderFrameDevToolsAgentHost::GetTitle() {
DevToolsManager* manager = DevToolsManager::GetInstance();
if (manager->delegate() && web_contents()) {
std::string title = manager->delegate()->GetTargetTitle(web_contents());
if (!title.empty())
return title;
}
if (IsChildFrame() && frame_host_)
return frame_host_->GetLastCommittedURL().spec();
if (web_contents())
return base::UTF16ToUTF8(web_contents()->GetTitle());
return GetURL().spec();
}
std::string RenderFrameDevToolsAgentHost::GetDescription() {
DevToolsManager* manager = DevToolsManager::GetInstance();
if (manager->delegate() && web_contents())
return manager->delegate()->GetTargetDescription(web_contents());
return std::string();
}
GURL RenderFrameDevToolsAgentHost::GetURL() {
// Order is important here.
WebContents* web_contents = GetWebContents();
if (web_contents && !IsChildFrame())
return web_contents->GetVisibleURL();
if (frame_host_)
return frame_host_->GetLastCommittedURL();
return GURL();
}
GURL RenderFrameDevToolsAgentHost::GetFaviconURL() {
WebContents* wc = web_contents();
if (!wc)
return GURL();
NavigationEntry* entry = wc->GetController().GetLastCommittedEntry();
if (entry)
return entry->GetFavicon().url;
return GURL();
}
bool RenderFrameDevToolsAgentHost::Activate() {
WebContentsImpl* wc = static_cast<WebContentsImpl*>(web_contents());
if (wc) {
wc->Activate();
return true;
}
return false;
}
void RenderFrameDevToolsAgentHost::Reload() {
WebContentsImpl* wc = static_cast<WebContentsImpl*>(web_contents());
if (wc)
wc->GetController().Reload(ReloadType::NORMAL, true);
}
bool RenderFrameDevToolsAgentHost::Close() {
if (web_contents()) {
web_contents()->ClosePage();
return true;
}
return false;
}
base::TimeTicks RenderFrameDevToolsAgentHost::GetLastActivityTime() {
if (WebContents* contents = web_contents())
return contents->GetLastActiveTime();
return base::TimeTicks();
}
void RenderFrameDevToolsAgentHost::SignalSynchronousSwapCompositorFrame(
RenderFrameHost* frame_host,
viz::CompositorFrameMetadata frame_metadata) {
scoped_refptr<RenderFrameDevToolsAgentHost> dtah(FindAgentHost(
static_cast<RenderFrameHostImpl*>(frame_host)->frame_tree_node()));
if (dtah) {
// Unblock the compositor.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(
&RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame,
dtah.get(), std::move(frame_metadata)));
}
}
void RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame(
viz::CompositorFrameMetadata frame_metadata) {
for (auto* page : protocol::PageHandler::ForAgentHost(this))
page->OnSynchronousSwapCompositorFrame(frame_metadata.Clone());
if (!frame_trace_recorder_)
return;
bool did_initiate_recording = false;
for (auto* tracing : protocol::TracingHandler::ForAgentHost(this))
did_initiate_recording |= tracing->did_initiate_recording();
if (did_initiate_recording) {
frame_trace_recorder_->OnSynchronousSwapCompositorFrame(frame_host_,
frame_metadata);
}
}
bool RenderFrameDevToolsAgentHost::EnsureAgent() {
if (!frame_host_ || !render_frame_alive_)
return false;
if (!agent_ptr_)
frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(&agent_ptr_);
return true;
}
bool RenderFrameDevToolsAgentHost::IsChildFrame() {
return frame_tree_node_ && frame_tree_node_->parent();
}
bool RenderFrameDevToolsAgentHost::IsFrameHostAllowedForRestrictedSessions() {
return !frame_host_ ||
(!frame_host_->web_ui() && !frame_host_->pending_web_ui());
}
} // namespace content