blob: 6379b80eebe3cfdbd552048a5c5830674442145d [file] [log] [blame]
// Copyright 2018 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/devtools_instrumentation.h"
#include "base/containers/adapters.h"
#include "base/feature_list.h"
#include "base/notreached.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/traced_value.h"
#include "components/download/public/common/download_create_info.h"
#include "components/download/public/common/download_item.h"
#include "components/download/public/common/download_url_parameters.h"
#include "content/browser/devtools/browser_devtools_agent_host.h"
#include "content/browser/devtools/dedicated_worker_devtools_agent_host.h"
#include "content/browser/devtools/devtools_issue_storage.h"
#include "content/browser/devtools/devtools_preload_storage.h"
#include "content/browser/devtools/protocol/audits.h"
#include "content/browser/devtools/protocol/audits_handler.h"
#include "content/browser/devtools/protocol/browser_handler.h"
#include "content/browser/devtools/protocol/device_access_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/input_handler.h"
#include "content/browser/devtools/protocol/log_handler.h"
#include "content/browser/devtools/protocol/network.h"
#include "content/browser/devtools/protocol/network_handler.h"
#include "content/browser/devtools/protocol/page_handler.h"
#include "content/browser/devtools/protocol/preload_handler.h"
#include "content/browser/devtools/protocol/security_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/devtools/render_frame_devtools_agent_host.h"
#include "content/browser/devtools/service_worker_devtools_agent_host.h"
#include "content/browser/devtools/shared_worker_devtools_agent_host.h"
#include "content/browser/devtools/web_contents_devtools_agent_host.h"
#include "content/browser/devtools/worker_devtools_manager.h"
#include "content/browser/preloading/prerender/prerender_final_status.h"
#include "content/browser/preloading/prerender/prerender_metrics.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/navigation_request.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_package/signed_exchange_envelope.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/cookie_insight_list_data.h"
#include "content/public/browser/cookie_insight_list_handler.h"
#include "devtools_agent_host_impl.h"
#include "devtools_instrumentation.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "net/base/features.h"
#include "net/base/load_flags.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_inclusion_status.h"
#include "net/filter/source_stream_type.h"
#include "net/http/http_request_headers.h"
#include "net/quic/web_transport_error.h"
#include "net/ssl/ssl_info.h"
#include "services/network/public/cpp/devtools_observer_util.h"
#include "services/network/public/cpp/url_loader_factory_builder.h"
#include "services/network/public/mojom/devtools_observer.mojom.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
#include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
namespace content {
namespace devtools_instrumentation {
namespace {
namespace AttributionReportingIssueTypeEnum =
protocol::Audits::AttributionReportingIssueTypeEnum;
const char kPrivacySandboxExtensionsAPI[] = "PrivacySandboxExtensionsAPI";
template <typename Handler, typename... MethodArgs, typename... Args>
void DispatchToAgents(DevToolsAgentHostImpl* host,
void (Handler::*method)(MethodArgs...),
Args&&... args) {
if (!host) {
return;
}
for (auto* h : Handler::ForAgentHost(host)) {
(h->*method)(std::forward<Args>(args)...);
}
}
template <typename Handler, typename... MethodArgs, typename... Args>
void DispatchToAgents(FrameTreeNode* frame_tree_node,
void (Handler::*method)(MethodArgs...),
Args&&... args) {
DevToolsAgentHostImpl* agent_host =
RenderFrameDevToolsAgentHost::GetFor(frame_tree_node);
DispatchToAgents(agent_host, method, std::forward<Args>(args)...);
}
template <typename Handler, typename... MethodArgs, typename... Args>
void DispatchToAgents(FrameTreeNodeId 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)...);
}
}
template <typename Handler, typename... MethodArgs, typename... Args>
void DispatchToAgents(WebContents* web_contents,
void (Handler::*method)(MethodArgs...),
Args&&... args) {
auto agent_host = DevToolsAgentHost::GetForTab(web_contents);
if (agent_host) {
DispatchToAgents(static_cast<DevToolsAgentHostImpl*>(agent_host.get()),
method, std::forward<Args>(args)...);
}
if (DevToolsAgentHost::HasFor(web_contents)) {
DispatchToAgents(
static_cast<DevToolsAgentHostImpl*>(
DevToolsAgentHost::GetOrCreateFor(web_contents).get()),
method, std::forward<Args>(args)...);
}
}
std::unique_ptr<protocol::Audits::InspectorIssue> BuildHeavyAdIssue(
const blink::mojom::HeavyAdIssueDetailsPtr& issue_details) {
protocol::String status =
(issue_details->resolution ==
blink::mojom::HeavyAdResolutionStatus::kHeavyAdBlocked)
? protocol::Audits::HeavyAdResolutionStatusEnum::HeavyAdBlocked
: protocol::Audits::HeavyAdResolutionStatusEnum::HeavyAdWarning;
protocol::String reason_string;
switch (issue_details->reason) {
case blink::mojom::HeavyAdReason::kNetworkTotalLimit:
reason_string = protocol::Audits::HeavyAdReasonEnum::NetworkTotalLimit;
break;
case blink::mojom::HeavyAdReason::kCpuTotalLimit:
reason_string = protocol::Audits::HeavyAdReasonEnum::CpuTotalLimit;
break;
case blink::mojom::HeavyAdReason::kCpuPeakLimit:
reason_string = protocol::Audits::HeavyAdReasonEnum::CpuPeakLimit;
break;
}
auto heavy_ad_details =
protocol::Audits::HeavyAdIssueDetails::Create()
.SetReason(reason_string)
.SetResolution(status)
.SetFrame(protocol::Audits::AffectedFrame::Create()
.SetFrameId(issue_details->frame->frame_id)
.Build())
.Build();
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetHeavyAdIssueDetails(std::move(heavy_ad_details))
.Build();
auto issue =
protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::HeavyAdIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return issue;
}
protocol::Audits::AttributionReportingIssueType
BuildAttributionReportingIssueViolationType(
blink::mojom::AttributionReportingIssueType type) {
switch (type) {
case blink::mojom::AttributionReportingIssueType::kPermissionPolicyDisabled:
return AttributionReportingIssueTypeEnum::PermissionPolicyDisabled;
case blink::mojom::AttributionReportingIssueType::
kUntrustworthyReportingOrigin:
return AttributionReportingIssueTypeEnum::UntrustworthyReportingOrigin;
case blink::mojom::AttributionReportingIssueType::kInsecureContext:
return AttributionReportingIssueTypeEnum::InsecureContext;
case blink::mojom::AttributionReportingIssueType::
kInvalidRegisterSourceHeader:
return AttributionReportingIssueTypeEnum::InvalidHeader;
case blink::mojom::AttributionReportingIssueType::
kInvalidRegisterTriggerHeader:
return AttributionReportingIssueTypeEnum::InvalidRegisterTriggerHeader;
case blink::mojom::AttributionReportingIssueType::kSourceAndTriggerHeaders:
return AttributionReportingIssueTypeEnum::SourceAndTriggerHeaders;
case blink::mojom::AttributionReportingIssueType::kSourceIgnored:
return AttributionReportingIssueTypeEnum::SourceIgnored;
case blink::mojom::AttributionReportingIssueType::kTriggerIgnored:
return AttributionReportingIssueTypeEnum::TriggerIgnored;
case blink::mojom::AttributionReportingIssueType::kOsSourceIgnored:
return AttributionReportingIssueTypeEnum::OsSourceIgnored;
case blink::mojom::AttributionReportingIssueType::kOsTriggerIgnored:
return AttributionReportingIssueTypeEnum::OsTriggerIgnored;
case blink::mojom::AttributionReportingIssueType::
kInvalidRegisterOsSourceHeader:
return AttributionReportingIssueTypeEnum::InvalidRegisterOsSourceHeader;
case blink::mojom::AttributionReportingIssueType::
kInvalidRegisterOsTriggerHeader:
return AttributionReportingIssueTypeEnum::InvalidRegisterOsTriggerHeader;
case blink::mojom::AttributionReportingIssueType::kWebAndOsHeaders:
return AttributionReportingIssueTypeEnum::WebAndOsHeaders;
case blink::mojom::AttributionReportingIssueType::kNoWebOrOsSupport:
return AttributionReportingIssueTypeEnum::NoWebOrOsSupport;
case blink::mojom::AttributionReportingIssueType::
kNavigationRegistrationWithoutTransientUserActivation:
// This issue is not reported from the browser.
NOTREACHED();
case blink::mojom::AttributionReportingIssueType::kInvalidInfoHeader:
return AttributionReportingIssueTypeEnum::InvalidInfoHeader;
case blink::mojom::AttributionReportingIssueType::kNoRegisterSourceHeader:
return AttributionReportingIssueTypeEnum::NoRegisterSourceHeader;
case blink::mojom::AttributionReportingIssueType::kNoRegisterTriggerHeader:
return AttributionReportingIssueTypeEnum::NoRegisterTriggerHeader;
case blink::mojom::AttributionReportingIssueType::kNoRegisterOsSourceHeader:
return AttributionReportingIssueTypeEnum::NoRegisterOsSourceHeader;
case blink::mojom::AttributionReportingIssueType::
kNoRegisterOsTriggerHeader:
return AttributionReportingIssueTypeEnum::NoRegisterOsTriggerHeader;
case blink::mojom::AttributionReportingIssueType::
kNavigationRegistrationUniqueScopeAlreadySet:
return AttributionReportingIssueTypeEnum::
NavigationRegistrationUniqueScopeAlreadySet;
}
}
std::unique_ptr<protocol::Audits::InspectorIssue>
BuildAttributionReportingIssue(
const blink::mojom::AttributionReportingIssueDetailsPtr& issue_details) {
protocol::String violation_type = BuildAttributionReportingIssueViolationType(
issue_details->violation_type);
auto request = protocol::Audits::AffectedRequest::Create()
.SetUrl(issue_details->request->url)
.Build();
if (issue_details->request->request_id.has_value()) {
request->SetRequestId(issue_details->request->request_id.value());
}
auto attribution_reporting_issue_details =
protocol::Audits::AttributionReportingIssueDetails::Create()
.SetViolationType(violation_type)
.SetRequest(std::move(request))
.Build();
if (issue_details->invalid_parameter.has_value()) {
attribution_reporting_issue_details->SetInvalidParameter(
issue_details->invalid_parameter.value());
}
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetAttributionReportingIssueDetails(
std::move(attribution_reporting_issue_details))
.Build();
auto issue = protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::
AttributionReportingIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return issue;
}
protocol::Audits::FederatedAuthRequestIssueReason
FederatedAuthRequestResultToProtocol(
blink::mojom::FederatedAuthRequestResult result) {
using blink::mojom::FederatedAuthRequestResult;
namespace FederatedAuthRequestIssueReasonEnum =
protocol::Audits::FederatedAuthRequestIssueReasonEnum;
switch (result) {
case FederatedAuthRequestResult::kShouldEmbargo: {
return FederatedAuthRequestIssueReasonEnum::ShouldEmbargo;
}
case FederatedAuthRequestResult::kDisabledInSettings: {
return FederatedAuthRequestIssueReasonEnum::DisabledInSettings;
}
case FederatedAuthRequestResult::kDisabledInFlags: {
return FederatedAuthRequestIssueReasonEnum::DisabledInFlags;
}
case FederatedAuthRequestResult::kIdpNotPotentiallyTrustworthy: {
return FederatedAuthRequestIssueReasonEnum::IdpNotPotentiallyTrustworthy;
}
case FederatedAuthRequestResult::kTooManyRequests: {
return FederatedAuthRequestIssueReasonEnum::TooManyRequests;
}
case FederatedAuthRequestResult::kWellKnownHttpNotFound: {
return FederatedAuthRequestIssueReasonEnum::WellKnownHttpNotFound;
}
case FederatedAuthRequestResult::kWellKnownNoResponse: {
return FederatedAuthRequestIssueReasonEnum::WellKnownNoResponse;
}
case FederatedAuthRequestResult::kWellKnownInvalidResponse: {
return FederatedAuthRequestIssueReasonEnum::WellKnownInvalidResponse;
}
case FederatedAuthRequestResult::kWellKnownListEmpty: {
return FederatedAuthRequestIssueReasonEnum::WellKnownListEmpty;
}
case FederatedAuthRequestResult::kWellKnownInvalidContentType: {
return FederatedAuthRequestIssueReasonEnum::WellKnownInvalidContentType;
}
case FederatedAuthRequestResult::kConfigNotInWellKnown: {
return FederatedAuthRequestIssueReasonEnum::ConfigNotInWellKnown;
}
case FederatedAuthRequestResult::kWellKnownTooBig: {
return FederatedAuthRequestIssueReasonEnum::WellKnownTooBig;
}
case FederatedAuthRequestResult::kConfigHttpNotFound: {
return FederatedAuthRequestIssueReasonEnum::ConfigHttpNotFound;
}
case FederatedAuthRequestResult::kConfigNoResponse: {
return FederatedAuthRequestIssueReasonEnum::ConfigNoResponse;
}
case FederatedAuthRequestResult::kConfigInvalidResponse: {
return FederatedAuthRequestIssueReasonEnum::ConfigInvalidResponse;
}
case FederatedAuthRequestResult::kConfigInvalidContentType: {
return FederatedAuthRequestIssueReasonEnum::ConfigInvalidContentType;
}
case FederatedAuthRequestResult::kClientMetadataHttpNotFound: {
return FederatedAuthRequestIssueReasonEnum::ClientMetadataHttpNotFound;
}
case FederatedAuthRequestResult::kClientMetadataNoResponse: {
return FederatedAuthRequestIssueReasonEnum::ClientMetadataNoResponse;
}
case FederatedAuthRequestResult::kClientMetadataInvalidResponse: {
return FederatedAuthRequestIssueReasonEnum::ClientMetadataInvalidResponse;
}
case FederatedAuthRequestResult::kClientMetadataInvalidContentType: {
return FederatedAuthRequestIssueReasonEnum::
ClientMetadataInvalidContentType;
}
case FederatedAuthRequestResult::kAccountsHttpNotFound: {
return FederatedAuthRequestIssueReasonEnum::AccountsHttpNotFound;
}
case FederatedAuthRequestResult::kAccountsNoResponse: {
return FederatedAuthRequestIssueReasonEnum::AccountsNoResponse;
}
case FederatedAuthRequestResult::kAccountsInvalidResponse: {
return FederatedAuthRequestIssueReasonEnum::AccountsInvalidResponse;
}
case FederatedAuthRequestResult::kAccountsListEmpty: {
return FederatedAuthRequestIssueReasonEnum::AccountsListEmpty;
}
case FederatedAuthRequestResult::kAccountsInvalidContentType: {
return FederatedAuthRequestIssueReasonEnum::AccountsInvalidContentType;
}
case FederatedAuthRequestResult::kIdTokenHttpNotFound: {
return FederatedAuthRequestIssueReasonEnum::IdTokenHttpNotFound;
}
case FederatedAuthRequestResult::kIdTokenNoResponse: {
return FederatedAuthRequestIssueReasonEnum::IdTokenNoResponse;
}
case FederatedAuthRequestResult::kIdTokenInvalidResponse: {
return FederatedAuthRequestIssueReasonEnum::IdTokenInvalidResponse;
}
case FederatedAuthRequestResult::kIdTokenIdpErrorResponse: {
return FederatedAuthRequestIssueReasonEnum::IdTokenIdpErrorResponse;
}
case FederatedAuthRequestResult::kIdTokenCrossSiteIdpErrorResponse: {
return FederatedAuthRequestIssueReasonEnum::
IdTokenCrossSiteIdpErrorResponse;
}
case FederatedAuthRequestResult::kIdTokenInvalidContentType: {
return FederatedAuthRequestIssueReasonEnum::IdTokenInvalidContentType;
}
case FederatedAuthRequestResult::kCanceled: {
return FederatedAuthRequestIssueReasonEnum::Canceled;
}
case FederatedAuthRequestResult::kRpPageNotVisible:
return FederatedAuthRequestIssueReasonEnum::RpPageNotVisible;
case FederatedAuthRequestResult::kError: {
return FederatedAuthRequestIssueReasonEnum::ErrorIdToken;
}
case FederatedAuthRequestResult::kSilentMediationFailure: {
return FederatedAuthRequestIssueReasonEnum::SilentMediationFailure;
}
case FederatedAuthRequestResult::kThirdPartyCookiesBlocked: {
return FederatedAuthRequestIssueReasonEnum::ThirdPartyCookiesBlocked;
}
case FederatedAuthRequestResult::kNotSignedInWithIdp: {
return FederatedAuthRequestIssueReasonEnum::NotSignedInWithIdp;
}
case FederatedAuthRequestResult::kMissingTransientUserActivation: {
return FederatedAuthRequestIssueReasonEnum::
MissingTransientUserActivation;
}
case FederatedAuthRequestResult::kReplacedByActiveMode: {
return FederatedAuthRequestIssueReasonEnum::ReplacedByActiveMode;
}
case FederatedAuthRequestResult::kInvalidFieldsSpecified: {
return FederatedAuthRequestIssueReasonEnum::InvalidFieldsSpecified;
}
case FederatedAuthRequestResult::kRelyingPartyOriginIsOpaque: {
return FederatedAuthRequestIssueReasonEnum::RelyingPartyOriginIsOpaque;
}
case FederatedAuthRequestResult::kTypeNotMatching: {
return FederatedAuthRequestIssueReasonEnum::TypeNotMatching;
}
case FederatedAuthRequestResult::kUiDismissedNoEmbargo: {
return FederatedAuthRequestIssueReasonEnum::UiDismissedNoEmbargo;
}
case FederatedAuthRequestResult::kCorsError: {
return FederatedAuthRequestIssueReasonEnum::CorsError;
}
case FederatedAuthRequestResult::kSuppressedBySegmentationPlatform: {
return FederatedAuthRequestIssueReasonEnum::
SuppressedBySegmentationPlatform;
}
case FederatedAuthRequestResult::kSuccess: {
NOTREACHED();
}
}
}
std::unique_ptr<protocol::Audits::InspectorIssue>
BuildFederatedAuthRequestIssue(
const blink::mojom::FederatedAuthRequestIssueDetailsPtr& issue_details) {
auto federated_auth_request_details =
protocol::Audits::FederatedAuthRequestIssueDetails::Create()
.SetFederatedAuthRequestIssueReason(
FederatedAuthRequestResultToProtocol(issue_details->status))
.Build();
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetFederatedAuthRequestIssueDetails(
std::move(federated_auth_request_details))
.Build();
auto issue = protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::
FederatedAuthRequestIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return issue;
}
protocol::Audits::FederatedAuthUserInfoRequestIssueReason
FederatedAuthUserInfoRequestResultToProtocol(
blink::mojom::FederatedAuthUserInfoRequestResult result) {
using blink::mojom::FederatedAuthUserInfoRequestResult;
namespace FederatedAuthUserInfoRequestIssueReasonEnum =
protocol::Audits::FederatedAuthUserInfoRequestIssueReasonEnum;
switch (result) {
case FederatedAuthUserInfoRequestResult::kNotSameOrigin: {
return FederatedAuthUserInfoRequestIssueReasonEnum::NotSameOrigin;
}
case FederatedAuthUserInfoRequestResult::kNotIframe: {
return FederatedAuthUserInfoRequestIssueReasonEnum::NotIframe;
}
case FederatedAuthUserInfoRequestResult::kNotPotentiallyTrustworthy: {
return FederatedAuthUserInfoRequestIssueReasonEnum::
NotPotentiallyTrustworthy;
}
case FederatedAuthUserInfoRequestResult::kNoApiPermission: {
return FederatedAuthUserInfoRequestIssueReasonEnum::NoApiPermission;
}
case FederatedAuthUserInfoRequestResult::kNotSignedInWithIdp: {
return FederatedAuthUserInfoRequestIssueReasonEnum::NotSignedInWithIdp;
}
case FederatedAuthUserInfoRequestResult::kNoAccountSharingPermission: {
return FederatedAuthUserInfoRequestIssueReasonEnum::
NoAccountSharingPermission;
}
case FederatedAuthUserInfoRequestResult::kInvalidConfigOrWellKnown: {
return FederatedAuthUserInfoRequestIssueReasonEnum::
InvalidConfigOrWellKnown;
}
case FederatedAuthUserInfoRequestResult::kInvalidAccountsResponse: {
return FederatedAuthUserInfoRequestIssueReasonEnum::
InvalidAccountsResponse;
}
case FederatedAuthUserInfoRequestResult::
kNoReturningUserFromFetchedAccounts: {
return FederatedAuthUserInfoRequestIssueReasonEnum::
NoReturningUserFromFetchedAccounts;
}
case FederatedAuthUserInfoRequestResult::kSuccess:
case FederatedAuthUserInfoRequestResult::kUnhandledRequest: {
NOTREACHED();
}
}
}
std::unique_ptr<protocol::Audits::InspectorIssue>
BuildFederatedAuthUserInfoRequestIssue(
const blink::mojom::FederatedAuthUserInfoRequestIssueDetailsPtr&
issue_details) {
auto federated_auth_user_info_request_details =
protocol::Audits::FederatedAuthUserInfoRequestIssueDetails::Create()
.SetFederatedAuthUserInfoRequestIssueReason(
FederatedAuthUserInfoRequestResultToProtocol(
issue_details->status))
.Build();
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetFederatedAuthUserInfoRequestIssueDetails(
std::move(federated_auth_user_info_request_details))
.Build();
auto issue = protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::
FederatedAuthUserInfoRequestIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return issue;
}
const char* DeprecationIssueTypeToProtocol(
blink::mojom::DeprecationIssueType error_type) {
switch (error_type) {
case blink::mojom::DeprecationIssueType::kPrivacySandboxExtensionsAPI:
return kPrivacySandboxExtensionsAPI;
}
}
std::unique_ptr<protocol::Audits::InspectorIssue> BuildDeprecationIssue(
const blink::mojom::DeprecationIssueDetailsPtr& issue_details) {
std::unique_ptr<protocol::Audits::SourceCodeLocation> source_code_location =
protocol::Audits::SourceCodeLocation::Create()
.SetUrl(issue_details->affected_location->url.value())
.SetLineNumber(issue_details->affected_location->line)
.SetColumnNumber(issue_details->affected_location->column)
.Build();
if (issue_details->affected_location->script_id.has_value()) {
source_code_location->SetScriptId(
issue_details->affected_location->script_id.value());
}
auto deprecation_issue_details =
protocol::Audits::DeprecationIssueDetails::Create()
.SetSourceCodeLocation(std::move(source_code_location))
.SetType(DeprecationIssueTypeToProtocol(issue_details->type))
.Build();
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetDeprecationIssueDetails(std::move(deprecation_issue_details))
.Build();
auto deprecation_issue =
protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::DeprecationIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return deprecation_issue;
}
std::unique_ptr<protocol::Audits::InspectorIssue> BuildBounceTrackingIssue(
const blink::mojom::BounceTrackingIssueDetailsPtr& issue_details) {
auto bounce_tracking_issue_details =
protocol::Audits::BounceTrackingIssueDetails::Create()
.SetTrackingSites(std::make_unique<protocol::Array<protocol::String>>(
issue_details->tracking_sites))
.Build();
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetBounceTrackingIssueDetails(
std::move(bounce_tracking_issue_details))
.Build();
auto issue =
protocol::Audits::InspectorIssue::Create()
.SetCode(
protocol::Audits::InspectorIssueCodeEnum::BounceTrackingIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return issue;
}
std::unique_ptr<protocol::Audits::InspectorIssue> BuildPartitioningBlobURLIssue(
const blink::mojom::PartitioningBlobURLIssueDetailsPtr& issue_details) {
protocol::String partitioning_blob_url_info_string;
switch (issue_details->partitioning_blob_url_info) {
case blink::mojom::PartitioningBlobURLInfo::kBlockedCrossPartitionFetching:
partitioning_blob_url_info_string = protocol::Audits::
PartitioningBlobURLInfoEnum::BlockedCrossPartitionFetching;
break;
case blink::mojom::PartitioningBlobURLInfo::kEnforceNoopenerForNavigation:
partitioning_blob_url_info_string = protocol::Audits::
PartitioningBlobURLInfoEnum::EnforceNoopenerForNavigation;
break;
default:
partitioning_blob_url_info_string = "Unknown";
break;
}
auto partitioning_blob_url_issue_details =
protocol::Audits::PartitioningBlobURLIssueDetails::Create()
.SetUrl(issue_details->url.spec())
.SetPartitioningBlobURLInfo(partitioning_blob_url_info_string)
.Build();
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetPartitioningBlobURLIssueDetails(
std::move(partitioning_blob_url_issue_details))
.Build();
auto issue = protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::
PartitioningBlobURLIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return issue;
}
void UpdateChildFrameTrees(FrameTreeNode* ftn, bool update_target_info) {
if (auto* agent_host = WebContentsDevToolsAgentHost::GetFor(
WebContentsImpl::FromFrameTreeNode(ftn))) {
agent_host->UpdateChildFrameTrees(update_target_info);
}
}
} // namespace
void 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 = FrameTreeNode::From(node->parent())) {
DispatchToAgents(node, &protocol::PageHandler::NavigationReset,
navigation_request);
}
}
void OnNavigationResponseReceived(const NavigationRequest& nav_request,
const network::mojom::URLResponseHead& head) {
// This response is artificial (see CachedNavigationURLLoader), so we don't
// want to report it.
if (nav_request.IsPageActivation()) {
return;
}
FrameTreeNode* ftn = nav_request.frame_tree_node();
std::string id = nav_request.devtools_navigation_token().ToString();
std::string frame_id =
ftn->current_frame_host()->devtools_frame_token().ToString();
GURL url = nav_request.common_params().url;
network::mojom::URLResponseHeadDevToolsInfoPtr head_info =
network::ExtractDevToolsInfo(head);
DispatchToAgents(ftn, &protocol::NetworkHandler::ResponseReceived, id, id,
url, protocol::Network::ResourceTypeEnum::Document,
*head_info, frame_id);
}
void OnFetchKeepAliveRequestWillBeSent(
FrameTreeNode* frame_tree_node,
const std::string& request_id,
const network::ResourceRequest& request,
std::optional<std::pair<const GURL&,
const network::mojom::URLResponseHeadDevToolsInfo&>>
redirect_info) {
CHECK(frame_tree_node);
auto timestamp = base::TimeTicks::Now();
std::string frame_token =
frame_tree_node->current_frame_host()->devtools_frame_token().ToString();
GURL initiator_url;
if (request.request_initiator.has_value()) {
initiator_url = request.request_initiator->GetURL();
}
DispatchToAgents(frame_tree_node,
&protocol::NetworkHandler::FetchKeepAliveRequestWillBeSent,
request_id, request, initiator_url, frame_token, timestamp,
redirect_info);
}
void OnFetchKeepAliveResponseReceived(
FrameTreeNode* frame_tree_node,
const std::string& request_id,
const GURL& url,
const network::mojom::URLResponseHead& head) {
CHECK(frame_tree_node);
std::string frame_token =
frame_tree_node->current_frame_host()->devtools_frame_token().ToString();
network::mojom::URLResponseHeadDevToolsInfoPtr head_info =
network::ExtractDevToolsInfo(head);
DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::ResponseReceived,
request_id, request_id, url,
protocol::Network::ResourceTypeEnum::Fetch, *head_info,
frame_token);
}
void OnFetchKeepAliveRequestComplete(
FrameTreeNode* frame_tree_node,
const std::string& request_id,
const network::URLLoaderCompletionStatus& status) {
CHECK(frame_tree_node);
DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::LoadingComplete,
request_id, protocol::Network::ResourceTypeEnum::Fetch,
status);
}
void BackForwardCacheNotUsed(
const NavigationRequest* nav_request,
const BackForwardCacheCanStoreDocumentResult* result,
const BackForwardCacheCanStoreTreeResult* tree_result) {
DCHECK(nav_request);
FrameTreeNode* ftn = nav_request->frame_tree_node();
DispatchToAgents(ftn, &protocol::PageHandler::BackForwardCacheNotUsed,
nav_request, result, tree_result);
}
void WillSwapFrameTreeNode(FrameTreeNode& old_node, FrameTreeNode& new_node) {
auto* host = static_cast<RenderFrameDevToolsAgentHost*>(
RenderFrameDevToolsAgentHost::GetFor(&old_node));
if (!host || host->HasSessionsWithoutTabTargetSupport()) {
return;
}
// The new node may have a previous host associated, disconnect it first.
scoped_refptr<RenderFrameDevToolsAgentHost> previous_host =
static_cast<RenderFrameDevToolsAgentHost*>(
RenderFrameDevToolsAgentHost::GetFor(&new_node));
// Disconnect old host entirely, so it detaches from renderer and does not
// cause problem if renderer comes back from the BFCache.
previous_host->DisconnectWebContents();
host->SetFrameTreeNode(&new_node);
}
void OnFrameTreeNodeDestroyed(FrameTreeNode& frame_tree_node) {
// If the child frame is an OOPIF, we emit Page.frameDetached event which
// otherwise might be lost because the OOPIF target is being destroyed.
RenderFrameHostImpl* parent = frame_tree_node.parent();
if (!parent) {
return;
}
if (RenderFrameDevToolsAgentHost::GetFor(&frame_tree_node) !=
RenderFrameDevToolsAgentHost::GetFor(parent)) {
DispatchToAgents(
RenderFrameDevToolsAgentHost::GetFor(parent),
&protocol::PageHandler::OnFrameDetached,
frame_tree_node.current_frame_host()->devtools_frame_token());
}
}
void DidUpdatePolicyContainerHost(FrameTreeNode* ftn) {
if (!ftn) {
return;
}
DispatchToAgents(ftn,
&protocol::NetworkHandler::OnPolicyContainerHostUpdated);
}
void DidUpdateSpeculationCandidates(
RenderFrameHost& rfh,
const std::vector<blink::mojom::SpeculationCandidatePtr>& candidates) {
if (auto* storage = DevToolsPreloadStorage::GetForCurrentDocument(&rfh)) {
storage->SpeculationCandidatesUpdated(candidates);
}
}
void DidUpdatePrefetchStatus(
FrameTreeNode* ftn,
const base::UnguessableToken& initiator_devtools_navigation_token,
const GURL& prefetch_url,
const base::UnguessableToken& preload_pipeline_id,
PreloadingTriggeringOutcome status,
PrefetchStatus prefetch_status,
const std::string& request_id) {
if (!ftn) {
return;
}
// See a comment in `PreloadingDecider::ctor()`.
auto* devtools_preload_storage =
DevToolsPreloadStorage::GetForCurrentDocument(ftn->current_frame_host());
if (!devtools_preload_storage) {
return;
}
// We update DevToolsPreloadStorage, even if there are no active DevTools
// sessions, to persist the latest status update.
devtools_preload_storage->UpdatePrefetchStatus(
prefetch_url, preload_pipeline_id, status, prefetch_status, request_id);
std::string initiating_frame_id =
ftn->current_frame_host()->devtools_frame_token().ToString();
DispatchToAgents(ftn, &protocol::PreloadHandler::DidUpdatePrefetchStatus,
initiator_devtools_navigation_token, initiating_frame_id,
prefetch_url, preload_pipeline_id, status, prefetch_status,
request_id);
}
void OnPrefetchRequestWillBeSent(
FrameTreeNode& ftn,
const std::string& request_id,
const GURL& initiator,
const network::ResourceRequest& request,
std::optional<std::pair<const GURL&,
const network::mojom::URLResponseHeadDevToolsInfo&>>
redirect_info) {
auto timestamp = base::TimeTicks::Now();
std::string frame_token =
ftn.current_frame_host()->devtools_frame_token().ToString();
DispatchToAgents(&ftn, &protocol::NetworkHandler::PrefetchRequestWillBeSent,
request_id, request, initiator, frame_token, timestamp,
redirect_info);
}
void OnPrefetchResponseReceived(FrameTreeNode* frame_tree_node,
const std::string& request_id,
const GURL& url,
const network::mojom::URLResponseHead& head) {
std::string frame_token =
frame_tree_node->current_frame_host()->devtools_frame_token().ToString();
network::mojom::URLResponseHeadDevToolsInfoPtr head_info =
network::ExtractDevToolsInfo(head);
DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::ResponseReceived,
request_id, request_id, url,
protocol::Network::ResourceTypeEnum::Prefetch, *head_info,
frame_token);
}
void OnPrefetchRequestComplete(
FrameTreeNode* frame_tree_node,
const std::string& request_id,
const network::URLLoaderCompletionStatus& status) {
DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::LoadingComplete,
request_id, protocol::Network::ResourceTypeEnum::Prefetch,
status);
}
void OnPrefetchBodyDataReceived(FrameTreeNode* frame_tree_node,
const std::string& request_id,
const std::string& body,
bool is_base64_encoded) {
DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::BodyDataReceived,
request_id, body, is_base64_encoded);
}
bool IsPrerenderAllowed(FrameTree& frame_tree) {
FrameTreeNode* ftn = frame_tree.root();
auto* render_frame_agent_host = static_cast<RenderFrameDevToolsAgentHost*>(
RenderFrameDevToolsAgentHost::GetFor(ftn));
if (render_frame_agent_host &&
render_frame_agent_host->HasSessionsWithoutTabTargetSupport()) {
return false;
}
bool is_allowed = true;
DispatchToAgents(ftn, &protocol::PageHandler::IsPrerenderingAllowed,
is_allowed);
return is_allowed;
}
void WillInitiatePrerender(FrameTree& frame_tree) {
DCHECK(frame_tree.is_prerendering());
auto* wc = WebContentsImpl::FromFrameTreeNode(frame_tree.root());
if (auto* host = WebContentsDevToolsAgentHost::GetFor(wc)) {
host->WillInitiatePrerender(frame_tree.root());
}
}
void DidActivatePrerender(const NavigationRequest& nav_request,
const std::optional<base::UnguessableToken>&
initiator_devtools_navigation_token) {
FrameTreeNode* ftn = nav_request.frame_tree_node();
UpdateChildFrameTrees(ftn, /* update_target_info= */ true);
}
void DidUpdatePrerenderStatus(
FrameTreeNodeId initiator_frame_tree_node_id,
const base::UnguessableToken& initiator_devtools_navigation_token,
const GURL& prerender_url,
std::optional<blink::mojom::SpeculationTargetHint> target_hint,
const base::UnguessableToken& preload_pipeline_id,
PreloadingTriggeringOutcome status,
std::optional<PrerenderFinalStatus> prerender_status,
std::optional<std::string> disallowed_mojo_interface,
const std::vector<PrerenderMismatchedHeaders>* mismatched_headers) {
auto* ftn = FrameTreeNode::GloballyFindByID(initiator_frame_tree_node_id);
// ftn will be null if this is browser-initiated, which has no initiator.
if (!ftn) {
return;
}
// See a comment in `PreloadingDecider::ctor()`.
auto* devtools_preload_storage =
DevToolsPreloadStorage::GetForCurrentDocument(ftn->current_frame_host());
if (!devtools_preload_storage) {
return;
}
// We update DevToolsPreloadStorage, even if there are no active DevTools
// sessions, to persist the latest status update.
devtools_preload_storage->UpdatePrerenderStatus(
prerender_url, target_hint, preload_pipeline_id, status, prerender_status,
disallowed_mojo_interface, mismatched_headers);
DispatchToAgents(ftn, &protocol::PreloadHandler::DidUpdatePrerenderStatus,
initiator_devtools_navigation_token, prerender_url,
target_hint, preload_pipeline_id, status, prerender_status,
disallowed_mojo_interface, mismatched_headers);
}
namespace {
protocol::String BuildBlockedByResponseReason(
network::mojom::BlockedByResponseReason reason) {
// TODO(crbug.com/336752983):
// Add specific error messages when a subresource load was blocked due to
// Document-Isolation-Policy (Dip).
switch (reason) {
case network::mojom::BlockedByResponseReason::
kCoepFrameResourceNeedsCoepHeader:
return protocol::Audits::BlockedByResponseReasonEnum::
CoepFrameResourceNeedsCoepHeader;
case network::mojom::BlockedByResponseReason::
kCoopSandboxedIFrameCannotNavigateToCoopPage:
return protocol::Audits::BlockedByResponseReasonEnum::
CoopSandboxedIFrameCannotNavigateToCoopPage;
case network::mojom::BlockedByResponseReason::kCorpNotSameOrigin:
return protocol::Audits::BlockedByResponseReasonEnum::CorpNotSameOrigin;
case network::mojom::BlockedByResponseReason::
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep:
case network::mojom::BlockedByResponseReason::
kCorpNotSameOriginAfterDefaultedToSameOriginByDip:
case network::mojom::BlockedByResponseReason::
kCorpNotSameOriginAfterDefaultedToSameOriginByCoepAndDip:
return protocol::Audits::BlockedByResponseReasonEnum::
CorpNotSameOriginAfterDefaultedToSameOriginByCoep;
case network::mojom::BlockedByResponseReason::kCorpNotSameSite:
return protocol::Audits::BlockedByResponseReasonEnum::CorpNotSameSite;
case network::mojom::BlockedByResponseReason::kSRIMessageSignatureMismatch:
return protocol::Audits::BlockedByResponseReasonEnum::
SRIMessageSignatureMismatch;
}
}
void ReportBlockedByResponseIssue(
const GURL& url,
std::string& requestId,
FrameTreeNode* ftn,
RenderFrameHostImpl* parent_frame,
const network::URLLoaderCompletionStatus& status) {
DCHECK(status.blocked_by_response_reason);
auto issueDetails = protocol::Audits::InspectorIssueDetails::Create();
auto request = protocol::Audits::AffectedRequest::Create()
.SetRequestId(requestId)
.SetUrl(url.spec())
.Build();
auto blockedByResponseDetails =
protocol::Audits::BlockedByResponseIssueDetails::Create()
.SetRequest(std::move(request))
.SetReason(
BuildBlockedByResponseReason(*status.blocked_by_response_reason))
.Build();
blockedByResponseDetails->SetBlockedFrame(
protocol::Audits::AffectedFrame::Create()
.SetFrameId(
ftn->current_frame_host()->devtools_frame_token().ToString())
.Build());
if (parent_frame) {
blockedByResponseDetails->SetParentFrame(
protocol::Audits::AffectedFrame::Create()
.SetFrameId(parent_frame->devtools_frame_token().ToString())
.Build());
}
issueDetails.SetBlockedByResponseIssueDetails(
std::move(blockedByResponseDetails));
auto inspector_issue =
protocol::Audits::InspectorIssue::Create()
.SetCode(
protocol::Audits::InspectorIssueCodeEnum::BlockedByResponseIssue)
.SetDetails(issueDetails.Build())
.Build();
ReportBrowserInitiatedIssue(ftn->current_frame_host(),
std::move(inspector_issue));
}
} // namespace
void OnNavigationRequestFailed(
const NavigationRequest& nav_request,
const network::URLLoaderCompletionStatus& status) {
FrameTreeNode* ftn = nav_request.frame_tree_node();
std::string id = nav_request.devtools_navigation_token().ToString();
if (status.blocked_by_response_reason) {
ReportBlockedByResponseIssue(
const_cast<NavigationRequest&>(nav_request).GetURL(), id, ftn,
ftn->parent(), status);
}
// If a BFCache navigation fails, it will be restarted as a regular
// navigation, so we don't want to report this failure.
if (nav_request.IsServedFromBackForwardCache()) {
return;
}
// Activation of a prerender page is synchronous with its own activation flow
// (crrev.com/c/2992411); if the prerender is cancelled (e.g. speculation rule
// removed), the flow will fallback to a normal navigation, which is no longer
// considered as a page activation.
DCHECK(!nav_request.IsPageActivation());
DispatchToAgents(ftn, &protocol::NetworkHandler::LoadingComplete, id,
protocol::Network::ResourceTypeEnum::Document, status);
}
bool ShouldBypassCSP(const NavigationRequest& nav_request) {
DevToolsAgentHostImpl* agent_host =
RenderFrameDevToolsAgentHost::GetFor(nav_request.frame_tree_node());
if (!agent_host) {
return false;
}
for (auto* page : protocol::PageHandler::ForAgentHost(agent_host)) {
if (page->ShouldBypassCSP()) {
return true;
}
}
return false;
}
bool ShouldBypassCertificateErrors(DevToolsAgentHost* agent_host) {
if (!agent_host) {
return false;
}
DevToolsAgentHostImpl* host_impl =
static_cast<DevToolsAgentHostImpl*>(agent_host);
for (auto* security_handler :
protocol::SecurityHandler::ForAgentHost(host_impl)) {
if (security_handler->IsIgnoreCertificateErrorsSet()) {
return true;
}
}
return false;
}
bool ShouldBypassCertificateErrors() {
for (auto* browser_agent_host : BrowserDevToolsAgentHost::Instances()) {
if (ShouldBypassCertificateErrors(browser_agent_host)) {
return true;
}
}
return false;
}
void ApplyNetworkOverridesForDownload(
RenderFrameHostImpl* rfh,
download::DownloadUrlParameters* parameters) {
FrameTreeNode* ftn =
FrameTreeNode::GloballyFindByID(rfh->GetFrameTreeNodeId());
if (ftn) {
DispatchToAgents(
ftn, &protocol::EmulationHandler::ApplyNetworkOverridesForDownload,
parameters);
}
}
void WillBeginDownload(download::DownloadCreateInfo* info,
download::DownloadItem* item) {
if (!item) {
return;
}
auto* rfh = static_cast<RenderFrameHostImpl*>(
RenderFrameHost::FromID(info->render_process_id, info->render_frame_id));
FrameTreeNode* ftn =
rfh ? FrameTreeNode::GloballyFindByID(rfh->GetFrameTreeNodeId())
: nullptr;
if (!ftn) {
return;
}
DispatchToAgents(ftn, &protocol::BrowserHandler::DownloadWillBegin, ftn,
item);
DispatchToAgents(ftn, &protocol::PageHandler::DownloadWillBegin, ftn, item);
for (auto* agent_host : BrowserDevToolsAgentHost::Instances()) {
for (auto* browser_handler :
protocol::BrowserHandler::ForAgentHost(agent_host)) {
browser_handler->DownloadWillBegin(ftn, item);
}
}
}
void OnSignedExchangeReceived(
FrameTreeNode* frame_tree_node,
std::optional<const base::UnguessableToken> devtools_navigation_token,
const GURL& outer_request_url,
const network::mojom::URLResponseHead& outer_response,
const std::optional<SignedExchangeEnvelope>& envelope,
const scoped_refptr<net::X509Certificate>& certificate,
const std::optional<net::SSLInfo>& ssl_info,
const std::vector<SignedExchangeError>& errors) {
DispatchToAgents(frame_tree_node,
&protocol::NetworkHandler::OnSignedExchangeReceived,
devtools_navigation_token, outer_request_url, outer_response,
envelope, certificate, ssl_info, errors);
}
namespace inspector_will_send_navigation_request_event {
std::unique_ptr<base::trace_event::TracedValue> Data(
const base::UnguessableToken& request_id) {
auto value = std::make_unique<base::trace_event::TracedValue>();
value->SetString("requestId", request_id.ToString());
return value;
}
} // namespace inspector_will_send_navigation_request_event
void OnSignedExchangeCertificateRequestSent(
FrameTreeNode* frame_tree_node,
const base::UnguessableToken& request_id,
const base::UnguessableToken& loader_id,
const network::ResourceRequest& request,
const GURL& signed_exchange_url) {
// Make sure both back-ends yield the same timestamp.
auto timestamp = base::TimeTicks::Now();
network::mojom::URLRequestDevToolsInfoPtr request_info =
network::ExtractDevToolsInfo(request);
DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::RequestSent,
request_id.ToString(), loader_id.ToString(), request.headers,
*request_info,
protocol::Network::Initiator::TypeEnum::SignedExchange,
signed_exchange_url, /*initiator_devtools_request_id=*/"",
/*frame_token=*/std::nullopt, timestamp);
auto value = std::make_unique<base::trace_event::TracedValue>();
value->SetString("requestId", request_id.ToString());
TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
"devtools.timeline", "ResourceWillSendRequest", TRACE_EVENT_SCOPE_PROCESS,
timestamp, "data",
inspector_will_send_navigation_request_event::Data(request_id));
}
void OnSignedExchangeCertificateResponseReceived(
FrameTreeNode* frame_tree_node,
const base::UnguessableToken& request_id,
const base::UnguessableToken& loader_id,
const GURL& url,
const network::mojom::URLResponseHead& head) {
network::mojom::URLResponseHeadDevToolsInfoPtr head_info =
network::ExtractDevToolsInfo(head);
DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::ResponseReceived,
request_id.ToString(), loader_id.ToString(), url,
protocol::Network::ResourceTypeEnum::Other, *head_info,
std::nullopt);
}
void OnSignedExchangeCertificateRequestCompleted(
FrameTreeNode* frame_tree_node,
const base::UnguessableToken& request_id,
const network::URLLoaderCompletionStatus& status) {
DispatchToAgents(frame_tree_node, &protocol::NetworkHandler::LoadingComplete,
request_id.ToString(),
protocol::Network::ResourceTypeEnum::Other, status);
}
void ThrottleForServiceWorkerAgentHost(
ServiceWorkerDevToolsAgentHost* agent_host,
DevToolsAgentHostImpl* requesting_agent_host,
scoped_refptr<DevToolsThrottleHandle> throttle_handle) {
for (auto* target_handler :
protocol::TargetHandler::ForAgentHost(requesting_agent_host)) {
target_handler->AddWorkerThrottle(agent_host, throttle_handle);
}
}
void CreateAndAddNavigationThrottles(NavigationThrottleRegistry& registry) {
auto* navigation_handle = &registry.GetNavigationHandle();
FrameTreeNode* frame_tree_node =
NavigationRequest::From(navigation_handle)->frame_tree_node();
FrameTreeNode* parent = FrameTreeNode::From(frame_tree_node->parent());
if (!parent) {
FrameTreeNode* outer_delegate_node =
frame_tree_node->render_manager()->GetOuterDelegateNode();
if (outer_delegate_node && frame_tree_node->IsFencedFrameRoot()) {
parent = outer_delegate_node->parent()->frame_tree_node();
} else if (frame_tree_node->GetFrameType() ==
FrameType::kPrerenderMainFrame &&
!frame_tree_node->current_frame_host()
->has_committed_any_navigation()) {
if (auto* agent_host = WebContentsDevToolsAgentHost::GetFor(
WebContentsImpl::FromFrameTreeNode(frame_tree_node))) {
// For prerender, perform auto-attach to tab target at the point of
// initial navigation.
agent_host->auto_attacher()->CreateAndAddNavigationThrottles(registry);
return;
}
}
}
if (parent) {
if (auto* agent_host = RenderFrameDevToolsAgentHost::GetFor(parent)) {
agent_host->auto_attacher()->CreateAndAddNavigationThrottles(registry);
}
} else {
for (DevToolsAgentHostImpl* host : BrowserDevToolsAgentHost::Instances()) {
host->auto_attacher()->CreateAndAddNavigationThrottles(registry);
}
}
}
void ThrottleServiceWorkerMainScriptFetch(
ServiceWorkerContextWrapper* wrapper,
int64_t version_id,
const GlobalRenderFrameHostId& requesting_frame_id,
scoped_refptr<DevToolsThrottleHandle> throttle_handle) {
ServiceWorkerDevToolsAgentHost* agent_host =
ServiceWorkerDevToolsManager::GetInstance()
->GetDevToolsAgentHostForNewInstallingWorker(wrapper, version_id);
DCHECK(agent_host);
// TODO(crbug.com/40276949): We should probably also add the
// possibility for Browser wide agents to throttle the request.
// If we have a requesting_frame_id, we should have a frame and a frame tree
// node. However since the lifetime of these objects can be complex, we check
// at each step that we indeed can go reach all the way to the FrameTreeNode.
if (!requesting_frame_id) {
return;
}
RenderFrameHostImpl* requesting_frame =
RenderFrameHostImpl::FromID(requesting_frame_id);
if (!requesting_frame) {
return;
}
FrameTreeNode* ftn = requesting_frame->frame_tree_node();
DCHECK(ftn);
DevToolsAgentHostImpl* requesting_agent_host =
RenderFrameDevToolsAgentHost::GetFor(ftn);
if (!requesting_agent_host) {
return;
}
ThrottleForServiceWorkerAgentHost(agent_host, requesting_agent_host,
throttle_handle);
}
void ThrottleWorkerMainScriptFetch(
const base::UnguessableToken& devtools_worker_token,
const GlobalRenderFrameHostId& ancestor_render_frame_host_id,
scoped_refptr<DevToolsThrottleHandle> throttle_handle) {
DedicatedWorkerDevToolsAgentHost* agent_host =
WorkerDevToolsManager::GetInstance().GetDevToolsHostFromToken(
devtools_worker_token);
if (!agent_host) {
return;
}
RenderFrameHostImpl* rfh =
RenderFrameHostImpl::FromID(ancestor_render_frame_host_id);
if (!rfh) {
return;
}
FrameTreeNode* ftn = rfh->frame_tree_node();
DispatchToAgents(ftn, &protocol::TargetHandler::AddWorkerThrottle, agent_host,
std::move(throttle_handle));
}
bool ShouldWaitForDebuggerInWindowOpen() {
for (auto* browser_agent_host : BrowserDevToolsAgentHost::Instances()) {
for (auto* target_handler :
protocol::TargetHandler::ForAgentHost(browser_agent_host)) {
if (target_handler->ShouldThrottlePopups()) {
return true;
}
}
}
return false;
}
namespace {
// This is a helper function used in ApplyNetworkRequestOverrides and
// ApplyUserAgentMetadataOverrides to help correctly set network request header
// overrides. It behaves the same as RenderFrameDevToolsAgentHost::GetFor for
// all FrameTreeNodes except those that are prerendering. For prerendering
// FrameTreeNodes, it returns the DevToolsAgentHost of the primary main frame,
// even if it has a DTAH of its own. The network header overrides are applied
// too early, before the correct values sent by the client are propagated to the
// prerender's DTAH's handlers. As a result, we use the values that were
// previously set for the primary main frame.
DevToolsAgentHostImpl* GetDevToolsAgentHostForNetworkOverrides(
FrameTreeNode* frame_tree_node) {
if (frame_tree_node->frame_tree().is_prerendering()) {
return RenderFrameDevToolsAgentHost::GetFor(
WebContentsImpl::FromFrameTreeNode(frame_tree_node)
->GetPrimaryMainFrame()
->frame_tree_node());
}
return RenderFrameDevToolsAgentHost::GetFor(frame_tree_node);
}
void ApplyNetworkRequestOverrides(
DevToolsAgentHostImpl* agent_host,
net::HttpRequestHeaders* headers,
bool* disable_cache,
bool* network_instrumentation_enabled,
bool* skip_service_worker,
std::optional<std::vector<net::SourceStreamType>>*
devtools_accepted_stream_types,
bool* devtools_user_agent_overridden,
bool* devtools_accept_language_overridden,
GURL* referrer_override) {
for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) {
if (!network->enabled()) {
continue;
}
if (network_instrumentation_enabled) {
*network_instrumentation_enabled = true;
}
network->ApplyOverrides(headers, skip_service_worker, disable_cache,
devtools_accepted_stream_types, referrer_override);
}
for (auto* emulation : protocol::EmulationHandler::ForAgentHost(agent_host)) {
bool ua_overridden = false;
bool accept_language_overridden = false;
emulation->ApplyOverrides(headers, &ua_overridden,
&accept_language_overridden);
if (devtools_user_agent_overridden) {
*devtools_user_agent_overridden |= ua_overridden;
}
if (devtools_accept_language_overridden) {
*devtools_accept_language_overridden |= accept_language_overridden;
}
}
}
} // namespace
void ApplyAuctionNetworkRequestOverrides(
FrameTreeNode* frame_tree_node,
network::ResourceRequest* request,
bool* network_instrumentation_enabled) {
bool disable_cache = false;
DevToolsAgentHostImpl* agent_host =
GetDevToolsAgentHostForNetworkOverrides(frame_tree_node);
if (!agent_host) {
return;
}
ApplyNetworkRequestOverrides(
agent_host, &request->headers, &disable_cache,
network_instrumentation_enabled, &request->skip_service_worker,
&request->devtools_accepted_stream_types, nullptr, nullptr, nullptr);
if (disable_cache) {
request->load_flags = net::LOAD_BYPASS_CACHE;
}
}
void ApplyNetworkRequestOverrides(
FrameTreeNode* frame_tree_node,
blink::mojom::BeginNavigationParams* begin_params,
bool* report_raw_headers,
std::optional<std::vector<net::SourceStreamType>>*
devtools_accepted_stream_types,
bool* devtools_user_agent_overridden,
bool* devtools_accept_language_overridden,
GURL* referrer_override) {
*devtools_user_agent_overridden = false;
*devtools_accept_language_overridden = false;
bool disable_cache = false;
DevToolsAgentHostImpl* agent_host =
GetDevToolsAgentHostForNetworkOverrides(frame_tree_node);
if (!agent_host) {
return;
}
net::HttpRequestHeaders headers;
headers.AddHeadersFromString(begin_params->headers);
ApplyNetworkRequestOverrides(
agent_host, &headers, &disable_cache, report_raw_headers,
&begin_params->skip_service_worker, devtools_accepted_stream_types,
devtools_user_agent_overridden, devtools_accept_language_overridden,
referrer_override);
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();
}
bool ApplyUserAgentMetadataOverrides(
FrameTreeNode* frame_tree_node,
std::optional<blink::UserAgentMetadata>* override_out) {
DevToolsAgentHostImpl* agent_host =
GetDevToolsAgentHostForNetworkOverrides(frame_tree_node);
if (!agent_host) {
return false;
}
bool result = false;
for (auto* emulation : protocol::EmulationHandler::ForAgentHost(agent_host)) {
result = emulation->ApplyUserAgentMetadataOverrides(override_out) || result;
}
return result;
}
bool ApplyNetworkCookieControlsOverrides(
RenderFrameHostImpl& rfh,
net::CookieSettingOverrides& overrides) {
FrameTreeNode* ftn = rfh.frame_tree_node();
if (!ftn) {
return false;
}
DevToolsAgentHostImpl* agent_host =
GetDevToolsAgentHostForNetworkOverrides(ftn);
return ApplyNetworkCookieControlsOverrides(agent_host, overrides);
}
bool ApplyNetworkCookieControlsOverrides(
DevToolsAgentHostImpl* agent_host,
net::CookieSettingOverrides& overrides) {
if (!agent_host) {
return false;
}
for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) {
if (network->enabled()) {
network->ApplyCookieControlsOverrides(overrides);
}
}
return !overrides.empty();
}
namespace {
template <typename HandlerType>
bool MaybeCreateProxyForInterception(
DevToolsAgentHostImpl* agent_host,
int process_id,
StoragePartition* storage_partition,
const base::UnguessableToken& frame_token,
bool is_navigation,
bool is_download,
network::mojom::URLLoaderFactoryOverride* agent_override) {
if (!agent_host) {
return false;
}
bool had_interceptors = false;
const auto& handlers = HandlerType::ForAgentHost(agent_host);
for (const auto& handler : base::Reversed(handlers)) {
had_interceptors |= handler->MaybeCreateProxyForInterception(
process_id, storage_partition, frame_token, is_navigation, is_download,
agent_override);
}
return had_interceptors;
}
} // namespace
bool WillCreateURLLoaderFactoryParams::Run(
bool is_navigation,
bool is_download,
network::URLLoaderFactoryBuilder& factory_builder,
network::mojom::URLLoaderFactoryOverridePtr* factory_override) {
CHECK(!is_download || is_navigation);
network::mojom::URLLoaderFactoryOverride devtools_override;
// If caller passed some existing overrides, use those.
// Otherwise, use our local var, then if handlers actually
// decide to intercept, move it to |factory_override|.
network::mojom::URLLoaderFactoryOverride* handler_override =
factory_override && *factory_override ? factory_override->get()
: &devtools_override;
// Order of targets and sessions matters -- the latter proxy is created,
// the closer it is to the network. So start with frame's NetworkHandler,
// then process frame's FetchHandler and then browser's FetchHandler.
// Within the target, the agents added earlier are closer to network.
bool had_interceptors =
MaybeCreateProxyForInterception<protocol::NetworkHandler>(
agent_host_, process_id_, storage_partition_, devtools_token_,
is_navigation, is_download, handler_override);
had_interceptors |= MaybeCreateProxyForInterception<protocol::FetchHandler>(
agent_host_, process_id_, storage_partition_, devtools_token_,
is_navigation, is_download, handler_override);
// TODO(caseq): assure deterministic order of browser agents (or sessions).
for (auto* browser_agent_host : BrowserDevToolsAgentHost::Instances()) {
had_interceptors |= MaybeCreateProxyForInterception<protocol::FetchHandler>(
browser_agent_host, process_id_, storage_partition_, devtools_token_,
is_navigation, is_download, handler_override);
}
if (!had_interceptors) {
return false;
}
CHECK(handler_override->overriding_factory);
CHECK(handler_override->overridden_factory_receiver);
if (!factory_override) {
// Not a subresource navigation, so just override the target receiver.
auto [receiver, remote] = factory_builder.Append();
mojo::FusePipes(std::move(receiver),
std::move(devtools_override.overriding_factory));
mojo::FusePipes(std::move(devtools_override.overridden_factory_receiver),
std::move(remote));
} else if (!*factory_override) {
// No other overrides, so just returns ours as is.
*factory_override = network::mojom::URLLoaderFactoryOverride::New(
std::move(devtools_override.overriding_factory),
std::move(devtools_override.overridden_factory_receiver),
/*skip_cors_enabled_scheme_check=*/false);
}
// ... else things are already taken care of, as handler_override was pointing
// to factory override and we've done all magic in-place.
CHECK(!devtools_override.overriding_factory);
CHECK(!devtools_override.overridden_factory_receiver);
return true;
}
WillCreateURLLoaderFactoryParams::WillCreateURLLoaderFactoryParams(
DevToolsAgentHostImpl* agent_host,
const base::UnguessableToken& devtools_token,
int process_id,
StoragePartition* storage_partition)
: agent_host_(agent_host),
devtools_token_(devtools_token),
process_id_(process_id),
storage_partition_(storage_partition) {}
WillCreateURLLoaderFactoryParams WillCreateURLLoaderFactoryParams::ForFrame(
RenderFrameHostImpl* rfh) {
return WillCreateURLLoaderFactoryParams(
RenderFrameDevToolsAgentHost::GetFor(rfh), rfh->GetDevToolsFrameToken(),
rfh->GetProcess()->GetDeprecatedID(),
rfh->GetProcess()->GetStoragePartition());
}
WillCreateURLLoaderFactoryParams
WillCreateURLLoaderFactoryParams::ForServiceWorker(RenderProcessHost& rph,
int routing_id) {
ServiceWorkerDevToolsAgentHost* agent_host =
ServiceWorkerDevToolsManager::GetInstance()
->GetDevToolsAgentHostForWorker(rph.GetDeprecatedID(), routing_id);
CHECK(agent_host);
return WillCreateURLLoaderFactoryParams(
agent_host, agent_host->devtools_worker_token(), rph.GetDeprecatedID(),
rph.GetStoragePartition());
}
std::optional<WillCreateURLLoaderFactoryParams>
WillCreateURLLoaderFactoryParams::ForServiceWorkerMainScript(
const ServiceWorkerContextWrapper* context_wrapper,
std::optional<int64_t> version_id) {
if (!version_id.has_value()) {
return std::nullopt;
}
// If we have a version_id, we are fetching a worker main script. We have a
// DevtoolsAgentHost ready for the worker and we can add the devtools override
// before instantiating the URLFactoryLoader.
ServiceWorkerDevToolsAgentHost* agent_host =
ServiceWorkerDevToolsManager::GetInstance()
->GetDevToolsAgentHostForNewInstallingWorker(context_wrapper,
*version_id);
CHECK(agent_host);
return WillCreateURLLoaderFactoryParams(
agent_host, agent_host->devtools_worker_token(),
ChildProcessHost::kInvalidUniqueID, context_wrapper->storage_partition());
}
std::optional<WillCreateURLLoaderFactoryParams>
WillCreateURLLoaderFactoryParams::ForSharedWorker(SharedWorkerHost* host) {
auto* agent_host = SharedWorkerDevToolsAgentHost::GetFor(host);
if (!agent_host) {
return std::nullopt;
}
RenderProcessHost* rph = agent_host->GetProcessHost();
CHECK(rph);
return WillCreateURLLoaderFactoryParams(
agent_host, agent_host->devtools_worker_token(), rph->GetDeprecatedID(),
rph->GetStoragePartition());
}
WillCreateURLLoaderFactoryParams
WillCreateURLLoaderFactoryParams::ForWorkerMainScript(
DevToolsAgentHostImpl* agent_host,
const base::UnguessableToken& worker_token,
RenderFrameHostImpl& ancestor_render_frame_host) {
// Use the ancestor frame's interceptor to align with the interception
// behavior in the renderer that reuses the same url loader factory from
// the ancestor frame for the worker.
return WillCreateURLLoaderFactoryParams::ForFrame(
&ancestor_render_frame_host);
}
void OnAuctionWorkletNetworkRequestWillBeSent(
FrameTreeNodeId frame_tree_node_id,
const network::ResourceRequest& request,
base::TimeTicks timestamp) {
if (request.devtools_request_id->empty()) {
return;
}
network::mojom::URLRequestDevToolsInfoPtr request_info =
network::ExtractDevToolsInfo(request);
GURL initiator_url;
if (request.request_initiator.has_value()) {
initiator_url = request.request_initiator->GetURL();
}
// if we cannot get the loader_id from the parent, use an empty string.
std::string loader_id = "";
if (frame_tree_node_id) {
FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (ftn == nullptr) {
return;
}
const std::optional<base::UnguessableToken>& devtools_navigation_token =
ftn->current_frame_host()->GetDevToolsNavigationToken();
if (devtools_navigation_token.has_value()) {
loader_id = devtools_navigation_token->ToString();
}
DispatchToAgents(
frame_tree_node_id, &protocol::NetworkHandler::RequestSent,
/*request_id=*/request.devtools_request_id.value(),
/*loader_id=*/loader_id, request.headers, *request_info,
/*initiator_type=*/protocol::Network::Initiator::TypeEnum::Other,
initiator_url,
/*initiator_devtools_request_id=*/"", /*frame_token=*/std::nullopt,
timestamp);
}
}
void OnAuctionWorkletNetworkResponseReceived(
FrameTreeNodeId frame_tree_node_id,
const std::string& request_id,
const std::string& loader_id,
const GURL& request_url,
const network::mojom::URLResponseHead& headers) {
network::mojom::URLResponseHeadDevToolsInfoPtr head_info =
network::ExtractDevToolsInfo(headers);
DispatchToAgents(frame_tree_node_id,
&protocol::NetworkHandler::ResponseReceived, request_id,
loader_id, request_url,
/*resource_type=*/protocol::Network::ResourceTypeEnum::Other,
*head_info, base::ToString(frame_tree_node_id));
}
void OnAuctionWorkletNetworkRequestComplete(
FrameTreeNodeId frame_tree_node_id,
const std::string& request_id,
const network::URLLoaderCompletionStatus& status) {
DispatchToAgents(frame_tree_node_id,
&protocol::NetworkHandler::LoadingComplete, request_id,
/*resource_type=*/protocol::Network::ResourceTypeEnum::Other,
status);
}
bool NeedInterestGroupAuctionEvents(FrameTreeNodeId frame_tree_node_id) {
FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (!ftn) {
return false;
}
DevToolsAgentHostImpl* agent_host = RenderFrameDevToolsAgentHost::GetFor(ftn);
if (!agent_host) {
return false;
}
for (auto* storage : protocol::StorageHandler::ForAgentHost(agent_host)) {
if (storage->interest_group_auction_tracking_enabled()) {
return true;
}
}
return false;
}
void OnInterestGroupAuctionEventOccurred(
FrameTreeNodeId frame_tree_node_id,
base::Time event_time,
InterestGroupAuctionEventType type,
const std::string& unique_auction_id,
base::optional_ref<const std::string> parent_auction_id,
const base::Value::Dict& auction_config) {
DispatchToAgents(
frame_tree_node_id,
&protocol::StorageHandler::NotifyInterestGroupAuctionEventOccurred,
event_time, type, unique_auction_id, parent_auction_id, auction_config);
}
void OnInterestGroupAuctionNetworkRequestCreated(
FrameTreeNodeId frame_tree_node_id,
InterestGroupAuctionFetchType type,
const std::string& request_id,
const std::vector<std::string>& devtools_auction_ids) {
DispatchToAgents(frame_tree_node_id,
&protocol::StorageHandler::
NotifyInterestGroupAuctionNetworkRequestCreated,
type, request_id, devtools_auction_ids);
}
void OnNavigationRequestWillBeSent(
const NavigationRequest& navigation_request) {
// Note this intentionally deviates from the usual instrumentation signal
// logic and dispatches to all agents upwards from the frame, to make sure
// the security checks are properly applied even if no DevTools session is
// established for the navigated frame itself. This is because the page
// agent may navigate all of its subframes currently.
for (RenderFrameHostImpl* rfh =
navigation_request.frame_tree_node()->current_frame_host();
rfh; rfh = rfh->GetParentOrOuterDocument()) {
// Only check frames that qualify as DevTools targets, i.e. (local)? roots.
if (!RenderFrameDevToolsAgentHost::ShouldCreateDevToolsForHost(rfh)) {
continue;
}
auto* agent_host = static_cast<RenderFrameDevToolsAgentHost*>(
RenderFrameDevToolsAgentHost::GetFor(rfh));
if (!agent_host) {
continue;
}
agent_host->OnNavigationRequestWillBeSent(navigation_request);
}
// We use CachedNavigationURLLoader for page activation (BFCache navigations
// and Prerender activations) and don't actually send a network request, so we
// don't report this request to DevTools.
if (navigation_request.IsPageActivation()) {
return;
}
// Make sure both back-ends yield the same timestamp.
auto timestamp = base::TimeTicks::Now();
DispatchToAgents(navigation_request.frame_tree_node(),
&protocol::NetworkHandler::NavigationRequestWillBeSent,
navigation_request, timestamp);
TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
"devtools.timeline", "ResourceWillSendRequest", TRACE_EVENT_SCOPE_PROCESS,
timestamp, "data",
inspector_will_send_navigation_request_event::Data(
navigation_request.devtools_navigation_token()));
}
// Notify the provided agent host of a certificate error. Returns true if one of
// the host's handlers will handle the certificate error.
bool NotifyCertificateError(DevToolsAgentHost* host,
int cert_error,
const GURL& request_url,
const CertErrorCallback& callback) {
DevToolsAgentHostImpl* host_impl = static_cast<DevToolsAgentHostImpl*>(host);
for (auto* security_handler :
protocol::SecurityHandler::ForAgentHost(host_impl)) {
if (security_handler->NotifyCertificateError(cert_error, request_url,
callback)) {
return true;
}
}
return false;
}
bool HandleCertificateError(WebContents* web_contents,
int cert_error,
const GURL& request_url,
CertErrorCallback callback) {
if (!DevToolsAgentHost::HasFor(web_contents)) {
return false;
}
scoped_refptr<DevToolsAgentHost> agent_host =
DevToolsAgentHost::GetOrCreateFor(web_contents).get();
if (NotifyCertificateError(agent_host.get(), cert_error, request_url,
callback)) {
// Only allow a single agent host to handle the error.
callback.Reset();
}
for (auto* browser_agent_host : BrowserDevToolsAgentHost::Instances()) {
if (NotifyCertificateError(browser_agent_host, cert_error, request_url,
callback)) {
// Only allow a single agent host to handle the error.
callback.Reset();
}
}
return !callback;
}
void FencedFrameCreated(
base::SafeRef<RenderFrameHostImpl> owner_render_frame_host,
FencedFrame* fenced_frame) {
auto* agent_host = static_cast<RenderFrameDevToolsAgentHost*>(
RenderFrameDevToolsAgentHost::GetFor(
owner_render_frame_host->frame_tree_node()));
if (!agent_host) {
return;
}
agent_host->DidCreateFencedFrame(fenced_frame);
}
void WillStartDragging(FrameTreeNode* main_frame_tree_node,
const DropData& drop_data,
const blink::mojom::DragDataPtr drag_data,
blink::DragOperationsMask drag_operations_mask,
bool* intercepted) {
DCHECK(main_frame_tree_node->frame_tree().root() == main_frame_tree_node);
DispatchToAgents(main_frame_tree_node, &protocol::InputHandler::StartDragging,
drop_data, *drag_data, drag_operations_mask, intercepted);
}
void DragEnded(FrameTreeNode& node) {
DCHECK(node.frame_tree().root() == &node);
DispatchToAgents(&node, &protocol::InputHandler::DragEnded);
}
namespace {
std::unique_ptr<protocol::Array<protocol::String>> BuildExclusionReasons(
net::CookieInclusionStatus status) {
auto exclusion_reasons =
std::make_unique<protocol::Array<protocol::String>>();
if (status.HasExclusionReason(
net::CookieInclusionStatus::ExclusionReason::
EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX)) {
exclusion_reasons->push_back(protocol::Audits::CookieExclusionReasonEnum::
ExcludeSameSiteUnspecifiedTreatedAsLax);
}
if (status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
EXCLUDE_SAMESITE_NONE_INSECURE)) {
exclusion_reasons->push_back(protocol::Audits::CookieExclusionReasonEnum::
ExcludeSameSiteNoneInsecure);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::ExclusionReason::EXCLUDE_SAMESITE_LAX)) {
exclusion_reasons->push_back(
protocol::Audits::CookieExclusionReasonEnum::ExcludeSameSiteLax);
}
if (status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
EXCLUDE_SAMESITE_STRICT)) {
exclusion_reasons->push_back(
protocol::Audits::CookieExclusionReasonEnum::ExcludeSameSiteStrict);
}
if (status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
EXCLUDE_DOMAIN_NON_ASCII)) {
exclusion_reasons->push_back(
protocol::Audits::CookieExclusionReasonEnum::ExcludeDomainNonASCII);
}
if (status.HasExclusionReason(
net::CookieInclusionStatus::ExclusionReason::
EXCLUDE_THIRD_PARTY_BLOCKED_WITHIN_FIRST_PARTY_SET)) {
exclusion_reasons->push_back(
protocol::Audits::CookieExclusionReasonEnum::
ExcludeThirdPartyCookieBlockedInFirstPartySet);
}
if (status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
EXCLUDE_THIRD_PARTY_PHASEOUT)) {
exclusion_reasons->push_back(
protocol::Audits::CookieExclusionReasonEnum::ExcludeThirdPartyPhaseout);
}
if (base::FeatureList::IsEnabled(net::features::kEnablePortBoundCookies) &&
status.HasExclusionReason(
net::CookieInclusionStatus::ExclusionReason::EXCLUDE_PORT_MISMATCH)) {
exclusion_reasons->push_back(
protocol::Audits::CookieExclusionReasonEnum::ExcludePortMismatch);
}
if (base::FeatureList::IsEnabled(net::features::kEnableSchemeBoundCookies) &&
status.HasExclusionReason(net::CookieInclusionStatus::ExclusionReason::
EXCLUDE_SCHEME_MISMATCH)) {
exclusion_reasons->push_back(
protocol::Audits::CookieExclusionReasonEnum::ExcludeSchemeMismatch);
}
return exclusion_reasons;
}
std::unique_ptr<protocol::Array<protocol::String>> BuildWarningReasons(
net::CookieInclusionStatus status) {
auto warning_reasons = std::make_unique<protocol::Array<protocol::String>>();
if (status.HasWarningReason(net::CookieInclusionStatus::WarningReason::
WARN_ATTRIBUTE_VALUE_EXCEEDS_MAX_SIZE)) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnAttributeValueExceedsMaxSize);
}
if (status.HasWarningReason(
net::CookieInclusionStatus::WarningReason::
WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT)) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnSameSiteUnspecifiedCrossSiteContext);
}
if (status.HasWarningReason(net::CookieInclusionStatus::WarningReason::
WARN_SAMESITE_NONE_INSECURE)) {
warning_reasons->push_back(
protocol::Audits::CookieWarningReasonEnum::WarnSameSiteNoneInsecure);
}
if (status.HasWarningReason(net::CookieInclusionStatus::WarningReason::
WARN_SAMESITE_UNSPECIFIED_LAX_ALLOW_UNSAFE)) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnSameSiteUnspecifiedLaxAllowUnsafe);
}
// There can only be one of the following warnings.
if (status.HasWarningReason(net::CookieInclusionStatus::WarningReason::
WARN_STRICT_LAX_DOWNGRADE_STRICT_SAMESITE)) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnSameSiteStrictLaxDowngradeStrict);
} else if (status.HasWarningReason(
net::CookieInclusionStatus::WarningReason::
WARN_STRICT_CROSS_DOWNGRADE_STRICT_SAMESITE)) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnSameSiteStrictCrossDowngradeStrict);
} else if (status.HasWarningReason(
net::CookieInclusionStatus::WarningReason::
WARN_STRICT_CROSS_DOWNGRADE_LAX_SAMESITE)) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnSameSiteStrictCrossDowngradeLax);
} else if (status.HasWarningReason(
net::CookieInclusionStatus::WarningReason::
WARN_LAX_CROSS_DOWNGRADE_STRICT_SAMESITE)) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnSameSiteLaxCrossDowngradeStrict);
} else if (status.HasWarningReason(
net::CookieInclusionStatus::WarningReason::
WARN_LAX_CROSS_DOWNGRADE_LAX_SAMESITE)) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnSameSiteLaxCrossDowngradeLax);
}
if (status.HasWarningReason(
net::CookieInclusionStatus::WarningReason::WARN_DOMAIN_NON_ASCII)) {
warning_reasons->push_back(
protocol::Audits::CookieWarningReasonEnum::WarnDomainNonASCII);
}
if (status.HasWarningReason(net::CookieInclusionStatus::WarningReason::
WARN_THIRD_PARTY_PHASEOUT)) {
warning_reasons->push_back(
protocol::Audits::CookieWarningReasonEnum::WarnThirdPartyPhaseout);
}
if (status.exemption_reason() ==
net::CookieInclusionStatus::ExemptionReason::k3PCDMetadata) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnDeprecationTrialMetadata);
} else if (status.exemption_reason() ==
net::CookieInclusionStatus::ExemptionReason::k3PCDHeuristics) {
warning_reasons->push_back(protocol::Audits::CookieWarningReasonEnum::
WarnThirdPartyCookieHeuristic);
}
// This warning only affects cookies when the corresponding feature is
// enabled, therefore we should only create an issue for it then.
if (base::FeatureList::IsEnabled(
net::features::kCookieSameSiteConsidersRedirectChain) &&
status.HasWarningReason(
net::CookieInclusionStatus::WarningReason::
WARN_CROSS_SITE_REDIRECT_DOWNGRADE_CHANGES_INCLUSION)) {
warning_reasons->push_back(
protocol::Audits::CookieWarningReasonEnum::
WarnCrossSiteRedirectDowngradeChangesInclusion);
}
return warning_reasons;
}
protocol::String BuildCookieOperation(blink::mojom::CookieOperation operation) {
switch (operation) {
case blink::mojom::CookieOperation::kReadCookie:
return protocol::Audits::CookieOperationEnum::ReadCookie;
case blink::mojom::CookieOperation::kSetCookie:
return protocol::Audits::CookieOperationEnum::SetCookie;
}
}
std::unique_ptr<protocol::Audits::InspectorIssue>
BuildCookieDeprecationMetadataIssue(
const blink::mojom::CookieDeprecationMetadataIssueDetailsPtr&
issue_details) {
auto metadata_issue_details =
protocol::Audits::CookieDeprecationMetadataIssueDetails::Create()
.SetAllowedSites(std::make_unique<protocol::Array<protocol::String>>(
issue_details->allowed_sites))
.SetOptOutPercentage(issue_details->opt_out_percentage)
.SetIsOptOutTopLevel(issue_details->is_opt_out_top_level)
.SetOperation(BuildCookieOperation(issue_details->operation))
.Build();
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetCookieDeprecationMetadataIssueDetails(
std::move(metadata_issue_details))
.Build();
auto issue = protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::
CookieDeprecationMetadataIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return issue;
}
std::unique_ptr<protocol::Audits::CookieIssueInsight> BuildCookieIssueInsight(
std::string_view cookie_domain,
const net::CookieInclusionStatus& status) {
std::optional<CookieIssueInsight> insight =
CookieInsightListHandler::GetInstance().GetInsight(cookie_domain, status);
if (!insight.has_value()) {
return nullptr;
}
switch (insight->type) {
case InsightType::kGitHubResource:
return protocol::Audits::CookieIssueInsight::Create()
.SetType(protocol::Audits::InsightTypeEnum::GitHubResource)
.SetTableEntryUrl(insight->domain_info.entry_url)
.Build();
case InsightType::kGracePeriod:
return protocol::Audits::CookieIssueInsight::Create()
.SetType(protocol::Audits::InsightTypeEnum::GracePeriod)
.Build();
case InsightType::kHeuristics:
return protocol::Audits::CookieIssueInsight::Create()
.SetType(protocol::Audits::InsightTypeEnum::Heuristics)
.Build();
default:
NOTREACHED();
}
}
} // namespace
void ReportCookieIssue(
RenderFrameHostImpl* render_frame_host_impl,
const network::mojom::CookieOrLineWithAccessResultPtr& excluded_cookie,
const GURL& url,
const net::SiteForCookies& site_for_cookies,
blink::mojom::CookieOperation operation,
const std::optional<std::string>& devtools_request_id,
const std::optional<std::string>& devtools_issue_id) {
auto exclusion_reasons =
BuildExclusionReasons(excluded_cookie->access_result.status);
auto warning_reasons =
BuildWarningReasons(excluded_cookie->access_result.status);
if (exclusion_reasons->empty() && warning_reasons->empty()) {
// If we don't report any reason, there is no point in informing DevTools.
return;
}
std::unique_ptr<protocol::Audits::AffectedRequest> affected_request;
if (devtools_request_id) {
// We can report the url here, because if devtools_request_id is set, the
// url is the url of the request.
affected_request = protocol::Audits::AffectedRequest::Create()
.SetRequestId(*devtools_request_id)
.SetUrl(url.spec())
.Build();
}
auto cookie_issue_details =
protocol::Audits::CookieIssueDetails::Create()
.SetCookieExclusionReasons(std::move(exclusion_reasons))
.SetCookieWarningReasons(std::move(warning_reasons))
.SetOperation(BuildCookieOperation(operation))
.SetCookieUrl(url.spec())
.SetRequest(std::move(affected_request))
.Build();
if (excluded_cookie->cookie_or_line->is_cookie()) {
const auto& cookie = excluded_cookie->cookie_or_line->get_cookie();
auto affected_cookie = protocol::Audits::AffectedCookie::Create()
.SetName(cookie.Name())
.SetPath(cookie.Path())
.SetDomain(cookie.Domain())
.Build();
cookie_issue_details->SetCookie(std::move(affected_cookie));
cookie_issue_details->SetInsight(BuildCookieIssueInsight(
cookie.DomainWithoutDot(), excluded_cookie->access_result.status));
} else {
CHECK(excluded_cookie->cookie_or_line->is_cookie_string());
cookie_issue_details->SetRawCookieLine(
excluded_cookie->cookie_or_line->get_cookie_string());
}
if (!site_for_cookies.IsNull()) {
cookie_issue_details->SetSiteForCookies(
site_for_cookies.RepresentativeUrl().spec());
}
auto details = protocol::Audits::InspectorIssueDetails::Create()
.SetCookieIssueDetails(std::move(cookie_issue_details))
.Build();
auto issue =
protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::CookieIssue)
.SetDetails(std::move(details))
.Build();
if (devtools_issue_id.has_value()) {
issue->SetIssueId(devtools_issue_id.value());
}
ReportBrowserInitiatedIssue(render_frame_host_impl, std::move(issue));
}
namespace {
const protocol::Audits::InspectorIssue& AddIssueToIssueStorage(
RenderFrameHost* rfh,
std::unique_ptr<protocol::Audits::InspectorIssue> issue) {
// We only utilize a central storage on the page. Each issue is still
// associated with the originating |RenderFrameHost| though.
DevToolsIssueStorage* issue_storage =
DevToolsIssueStorage::GetOrCreateForPage(
rfh->GetOutermostMainFrame()->GetPage());
return issue_storage->AddInspectorIssue(rfh, std::move(issue));
}
} // namespace
namespace {
std::unique_ptr<protocol::Audits::InspectorIssue>
BuildUserReidentificationIssue(
const blink::mojom::UserReidentificationIssueDetailsPtr& issue_details) {
auto affected_request = issue_details->request.is_null()
? nullptr
: protocol::Audits::AffectedRequest::Create()
.SetUrl(issue_details->request->url)
.Build();
auto source_code_location =
issue_details->sourceCodeLocation.is_null()
? nullptr
: protocol::Audits::SourceCodeLocation::Create()
.SetUrl(issue_details->sourceCodeLocation->url.value())
.SetLineNumber(issue_details->sourceCodeLocation->line)
.SetColumnNumber(issue_details->sourceCodeLocation->column)
.Build();
std::string issue_type;
switch (issue_details->type) {
case blink::mojom::UserReidentificationIssueType::kBlockedFrameNavigation:
issue_type = protocol::Audits::UserReidentificationIssueTypeEnum::
BlockedFrameNavigation;
break;
case blink::mojom::UserReidentificationIssueType::kBlockedSubresource:
issue_type = protocol::Audits::UserReidentificationIssueTypeEnum::
BlockedSubresource;
break;
case blink::mojom::UserReidentificationIssueType::kNoisedCanvasReadback:
issue_type = protocol::Audits::UserReidentificationIssueTypeEnum::
NoisedCanvasReadback;
break;
default:
NOTREACHED();
}
auto reidentification_issue_details =
protocol::Audits::UserReidentificationIssueDetails::Create()
.SetType(issue_type)
.SetRequest(std::move(affected_request))
.Build();
auto protocol_issue_details =
protocol::Audits::InspectorIssueDetails::Create()
.SetUserReidentificationIssueDetails(
std::move(reidentification_issue_details))
.Build();
auto issue = protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::
UserReidentificationIssue)
.SetDetails(std::move(protocol_issue_details))
.Build();
return issue;
}
} // namespace
void ReportBrowserInitiatedIssue(
RenderFrameHostImpl* frame,
std::unique_ptr<protocol::Audits::InspectorIssue> issue) {
FrameTreeNode* ftn = frame->frame_tree_node();
if (!ftn) {
return;
}
const auto& issue_ptr = AddIssueToIssueStorage(frame, std::move(issue));
DispatchToAgents(ftn, &protocol::AuditsHandler::OnIssueAdded, issue_ptr);
}
void BuildAndReportBrowserInitiatedIssue(
RenderFrameHostImpl* frame,
blink::mojom::InspectorIssueInfoPtr info) {
std::unique_ptr<protocol::Audits::InspectorIssue> issue;
if (info->code == blink::mojom::InspectorIssueCode::kHeavyAdIssue) {
issue = BuildHeavyAdIssue(info->details->heavy_ad_issue_details);
} else if (info->code ==
blink::mojom::InspectorIssueCode::kFederatedAuthRequestIssue) {
issue = BuildFederatedAuthRequestIssue(
info->details->federated_auth_request_details);
} else if (info->code ==
blink::mojom::InspectorIssueCode::kDeprecationIssue) {
issue = BuildDeprecationIssue(info->details->deprecation_issue_details);
} else if (info->code ==
blink::mojom::InspectorIssueCode::kBounceTrackingIssue) {
issue =
BuildBounceTrackingIssue(info->details->bounce_tracking_issue_details);
} else if (info->code ==
blink::mojom::InspectorIssueCode::kPartitioningBlobURLIssue) {
issue = BuildPartitioningBlobURLIssue(
info->details->partitioning_blob_url_issue_details);
} else if (info->code == blink::mojom::InspectorIssueCode::
kCookieDeprecationMetadataIssue) {
issue = BuildCookieDeprecationMetadataIssue(
info->details->cookie_deprecation_metadata_issue_details);
} else if (info->code == blink::mojom::InspectorIssueCode::
kFederatedAuthUserInfoRequestIssue) {
issue = BuildFederatedAuthUserInfoRequestIssue(
info->details->federated_auth_user_info_request_details);
} else if (info->code ==
blink::mojom::InspectorIssueCode::kAttributionReportingIssue) {
issue = BuildAttributionReportingIssue(
info->details->attribution_reporting_issue_details);
} else if (info->code ==
blink::mojom::InspectorIssueCode::kUserReidentificationIssue) {
issue = BuildUserReidentificationIssue(
info->details->user_reidentification_issue_details);
} else {
NOTREACHED() << "Unsupported type of browser-initiated issue";
}
ReportBrowserInitiatedIssue(frame, std::move(issue));
}
void OnWebTransportHandshakeFailed(
RenderFrameHostImpl* frame,
const GURL& url,
const std::optional<net::WebTransportError>& error) {
FrameTreeNode* ftn = frame->frame_tree_node();
if (!ftn) {
return;
}
std::string text = base::StringPrintf(
"Failed to establish a connection to %s", url.spec().c_str());
if (error) {
text += ": ";
text += net::WebTransportErrorToString(*error);
}
text += ".";
auto entry =
protocol::Log::LogEntry::Create()
.SetSource(protocol::Log::LogEntry::SourceEnum::Network)
.SetLevel(protocol::Log::LogEntry::LevelEnum::Error)
.SetText(text)
.SetTimestamp(base::Time::Now().InMillisecondsFSinceUnixEpoch())
.Build();
DispatchToAgents(ftn, &protocol::LogHandler::EntryAdded, entry.get());
}
void OnServiceWorkerMainScriptFetchingFailed(
const GlobalRenderFrameHostId& requesting_frame_id,
const ServiceWorkerContextWrapper* context_wrapper,
int64_t version_id,
const std::string& error,
const network::URLLoaderCompletionStatus& status,
const network::mojom::URLResponseHead* response_head,
const GURL& url) {
DCHECK(!error.empty());
DCHECK_NE(net::OK, status.error_code);
// If we have a requesting_frame_id, we should have a frame and a frame tree
// node. However since the lifetime of these objects can be complex, we check
// at each step that we indeed can go reach all the way to the FrameTreeNode.
if (!requesting_frame_id) {
return;
}
RenderFrameHostImpl* requesting_frame =
RenderFrameHostImpl::FromID(requesting_frame_id);
if (!requesting_frame) {
return;
}
FrameTreeNode* ftn = requesting_frame->frame_tree_node();
if (!ftn) {
return;
}
auto entry =
protocol::Log::LogEntry::Create()
.SetSource(protocol::Log::LogEntry::SourceEnum::Network)
.SetLevel(protocol::Log::LogEntry::LevelEnum::Error)
.SetText(error)
.SetTimestamp(base::Time::Now().InMillisecondsFSinceUnixEpoch())
.Build();
DispatchToAgents(ftn, &protocol::LogHandler::EntryAdded, entry.get());
ServiceWorkerDevToolsAgentHost* agent_host =
ServiceWorkerDevToolsManager::GetInstance()
->GetDevToolsAgentHostForNewInstallingWorker(context_wrapper,
version_id);
if (response_head) {
DCHECK(agent_host);
network::mojom::URLResponseHeadDevToolsInfoPtr head_info =
network::ExtractDevToolsInfo(*response_head);
auto worker_token = agent_host->devtools_worker_token().ToString();
for (auto* network_handler :
protocol::NetworkHandler::ForAgentHost(agent_host)) {
network_handler->ResponseReceived(
worker_token, worker_token, url,
protocol::Network::ResourceTypeEnum::Other, *head_info,
requesting_frame->devtools_frame_token().ToString());
network_handler->frontend()->LoadingFinished(
worker_token,
status.completion_time.ToInternalValue() /
static_cast<double>(base::Time::kMicrosecondsPerSecond),
status.encoded_data_length);
}
} else if (agent_host) {
for (auto* network_handler :
protocol::NetworkHandler::ForAgentHost(agent_host)) {
network_handler->LoadingComplete(
agent_host->devtools_worker_token().ToString(),
protocol::Network::ResourceTypeEnum::Other, status);
}
}
}
namespace {
// Only assign request id if there's an enabled agent host.
void MaybeAssignResourceRequestId(DevToolsAgentHostImpl* host,
const std::string& id,
network::ResourceRequest& request) {
DCHECK(!request.devtools_request_id.has_value());
for (auto* network_handler : protocol::NetworkHandler::ForAgentHost(host)) {
if (network_handler->enabled()) {
request.devtools_request_id = id;
return;
}
}
}
} // namespace
void MaybeAssignResourceRequestId(FrameTreeNode* ftn,
const std::string& id,
network::ResourceRequest& request) {
if (auto* host = RenderFrameDevToolsAgentHost::GetFor(ftn)) {
MaybeAssignResourceRequestId(host, id, request);
}
}
void MaybeAssignResourceRequestId(FrameTreeNodeId frame_tree_node_id,
const std::string& id,
network::ResourceRequest& request) {
auto* frame_tree_node = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (frame_tree_node) {
MaybeAssignResourceRequestId(frame_tree_node, id, request);
}
}
void OnServiceWorkerMainScriptRequestWillBeSent(
const GlobalRenderFrameHostId& requesting_frame_id,
const ServiceWorkerContextWrapper* context_wrapper,
int64_t version_id,
network::ResourceRequest& request) {
// Currently, `requesting_frame_id` is invalid when payment apps and
// extensions register a service worker. See the callers of
// ServiceWorkerContextWrapper::RegisterServiceWorker().
if (!requesting_frame_id) {
return;
}
RenderFrameHostImpl* requesting_frame =
RenderFrameHostImpl::FromID(requesting_frame_id);
if (!requesting_frame) {
return;
}
auto timestamp = base::TimeTicks::Now();
network::mojom::URLRequestDevToolsInfoPtr request_info =
network::ExtractDevToolsInfo(request);
ServiceWorkerDevToolsAgentHost* agent_host =
ServiceWorkerDevToolsManager::GetInstance()
->GetDevToolsAgentHostForNewInstallingWorker(context_wrapper,
version_id);
DCHECK(agent_host);
const std::string request_id = agent_host->devtools_worker_token().ToString();
MaybeAssignResourceRequestId(agent_host, request_id, request);
for (auto* network_handler :
protocol::NetworkHandler::ForAgentHost(agent_host)) {
network_handler->RequestSent(request_id,
/*loader_id=*/"", request.headers,
*request_info,
protocol::Network::Initiator::TypeEnum::Other,
requesting_frame->GetLastCommittedURL(),
/*initiator_devtools_request_id=*/"",
/*frame_token=*/std::nullopt, timestamp);
}
}
void OnWorkerMainScriptLoadingFailed(
const GURL& url,
const base::UnguessableToken& worker_token,
FrameTreeNode* ftn,
RenderFrameHostImpl* ancestor_rfh,
const network::URLLoaderCompletionStatus& status) {
DCHECK(ftn);
std::string id = worker_token.ToString();
if (status.blocked_by_response_reason) {
ReportBlockedByResponseIssue(url, id, ftn, ancestor_rfh, status);
}
DispatchToAgents(ftn, &protocol::NetworkHandler::LoadingComplete, id,
protocol::Network::ResourceTypeEnum::Other, status);
}
void OnWorkerMainScriptRequestWillBeSent(
RenderFrameHostImpl& ancestor_frame_host,
const base::UnguessableToken& worker_token,
network::ResourceRequest& request) {
FrameTreeNode* ftn = ancestor_frame_host.frame_tree_node();
auto timestamp = base::TimeTicks::Now();
network::mojom::URLRequestDevToolsInfoPtr request_info =
network::ExtractDevToolsInfo(request);
auto* owner_host = RenderFrameDevToolsAgentHost::GetFor(ftn);
if (!owner_host) {
return;
}
MaybeAssignResourceRequestId(owner_host, worker_token.ToString(), request);
// Note: we apply overrides from the owner frame to match the behavior in the
// renderer.
bool disable_cache = false;
ApplyNetworkRequestOverrides(owner_host, &request.headers, &disable_cache,
nullptr, &request.skip_service_worker,
&request.devtools_accepted_stream_types, nullptr,
nullptr, nullptr);
if (disable_cache) {
request.load_flags &=
~(net::LOAD_VALIDATE_CACHE | net::LOAD_SKIP_CACHE_VALIDATION |
net::LOAD_ONLY_FROM_CACHE | net::LOAD_DISABLE_CACHE);
request.load_flags |= net::LOAD_BYPASS_CACHE;
}
DispatchToAgents(
ftn, &protocol::NetworkHandler::RequestSent, worker_token.ToString(),
/*loader_id=*/"", request.headers, *request_info,
protocol::Network::Initiator::TypeEnum::Other, ftn->current_url(),
/*initiator_devtools_request_id*/ "",
ancestor_frame_host.devtools_frame_token(), timestamp);
}
void LogWorkletMessage(RenderFrameHostImpl& frame_host,
blink::mojom::ConsoleMessageLevel log_level,
const std::string& message) {
FrameTreeNode* ftn = frame_host.frame_tree_node();
if (!ftn) {
return;
}
std::string log_level_string;
switch (log_level) {
case blink::mojom::ConsoleMessageLevel::kVerbose:
log_level_string = protocol::Log::LogEntry::LevelEnum::Verbose;
break;
case blink::mojom::ConsoleMessageLevel::kInfo:
log_level_string = protocol::Log::LogEntry::LevelEnum::Info;
break;
case blink::mojom::ConsoleMessageLevel::kWarning:
log_level_string = protocol::Log::LogEntry::LevelEnum::Warning;
break;
case blink::mojom::ConsoleMessageLevel::kError:
log_level_string = protocol::Log::LogEntry::LevelEnum::Error;
break;
}
DCHECK(!log_level_string.empty());
auto entry =
protocol::Log::LogEntry::Create()
.SetSource(protocol::Log::LogEntry::SourceEnum::Other)
.SetLevel(log_level_string)
.SetText(message)
.SetTimestamp(base::Time::Now().InMillisecondsFSinceUnixEpoch())
.Build();
DispatchToAgents(ftn, &protocol::LogHandler::EntryAdded, entry.get());
// Manually trigger RenderFrameHostImpl::DidAddMessageToConsole, so that the
// observer behavior aligns more with the observer behavior for the regular
// devtools logging path from the renderer.
frame_host.DidAddMessageToConsole(log_level, base::UTF8ToUTF16(message),
/*line_no=*/0, /*source_id=*/{},
/*untrusted_stack_trace=*/{});
}
void ApplyNetworkContextParamsOverrides(
BrowserContext* browser_context,
network::mojom::NetworkContextParams* context_params) {
for (auto* agent_host : BrowserDevToolsAgentHost::Instances()) {
for (auto* target_handler :
protocol::TargetHandler::ForAgentHost(agent_host)) {
target_handler->ApplyNetworkContextParamsOverrides(browser_context,
context_params);
}
}
}
protocol::Audits::GenericIssueErrorType GenericIssueErrorTypeToProtocol(
blink::mojom::GenericIssueErrorType error_type) {
switch (error_type) {
case blink::mojom::GenericIssueErrorType::kFormLabelForNameError:
return protocol::Audits::GenericIssueErrorTypeEnum::FormLabelForNameError;
case blink::mojom::GenericIssueErrorType::kFormDuplicateIdForInputError:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormDuplicateIdForInputError;
case blink::mojom::GenericIssueErrorType::kFormInputWithNoLabelError:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormInputWithNoLabelError;
case blink::mojom::GenericIssueErrorType::
kFormAutocompleteAttributeEmptyError:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormAutocompleteAttributeEmptyError;
case blink::mojom::GenericIssueErrorType::
kFormEmptyIdAndNameAttributesForInputError:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormEmptyIdAndNameAttributesForInputError;
case blink::mojom::GenericIssueErrorType::
kFormAriaLabelledByToNonExistingId:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormAriaLabelledByToNonExistingId;
case blink::mojom::GenericIssueErrorType::
kFormInputAssignedAutocompleteValueToIdOrNameAttributeError:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormInputAssignedAutocompleteValueToIdOrNameAttributeError;
case blink::mojom::GenericIssueErrorType::
kFormLabelHasNeitherForNorNestedInput:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormLabelHasNeitherForNorNestedInput;
case blink::mojom::GenericIssueErrorType::
kFormLabelForMatchesNonExistingIdError:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormLabelForMatchesNonExistingIdError;
case blink::mojom::GenericIssueErrorType::
kFormInputHasWrongButWellIntendedAutocompleteValueError:
return protocol::Audits::GenericIssueErrorTypeEnum::
FormInputHasWrongButWellIntendedAutocompleteValueError;
case blink::mojom::GenericIssueErrorType::kResponseWasBlockedByORB:
return protocol::Audits::GenericIssueErrorTypeEnum::
ResponseWasBlockedByORB;
}
}
void UpdateDeviceRequestPrompt(RenderFrameHost* render_frame_host,
DevtoolsDeviceRequestPromptInfo* prompt_info) {
FrameTreeNode* ftn = FrameTreeNode::From(render_frame_host);
if (!ftn) {
return;
}
DispatchToAgents(ftn,
&protocol::DeviceAccessHandler::UpdateDeviceRequestPrompt,
prompt_info);
}
void CleanUpDeviceRequestPrompt(RenderFrameHost* render_frame_host,
DevtoolsDeviceRequestPromptInfo* prompt_info) {
FrameTreeNode* ftn = FrameTreeNode::From(render_frame_host);
if (!ftn) {
return;
}
DispatchToAgents(ftn,
&protocol::DeviceAccessHandler::CleanUpDeviceRequestPrompt,
prompt_info);
}
void WillSendFedCmRequest(RenderFrameHost& render_frame_host,
bool* intercept,
bool* disable_delay) {
FrameTreeNode* ftn = FrameTreeNode::From(&render_frame_host);
if (!ftn) {
return;
}
DispatchToAgents(ftn, &protocol::FedCmHandler::WillSendRequest, intercept,
disable_delay);
}
void WillShowFedCmDialog(RenderFrameHost& render_frame_host, bool* intercept) {
FrameTreeNode* ftn = FrameTreeNode::From(&render_frame_host);
if (!ftn) {
return;
}
DispatchToAgents(ftn, &protocol::FedCmHandler::WillShowDialog, intercept);
}
void DidShowFedCmDialog(RenderFrameHost& render_frame_host) {
FrameTreeNode* ftn = FrameTreeNode::From(&render_frame_host);
if (!ftn) {
return;
}
DispatchToAgents(ftn, &protocol::FedCmHandler::DidShowDialog);
}
void DidCloseFedCmDialog(RenderFrameHost& render_frame_host) {
FrameTreeNode* ftn = FrameTreeNode::From(&render_frame_host);
if (!ftn) {
return;
}
DispatchToAgents(ftn, &protocol::FedCmHandler::DidCloseDialog);
}
void WillSendFedCmNetworkRequest(FrameTreeNodeId frame_tree_node_id,
const network::ResourceRequest& request) {
FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (!ftn) {
return;
}
// Get the DevTools navigation token from the current frame host
const std::optional<base::UnguessableToken>& loader_id =
ftn->current_frame_host()->GetDevToolsNavigationToken();
if (!loader_id.has_value()) {
return;
}
std::optional<base::UnguessableToken> frame_token =
ftn->current_frame_host()->devtools_frame_token();
GURL initiator_url;
if (request.request_initiator.has_value()) {
initiator_url = request.request_initiator->GetURL();
}
network::mojom::URLRequestDevToolsInfoPtr request_info =
network::ExtractDevToolsInfo(request);
DispatchToAgents(frame_tree_node_id, &protocol::NetworkHandler::RequestSent,
request.devtools_request_id.value(),
loader_id.value().ToString(), request.headers, *request_info,
protocol::Network::ResourceTypeEnum::FedCM, initiator_url,
/*initiator_devtools_request_id=*/"", frame_token,
base::TimeTicks::Now());
}
void DidReceiveFedCmNetworkResponse(
FrameTreeNodeId frame_tree_node_id,
const std::string& devtools_request_id,
const GURL& url,
const network::mojom::URLResponseHead* response_head,
const std::string& response_body,
const network::URLLoaderCompletionStatus& status) {
FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
if (!ftn) {
return;
}
// Get the DevTools navigation token from the current frame host
const std::optional<base::UnguessableToken>& loader_id =
ftn->current_frame_host()->GetDevToolsNavigationToken();
if (!loader_id.has_value()) {
return;
}
if (response_head) {
network::mojom::URLResponseHeadDevToolsInfoPtr head_info =
network::ExtractDevToolsInfo(*response_head);
std::optional<base::UnguessableToken> frame_token =
ftn->current_frame_host()->devtools_frame_token();
DispatchToAgents(frame_tree_node_id,
&protocol::NetworkHandler::ResponseReceived,
devtools_request_id, loader_id.value().ToString(), url,
protocol::Network::ResourceTypeEnum::FedCM, *head_info,
frame_token.value().ToString());
}
if (status.error_code == net::OK) {
DispatchToAgents(frame_tree_node_id,
&protocol::NetworkHandler::BodyDataReceived,
devtools_request_id, response_body,
/*is_base64_encoded=*/false);
}
DispatchToAgents(
frame_tree_node_id, &protocol::NetworkHandler::LoadingComplete,
devtools_request_id, protocol::Network::ResourceTypeEnum::FedCM, status);
}
void OnFencedFrameReportRequestSent(
FrameTreeNodeId initiator_frame_tree_node_id,
const std::string& devtools_request_id,
network::ResourceRequest& request,
const std::string& event_data) {
DispatchToAgents(initiator_frame_tree_node_id,
&protocol::NetworkHandler::FencedFrameReportRequestSent,
/*request_id=*/devtools_request_id, request, event_data,
base::TimeTicks::Now());
}
void OnFencedFrameReportResponseReceived(
FrameTreeNodeId initiator_frame_tree_node_id,
const std::string& devtools_request_id,
const GURL& final_url,
scoped_refptr<net::HttpResponseHeaders> headers) {
network::mojom::URLResponseHeadDevToolsInfoPtr response_info =
network::mojom::URLResponseHeadDevToolsInfo::New();
response_info->headers = headers;
DispatchToAgents(initiator_frame_tree_node_id,
&protocol::NetworkHandler::ResponseReceived,
/*request_id=*/devtools_request_id,
/*loader_id=*/devtools_request_id, final_url,
protocol::Network::ResourceTypeEnum::Other, *response_info,
/*frame_id=*/std::nullopt);
DispatchToAgents(initiator_frame_tree_node_id,
&protocol::NetworkHandler::LoadingComplete,
/*request_id=*/devtools_request_id,
protocol::Network::Initiator::TypeEnum::Other,
network::URLLoaderCompletionStatus(net::OK));
}
void DidChangeFrameLoadingState(FrameTreeNode& ftn) {
DispatchToAgents(&ftn, &protocol::PageHandler::DidChangeFrameLoadingState,
ftn);
}
void DidStartNavigating(FrameTreeNode& ftn,
const GURL& url,
const base::UnguessableToken& loader_id,
const blink::mojom::NavigationType& navigation_type) {
DispatchToAgents(&ftn, &protocol::PageHandler::DidStartNavigating, ftn, url,
loader_id, navigation_type);
}
} // namespace devtools_instrumentation
} // namespace content