blob: 88bad6ab74363a84393cf14580bd4b5a86c21dc2 [file] [log] [blame]
// Copyright 2022 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/print_to_pdf/pdf_print_job.h"
#include "base/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"
#include "third_party/abseil-cpp/absl/types/variant.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 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;
}
absl::variant<printing::PageRanges, PdfPrintResult> pages =
print_to_pdf::TextPageRangesToPageRanges(page_ranges);
if (absl::holds_alternative<PdfPrintResult>(pages)) {
std::move(callback).Run(absl::get<PdfPrintResult>(pages), nullptr);
return;
}
print_pages_params->pages = absl::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::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;
}
}
printing::mojom::DidPrintDocumentParamsPtr& params = result->get_params();
auto& content = *params->content;
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<char>())) {
ReportMemoryRegion(region);
return;
}
// Otherwise assume this is a composite document and invoke compositor.
printing::PrintCompositeClient::FromWebContents(web_contents())
->DoCompositeDocumentToPdf(
params->document_cookie, printing_rfh_, content,
base::BindOnce(&PdfPrintJob::OnCompositeDocumentToPdfDone,
weak_ptr_factory_.GetWeakPtr(), params->page_size,
params->content_area, params->physical_offsets));
}
void PdfPrintJob::OnCompositeDocumentToPdfDone(
const gfx::Size& page_size,
const gfx::Rect& content_area,
const gfx::Point& physical_offsets,
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<char>()));
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::RefCountedString::TakeString(&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