910e62b5创建于 1月15日历史提交
// 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 "base/feature_list.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/cpp/features.h"
#include "services/network/public/mojom/http_raw_headers.mojom.h"
#include "services/network/public/mojom/shared_dictionary_error.mojom.h"
#include "services/network/public/mojom/sri_message_signature.mojom.h"
#include "services/network/public/mojom/unencoded_digest.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)...);
}

RenderFrameHostImpl* GetRenderFrameHostImplFrom(
    FrameTreeNodeId frame_tree_node_id) {
  auto* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
  if (!ftn) {
    return nullptr;
  }

  RenderFrameHostImpl* rfhi = ftn->current_frame_host();
  return rfhi;
}

}  // namespace

NetworkServiceDevToolsObserver::NetworkServiceDevToolsObserver(
    base::PassKey<NetworkServiceDevToolsObserver> pass_key,
    const std::string& id,
    FrameTreeNodeId 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_) {
    auto* frame_tree_node =
        FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
    if (!frame_tree_node)
      return nullptr;
    return RenderFrameDevToolsAgentHost::GetForWithAncestorFallback(
        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,
    const std::optional<base::UnguessableToken>&
        applied_network_conditions_id) {
  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, applied_network_conditions_id);
}

void NetworkServiceDevToolsObserver::OnRawResponse(
    const std::string& devtools_request_id,
    const net::CookieAndLineAccessResultList& response_cookie_list,
    std::vector<network::mojom::HttpRawHeaderPairPtr> response_headers,
    const std::optional<std::string>& response_headers_text,
    network::mojom::IPAddressSpace resource_address_space,
    int32_t http_status_code,
    const std::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::OnEarlyHintsResponse(
    const std::string& devtools_request_id,
    std::vector<network::mojom::HttpRawHeaderPairPtr> headers) {
  auto* host = GetDevToolsAgentHost();
  if (!host) {
    return;
  }
  DispatchToAgents(host,
                   &protocol::NetworkHandler::OnResponseReceivedEarlyHints,
                   devtools_request_id, headers);
}

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::OnPrivateNetworkRequest(
    const std::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_.is_null()) {
    return;
  }
  auto* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
  if (!ftn)
    return;

  auto cors_error_status =
      protocol::Network::CorsErrorStatus::Create()
          .SetCorsError(
              base::FeatureList::IsEnabled(
                  network::features::kLocalNetworkAccessChecks)
                  ? protocol::Network::CorsErrorEnum::
                        LocalNetworkAccessPermissionDenied
                  : 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();
  if (auto maybe_protocol_security_state =
          protocol::NetworkHandler::MaybeBuildClientSecurityState(
              client_security_state)) {
    cors_issue_details->SetClientSecurityState(
        std::move(maybe_protocol_security_state));
  }
  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(), std::move(issue));
}

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,
                   /*frame_token=*/std::nullopt, 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,
                   std::nullopt);
}

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 std::optional<std::string>& devtools_request_id,
    const std::optional<::url::Origin>& initiator_origin,
    network::mojom::ClientSecurityStatePtr client_security_state,
    const GURL& url,
    const network::CorsErrorStatus& cors_error_status,
    bool is_warning) {
  RenderFrameHostImpl* rfhi = GetRenderFrameHostImplFrom(frame_tree_node_id_);
  if (!rfhi)
    return;

  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());
  }
  if (auto maybe_protocol_security_state =
          protocol::NetworkHandler::MaybeBuildClientSecurityState(
              client_security_state)) {
    cors_issue_details->SetClientSecurityState(
        std::move(maybe_protocol_security_state));
  }

  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, std::move(issue));
}

void NetworkServiceDevToolsObserver::OnOrbError(
    const std::optional<std::string>& devtools_request_id,
    const GURL& url) {
  RenderFrameHostImpl* rfhi = GetRenderFrameHostImplFrom(frame_tree_node_id_);
  if (!rfhi) {
    return;
  }

  std::unique_ptr<protocol::Audits::AffectedRequest> affected_request =
      protocol::Audits::AffectedRequest::Create()
          .SetRequestId(devtools_request_id.value_or(""))
          .SetUrl(url.spec())
          .Build();
  auto generic_details =
      protocol::Audits::GenericIssueDetails::Create()
          .SetErrorType(protocol::Audits::GenericIssueErrorTypeEnum::
                            ResponseWasBlockedByORB)
          .SetRequest(std::move(affected_request))
          .Build();
  auto details = protocol::Audits::InspectorIssueDetails::Create()
                     .SetGenericIssueDetails(std::move(generic_details))
                     .Build();
  auto issue =
      protocol::Audits::InspectorIssue::Create()
          .SetCode(protocol::Audits::InspectorIssueCodeEnum::GenericIssue)
          .SetDetails(std::move(details))
          .Build();
  devtools_instrumentation::ReportBrowserInitiatedIssue(rfhi, std::move(issue));
}

