910e62b5创建于 1月15日历史提交
// Copyright 2015 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/protocol/security_handler.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/containers/contains.h"
#include "base/strings/string_number_conversions.h"
#include "content/browser/devtools/devtools_agent_host_impl.h"
#include "content/browser/renderer_host/back_forward_cache_disable.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"

namespace content {
namespace protocol {

// static
std::vector<SecurityHandler*> SecurityHandler::ForAgentHost(
    DevToolsAgentHostImpl* host) {
  return host->HandlersByName<SecurityHandler>(Security::Metainfo::domainName);
}

SecurityHandler::SecurityHandler()
    : DevToolsDomainHandler(Security::Metainfo::domainName),
      enabled_(false),
      host_(nullptr) {
}

SecurityHandler::~SecurityHandler() = default;

void SecurityHandler::Wire(UberDispatcher* dispatcher) {
  frontend_ = std::make_unique<Security::Frontend>(dispatcher->channel());
  Security::Dispatcher::wire(dispatcher, this);
}

void SecurityHandler::AttachToRenderFrameHost() {
  DCHECK(host_);
  WebContents* web_contents = WebContents::FromRenderFrameHost(host_);
  WebContentsObserver::Observe(web_contents);

  // Send an initial DidChangeVisibleSecurityState event.
  DCHECK(enabled_);
  DidChangeVisibleSecurityState();
}

void SecurityHandler::SetRenderer(int process_host_id,
                                  RenderFrameHostImpl* frame_host) {
  host_ = frame_host;
  if (enabled_ && host_)
    AttachToRenderFrameHost();
}

void SecurityHandler::DidFinishNavigation(NavigationHandle* navigation_handle) {
  if (cert_error_override_mode_ == CertErrorOverrideMode::kHandleEvents) {
    BackForwardCache::DisableForRenderFrameHost(
        navigation_handle->GetPreviousRenderFrameHostId(),
        BackForwardCacheDisable::DisabledReason(
            BackForwardCacheDisable::DisabledReasonId::kSecurityHandler));
    FlushPendingCertificateErrorNotifications();
  }
}

void SecurityHandler::FlushPendingCertificateErrorNotifications() {
  for (auto& callback : cert_error_callbacks_) {
    std::move(callback).second.Run(
        content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL);
  }
  cert_error_callbacks_.clear();
}

bool SecurityHandler::IsIgnoreCertificateErrorsSet() const {
  return cert_error_override_mode_ == CertErrorOverrideMode::kIgnoreAll;
}

bool SecurityHandler::NotifyCertificateError(int cert_error,
                                             const GURL& request_url,
                                             CertErrorCallback handler) {
  if (cert_error_override_mode_ == CertErrorOverrideMode::kIgnoreAll) {
    if (handler)
      std::move(handler).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE);
    return true;
  }

  if (!enabled_)
    return false;

  frontend_->CertificateError(++last_cert_error_id_,
                              net::ErrorToShortString(cert_error),
                              request_url.spec());

  if (!handler ||
      cert_error_override_mode_ != CertErrorOverrideMode::kHandleEvents) {
    return false;
  }

  cert_error_callbacks_[last_cert_error_id_] = std::move(handler);
  return true;
}

Response SecurityHandler::Enable() {
  if (host_) {
    Response response = AssureTopLevelActiveFrame();
    if (response.IsError())
      return response;
  }
  enabled_ = true;
  if (host_)
    AttachToRenderFrameHost();

  return Response::Success();
}

Response SecurityHandler::Disable() {
  enabled_ = false;
  cert_error_override_mode_ = CertErrorOverrideMode::kDisabled;
  WebContentsObserver::Observe(nullptr);
  FlushPendingCertificateErrorNotifications();
  return Response::Success();
}

Response SecurityHandler::HandleCertificateError(int event_id,
                                                 const String& action) {
  if (!base::Contains(cert_error_callbacks_, event_id)) {
    return Response::ServerError(
        String("Unknown event id: " + base::NumberToString(event_id)));
  }
  content::CertificateRequestResultType type =
      content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL;
  Response response = Response::Success();
  if (action == Security::CertificateErrorActionEnum::Continue) {
    type = content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE;
  } else if (action == Security::CertificateErrorActionEnum::Cancel) {
    type = content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL;
  } else {
    response = Response::ServerError(
        String("Unknown Certificate Error Action: " + action));
  }
  std::move(cert_error_callbacks_[event_id]).Run(type);
  cert_error_callbacks_.erase(event_id);
  return response;
}

Response SecurityHandler::SetOverrideCertificateErrors(bool override) {
  if (override) {
    if (!enabled_)
      return Response::ServerError("Security domain not enabled");
    if (cert_error_override_mode_ == CertErrorOverrideMode::kIgnoreAll) {
      return Response::ServerError(
          "Certificate errors are already being ignored.");
    }
    cert_error_override_mode_ = CertErrorOverrideMode::kHandleEvents;
  } else {
    cert_error_override_mode_ = CertErrorOverrideMode::kDisabled;
    FlushPendingCertificateErrorNotifications();
  }
  return Response::Success();
}

Response SecurityHandler::SetIgnoreCertificateErrors(bool ignore) {
  if (ignore) {
    if (cert_error_override_mode_ == CertErrorOverrideMode::kHandleEvents)
      return Response::ServerError(
          "Certificate errors are already overridden.");
    cert_error_override_mode_ = CertErrorOverrideMode::kIgnoreAll;
  } else {
    cert_error_override_mode_ = CertErrorOverrideMode::kDisabled;
  }
  return Response::Success();
}

Response SecurityHandler::AssureTopLevelActiveFrame() {
  DCHECK(host_);
  constexpr char kCommandIsOnlyAvailableAtTopTarget[] =
      "Command can only be executed on top-level targets";
  if (host_->GetParentOrOuterDocument())
    return Response::ServerError(kCommandIsOnlyAvailableAtTopTarget);

  constexpr char kErrorInactivePage[] = "Not attached to an active page";
  if (!host_->IsActive())
    return Response::ServerError(kErrorInactivePage);

  return Response::Success();
}

}  // namespace protocol
}  // namespace content