| // Copyright 2012 The Chromium Authors | |
| // 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 <algorithm> | |
| #include <set> | |
| #include <string> | |
| #include <tuple> | |
| #include <utility> | |
| #include "base/functional/bind.h" | |
| #include "base/json/json_reader.h" | |
| #include "base/memory/ptr_util.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/metrics/histogram_functions.h" | |
| #include "base/no_destructor.h" | |
| #include "base/strings/utf_string_conversions.h" | |
| #include "build/build_config.h" | |
| #include "components/viz/common/buildflags.h" | |
| #include "content/browser/bad_message.h" | |
| #include "content/browser/devtools/devtools_manager.h" | |
| #include "content/browser/devtools/devtools_renderer_channel.h" | |
| #include "content/browser/devtools/devtools_session.h" | |
| #include "content/browser/devtools/frame_auto_attacher.h" | |
| #include "content/browser/devtools/protocol/audits_handler.h" | |
| #include "content/browser/devtools/protocol/background_service_handler.h" | |
| #include "content/browser/devtools/protocol/browser_handler.h" | |
| #include "content/browser/devtools/protocol/debugger_handler.h" | |
| #include "content/browser/devtools/protocol/device_access_handler.h" | |
| #include "content/browser/devtools/protocol/device_orientation_handler.h" | |
| #include "content/browser/devtools/protocol/digital_credentials_handler.h" | |
| #include "content/browser/devtools/protocol/dom_handler.h" | |
| #include "content/browser/devtools/protocol/emulation_handler.h" | |
| #include "content/browser/devtools/protocol/fedcm_handler.h" | |
| #include "content/browser/devtools/protocol/fetch_handler.h" | |
| #include "content/browser/devtools/protocol/handler_helpers.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/log_handler.h" | |
| #include "content/browser/devtools/protocol/memory_handler.h" | |
| #include "content/browser/devtools/protocol/network_handler.h" | |
| #include "content/browser/devtools/protocol/overlay_handler.h" | |
| #include "content/browser/devtools/protocol/page_handler.h" | |
| #include "content/browser/devtools/protocol/preload_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/system_info_handler.h" | |
| #include "content/browser/devtools/protocol/target_handler.h" | |
| #include "content/browser/devtools/protocol/tracing_handler.h" | |
| #include "content/browser/devtools/protocol/webmcp_handler.h" | |
| #include "content/browser/devtools/web_contents_devtools_agent_host.h" | |
| #include "content/browser/preloading/prerender/prerender_host_registry.h" | |
| #include "content/browser/renderer_host/navigation_request.h" | |
| #include "content/browser/renderer_host/render_frame_host_impl.h" | |
| #include "content/browser/renderer_host/render_frame_host_manager.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/browser/web_package/signed_exchange_envelope.h" | |
| #include "content/browser/worker_host/dedicated_worker_hosts_for_document.h" | |
| #include "content/public/browser/back_forward_cache.h" | |
| #include "content/public/browser/browser_context.h" | |
| #include "content/public/browser/browser_task_traits.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_frame_host.h" | |
| #include "content/public/browser/render_widget_host_iterator.h" | |
| #include "content/public/browser/web_contents.h" | |
| #include "content/public/browser/web_contents_delegate.h" | |
| #include "content/public/common/child_process_id_util.h" | |
| #include "content/public/common/content_features.h" | |
| #include "services/network/public/mojom/network_service.mojom.h" | |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" | |
| #include "third_party/blink/public/common/features.h" | |
| #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h" | |
| #if BUILDFLAG(IS_ANDROID) | |
| #include "content/browser/renderer_host/compositor_impl_android.h" | |
| #include "content/public/browser/render_widget_host_view.h" | |
| #include "mojo/public/cpp/bindings/pending_receiver.h" | |
| #include "services/device/public/mojom/wake_lock_context.mojom.h" | |
| #else | |
| #include "content/browser/devtools/protocol/webauthn_handler.h" | |
| #endif | |
| #ifdef ENABLE_SMART_CARD | |
| #include "content/browser/devtools/protocol/smart_card_emulation_handler.h" | |
| #endif | |
| #if BUILDFLAG(USE_VIZ_DEBUGGER) | |
| #include "content/browser/devtools/protocol/visual_debugger_handler.h" | |
| #endif | |
| namespace content { | |
| namespace { | |
| using RenderFrameDevToolsMap = | |
| std::map<FrameTreeNode*, RenderFrameDevToolsAgentHost*>; | |
| RenderFrameDevToolsMap& GetAgentHostInstances() { | |
| static base::NoDestructor<RenderFrameDevToolsMap> agent_host_instances; | |
| return *agent_host_instances; | |
| } | |
| static bool g_was_ever_attached_to_any_frame = false; | |
| RenderFrameDevToolsAgentHost* FindAgentHost(FrameTreeNode* frame_tree_node) { | |
| auto it = GetAgentHostInstances().find(frame_tree_node); | |
| return it == GetAgentHostInstances().end() ? nullptr : it->second; | |
| } | |
| bool ShouldCreateDevToolsForNode(FrameTreeNode* ftn) { | |
| return !ftn->parent() || | |
| (ftn->current_frame_host() && | |
| RenderFrameDevToolsAgentHost::ShouldCreateDevToolsForHost( | |
| ftn->current_frame_host())); | |
| } | |
| bool IsPrerenderPrimaryMainFramePlaceholder(WebContentsImpl* web_contents, | |
| RenderFrameHostImpl* frame_host) { | |
| return web_contents->GetVisibility() == Visibility::HIDDEN && | |
| frame_host->is_initial_empty_document() && | |
| !web_contents->GetPrerenderHostRegistry() | |
| ->GetPrerenderFrameTrees() | |
| .empty(); | |
| } | |
| bool MaybeInitializePrerenderPrimaryMainFrame(RenderFrameHostImpl* frame_host, | |
| bool* did_try_to_initialize) { | |
| CHECK(did_try_to_initialize, base::NotFatalUntil::M159); | |
| *did_try_to_initialize = false; | |
| if (!frame_host || frame_host->IsRenderFrameLive()) { | |
| return false; | |
| } | |
| FrameTreeNode* frame_tree_node = frame_host->frame_tree_node(); | |
| if (!frame_tree_node || frame_tree_node->current_frame_host() != frame_host || | |
| frame_tree_node->GetFrameType() != FrameType::kPrimaryMainFrame) { | |
| return false; | |
| } | |
| WebContentsImpl* web_contents = | |
| WebContentsImpl::FromRenderFrameHostImpl(frame_host); | |
| if (!web_contents || web_contents->IsBeingDestroyed() || | |
| web_contents->IsCrashed() || | |
| web_contents->GetPrimaryMainFrame() != frame_host || | |
| !IsPrerenderPrimaryMainFramePlaceholder(web_contents, frame_host)) { | |
| return false; | |
| } | |
| frame_tree_node->render_manager()->InitRenderView( | |
| frame_host->GetSiteInstance()->group(), web_contents->GetRenderViewHost(), | |
| /*proxy=*/nullptr, /*navigation_metrics_token=*/std::nullopt); | |
| *did_try_to_initialize = true; | |
| return frame_host->IsRenderFrameLive(); | |
| } | |
| } // namespace | |
| FrameTreeNode* GetFrameTreeNodeAncestor(FrameTreeNode* frame_tree_node) { | |
| while (frame_tree_node && !ShouldCreateDevToolsForNode(frame_tree_node)) | |
| frame_tree_node = FrameTreeNode::From(frame_tree_node->parent()); | |
| CHECK(frame_tree_node, base::NotFatalUntil::M159); | |
| return frame_tree_node; | |
| } | |
| // static | |
| scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::GetOrCreateFor( | |
| WebContents* web_contents) { | |
| FrameTreeNode* node = | |
| static_cast<WebContentsImpl*>(web_contents)->GetPrimaryFrameTree().root(); | |
| // TODO(dgozman): this check should not be necessary. See | |
| // http://crbug.com/489664. | |
| if (!node) | |
| return nullptr; | |
| return RenderFrameDevToolsAgentHost::GetOrCreateFor(node); | |
| } | |
| // static | |
| DevToolsAgentHostImpl* RenderFrameDevToolsAgentHost::GetFor( | |
| FrameTreeNode* frame_tree_node) { | |
| frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node); | |
| return FindAgentHost(frame_tree_node); | |
| } | |
| // static | |
| DevToolsAgentHostImpl* RenderFrameDevToolsAgentHost::GetForWithAncestorFallback( | |
| FrameTreeNode* frame_tree_node) { | |
| frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node); | |
| DevToolsAgentHostImpl* agent_host = FindAgentHost(frame_tree_node); | |
| if (agent_host || !frame_tree_node->parent()) { | |
| return agent_host; | |
| } | |
| return FindAgentHost( | |
| GetFrameTreeNodeAncestor(FrameTreeNode::From(frame_tree_node->parent()))); | |
| } | |
| // static | |
| DevToolsAgentHostImpl* RenderFrameDevToolsAgentHost::GetFor( | |
| RenderFrameHostImpl* rfh) { | |
| return ShouldCreateDevToolsForHost(rfh) | |
| ? FindAgentHost(rfh->frame_tree_node()) | |
| : GetFor(rfh->frame_tree_node()); | |
| } | |
| 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, frame_tree_node->current_frame_host()); | |
| } | |
| return result; | |
| } | |
| // static | |
| bool RenderFrameDevToolsAgentHost::ShouldCreateDevToolsForHost( | |
| RenderFrameHostImpl* rfh) { | |
| CHECK(rfh, base::NotFatalUntil::M159); | |
| return rfh->is_local_root(); | |
| } | |
| // static | |
| scoped_refptr<RenderFrameDevToolsAgentHost> | |
| RenderFrameDevToolsAgentHost::CreateForLocalRootOrEmbeddedPageNavigation( | |
| NavigationRequest* request) { | |
| // 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. Instead target frame from the | |
| // navigation handle is used. When this method is invoked it's already known | |
| // that the navigation will commit to the new frame host. | |
| FrameTreeNode* frame_tree_node = request->frame_tree_node(); | |
| CHECK(!FindAgentHost(frame_tree_node), base::NotFatalUntil::M159); | |
| return new RenderFrameDevToolsAgentHost(frame_tree_node, | |
| request->GetRenderFrameHost()); | |
| } | |
| // static | |
| scoped_refptr<RenderFrameDevToolsAgentHost> | |
| 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)->GetPrimaryFrameTree().root(); | |
| return node ? FindAgentHost(node) != nullptr : false; | |
| } | |
| // static | |
| bool RenderFrameDevToolsAgentHost::WasEverAttachedToAnyFrame() { | |
| return g_was_ever_attached_to_any_frame; | |
| } | |
| // static | |
| bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) { | |
| return RenderFrameDevToolsAgentHost::IsDebuggerAttached(web_contents) || | |
| WebContentsDevToolsAgentHost::IsDebuggerAttached(web_contents); | |
| } | |
| // static | |
| bool RenderFrameDevToolsAgentHost::IsDebuggerAttached( | |
| WebContents* web_contents) { | |
| FrameTreeNode* node = | |
| static_cast<WebContentsImpl*>(web_contents)->GetPrimaryFrameTree().root(); | |
| RenderFrameDevToolsAgentHost* host = node ? FindAgentHost(node) : nullptr; | |
| return host && host->IsAttached(); | |
| } | |
| // static | |
| void RenderFrameDevToolsAgentHost::AddAllAgentHosts( | |
| DevToolsAgentHost::List* result) { | |
| for (WebContentsImpl* wc : WebContentsImpl::GetAllWebContents()) { | |
| // Inner web contents such as guestviews are already handled by | |
| // ForEachRenderFrameHostImpl. | |
| // TODO(crbug.com/40202416): Remove this after migration is complete. | |
| if (wc->GetOutermostWebContents() != wc) | |
| continue; | |
| wc->GetPrimaryMainFrame()->ForEachRenderFrameHostImpl( | |
| [result](RenderFrameHostImpl* render_frame_host) { | |
| FrameTreeNode* node = FrameTreeNode::From(render_frame_host); | |
| if (!ShouldCreateDevToolsForNode(node)) | |
| return; | |
| result->push_back(RenderFrameDevToolsAgentHost::GetOrCreateFor(node)); | |
| }); | |
| } | |
| } | |
| // static | |
| void RenderFrameDevToolsAgentHost::AttachToWebContents( | |
| WebContents* web_contents) { | |
| if (ShouldForceCreation()) { | |
| // Force agent hosts. | |
| DevToolsAgentHost::GetOrCreateForTab(web_contents); | |
| DevToolsAgentHost::GetOrCreateFor(web_contents); | |
| } | |
| } | |
| // static | |
| void RenderFrameDevToolsAgentHost::UpdateRawHeadersAccess( | |
| RenderFrameHostImpl* rfh, | |
| RenderFrameDevToolsAgentHost* force_include_host) { | |
| if (!rfh) { | |
| return; | |
| } | |
| RenderProcessHost* rph = rfh->GetProcess(); | |
| std::set<url::Origin> process_origins; | |
| for (const auto& entry : GetAgentHostInstances()) { | |
| RenderFrameHostImpl* frame_host = entry.second->frame_host_; | |
| if (!frame_host) { | |
| continue; | |
| } | |
| if (!entry.second->IsAttached() && entry.second != force_include_host) { | |
| continue; | |
| } | |
| RenderProcessHost* process_host = frame_host->GetProcess(); | |
| if (process_host == rph) | |
| process_origins.insert(frame_host->GetLastCommittedOrigin()); | |
| } | |
| GetNetworkService()->SetRawHeadersAccess( | |
| ToRendererProcessId(rph->GetID()), | |
| std::vector<url::Origin>(process_origins.begin(), process_origins.end())); | |
| } | |
| RenderFrameDevToolsAgentHost::RenderFrameDevToolsAgentHost( | |
| FrameTreeNode* frame_tree_node, | |
| RenderFrameHostImpl* frame_host) | |
| : DevToolsAgentHostImpl(frame_host->devtools_frame_token().ToString()), | |
| auto_attacher_(std::make_unique<FrameAutoAttacher>(GetRendererChannel())), | |
| frame_tree_node_(nullptr) { | |
| g_was_ever_attached_to_any_frame = true; | |
| AddRef(); // Balanced in DestroyOnRenderFrameGone. | |
| auto* wc = WebContentsImpl::FromRenderFrameHostImpl(frame_host); | |
| CHECK(!wc->IsBeingDestroyed()); | |
| WebContentsObserver::Observe(wc); | |
| SetFrameTreeNode(frame_tree_node); | |
| ChangeFrameHostAndObservedProcess(frame_host); | |
| render_frame_alive_ = frame_host_ && frame_host_->IsRenderFrameLive(); | |
| CHECK(!render_frame_alive_ || | |
| frame_host->GetProcess()->IsInitializedAndNotDead()); | |
| if (frame_tree_node->GetFrameType() != FrameType::kPrimaryMainFrame && | |
| frame_tree_node->GetFrameType() != FrameType::kPrerenderMainFrame) { | |
| render_frame_crashed_ = !render_frame_alive_; | |
| } else { | |
| WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); | |
| render_frame_crashed_ = web_contents && web_contents->IsCrashed(); | |
| } | |
| NotifyCreated(); | |
| } | |
| void RenderFrameDevToolsAgentHost::SetFrameTreeNode( | |
| FrameTreeNode* frame_tree_node) { | |
| if (frame_tree_node_) | |
| GetAgentHostInstances().erase(frame_tree_node_); | |
| frame_tree_node_ = frame_tree_node; | |
| if (frame_tree_node_) { | |
| CHECK(web_contents() == WebContentsImpl::FromFrameTreeNode(frame_tree_node), | |
| base::NotFatalUntil::M159); | |
| // 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. | |
| GetAgentHostInstances()[frame_tree_node] = this; | |
| } | |
| } | |
| BrowserContext* RenderFrameDevToolsAgentHost::GetBrowserContext() { | |
| WebContents* contents = web_contents(); | |
| return contents ? contents->GetBrowserContext() : nullptr; | |
| } | |
| WebContents* RenderFrameDevToolsAgentHost::GetWebContents() { | |
| return web_contents(); | |
| } | |
| bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) { | |
| if (!ShouldAllowSession(frame_host_, session)) { | |
| return false; | |
| } | |
| const bool is_main_frame = !frame_tree_node_ || !frame_tree_node_->parent(); | |
| if (frame_tree_node_ && !frame_tree_node_->parent() && | |
| frame_tree_node_->is_on_initial_empty_document()) { | |
| // Since the DevTools protocol can be used to modify the initial empty | |
| // document of a tab, notify the browser that the pending URL shouldn't be | |
| // displayed anymore to eliminate a URL spoof risk. | |
| frame_host_->DidAccessInitialMainDocument(); | |
| } | |
| if (!navigation_requests_.empty()) { | |
| session->SuspendSendingMessagesToAgent(); | |
| } | |
| session->CreateAndAddHandler<protocol::AuditsHandler>(); | |
| session->CreateAndAddHandler<protocol::BackgroundServiceHandler>(); | |
| auto* browser_handler = | |
| session->CreateAndAddHandler<protocol::BrowserHandler>( | |
| session->GetClient()->MayWriteLocalFiles()); | |
| session->CreateAndAddHandler<protocol::DebuggerHandler>(); | |
| session->CreateAndAddHandler<protocol::DeviceAccessHandler>(); | |
| session->CreateAndAddHandler<protocol::DeviceOrientationHandler>(); | |
| session->CreateAndAddHandler<protocol::DOMHandler>( | |
| session->GetClient()->MayReadLocalFiles()); | |
| auto* emulation_handler = | |
| session->CreateAndAddHandler<protocol::EmulationHandler>(); | |
| session->CreateAndAddHandler<protocol::InputHandler>( | |
| session->GetClient()->MayReadLocalFiles(), | |
| session->GetClient()->IsTrusted()); | |
| session->CreateAndAddHandler<protocol::InspectorHandler>(); | |
| session->CreateAndAddHandler<protocol::IOHandler>(GetIOContext()); | |
| session->CreateAndAddHandler<protocol::MemoryHandler>(); | |
| #ifdef ENABLE_SMART_CARD | |
| if (is_main_frame) { | |
| session->CreateAndAddHandler<protocol::SmartCardEmulationHandler>( | |
| content::WebContents::FromRenderFrameHost(frame_host_)); | |
| } | |
| #endif | |
| #if BUILDFLAG(USE_VIZ_DEBUGGER) | |
| session->CreateAndAddHandler<protocol::VisualDebuggerHandler>(); | |
| #endif | |
| session->CreateAndAddHandler<protocol::OverlayHandler>(); | |
| session->CreateAndAddHandler<protocol::NetworkHandler>( | |
| GetId(), | |
| frame_host_ ? frame_host_->devtools_frame_token() | |
| : base::UnguessableToken(), | |
| GetIOContext(), session, /*maybe_storage_partition=*/nullptr, | |
| session->GetClient()); | |
| session->CreateAndAddHandler<protocol::FetchHandler>( | |
| GetIOContext(), session->GetRootSession()->GetClient(), | |
| base::BindRepeating( | |
| [](RenderFrameDevToolsAgentHost* self, | |
| base::OnceClosure done_callback) { | |
| self->UpdateResourceLoaderFactories(); | |
| std::move(done_callback).Run(); | |
| }, | |
| base::Unretained(this))); | |
| session->CreateAndAddHandler<protocol::SchemaHandler>(); | |
| const bool may_attach_to_browser = session->GetClient()->IsTrusted(); | |
| session->CreateAndAddHandler<protocol::ServiceWorkerHandler>(); | |
| session->CreateAndAddHandler<protocol::StorageHandler>(this, | |
| session->GetClient()); | |
| session->CreateAndAddHandler<protocol::SystemInfoHandler>( | |
| /* is_browser_session= */ false); | |
| session->CreateAndAddHandler<protocol::TargetHandler>( | |
| may_attach_to_browser | |
| ? protocol::TargetHandler::AccessMode::kRegular | |
| : protocol::TargetHandler::AccessMode::kAutoAttachOnly, | |
| GetId(), auto_attacher_.get(), session); | |
| session->CreateAndAddHandler<protocol::PreloadHandler>(); | |
| session->CreateAndAddHandler<protocol::PageHandler>( | |
| GetIOContext(), emulation_handler, browser_handler, | |
| session->GetClient()->AllowUnsafeOperations(), | |
| session->GetClient()->IsTrusted(), | |
| session->GetClient()->GetNavigationInitiatorOrigin(), | |
| session->GetClient()->MayReadLocalFiles(), | |
| session->MakePrepareForReloadCallback()); | |
| session->CreateAndAddHandler<protocol::SecurityHandler>(); | |
| if (is_main_frame) { | |
| DevToolsSession* root_session = session->GetRootSession(); | |
| CHECK(root_session); | |
| session->CreateAndAddHandler<protocol::TracingHandler>( | |
| this, GetIOContext(), root_session, session->GetClient()->IsTrusted()); | |
| } | |
| session->CreateAndAddHandler<protocol::WebMCPHandler>(); | |
| session->CreateAndAddHandler<protocol::LogHandler>(); | |
| session->CreateAndAddHandler<protocol::FedCmHandler>(); | |
| session->CreateAndAddHandler<protocol::DigitalCredentialsHandler>(); | |
| #if !BUILDFLAG(IS_ANDROID) | |
| session->CreateAndAddHandler<protocol::WebAuthnHandler>(); | |
| #endif // !BUILDFLAG(IS_ANDROID) | |
| if (sessions().empty()) { | |
| UpdateRawHeadersAccess(frame_host_, this); | |
| #if BUILDFLAG(IS_ANDROID) | |
| GetWakeLock()->RequestWakeLock(); | |
| #endif | |
| } | |
| return true; | |
| } | |
| void RenderFrameDevToolsAgentHost::DetachSession(DevToolsSession* session) { | |
| // Destroying session automatically detaches in renderer. | |
| if (sessions().empty()) { | |
| UpdateRawHeadersAccess(frame_host_, nullptr); | |
| #if BUILDFLAG(IS_ANDROID) | |
| GetWakeLock()->CancelWakeLock(); | |
| #endif | |
| } | |
| } | |
| 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 (RenderWidgetHostViewBase* view = host->frame_host_->GetView()) { | |
| point = gfx::ToRoundedPoint( | |
| view->TransformRootPointToViewCoordSpace(gfx::PointF(point))); | |
| } | |
| } | |
| host->GetRendererChannel()->InspectElement(point); | |
| } | |
| RenderFrameDevToolsAgentHost::~RenderFrameDevToolsAgentHost() { | |
| SetFrameTreeNode(nullptr); | |
| ChangeFrameHostAndObservedProcess(nullptr); | |
| } | |
| void RenderFrameDevToolsAgentHost::ReadyToCommitNavigation( | |
| NavigationHandle* navigation_handle) { | |
| if (!frame_tree_node_) { | |
| return; | |
| } | |
| NavigationRequest* request = NavigationRequest::From(navigation_handle); | |
| for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) { | |
| tracing->ReadyToCommitNavigation(request); | |
| } | |
| for (auto* page : protocol::PageHandler::ForAgentHost(this)) { | |
| page->ReadyToCommitNavigation(request); | |
| } | |
| if (request->frame_tree_node() != frame_tree_node_) { | |
| if (ShouldForceCreation() && request->GetRenderFrameHost() && | |
| request->GetRenderFrameHost()->is_local_root_subframe()) { | |
| // An agent may have been created earlier if auto attach is on. | |
| if (!FindAgentHost(request->frame_tree_node())) | |
| CreateForLocalRootOrEmbeddedPageNavigation(request); | |
| } | |
| return; | |
| } | |
| // Child workers will eventually disconnect, but timing depends on the | |
| // renderer process. To ensure consistent view over protocol, disconnect them | |
| // right now. | |
| GetRendererChannel()->ForceDetachWorkerSessions(); | |
| UpdateFrameHost(request->GetRenderFrameHost()); | |
| // UpdateFrameHost may destruct |this|. | |
| } | |
| void RenderFrameDevToolsAgentHost::DidFinishNavigation( | |
| NavigationHandle* navigation_handle) { | |
| NavigationRequest* request = NavigationRequest::From(navigation_handle); | |
| // If we opt for retaning self within the conditional block below, do so | |
| // till the end of the function, as we require |this| after the conditional. | |
| scoped_refptr<RenderFrameDevToolsAgentHost> protect; | |
| if (request->frame_tree_node() == frame_tree_node_) { | |
| // We didn't suspend for a same-document navigation. | |
| if (!request->IsSameDocument()) { | |
| navigation_requests_.erase(request); | |
| } | |
| if (request->HasCommitted()) | |
| NotifyNavigated(); | |
| if (IsAttached()) { | |
| UpdateRawHeadersAccess(frame_tree_node_->current_frame_host(), nullptr); | |
| } | |
| // Same-document navigations don't get a new RFH, so there isn't really | |
| // anything to update here. Eagerly updating the tracked RFH is harmful, | |
| // since the same-document navigation may interleave with cross-document | |
| // navigations, e.g. if triggered from an unload handler. | |
| // Another navigation may still be waiting to commit in the selected RFH. | |
| // Finishing this navigation must not switch away from that RFH. | |
| const bool should_update_frame_host = | |
| !request->IsSameDocument() && | |
| std::ranges::none_of( | |
| navigation_requests_, [this](const auto& pending_request) { | |
| return pending_request->IsWaitingToCommit() && | |
| pending_request->GetRenderFrameHost() == frame_host_.get(); | |
| }); | |
| if (should_update_frame_host) { | |
| // UpdateFrameHost may destruct |this|. | |
| protect = this; | |
| UpdateFrameHost(frame_tree_node_->current_frame_host()); | |
| } | |
| if (navigation_requests_.empty()) { | |
| for (DevToolsSession* session : sessions()) | |
| session->ResumeSendingMessagesToAgent(); | |
| } | |
| } | |
| auto_attacher_->DidFinishNavigation( | |
| NavigationRequest::From(navigation_handle)); | |
| } | |
| void RenderFrameDevToolsAgentHost::UpdateFrameHost( | |
| RenderFrameHostImpl* frame_host) { | |
| if (frame_host && !ShouldCreateDevToolsForHost(frame_host)) { | |
| DestroyOnRenderFrameGone(); | |
| // |this| may be deleted at this point. | |
| return; | |
| } | |
| std::vector<DevToolsSession*> restricted_sessions; | |
| for (DevToolsSession* session : sessions()) { | |
| if (!ShouldAllowSession(frame_host, session)) { | |
| restricted_sessions.push_back(session); | |
| } | |
| } | |
| scoped_refptr<RenderFrameDevToolsAgentHost> protect; | |
| if (!restricted_sessions.empty()) { | |
| protect = this; | |
| ForceDetachRestrictedSessions(restricted_sessions); | |
| } | |
| if (frame_host == frame_host_) { | |
| if (frame_host && !render_frame_alive_) | |
| UpdateFrameAlive(); | |
| return; | |
| } | |
| RenderFrameHostImpl* old_host = frame_host_; | |
| ChangeFrameHostAndObservedProcess(frame_host); | |
| if (IsAttached()) { | |
| UpdateRawHeadersAccess(old_host, nullptr); | |
| } | |
| UpdateFrameAlive(); | |
| } | |
| void RenderFrameDevToolsAgentHost::DidStartNavigation( | |
| NavigationHandle* navigation_handle) { | |
| NavigationRequest* request = NavigationRequest::From(navigation_handle); | |
| if (request->frame_tree_node() != frame_tree_node_) | |
| return; | |
| if (request->IsSameDocument()) { | |
| return; | |
| } | |
| if (navigation_requests_.empty()) { | |
| for (DevToolsSession* session : sessions()) | |
| session->SuspendSendingMessagesToAgent(); | |
| } | |
| navigation_requests_.insert(request); | |
| } | |
| void RenderFrameDevToolsAgentHost::RenderFrameHostChanged( | |
| RenderFrameHost* old_host, | |
| RenderFrameHost* new_host) { | |
| auto* new_host_impl = static_cast<RenderFrameHostImpl*>(new_host); | |
| FrameTreeNode* frame_tree_node = new_host_impl->frame_tree_node(); | |
| if (frame_tree_node != frame_tree_node_) | |
| return; | |
| UpdateFrameHost(new_host_impl); | |
| // UpdateFrameHost may destruct |this|. | |
| } | |
| void RenderFrameDevToolsAgentHost::FrameDeleted( | |
| FrameTreeNodeId frame_tree_node_id) { | |
| for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) | |
| tracing->FrameDeleted(frame_tree_node_id); | |
| if (frame_tree_node_ && | |
| frame_tree_node_id == frame_tree_node_->frame_tree_node_id()) { | |
| DestroyOnRenderFrameGone(); | |
| // |this| may be deleted at this point. | |
| } | |
| } | |
| void RenderFrameDevToolsAgentHost::RenderFrameDeleted(RenderFrameHost* rfh) { | |
| if (rfh == frame_host_) { | |
| if (frame_tree_node_) { | |
| render_frame_alive_ = false; | |
| UpdateRendererChannel(IsAttached()); | |
| } else { | |
| // We're already detached from FTN, so this must be a cached | |
| // instance going away. | |
| DestroyOnRenderFrameGone(); | |
| } | |
| } | |
| } | |
| void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() { | |
| scoped_refptr<DevToolsAgentHost> retain_this; | |
| if (IsAttached()) { | |
| retain_this = ForceDetachAllSessionsImpl(); | |
| UpdateRawHeadersAccess(frame_host_, nullptr); | |
| } | |
| WebContentsObserver::Observe(nullptr); | |
| ChangeFrameHostAndObservedProcess(nullptr); | |
| UpdateRendererChannel(IsAttached()); | |
| SetFrameTreeNode(nullptr); | |
| Release(); | |
| } | |
| #if BUILDFLAG(IS_ANDROID) | |
| device::mojom::WakeLock* RenderFrameDevToolsAgentHost::GetWakeLock() { | |
| // Here is a lazy binding, and will not reconnect after connection error. | |
| if (!wake_lock_) { | |
| mojo::PendingReceiver<device::mojom::WakeLock> receiver = | |
| wake_lock_.BindNewPipeAndPassReceiver(); | |
| 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(receiver)); | |
| } | |
| } | |
| return wake_lock_.get(); | |
| } | |
| #endif | |
| void RenderFrameDevToolsAgentHost::ChangeFrameHostAndObservedProcess( | |
| RenderFrameHostImpl* frame_host) { | |
| if (frame_host_ != frame_host) { | |
| did_try_to_initialize_prerender_primary_main_frame_ = false; | |
| } | |
| if (frame_host_) | |
| frame_host_->GetProcess()->RemoveObserver(this); | |
| frame_host_ = frame_host; | |
| if (frame_host_) { | |
| CHECK( | |
| WebContentsImpl::FromRenderFrameHostImpl(frame_host_) == web_contents(), | |
| base::NotFatalUntil::M159); | |
| frame_host_->GetProcess()->AddObserver(this); | |
| ProcessHostChanged(); | |
| } | |
| } | |
| void RenderFrameDevToolsAgentHost::UpdateFrameAlive() { | |
| render_frame_alive_ = frame_host_ && frame_host_->IsRenderFrameLive(); | |
| if (render_frame_alive_ && render_frame_crashed_) { | |
| render_frame_crashed_ = false; | |
| for (DevToolsSession* session : sessions()) | |
| session->ClearPendingMessages(/*did_crash=*/true); | |
| for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this)) | |
| inspector->TargetReloadedAfterCrash(); | |
| } | |
| UpdateRendererChannel(IsAttached()); | |
| } | |
| void RenderFrameDevToolsAgentHost::RenderProcessExited( | |
| RenderProcessHost* host, | |
| const ChildProcessTerminationInfo& info) { | |
| switch (info.status) { | |
| case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: | |
| case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: | |
| #if BUILDFLAG(IS_CHROMEOS) | |
| case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM: | |
| #endif | |
| case base::TERMINATION_STATUS_PROCESS_CRASHED: | |
| #if BUILDFLAG(IS_ANDROID) | |
| case base::TERMINATION_STATUS_OOM_PROTECTED: | |
| #endif | |
| case base::TERMINATION_STATUS_LAUNCH_FAILED: | |
| for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this)) | |
| inspector->TargetCrashed(); | |
| NotifyCrashed(info.status); | |
| render_frame_crashed_ = true; | |
| break; | |
| default: | |
| for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this)) | |
| inspector->TargetDetached("Render process gone."); | |
| break; | |
| } | |
| } | |
| void RenderFrameDevToolsAgentHost::OnVisibilityChanged( | |
| content::Visibility visibility) { | |
| #if BUILDFLAG(IS_ANDROID) | |
| if (!sessions().empty()) { | |
| if (visibility == content::Visibility::HIDDEN) { | |
| GetWakeLock()->CancelWakeLock(); | |
| } else { | |
| GetWakeLock()->RequestWakeLock(); | |
| } | |
| } | |
| #endif | |
| } | |
| void RenderFrameDevToolsAgentHost::OnNavigationRequestWillBeSent( | |
| const NavigationRequest& navigation_request) { | |
| GURL url = navigation_request.common_params().url; | |
| if (url.SchemeIs(url::kJavaScriptScheme) && frame_host_) | |
| url = frame_host_->GetLastCommittedURL(); | |
| std::vector<DevToolsSession*> restricted_sessions; | |
| bool is_webui = frame_host_ && frame_host_->web_ui(); | |
| for (DevToolsSession* session : sessions()) { | |
| if (!session->GetClient()->MayAttachToURL(url, is_webui)) | |
| restricted_sessions.push_back(session); | |
| } | |
| if (!restricted_sessions.empty()) | |
| ForceDetachRestrictedSessions(restricted_sessions); | |
| } | |
| void RenderFrameDevToolsAgentHost::DisconnectWebContents() { | |
| WebContentsObserver::Observe(nullptr); | |
| navigation_requests_.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->GetPrimaryMainFrame()); | |
| CHECK(host, base::NotFatalUntil::M159); | |
| WebContentsObserver::Observe(wc); | |
| 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()->frame_tree_node()); | |
| // During teardown/disconnect of the parent frame, the parent's agent host | |
| // may have been unmapped. Avoid recreating it here to prevent registering | |
| // a duplicate agent host ID (which would cause a crash). | |
| DevToolsAgentHostImpl* parent = | |
| RenderFrameDevToolsAgentHost::GetFor(frame_tree_node); | |
| return parent ? parent->GetId() : std::string(); | |
| } | |
| WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents()); | |
| if (!contents) { | |
| return std::string(); | |
| } | |
| if (!base::FeatureList::IsEnabled(features::kGuestViewMPArch)) { | |
| if (WebContents* outer_contents = contents->GetOuterWebContents()) { | |
| auto* delegate = DevToolsManager::GetInstance()->delegate(); | |
| if (delegate && | |
| delegate->ShouldReportAsTabTarget(web_contents()).value_or(false)) { | |
| // Delegates wants to report it as Tab, report as top level target. | |
| return std::string(); | |
| } | |
| return DevToolsAgentHost::GetOrCreateFor(outer_contents)->GetId(); | |
| } | |
| } else { | |
| if (frame_tree_node_ && | |
| frame_tree_node_->GetFrameType() == FrameType::kGuestMainFrame) { | |
| return DevToolsAgentHost::GetOrCreateFor(contents)->GetId(); | |
| } | |
| } | |
| return std::string(); | |
| } | |
| std::string RenderFrameDevToolsAgentHost::GetOpenerId() { | |
| if (!frame_tree_node_) | |
| return std::string(); | |
| FrameTreeNode* opener = | |
| frame_tree_node_->first_live_main_frame_in_original_opener_chain(); | |
| return opener | |
| ? opener->current_frame_host()->devtools_frame_token().ToString() | |
| : std::string(); | |
| } | |
| std::string RenderFrameDevToolsAgentHost::GetOpenerFrameId() { | |
| if (!frame_tree_node_) | |
| return std::string(); | |
| const std::optional<base::UnguessableToken>& opener_devtools_frame_token = | |
| frame_tree_node_->opener_devtools_frame_token(); | |
| return opener_devtools_frame_token ? opener_devtools_frame_token->ToString() | |
| : std::string(); | |
| } | |
| std::string RenderFrameDevToolsAgentHost::GetParentFrameId() { | |
| auto* parent = | |
| frame_tree_node_ ? frame_tree_node_->GetParentOrOuterDocument() : nullptr; | |
| return parent ? parent->devtools_frame_token().ToString() : std::string(); | |
| } | |
| bool RenderFrameDevToolsAgentHost::CanAccessOpener() { | |
| return (frame_tree_node_ && frame_tree_node_->opener()); | |
| } | |
| std::string RenderFrameDevToolsAgentHost::GetType() { | |
| if (IsChildFrame()) | |
| return kTypeFrame; | |
| if (frame_tree_node_ && frame_tree_node_->IsFencedFrameRoot()) | |
| return kTypeFrame; | |
| // Prerender pages should always be reported as "page" type, even when they | |
| // live in a separate WebContents (target_hint="_blank") that may not be | |
| // associated with a browser tab. Without this, the embedder's | |
| // GetTargetType() could return "other" for the prerender's WebContents, | |
| // preventing DevTools from inspecting the prerendered page. | |
| if (frame_tree_node_ && | |
| frame_tree_node_->GetFrameType() == FrameType::kPrerenderMainFrame) { | |
| return kTypePage; | |
| } | |
| if (!base::FeatureList::IsEnabled(features::kGuestViewMPArch)) { | |
| if (web_contents() && | |
| static_cast<WebContentsImpl*>(web_contents())->GetOuterWebContents()) { | |
| auto* delegate = DevToolsManager::GetInstance()->delegate(); | |
| // If delegate does not indicate that it should be reported as Tab, report | |
| // the default kTypeGuest. Otherwise, continue with code below to get | |
| // target type from delegate. | |
| if (!delegate || | |
| !delegate->ShouldReportAsTabTarget(web_contents()).value_or(false)) { | |
| return kTypeGuest; | |
| } | |
| } | |
| } else { | |
| if (frame_tree_node_ && | |
| frame_tree_node_->GetFrameType() == FrameType::kGuestMainFrame) { | |
| return kTypeGuest; | |
| } | |
| } | |
| 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 (frame_host_) { | |
| if (IsChildFrame()) | |
| return frame_host_->GetLastCommittedURL().spec(); | |
| if (!frame_host_->GetPage().IsPrimary()) { | |
| NavigationEntryImpl* entry = frame_host_->frame_tree_node() | |
| ->frame_tree() | |
| .controller() | |
| .GetLastCommittedEntry(); | |
| return entry ? base::UTF16ToUTF8(entry->GetTitleForDisplay()) | |
| : std::string(); | |
| } | |
| } | |
| 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. | |
| if (frame_host_ && (IsChildFrame() || !frame_host_->GetPage().IsPrimary())) | |
| return frame_host_->GetLastCommittedURL(); | |
| WebContents* web_contents = GetWebContents(); | |
| if (web_contents) { | |
| return web_contents->GetVisibleURL(); | |
| } | |
| return GURL(); | |
| } | |
| GURL RenderFrameDevToolsAgentHost::GetFaviconURL() { | |
| if (!frame_tree_node_) { | |
| return GURL(); | |
| } | |
| NavigationEntry* entry = | |
| frame_tree_node_->frame_tree().controller().GetLastCommittedEntry(); | |
| return entry ? entry->GetFavicon().url : 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->GetLastActiveTimeTicks(); | |
| return base::TimeTicks(); | |
| } | |
| void RenderFrameDevToolsAgentHost::UpdateRendererChannel(bool force) { | |
| is_debugger_paused_ = false; | |
| is_debugger_pause_situation_recorded_ = false; | |
| if (force && frame_host_ && !render_frame_alive_ && | |
| !did_try_to_initialize_prerender_primary_main_frame_) { | |
| bool did_try_to_initialize = false; | |
| if (MaybeInitializePrerenderPrimaryMainFrame(frame_host_, | |
| &did_try_to_initialize)) { | |
| render_frame_alive_ = true; | |
| } | |
| did_try_to_initialize_prerender_primary_main_frame_ = did_try_to_initialize; | |
| } | |
| mojo::PendingAssociatedRemote<blink::mojom::DevToolsAgent> agent_remote; | |
| mojo::PendingAssociatedReceiver<blink::mojom::DevToolsAgentHost> | |
| host_receiver; | |
| if (frame_host_ && render_frame_alive_ && force) { | |
| mojo::PendingAssociatedRemote<blink::mojom::DevToolsAgentHost> host_remote; | |
| host_receiver = host_remote.InitWithNewEndpointAndPassReceiver(); | |
| frame_host_->BindDevToolsAgent( | |
| std::move(host_remote), | |
| agent_remote.InitWithNewEndpointAndPassReceiver()); | |
| } | |
| int process_id = frame_host_ ? frame_host_->GetProcess()->GetDeprecatedID() | |
| : ChildProcessHost::kInvalidUniqueID; | |
| GetRendererChannel()->SetRendererAssociated(std::move(agent_remote), | |
| std::move(host_receiver), | |
| process_id, frame_host_); | |
| auto_attacher_->SetRenderFrameHost(frame_host_); | |
| } | |
| protocol::TargetAutoAttacher* RenderFrameDevToolsAgentHost::auto_attacher() { | |
| return auto_attacher_.get(); | |
| } | |
| namespace { | |
| constexpr char kSubtypeDisconnected[] = "disconnected"; | |
| constexpr char kSubtypePrerender[] = "prerender"; | |
| constexpr char kSubtypeFenced[] = "fenced"; | |
| } // namespace | |
| std::string RenderFrameDevToolsAgentHost::GetSubtype() { | |
| if (!frame_tree_node_) | |
| return kSubtypeDisconnected; | |
| switch (frame_tree_node_->GetFrameType()) { | |
| case FrameType::kPrimaryMainFrame: | |
| case FrameType::kGuestMainFrame: | |
| // TODO(caseq): figure out what's best to return for subframes in a tree | |
| // other than primary. | |
| case FrameType::kSubframe: | |
| return ""; | |
| case FrameType::kPrerenderMainFrame: | |
| return kSubtypePrerender; | |
| case FrameType::kFencedFrameRoot: | |
| return kSubtypeFenced; | |
| } | |
| } | |
| RenderProcessHost* RenderFrameDevToolsAgentHost::GetProcessHost() { | |
| return frame_host_ ? frame_host_->GetProcess() : nullptr; | |
| } | |
| void RenderFrameDevToolsAgentHost::MainThreadDebuggerPaused() { | |
| is_debugger_paused_ = true; | |
| if (!GetWebContents() || is_debugger_pause_situation_recorded_) { | |
| return; | |
| } | |
| if (GetWebContents() != GetWebContents()->GetOutermostWebContents()) { | |
| return; | |
| } | |
| url::Origin our_origin = | |
| url::Origin::Create(GetWebContents()->GetLastCommittedURL()); | |
| bool is_same_origin_debugger_paused_in_another_renderer = false; | |
| for (const auto& entry : GetAgentHostInstances()) { | |
| RenderFrameDevToolsAgentHost* agent_host = entry.second; | |
| if (agent_host == this || !agent_host->GetWebContents()) { | |
| continue; | |
| } | |
| if (agent_host->GetWebContents() != | |
| agent_host->GetWebContents()->GetOutermostWebContents()) { | |
| continue; | |
| } | |
| if (!our_origin.IsSameOriginWith( | |
| agent_host->GetWebContents()->GetLastCommittedURL())) { | |
| continue; | |
| } | |
| if (agent_host->is_debugger_paused_) { | |
| is_same_origin_debugger_paused_in_another_renderer = true; | |
| } | |
| } | |
| base::UmaHistogramBoolean( | |
| "DevTools.IsSameOriginDebuggerPausedInAnotherRenderer", | |
| is_same_origin_debugger_paused_in_another_renderer); | |
| is_debugger_pause_situation_recorded_ = true; | |
| } | |
| void RenderFrameDevToolsAgentHost::MainThreadDebuggerResumed() { | |
| is_debugger_paused_ = false; | |
| } | |
| bool RenderFrameDevToolsAgentHost::IsChildFrame() { | |
| return frame_tree_node_ && frame_tree_node_->parent(); | |
| } | |
| bool RenderFrameDevToolsAgentHost::ShouldAllowSession( | |
| RenderFrameHost* frame_host, | |
| DevToolsSession* session) { | |
| // There's not much we can say if there's not host yet, but we'll | |
| // check again when host is updated. | |
| if (!frame_host) { | |
| return true; | |
| } | |
| DevToolsManager* manager = DevToolsManager::GetInstance(); | |
| if (manager->delegate() && | |
| !manager->delegate()->AllowInspectingRenderFrameHost(frame_host)) { | |
| return false; | |
| } | |
| return session->GetClient()->MayAttachToRenderFrameHost(frame_host); | |
| } | |
| void RenderFrameDevToolsAgentHost::UpdateResourceLoaderFactories() { | |
| if (!frame_host_) | |
| return; | |
| frame_host_->ForEachRenderFrameHostImplWithAction([this](RenderFrameHostImpl* | |
| rfh) { | |
| if (frame_host_ != rfh && (rfh->is_local_root_subframe() || | |
| &frame_host_->GetPage() != &rfh->GetPage())) { | |
| return content::RenderFrameHost::FrameIterationAction::kSkipChildren; | |
| } | |
| rfh->UpdateSubresourceLoaderFactories(); | |
| // Network requests from dedicated workers are intercepted through the owner | |
| // frame target, so we should update loader factories when interception | |
| // parameters change. | |
| DedicatedWorkerHostsForDocument::GetOrCreateForCurrentDocument(rfh) | |
| ->UpdateSubresourceLoaderFactories(); | |
| return content::RenderFrameHost::FrameIterationAction::kContinue; | |
| }); | |
| } | |
| std::optional<network::CrossOriginEmbedderPolicy> | |
| RenderFrameDevToolsAgentHost::cross_origin_embedder_policy( | |
| const std::string& id) { | |
| FrameTreeNode* frame_tree_node = | |
| protocol::FrameTreeNodeFromDevToolsFrameToken( | |
| frame_host_->frame_tree_node(), id); | |
| if (!frame_tree_node) { | |
| return std::nullopt; | |
| } | |
| RenderFrameHostImpl* rfhi = frame_tree_node->current_frame_host(); | |
| return rfhi->cross_origin_embedder_policy(); | |
| } | |
| std::optional<network::CrossOriginOpenerPolicy> | |
| RenderFrameDevToolsAgentHost::cross_origin_opener_policy( | |
| const std::string& id) { | |
| FrameTreeNode* frame_tree_node = | |
| protocol::FrameTreeNodeFromDevToolsFrameToken( | |
| frame_host_->frame_tree_node(), id); | |
| if (!frame_tree_node) { | |
| return std::nullopt; | |
| } | |
| RenderFrameHostImpl* rfhi = frame_tree_node->current_frame_host(); | |
| return rfhi->cross_origin_opener_policy(); | |
| } | |
| std::optional<std::vector<network::mojom::ContentSecurityPolicyHeader>> | |
| RenderFrameDevToolsAgentHost::content_security_policy(const std::string& id) { | |
| FrameTreeNode* frame_tree_node = | |
| protocol::FrameTreeNodeFromDevToolsFrameToken( | |
| frame_host_->frame_tree_node(), id); | |
| if (!frame_tree_node) { | |
| return std::nullopt; | |
| } | |
| RenderFrameHostImpl* rfhi = frame_tree_node->current_frame_host(); | |
| const PolicyContainerPolicies& policies = | |
| rfhi->policy_container_host()->policies(); | |
| if (policies.content_security_policies.empty()) { | |
| return std::nullopt; | |
| } else { | |
| std::vector<network::mojom::ContentSecurityPolicyHeader> csp_headers; | |
| for (const auto& content_security_policy : | |
| policies.content_security_policies) { | |
| csp_headers.push_back(*content_security_policy->header); | |
| } | |
| return csp_headers; | |
| } | |
| } | |
| bool RenderFrameDevToolsAgentHost::HasSessionsWithoutTabTargetSupport() const { | |
| const std::vector<raw_ptr<DevToolsSession, VectorExperimental>>& sessions = | |
| DevToolsAgentHostImpl::sessions(); | |
| return std::any_of(sessions.begin(), sessions.end(), [](DevToolsSession* s) { | |
| return s->session_mode() == DevToolsSession::Mode::kDoesNotSupportTabTarget; | |
| }); | |
| } | |
| } // namespace content |