namespace {

protocol::String BuildSharedDictionaryError(
    network::mojom::SharedDictionaryError write_error) {
  using network::mojom::SharedDictionaryError;
  namespace SharedDictionaryErrorEnum =
      protocol::Audits::SharedDictionaryErrorEnum;
  switch (write_error) {
    case SharedDictionaryError::kUseErrorCrossOriginNoCorsRequest:
      return SharedDictionaryErrorEnum::UseErrorCrossOriginNoCorsRequest;
    case SharedDictionaryError::kUseErrorDictionaryLoadFailure:
      return SharedDictionaryErrorEnum::UseErrorDictionaryLoadFailure;
    case SharedDictionaryError::kUseErrorMatchingDictionaryNotUsed:
      return SharedDictionaryErrorEnum::UseErrorMatchingDictionaryNotUsed;
    case SharedDictionaryError::kUseErrorUnexpectedContentDictionaryHeader:
      return SharedDictionaryErrorEnum::
          UseErrorUnexpectedContentDictionaryHeader;
    case SharedDictionaryError::kWriteErrorAlreadyRegistered:
      NOTREACHED();
    case SharedDictionaryError::kWriteErrorCossOriginNoCorsRequest:
      return SharedDictionaryErrorEnum::WriteErrorCossOriginNoCorsRequest;
    case SharedDictionaryError::kWriteErrorDisallowedBySettings:
      return SharedDictionaryErrorEnum::WriteErrorDisallowedBySettings;
    case SharedDictionaryError::kWriteErrorExpiredResponse:
      return SharedDictionaryErrorEnum::WriteErrorExpiredResponse;
    case SharedDictionaryError::kWriteErrorFeatureDisabled:
      return SharedDictionaryErrorEnum::WriteErrorFeatureDisabled;
    case SharedDictionaryError::kWriteErrorInsufficientResources:
      return SharedDictionaryErrorEnum::WriteErrorInsufficientResources;
    case SharedDictionaryError::kWriteErrorInvalidMatchField:
      return SharedDictionaryErrorEnum::WriteErrorInvalidMatchField;
    case SharedDictionaryError::kWriteErrorInvalidStructuredHeader:
      return SharedDictionaryErrorEnum::WriteErrorInvalidStructuredHeader;
    case SharedDictionaryError::kWriteErrorInvalidTTLField:
      return SharedDictionaryErrorEnum::WriteErrorInvalidTTLField;
    case SharedDictionaryError::kWriteErrorNavigationRequest:
      return SharedDictionaryErrorEnum::WriteErrorNavigationRequest;
    case SharedDictionaryError::kWriteErrorNoMatchField:
      return SharedDictionaryErrorEnum::WriteErrorNoMatchField;
    case SharedDictionaryError::kWriteErrorNonListMatchDestField:
      return SharedDictionaryErrorEnum::WriteErrorNonListMatchDestField;
    case SharedDictionaryError::kWriteErrorNonSecureContext:
      return SharedDictionaryErrorEnum::WriteErrorNonSecureContext;
    case SharedDictionaryError::kWriteErrorNonStringIdField:
      return SharedDictionaryErrorEnum::WriteErrorNonStringIdField;
    case SharedDictionaryError::kWriteErrorNonStringInMatchDestList:
      return SharedDictionaryErrorEnum::WriteErrorNonStringInMatchDestList;
    case SharedDictionaryError::kWriteErrorNonStringMatchField:
      return SharedDictionaryErrorEnum::WriteErrorNonStringMatchField;
    case SharedDictionaryError::kWriteErrorNonTokenTypeField:
      return SharedDictionaryErrorEnum::WriteErrorNonTokenTypeField;
    case SharedDictionaryError::kWriteErrorRequestAborted:
      return SharedDictionaryErrorEnum::WriteErrorRequestAborted;
    case SharedDictionaryError::kWriteErrorShuttingDown:
      return SharedDictionaryErrorEnum::WriteErrorShuttingDown;
    case SharedDictionaryError::kWriteErrorTooLongIdField:
      return SharedDictionaryErrorEnum::WriteErrorTooLongIdField;
    case SharedDictionaryError::kWriteErrorUnsupportedType:
      return SharedDictionaryErrorEnum::WriteErrorUnsupportedType;
    case SharedDictionaryError::kWriteErrorNonIntegerTTLField:
      return SharedDictionaryErrorEnum::WriteErrorNonIntegerTTLField;
  }
}

protocol::String ConvertToDevtoolsEnum(
    network::mojom::SRIMessageSignatureError error) {
  using network::mojom::SRIMessageSignatureError;
  namespace SRIMessageSignatureErrorEnum =
      protocol::Audits::SRIMessageSignatureErrorEnum;
  switch (error) {
    case SRIMessageSignatureError::kMissingSignatureHeader:
      return SRIMessageSignatureErrorEnum::MissingSignatureHeader;
    case SRIMessageSignatureError::kMissingSignatureInputHeader:
      return SRIMessageSignatureErrorEnum::MissingSignatureInputHeader;
    case SRIMessageSignatureError::kInvalidSignatureHeader:
      return SRIMessageSignatureErrorEnum::InvalidSignatureHeader;
    case SRIMessageSignatureError::kInvalidSignatureInputHeader:
      return SRIMessageSignatureErrorEnum::InvalidSignatureInputHeader;
    case SRIMessageSignatureError::kSignatureHeaderValueIsNotByteSequence:
      return SRIMessageSignatureErrorEnum::
          SignatureHeaderValueIsNotByteSequence;
    case SRIMessageSignatureError::kSignatureHeaderValueIsParameterized:
      return SRIMessageSignatureErrorEnum::SignatureHeaderValueIsParameterized;
    case SRIMessageSignatureError::kSignatureHeaderValueIsIncorrectLength:
      return SRIMessageSignatureErrorEnum::
          SignatureHeaderValueIsIncorrectLength;
    case SRIMessageSignatureError::kSignatureInputHeaderMissingLabel:
      return SRIMessageSignatureErrorEnum::SignatureInputHeaderMissingLabel;
    case SRIMessageSignatureError::kSignatureInputHeaderValueNotInnerList:
      return SRIMessageSignatureErrorEnum::
          SignatureInputHeaderValueNotInnerList;
    case SRIMessageSignatureError::kSignatureInputHeaderValueMissingComponents:
      return SRIMessageSignatureErrorEnum::
          SignatureInputHeaderValueMissingComponents;
    case SRIMessageSignatureError::kSignatureInputHeaderInvalidComponentType:
      return SRIMessageSignatureErrorEnum::
          SignatureInputHeaderInvalidComponentType;
    case SRIMessageSignatureError::kSignatureInputHeaderInvalidComponentName:
      return SRIMessageSignatureErrorEnum::
          SignatureInputHeaderInvalidComponentName;
    case SRIMessageSignatureError::
        kSignatureInputHeaderInvalidHeaderComponentParameter:
      return SRIMessageSignatureErrorEnum::
          SignatureInputHeaderInvalidHeaderComponentParameter;
    case SRIMessageSignatureError::
        kSignatureInputHeaderInvalidDerivedComponentParameter:
      return SRIMessageSignatureErrorEnum::
          SignatureInputHeaderInvalidDerivedComponentParameter;
    case SRIMessageSignatureError::kSignatureInputHeaderKeyIdLength:
      return SRIMessageSignatureErrorEnum::SignatureInputHeaderKeyIdLength;
    case SRIMessageSignatureError::kSignatureInputHeaderInvalidParameter:
      return SRIMessageSignatureErrorEnum::SignatureInputHeaderInvalidParameter;
    case SRIMessageSignatureError::
        kSignatureInputHeaderMissingRequiredParameters:
      return SRIMessageSignatureErrorEnum::
          SignatureInputHeaderMissingRequiredParameters;
    case SRIMessageSignatureError::kValidationFailedSignatureExpired:
      return SRIMessageSignatureErrorEnum::ValidationFailedSignatureExpired;
    case SRIMessageSignatureError::kValidationFailedSignatureMismatch:
      return SRIMessageSignatureErrorEnum::ValidationFailedSignatureMismatch;
    case SRIMessageSignatureError::kValidationFailedInvalidLength:
      return SRIMessageSignatureErrorEnum::ValidationFailedInvalidLength;
    case SRIMessageSignatureError::kValidationFailedIntegrityMismatch:
      return SRIMessageSignatureErrorEnum::ValidationFailedIntegrityMismatch;
  }
}

protocol::String ConvertToDevtoolsEnum(
    network::mojom::UnencodedDigestIssue error) {
  using network::mojom::UnencodedDigestIssue;
  namespace UnencodedDigestErrorEnum =
      protocol::Audits::UnencodedDigestErrorEnum;
  switch (error) {
    case UnencodedDigestIssue::kMalformedDictionary:
      return UnencodedDigestErrorEnum::MalformedDictionary;
    case UnencodedDigestIssue::kUnknownAlgorithm:
      return UnencodedDigestErrorEnum::UnknownAlgorithm;
    case UnencodedDigestIssue::kIncorrectDigestType:
      return UnencodedDigestErrorEnum::IncorrectDigestType;
    case UnencodedDigestIssue::kIncorrectDigestLength:
      return UnencodedDigestErrorEnum::IncorrectDigestLength;
  }
}

}  // namespace

