910e62b5创建于 1月15日历史提交
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/devtools/protocol/page_handler.h"

#include <variant>

#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "components/custom_handlers/protocol_handler_registry.h"
#include "components/payments/content/payment_request_web_contents_manager.h"
#include "components/subresource_filter/content/browser/devtools_interaction_tracker.h"
#include "third_party/blink/public/common/manifest/manifest_util.h"
#include "ui/gfx/image/image.h"

#if BUILDFLAG(ENABLE_PRINTING)
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
#include "chrome/browser/printing/print_view_manager.h"
#else
#include "chrome/browser/printing/print_view_manager_basic.h"
#endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
#endif  // BUILDFLAG(ENABLE_PRINTING)

#if BUILDFLAG(ENABLE_PRINTING)

#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
using ActivePrintManager = printing::PrintViewManager;
#else
using ActivePrintManager = printing::PrintViewManagerBasic;
#endif

#endif  // BUILDFLAG(ENABLE_PRINTING)

PageHandler::PageHandler(scoped_refptr<content::DevToolsAgentHost> agent_host,
                         content::WebContents* web_contents,
                         protocol::UberDispatcher* dispatcher)
    : agent_host_(agent_host), web_contents_(web_contents->GetWeakPtr()) {
  protocol::Page::Dispatcher::wire(dispatcher, this);
}

PageHandler::~PageHandler() {
  Disable();
}

void PageHandler::ToggleAdBlocking(bool enabled) {
  if (!web_contents_)
    return;

  // Create the DevtoolsInteractionTracker lazily (note that this call is a
  // no-op if the object was already created).
  subresource_filter::DevtoolsInteractionTracker::CreateForWebContents(
      web_contents_.get());

  subresource_filter::DevtoolsInteractionTracker::FromWebContents(
      web_contents_.get())
      ->ToggleForceActivation(enabled);
}

protocol::Response PageHandler::Enable(
    std::optional<bool> enable_file_chooser_opened_event) {
  enabled_ = true;
  // Do not mark the command as handled. Let it fall through instead, so that
  // the handler in content gets a chance to process the command.
  return protocol::Response::FallThrough();
}

protocol::Response PageHandler::Disable() {
  enabled_ = false;
  ToggleAdBlocking(false /* enable */);
  SetSPCTransactionMode(protocol::Page::SetSPCTransactionMode::ModeEnum::None);
  // Do not mark the command as handled. Let it fall through instead, so that
  // the handler in content gets a chance to process the command.
  return protocol::Response::FallThrough();
}

protocol::Response PageHandler::SetAdBlockingEnabled(bool enabled) {
  if (!enabled_)
    return protocol::Response::ServerError("Page domain is disabled.");
  ToggleAdBlocking(enabled);
  return protocol::Response::Success();
}

protocol::Response PageHandler::SetSPCTransactionMode(
    const protocol::String& mode) {
  if (!web_contents_)
    return protocol::Response::ServerError("No web contents to host a dialog.");

  payments::SPCTransactionMode spc_mode = payments::SPCTransactionMode::kNone;
  if (mode == protocol::Page::SetSPCTransactionMode::ModeEnum::AutoAccept) {
    spc_mode = payments::SPCTransactionMode::kAutoAccept;
  } else if (mode == protocol::Page::SetSPCTransactionMode::ModeEnum::
                         AutoChooseToAuthAnotherWay) {
    spc_mode = payments::SPCTransactionMode::kAutoAuthAnotherWay;
  } else if (mode ==
             protocol::Page::SetSPCTransactionMode::ModeEnum::AutoReject) {
    spc_mode = payments::SPCTransactionMode::kAutoReject;
  } else if (mode ==
             protocol::Page::SetSPCTransactionMode::ModeEnum::AutoOptOut) {
    spc_mode = payments::SPCTransactionMode::kAutoOptOut;
  } else if (mode != protocol::Page::SetSPCTransactionMode::ModeEnum::None) {
    return protocol::Response::ServerError("Unrecognized mode value");
  }

  auto* payment_request_manager =
      payments::PaymentRequestWebContentsManager::GetOrCreateForWebContents(
          web_contents_.get());
  payment_request_manager->SetSPCTransactionMode(spc_mode);
  return protocol::Response::Success();
}

