blob: 65d06971212dfafd2edb6d52e22844bb07136a5d [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/printing/browser/headless/headless_print_manager.h"
#include <utility>
#include "base/bind.h"
#include "build/build_config.h"
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
#include "printing/mojom/print.mojom.h"
#include "printing/page_range.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
#include "mojo/public/cpp/bindings/message.h"
#endif
using print_to_pdf::PdfPrintResult;
namespace headless {
namespace {
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
constexpr char kUnexpectedPrintManagerCall[] = "Unexpected Print Manager call";
#endif
} // namespace
HeadlessPrintManager::HeadlessPrintManager(content::WebContents* web_contents)
: printing::PrintManager(web_contents),
content::WebContentsUserData<HeadlessPrintManager>(*web_contents) {}
HeadlessPrintManager::~HeadlessPrintManager() = default;
// static
void HeadlessPrintManager::BindPrintManagerHost(
mojo::PendingAssociatedReceiver<printing::mojom::PrintManagerHost> receiver,
content::RenderFrameHost* rfh) {
auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
if (!web_contents)
return;
auto* print_manager = HeadlessPrintManager::FromWebContents(web_contents);
if (!print_manager)
return;
print_manager->BindReceiver(std::move(receiver), rfh);
}
void HeadlessPrintManager::PrintToPdf(
content::RenderFrameHost* rfh,
const std::string& page_ranges,
printing::mojom::PrintPagesParamsPtr print_pages_params,
PrintToPdfCallback callback) {
DCHECK(callback);
if (print_to_pdf_callback_) {
std::move(callback).Run(PdfPrintResult::kSimultaneousPrintActive,
base::MakeRefCounted<base::RefCountedString>());
return;
}
if (!rfh->IsRenderFrameLive()) {
std::move(callback).Run(PdfPrintResult::kPrintFailure,
base::MakeRefCounted<base::RefCountedString>());
return;
}
absl::variant<printing::PageRanges, PdfPrintResult> parsed_ranges =
print_to_pdf::TextPageRangesToPageRanges(page_ranges);
if (absl::holds_alternative<PdfPrintResult>(parsed_ranges)) {
DCHECK_NE(absl::get<PdfPrintResult>(parsed_ranges),
PdfPrintResult::kPrintSuccess);
std::move(callback).Run(absl::get<PdfPrintResult>(parsed_ranges),
base::MakeRefCounted<base::RefCountedString>());
return;
}
printing_rfh_ = rfh;
print_pages_params->pages = absl::get<printing::PageRanges>(parsed_ranges);
print_to_pdf_callback_ = std::move(callback);
// There is no need for a weak pointer here since the mojo proxy is held
// in the base class. If we're gone, mojo will discard the callback.
GetPrintRenderFrame(rfh)->PrintWithParams(
std::move(print_pages_params),
base::BindOnce(&HeadlessPrintManager::OnDidPrintWithParams,
base::Unretained(this)));
}
void HeadlessPrintManager::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
PrintManager::RenderFrameDeleted(render_frame_host);
if (printing_rfh_ != render_frame_host)
return;
FailJob(PdfPrintResult::kPrintFailure);
}
void HeadlessPrintManager::GetDefaultPrintSettings(
GetDefaultPrintSettingsCallback callback) {
DLOG(ERROR) << "Scripted print is not supported";
std::move(callback).Run(printing::mojom::PrintParams::New());
}
void HeadlessPrintManager::ScriptedPrint(
printing::mojom::ScriptedPrintParamsPtr params,
ScriptedPrintCallback callback) {
auto default_param = printing::mojom::PrintPagesParams::New();
default_param->params = printing::mojom::PrintParams::New();
DLOG(ERROR) << "Scripted print is not supported";
std::move(callback).Run(std::move(default_param));
}
void HeadlessPrintManager::ShowInvalidPrinterSettingsError() {
FailJob(PdfPrintResult::kInvalidPrinterSettings);
}
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
void HeadlessPrintManager::UpdatePrintSettings(
int32_t cookie,
base::Value::Dict job_settings,
UpdatePrintSettingsCallback callback) {
mojo::ReportBadMessage(kUnexpectedPrintManagerCall);
}
void HeadlessPrintManager::SetupScriptedPrintPreview(
SetupScriptedPrintPreviewCallback callback) {
mojo::ReportBadMessage(kUnexpectedPrintManagerCall);
}
void HeadlessPrintManager::ShowScriptedPrintPreview(bool source_is_modifiable) {
mojo::ReportBadMessage(kUnexpectedPrintManagerCall);
}
void HeadlessPrintManager::RequestPrintPreview(
printing::mojom::RequestPrintPreviewParamsPtr params) {
mojo::ReportBadMessage(kUnexpectedPrintManagerCall);
}
void HeadlessPrintManager::CheckForCancel(int32_t preview_ui_id,
int32_t request_id,
CheckForCancelCallback callback) {
mojo::ReportBadMessage(kUnexpectedPrintManagerCall);
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
#if BUILDFLAG(ENABLE_TAGGED_PDF)
void HeadlessPrintManager::SetAccessibilityTree(
int32_t cookie,
const ui::AXTreeUpdate& accessibility_tree) {
mojo::ReportBadMessage(kUnexpectedPrintManagerCall);
}
#endif
#if BUILDFLAG(IS_ANDROID)
void HeadlessPrintManager::PdfWritingDone(int page_count) {}
#endif
void HeadlessPrintManager::OnDidPrintWithParams(
printing::mojom::PrintWithParamsResultPtr result) {
if (result->is_failure_reason()) {
switch (result->get_failure_reason()) {
case printing::mojom::PrintFailureReason::kGeneralFailure:
FailJob(PdfPrintResult::kPrintFailure);
return;
case printing::mojom::PrintFailureReason::kInvalidPageRange:
FailJob(PdfPrintResult::kPageCountExceeded);
return;
}
}
auto& content = *result->get_params()->content;
if (!content.metafile_data_region.IsValid()) {
FailJob(PdfPrintResult::kInvalidMemoryHandle);
return;
}
base::ReadOnlySharedMemoryMapping map = content.metafile_data_region.Map();
if (!map.IsValid()) {
FailJob(PdfPrintResult::kMetafileMapError);
return;
}
std::string data =
std::string(static_cast<const char*>(map.memory()), map.size());
std::move(print_to_pdf_callback_)
.Run(PdfPrintResult::kPrintSuccess,
base::RefCountedString::TakeString(&data));
Reset();
}
void HeadlessPrintManager::FailJob(PdfPrintResult result) {
DCHECK_NE(result, PdfPrintResult::kPrintSuccess);
if (print_to_pdf_callback_) {
std::move(print_to_pdf_callback_)
.Run(result, base::MakeRefCounted<base::RefCountedString>());
}
Reset();
}
void HeadlessPrintManager::Reset() {
printing_rfh_ = nullptr;
// The callback is supposed to be consumed at this point meaning we
// reported results to the PrintToPdf() caller.
CHECK(!print_to_pdf_callback_);
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(HeadlessPrintManager);
} // namespace headless