void NetworkServiceDevToolsObserver::OnSharedDictionaryError(
    const std::string& devtool_request_id,
    const GURL& url,
    network::mojom::SharedDictionaryError error) {
  RenderFrameHostImpl* rfhi = GetRenderFrameHostImplFrom(frame_tree_node_id_);
  if (!rfhi) {
    return;
  }
  auto affected_request = protocol::Audits::AffectedRequest::Create()
                              .SetRequestId(devtool_request_id)
                              .SetUrl(url.spec())
                              .Build();
  auto shared_dictionary_issue_details =
      protocol::Audits::SharedDictionaryIssueDetails::Create()
          .SetSharedDictionaryError(BuildSharedDictionaryError(error))
          .SetRequest(std::move(affected_request))
          .Build();
  auto details = protocol::Audits::InspectorIssueDetails::Create()
                     .SetSharedDictionaryIssueDetails(
                         std::move(shared_dictionary_issue_details))
                     .Build();
  auto issue =
      protocol::Audits::InspectorIssue::Create()
          .SetCode(
              protocol::Audits::InspectorIssueCodeEnum::SharedDictionaryIssue)
          .SetDetails(std::move(details))
          .Build();
  devtools_instrumentation::ReportBrowserInitiatedIssue(rfhi, std::move(issue));
}