protocol::Response PageHandler::SetRPHRegistrationMode(
    const protocol::String& mode) {
  if (!web_contents_) {
    return protocol::Response::ServerError("No web contents to host a dialog.");
  }

  custom_handlers::RphRegistrationMode rph_mode =
      custom_handlers::RphRegistrationMode::kNone;
  if (mode == protocol::Page::SetRPHRegistrationMode::ModeEnum::AutoAccept) {
    rph_mode = custom_handlers::RphRegistrationMode::kAutoAccept;
  } else if (mode ==
             protocol::Page::SetRPHRegistrationMode::ModeEnum::AutoReject) {
    rph_mode = custom_handlers::RphRegistrationMode::kAutoReject;
  } else if (mode != protocol::Page::SetRPHRegistrationMode::ModeEnum::None) {
    return protocol::Response::ServerError("Unrecognized mode value");
  }

  custom_handlers::ProtocolHandlerRegistry* registry =
      ProtocolHandlerRegistryFactory::GetForBrowserContext(
          web_contents_->GetBrowserContext());
  registry->SetRphRegistrationMode(rph_mode);
  return protocol::Response::Success();
}

void PageHandler::GetInstallabilityErrors(
    std::unique_ptr<GetInstallabilityErrorsCallback> callback) {
  auto errors = std::make_unique<protocol::Array<std::string>>();
  webapps::InstallableManager* manager =
      web_contents_
          ? webapps::InstallableManager::FromWebContents(web_contents_.get())
          : nullptr;
  if (!manager) {
    callback->sendFailure(
        protocol::Response::ServerError("Unable to fetch errors for target"));
    return;
  }
  manager->GetAllErrors(base::BindOnce(&PageHandler::GotInstallabilityErrors,
                                       std::move(callback)));
}

// static
void PageHandler::GotInstallabilityErrors(
    std::unique_ptr<GetInstallabilityErrorsCallback> callback,
    std::vector<content::InstallabilityError> installability_errors) {
  auto result_installability_errors =
      std::make_unique<protocol::Array<protocol::Page::InstallabilityError>>();
  for (const auto& installability_error : installability_errors) {
    auto installability_error_arguments = std::make_unique<
        protocol::Array<protocol::Page::InstallabilityErrorArgument>>();
    for (const auto& error_argument :
         installability_error.installability_error_arguments) {
      installability_error_arguments->emplace_back(
          protocol::Page::InstallabilityErrorArgument::Create()
              .SetName(error_argument.name)
              .SetValue(error_argument.value)
              .Build());
    }
    result_installability_errors->emplace_back(
        protocol::Page::InstallabilityError::Create()
            .SetErrorId(installability_error.error_id)
            .SetErrorArguments(std::move(installability_error_arguments))
            .Build());
  }
  callback->sendSuccess(std::move(result_installability_errors));
}

void PageHandler::GetManifestIcons(
    std::unique_ptr<GetManifestIconsCallback> callback) {
  webapps::InstallableManager* manager =
      web_contents_
          ? webapps::InstallableManager::FromWebContents(web_contents_.get())
          : nullptr;

  if (!manager) {
    callback->sendFailure(
        protocol::Response::ServerError("Unable to fetch icons for target"));
    return;
  }

  manager->GetPrimaryIcon(
      base::BindOnce(&PageHandler::GotManifestIcons, std::move(callback)));
}

void PageHandler::GotManifestIcons(
    std::unique_ptr<GetManifestIconsCallback> callback,
    const SkBitmap* primary_icon) {
  std::optional<protocol::Binary> primaryIconAsBinary;

  if (primary_icon && !primary_icon->empty()) {
    primaryIconAsBinary = protocol::Binary::fromRefCounted(
        gfx::Image::CreateFrom1xBitmap(*primary_icon).As1xPNGBytes());
  }

  callback->sendSuccess(std::move(primaryIconAsBinary));
}

