blob: 5acab8e7d18782dc0eee96ca18cc4bd24881be38 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/printing/browser/print_to_pdf/pdf_print_job.h"
#include <variant>
#include "base/functional/bind.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "components/printing/browser/print_composite_client.h"
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
#include "components/printing/common/print.mojom.h"
#include "printing/mojom/print.mojom.h"
#include "printing/page_range.h"
#include "printing/printing_utils.h"
namespace print_to_pdf {
PdfPrintJob::PdfPrintJob(content::WebContents* contents,
content::RenderFrameHost* rfh,
PrintToPdfCallback callback)
: content::WebContentsObserver(contents),
printing_rfh_(rfh),
print_to_pdf_callback_(std::move(callback)) {}
PdfPrintJob::~PdfPrintJob() {
// The callback is supposed to be consumed at this point confirming
// that the job result was reported to the job starter.
DCHECK(!print_to_pdf_callback_);
}
void PdfPrintJob::StartJob(
content::WebContents* contents,
content::RenderFrameHost* rfh,
const mojo::AssociatedRemote<printing::mojom::PrintRenderFrame>& remote,
const std::string& page_ranges,
printing::mojom::PrintPagesParamsPtr print_pages_params,
PrintToPdfCallback callback) {
DCHECK(callback);
if (!rfh->IsRenderFrameLive()) {
std::move(callback).Run(PdfPrintResult::kPrintFailure, nullptr);
return;
}
std::variant<printing::PageRanges, PdfPrintResult> pages =
TextPageRangesToPageRanges(page_ranges);
if (std::holds_alternative<PdfPrintResult>(pages)) {
std::move(callback).Run(std::get<PdfPrintResult>(pages), nullptr);
return;
}
print_pages_params->pages = std::get<printing::PageRanges>(pages);
// Job is self-owned and will delete itself when complete.
auto* job = new PdfPrintJob(contents, rfh, std::move(callback));
remote->PrintWithParams(std::move(print_pages_params),
base::BindOnce(&PdfPrintJob::OnDidPrintWithParams,
job->weak_ptr_factory_.GetWeakPtr()));
}
void PdfPrintJob::OnDidPrintWithParams(
printing::mojom::PrintRenderFrame::PrintWithParamsResult result) {
if (!result.has_value()) {
switch (result.error()) {
case printing::mojom::PrintFailureReason::kGeneralFailure:
FailJob(PdfPrintResult::kPrintFailure);
return;
case printing::mojom::PrintFailureReason::kInvalidPageRange:
FailJob(PdfPrintResult::kPageCountExceeded);
return;
case printing::mojom::PrintFailureReason::kPrintingInProgress:
FailJob(PdfPrintResult::kPrintingInProgress);
return;
}
}
const printing::mojom::DidPrintDocumentParamsPtr& params =
result.value()->params;
const auto& content = *params->content;
const auto& region = content.metafile_data_region;
if (!region.IsValid()) {
FailJob(PdfPrintResult::kInvalidSharedMemoryRegion);
return;
}
// If the printed data already looks like a PDF, report it now.
if (printing::LooksLikePdf(region.Map().GetMemoryAsSpan<const uint8_t>())) {
ReportMemoryRegion(region);
return;
}
// Otherwise assume this is a composite document and invoke compositor.
printing::PrintCompositeClient::FromWebContents(web_contents())
->CompositeDocument(
params->document_cookie, printing_rfh_, content,
result.value()->accessibility_tree,
result.value()->generate_document_outline,
printing::mojom::PrintCompositor::DocumentType::kPDF,
base::BindOnce(&PdfPrintJob::OnCompositeDocumentToPdfDone,
weak_ptr_factory_.GetWeakPtr()));
}
void PdfPrintJob::OnCompositeDocumentToPdfDone(
printing::mojom::PrintCompositor::Status status,
base::ReadOnlySharedMemoryRegion region) {
if (status != printing::mojom::PrintCompositor::Status::kSuccess) {
DLOG(ERROR) << "Compositing pdf failed with error " << status;
FailJob(PdfPrintResult::kPrintFailure);
return;
}
if (!region.IsValid()) {
FailJob(PdfPrintResult::kInvalidSharedMemoryRegion);
return;
}
ReportMemoryRegion(region);
}
void PdfPrintJob::ReportMemoryRegion(
const base::ReadOnlySharedMemoryRegion& region) {
DCHECK(region.IsValid());
DCHECK(printing::LooksLikePdf(region.Map().GetMemoryAsSpan<const uint8_t>()));
base::ReadOnlySharedMemoryMapping mapping = region.Map();
if (!mapping.IsValid()) {
FailJob(PdfPrintResult::kInvalidSharedMemoryMapping);
return;
}
std::string data =
std::string(static_cast<const char*>(mapping.memory()), mapping.size());
std::move(print_to_pdf_callback_)
.Run(PdfPrintResult::kPrintSuccess,
base::MakeRefCounted<base::RefCountedString>(std::move(data)));
delete this;
}
void PdfPrintJob::FailJob(PdfPrintResult result) {
DCHECK_NE(result, PdfPrintResult::kPrintSuccess);
DCHECK(print_to_pdf_callback_);
std::move(print_to_pdf_callback_).Run(result, nullptr);
delete this;
}
void PdfPrintJob::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
if (render_frame_host != printing_rfh_)
return;
FailJob(PdfPrintResult::kPrintFailure);
}
} // namespace print_to_pdf