void NetworkServiceDevToolsObserver::OnSRIMessageSignatureIssue(
    const std::string& devtool_request_id,
    const GURL& url,
    std::vector<network::mojom::SRIMessageSignatureIssuePtr> issues) {
  RenderFrameHostImpl* rfhi = GetRenderFrameHostImplFrom(frame_tree_node_id_);
  if (!rfhi) {
    return;
  }
  for (const auto& issue : issues) {
    auto affected_request = protocol::Audits::AffectedRequest::Create()
                                .SetRequestId(devtool_request_id)
                                .SetUrl(url.spec())
                                .Build();
    auto issue_details =
        protocol::Audits::SRIMessageSignatureIssueDetails::Create()
            .SetError(ConvertToDevtoolsEnum(issue->error))
            .SetRequest(std::move(affected_request))
            .SetSignatureBase(issue->signature_base.value_or(""))
            .SetIntegrityAssertions(
                issue->integrity_assertions.has_value()
                    ? std::make_unique<protocol::Array<protocol::String>>(
                          std::move(issue->integrity_assertions.value()))
                    : std::make_unique<protocol::Array<protocol::String>>())
            .Build();
    auto details =
        protocol::Audits::InspectorIssueDetails::Create()
            .SetSriMessageSignatureIssueDetails(std::move(issue_details))
            .Build();
    auto devtools_issue =
        protocol::Audits::InspectorIssue::Create()
            .SetCode(protocol::Audits::InspectorIssueCodeEnum::
                         SRIMessageSignatureIssue)
            .SetDetails(std::move(details))
            .Build();
    devtools_instrumentation::ReportBrowserInitiatedIssue(
        rfhi, std::move(devtools_issue));
  }
}

void NetworkServiceDevToolsObserver::OnUnencodedDigestError(
    const std::string& devtool_request_id,
    const GURL& url,
    network::mojom::UnencodedDigestIssue issue) {
  RenderFrameHostImpl* rfhi = GetRenderFrameHostImplFrom(frame_tree_node_id_);
  if (!rfhi) {
    return;
  }
  auto affected_request = protocol::Audits::AffectedRequest::Create()
                              .SetRequestId(devtool_request_id)
                              .SetUrl(url.spec())
                              .Build();
  auto issue_details = protocol::Audits::UnencodedDigestIssueDetails::Create()
                           .SetError(ConvertToDevtoolsEnum(issue))
                           .SetRequest(std::move(affected_request))
                           .Build();
  auto details = protocol::Audits::InspectorIssueDetails::Create()
                     .SetUnencodedDigestIssueDetails(std::move(issue_details))
                     .Build();
  auto devtools_issue =
      protocol::Audits::InspectorIssue::Create()
          .SetCode(
              protocol::Audits::InspectorIssueCodeEnum::UnencodedDigestIssue)
          .SetDetails(std::move(details))
          .Build();
  devtools_instrumentation::ReportBrowserInitiatedIssue(
      rfhi, std::move(devtools_issue));
}

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,
          FrameTreeNodeId()),
      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