void PageHandler::PrintToPDF(std::optional<bool> landscape,
                             std::optional<bool> display_header_footer,
                             std::optional<bool> print_background,
                             std::optional<double> scale,
                             std::optional<double> paper_width,
                             std::optional<double> paper_height,
                             std::optional<double> margin_top,
                             std::optional<double> margin_bottom,
                             std::optional<double> margin_left,
                             std::optional<double> margin_right,
                             std::optional<protocol::String> page_ranges,
                             std::optional<protocol::String> header_template,
                             std::optional<protocol::String> footer_template,
                             std::optional<bool> prefer_css_page_size,
                             std::optional<protocol::String> transfer_mode,
                             std::optional<bool> generate_tagged_pdf,
                             std::optional<bool> generate_document_outline,
                             std::unique_ptr<PrintToPDFCallback> callback) {
  DCHECK(callback);

#if BUILDFLAG(ENABLE_PRINTING)
  if (!web_contents_) {
    callback->sendFailure(
        protocol::Response::ServerError("No web contents to print"));
    return;
  }

  std::variant<printing::mojom::PrintPagesParamsPtr, std::string>
      print_pages_params = print_to_pdf::GetPrintPagesParams(
          web_contents_->GetPrimaryMainFrame()->GetLastCommittedURL(),
          landscape, display_header_footer, print_background, scale,
          paper_width, paper_height, margin_top, margin_bottom, margin_left,
          margin_right, header_template, footer_template, prefer_css_page_size,
          generate_tagged_pdf, generate_document_outline);
  if (std::holds_alternative<std::string>(print_pages_params)) {
    callback->sendFailure(protocol::Response::InvalidParams(
        std::get<std::string>(print_pages_params)));
    return;
  }

  DCHECK(std::holds_alternative<printing::mojom::PrintPagesParamsPtr>(
      print_pages_params));

  bool return_as_stream =
      transfer_mode.value_or("") ==
      protocol::Page::PrintToPDF::TransferModeEnum::ReturnAsStream;

  // First check if headless printer manager is active and use it if so.
  // Note that headless mode uses alternate print manager that shortcuts
  // most of the regular print manager calls providing only the PrintToPDF
  // functionality.
  if (auto* print_manager = headless::HeadlessPrintManager::FromWebContents(
          web_contents_.get())) {
    print_manager->PrintToPdf(
        web_contents_->GetPrimaryMainFrame(), page_ranges.value_or(""),
        std::move(
            std::get<printing::mojom::PrintPagesParamsPtr>(print_pages_params)),
        base::BindOnce(&PageHandler::OnPDFCreated,
                       weak_ptr_factory_.GetWeakPtr(), return_as_stream,
                       std::move(callback)));
    return;
  }

  // Try the regular print manager. See printing::InitializePrinting()
  // for details.
  if (auto* print_manager =
          ActivePrintManager::FromWebContents(web_contents_.get())) {
    print_manager->PrintToPdf(
        web_contents_->GetPrimaryMainFrame(), page_ranges.value_or(""),
        std::move(
            std::get<printing::mojom::PrintPagesParamsPtr>(print_pages_params)),
        base::BindOnce(&PageHandler::OnPDFCreated,
                       weak_ptr_factory_.GetWeakPtr(), return_as_stream,
                       std::move(callback)));
    return;
  }
#endif  // BUILDFLAG(ENABLE_PRINTING)

  callback->sendFailure(
      protocol::Response::ServerError("Printing is not available"));
}

void PageHandler::GetAppId(std::unique_ptr<GetAppIdCallback> callback) {
  webapps::InstallableManager* manager =
      web_contents_
          ? webapps::InstallableManager::FromWebContents(web_contents_.get())
          : nullptr;

  if (!manager) {
    callback->sendFailure(
        protocol::Response::ServerError("Unable to fetch app id for target"));
    return;
  }

  webapps::InstallableParams params;
  manager->GetData(params, base::BindOnce(&PageHandler::OnDidGetManifest,
                                          weak_ptr_factory_.GetWeakPtr(),
                                          std::move(callback)));
}

void PageHandler::OnDidGetManifest(std::unique_ptr<GetAppIdCallback> callback,
                                   const webapps::InstallableData& data) {
  if (data.manifest_url->is_empty()) {
    callback->sendSuccess(std::nullopt, std::nullopt);
    return;
  }
  // Either both the id and start_url are present, or they are both empty.
  std::string current_app_id_str;
  std::string recommended_manifest_id_path_only;
  if (data.manifest->id.is_valid()) {
    CHECK(data.manifest->start_url.is_valid());
    current_app_id_str = data.manifest->id.spec();
    recommended_manifest_id_path_only =
        web_app::GenerateManifestIdFromStartUrlOnly(data.manifest->start_url)
            .PathForRequest();
  } else {
    CHECK(!data.manifest->start_url.is_valid());
  }
  callback->sendSuccess(current_app_id_str, recommended_manifest_id_path_only);
}

#if BUILDFLAG(ENABLE_PRINTING)
void PageHandler::OnPDFCreated(bool return_as_stream,
                               std::unique_ptr<PrintToPDFCallback> callback,
                               print_to_pdf::PdfPrintResult print_result,
                               scoped_refptr<base::RefCountedMemory> data) {
  if (print_result != print_to_pdf::PdfPrintResult::kPrintSuccess) {
    callback->sendFailure(protocol::Response::ServerError(
        print_to_pdf::PdfPrintResultToString(print_result)));
    return;
  }

  if (return_as_stream) {
    std::string handle = agent_host_->CreateIOStreamFromData(data);
    callback->sendSuccess(protocol::Binary(), handle);
  } else {
    callback->sendSuccess(protocol::Binary::fromRefCounted(data), std::nullopt);
  }
}
#endif  // BUILDFLAG(ENABLE_PRINTING)