blob: 5960e61f720552a1f5670af98ee6eecaa28c2302 [file] [log] [blame]
// Copyright 2021 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/network_service_devtools_observer.h"
#include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/devtools/protocol/audits_handler.h"
#include "content/browser/devtools/protocol/network_handler.h"
#include "content/browser/devtools/render_frame_devtools_agent_host.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/public/common/content_client.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "services/network/public/mojom/http_raw_headers.mojom.h"
#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
namespace content {
namespace {
template <typename Handler, typename... MethodArgs, typename... Args>
void DispatchToAgents(DevToolsAgentHostImpl* agent_host,
void (Handler::*method)(MethodArgs...),
Args&&... args) {
for (auto* h : Handler::ForAgentHost(agent_host))
(h->*method)(std::forward<Args>(args)...);
}
} // namespace
NetworkServiceDevToolsObserver::NetworkServiceDevToolsObserver(
base::PassKey<NetworkServiceDevToolsObserver> pass_key,
const std::string& id,
int frame_tree_node_id)
: devtools_agent_id_(id), frame_tree_node_id_(frame_tree_node_id) {}
NetworkServiceDevToolsObserver::~NetworkServiceDevToolsObserver() = default;
DevToolsAgentHostImpl* NetworkServiceDevToolsObserver::GetDevToolsAgentHost() {
if (frame_tree_node_id_ != FrameTreeNode::kFrameTreeNodeInvalidId) {
auto* frame_tree_node =
FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
if (!frame_tree_node)
return nullptr;
return RenderFrameDevToolsAgentHost::GetFor(frame_tree_node);
}
auto host = DevToolsAgentHostImpl::GetForId(devtools_agent_id_);
if (!host)
return nullptr;
return host.get();
}
void NetworkServiceDevToolsObserver::OnRawRequest(
const std::string& devtools_request_id,
const net::CookieAccessResultList& request_cookie_list,
std::vector<network::mojom::HttpRawHeaderPairPtr> request_headers,
base::TimeTicks timestamp,
network::mojom::ClientSecurityStatePtr security_state,
network::mojom::OtherPartitionInfoPtr other_partition_info) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
DispatchToAgents(host,
&protocol::NetworkHandler::OnRequestWillBeSentExtraInfo,
devtools_request_id, request_cookie_list, request_headers,
timestamp, security_state, other_partition_info);
}
void NetworkServiceDevToolsObserver::OnRawResponse(
const std::string& devtools_request_id,
const net::CookieAndLineAccessResultList& response_cookie_list,
std::vector<network::mojom::HttpRawHeaderPairPtr> response_headers,
const absl::optional<std::string>& response_headers_text,
network::mojom::IPAddressSpace resource_address_space,
int32_t http_status_code,
const absl::optional<net::CookiePartitionKey>& cookie_partition_key) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
DispatchToAgents(host, &protocol::NetworkHandler::OnResponseReceivedExtraInfo,
devtools_request_id, response_cookie_list, response_headers,
response_headers_text, resource_address_space,
http_status_code, cookie_partition_key);
}
void NetworkServiceDevToolsObserver::OnTrustTokenOperationDone(
const std::string& devtools_request_id,
network::mojom::TrustTokenOperationResultPtr result) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
DispatchToAgents(host, &protocol::NetworkHandler::OnTrustTokenOperationDone,
devtools_request_id, *result);
}
void NetworkServiceDevToolsObserver::OnLocalNetworkRequest(
const absl::optional<std::string>& devtools_request_id,
const GURL& url,
bool is_warning,
network::mojom::IPAddressSpace resource_address_space,
network::mojom::ClientSecurityStatePtr client_security_state) {
if (frame_tree_node_id_ == FrameTreeNode::kFrameTreeNodeInvalidId)
return;
auto* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
if (!ftn)
return;
auto cors_error_status =
protocol::Network::CorsErrorStatus::Create()
.SetCorsError(
protocol::Network::CorsErrorEnum::InsecurePrivateNetwork)
.SetFailedParameter("")
.Build();
std::unique_ptr<protocol::Audits::AffectedRequest> affected_request =
protocol::Audits::AffectedRequest::Create()
.SetRequestId(devtools_request_id.value_or(""))
.SetUrl(url.spec())
.Build();
auto cors_issue_details =
protocol::Audits::CorsIssueDetails::Create()
.SetIsWarning(is_warning)
.SetResourceIPAddressSpace(
protocol::NetworkHandler::BuildIpAddressSpace(
resource_address_space))
.SetRequest(std::move(affected_request))
.SetCorsErrorStatus(std::move(cors_error_status))
.Build();
auto maybe_protocol_security_state =
protocol::NetworkHandler::MaybeBuildClientSecurityState(
client_security_state);
if (maybe_protocol_security_state.isJust()) {
cors_issue_details->SetClientSecurityState(
maybe_protocol_security_state.takeJust());
}
auto details = protocol::Audits::InspectorIssueDetails::Create()
.SetCorsIssueDetails(std::move(cors_issue_details))
.Build();
auto issue = protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::CorsIssue)
.SetDetails(std::move(details))
.Build();
devtools_instrumentation::ReportBrowserInitiatedIssue(
ftn->current_frame_host(), issue.get());
}
void NetworkServiceDevToolsObserver::OnCorsPreflightRequest(
const base::UnguessableToken& devtools_request_id,
const net::HttpRequestHeaders& request_headers,
network::mojom::URLRequestDevToolsInfoPtr request_info,
const GURL& initiator_url,
const std::string& initiator_devtools_request_id) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
auto timestamp = base::TimeTicks::Now();
auto id = devtools_request_id.ToString();
DispatchToAgents(host, &protocol::NetworkHandler::RequestSent, id,
/* loader_id=*/"", request_headers, *request_info,
protocol::Network::Initiator::TypeEnum::Preflight,
initiator_url, initiator_devtools_request_id, timestamp);
}
void NetworkServiceDevToolsObserver::OnCorsPreflightResponse(
const base::UnguessableToken& devtools_request_id,
const GURL& url,
network::mojom::URLResponseHeadDevToolsInfoPtr head) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
auto id = devtools_request_id.ToString();
DispatchToAgents(host, &protocol::NetworkHandler::ResponseReceived, id,
/* loader_id=*/"", url,
protocol::Network::ResourceTypeEnum::Preflight, *head,
protocol::Maybe<std::string>());
}
void NetworkServiceDevToolsObserver::OnCorsPreflightRequestCompleted(
const base::UnguessableToken& devtools_request_id,
const network::URLLoaderCompletionStatus& status) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
auto id = devtools_request_id.ToString();
DispatchToAgents(host, &protocol::NetworkHandler::LoadingComplete, id,
protocol::Network::ResourceTypeEnum::Preflight, status);
}
void NetworkServiceDevToolsObserver::OnCorsError(
const absl::optional<std::string>& devtools_request_id,
const absl::optional<::url::Origin>& initiator_origin,
network::mojom::ClientSecurityStatePtr client_security_state,
const GURL& url,
const network::CorsErrorStatus& cors_error_status,
bool is_warning) {
if (frame_tree_node_id_ == FrameTreeNode::kFrameTreeNodeInvalidId)
return;
auto* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
if (!ftn)
return;
RenderFrameHostImpl* rfhi = ftn->current_frame_host();
if (!rfhi)
return;
// TODO(https://crbug.com/1268378): Remove this once enforcement is always
// enabled and warnings are no more.
if (is_warning) {
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
rfhi,
blink::mojom::WebFeature::kPrivateNetworkAccessIgnoredPreflightError);
if (initiator_origin.has_value() &&
!initiator_origin->IsSameOriginWith(url)) {
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
rfhi, blink::mojom::WebFeature::
kPrivateNetworkAccessIgnoredCrossOriginPreflightError);
}
if (initiator_origin.has_value() &&
net::SchemefulSite(initiator_origin.value()) !=
net::SchemefulSite(url)) {
GetContentClient()->browser()->LogWebFeatureForCurrentPage(
rfhi, blink::mojom::WebFeature::
kPrivateNetworkAccessIgnoredCrossSitePreflightError);
}
}
std::unique_ptr<protocol::Audits::AffectedRequest> affected_request =
protocol::Audits::AffectedRequest::Create()
.SetRequestId(devtools_request_id ? *devtools_request_id : "")
.SetUrl(url.spec())
.Build();
auto cors_issue_details =
protocol::Audits::CorsIssueDetails::Create()
.SetIsWarning(is_warning)
.SetRequest(std::move(affected_request))
.SetCorsErrorStatus(
protocol::NetworkHandler::BuildCorsErrorStatus(cors_error_status))
.Build();
if (initiator_origin) {
cors_issue_details->SetInitiatorOrigin(initiator_origin->GetURL().spec());
}
auto maybe_protocol_security_state =
protocol::NetworkHandler::MaybeBuildClientSecurityState(
client_security_state);
if (maybe_protocol_security_state.isJust()) {
cors_issue_details->SetClientSecurityState(
maybe_protocol_security_state.takeJust());
}
auto details = protocol::Audits::InspectorIssueDetails::Create()
.SetCorsIssueDetails(std::move(cors_issue_details))
.Build();
auto issue = protocol::Audits::InspectorIssue::Create()
.SetCode(protocol::Audits::InspectorIssueCodeEnum::CorsIssue)
.SetDetails(std::move(details))
.SetIssueId(cors_error_status.issue_id.ToString())
.Build();
devtools_instrumentation::ReportBrowserInitiatedIssue(rfhi, issue.get());
}
void NetworkServiceDevToolsObserver::OnSubresourceWebBundleMetadata(
const std::string& devtools_request_id,
const std::vector<GURL>& urls) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
DispatchToAgents(host,
&protocol::NetworkHandler::OnSubresourceWebBundleMetadata,
devtools_request_id, urls);
}
void NetworkServiceDevToolsObserver::OnSubresourceWebBundleMetadataError(
const std::string& devtools_request_id,
const std::string& error_message) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
DispatchToAgents(
host, &protocol::NetworkHandler::OnSubresourceWebBundleMetadataError,
devtools_request_id, error_message);
}
void NetworkServiceDevToolsObserver::OnSubresourceWebBundleInnerResponse(
const std::string& inner_request_devtools_id,
const GURL& url,
const absl::optional<std::string>& bundle_request_devtools_id) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
DispatchToAgents(
host, &protocol::NetworkHandler::OnSubresourceWebBundleInnerResponse,
inner_request_devtools_id, url, bundle_request_devtools_id);
}
void NetworkServiceDevToolsObserver::OnSubresourceWebBundleInnerResponseError(
const std::string& inner_request_devtools_id,
const GURL& url,
const std::string& error_message,
const absl::optional<std::string>& bundle_request_devtools_id) {
auto* host = GetDevToolsAgentHost();
if (!host)
return;
DispatchToAgents(
host, &protocol::NetworkHandler::OnSubresourceWebBundleInnerResponseError,
inner_request_devtools_id, url, error_message,
bundle_request_devtools_id);
}
void NetworkServiceDevToolsObserver::Clone(
mojo::PendingReceiver<network::mojom::DevToolsObserver> observer) {
mojo::MakeSelfOwnedReceiver(
std::make_unique<NetworkServiceDevToolsObserver>(
base::PassKey<NetworkServiceDevToolsObserver>(), devtools_agent_id_,
frame_tree_node_id_),
std::move(observer));
}
mojo::PendingRemote<network::mojom::DevToolsObserver>
NetworkServiceDevToolsObserver::MakeSelfOwned(const std::string& id) {
mojo::PendingRemote<network::mojom::DevToolsObserver> remote;
mojo::MakeSelfOwnedReceiver(
std::make_unique<NetworkServiceDevToolsObserver>(
base::PassKey<NetworkServiceDevToolsObserver>(), id,
FrameTreeNode::kFrameTreeNodeInvalidId),
remote.InitWithNewPipeAndPassReceiver());
return remote;
}
mojo::PendingRemote<network::mojom::DevToolsObserver>
NetworkServiceDevToolsObserver::MakeSelfOwned(FrameTreeNode* frame_tree_node) {
mojo::PendingRemote<network::mojom::DevToolsObserver> remote;
mojo::MakeSelfOwnedReceiver(
std::make_unique<NetworkServiceDevToolsObserver>(
base::PassKey<NetworkServiceDevToolsObserver>(), std::string(),
frame_tree_node->frame_tree_node_id()),
remote.InitWithNewPipeAndPassReceiver());
return remote;
}
} // namespace content