blob: 44421c66146f5e63c9fab64e4f91fb2f8c102135 [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/print_to_pdf/pdf_print_manager.h"
#include <utility>
#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
namespace print_to_pdf {
namespace {
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
constexpr char kInvalidUpdatePrintSettingsCall[] =
"Invalid UpdatePrintSettings Call";
constexpr char kInvalidSetupScriptedPrintPreviewCall[] =
"Invalid SetupScriptedPrintPreview Call";
constexpr char kInvalidShowScriptedPrintPreviewCall[] =
"Invalid ShowScriptedPrintPreview Call";
constexpr char kInvalidRequestPrintPreviewCall[] =
"Invalid RequestPrintPreview Call";
constexpr char kInvalidCheckForCancelCall[] = "Invalid CheckForCancel Call";
#endif
#if BUILDFLAG(ENABLE_TAGGED_PDF)
constexpr char kInvalidSetAccessibilityTreeCall[] =
"Invalid SetAccessibilityTree Call";
#endif
} // namespace
PdfPrintManager::PdfPrintManager(content::WebContents* web_contents)
: printing::PrintManager(web_contents),
content::WebContentsUserData<PdfPrintManager>(*web_contents) {}
PdfPrintManager::~PdfPrintManager() = default;
// static
void PdfPrintManager::BindPrintManagerHost(
mojo::PendingAssociatedReceiver<printing::mojom::PrintManagerHost> receiver,
content::RenderFrameHost* rfh) {
auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
if (!web_contents)
return;
auto* print_manager = PdfPrintManager::FromWebContents(web_contents);
if (!print_manager)
return;
print_manager->BindReceiver(std::move(receiver), rfh);
}
// static
std::string PdfPrintManager::PrintResultToString(PrintResult result) {
switch (result) {
case PRINT_SUCCESS:
return std::string(); // no error message
case PRINTING_FAILED:
return "Printing failed";
case INVALID_PRINTER_SETTINGS:
return "Show invalid printer settings error";
case INVALID_MEMORY_HANDLE:
return "Invalid memory handle";
case METAFILE_MAP_ERROR:
return "Map to shared memory error";
case METAFILE_INVALID_HEADER:
return "Invalid metafile header";
case METAFILE_GET_DATA_ERROR:
return "Get data from metafile error";
case SIMULTANEOUS_PRINT_ACTIVE:
return "The previous printing job hasn't finished";
case PAGE_RANGE_SYNTAX_ERROR:
return "Page range syntax error";
case PAGE_COUNT_EXCEEDED:
return "Page range exceeds page count";
default:
NOTREACHED();
return "Unknown PrintResult";
}
}
void PdfPrintManager::PrintToPdf(
content::RenderFrameHost* rfh,
const std::string& page_ranges,
bool ignore_invalid_page_ranges,
printing::mojom::PrintPagesParamsPtr print_pages_params,
PrintToPdfCallback callback) {
DCHECK(callback);
if (callback_) {
std::move(callback).Run(SIMULTANEOUS_PRINT_ACTIVE,
base::MakeRefCounted<base::RefCountedString>());
return;
}
if (!rfh->IsRenderFrameLive()) {
std::move(callback).Run(PRINTING_FAILED,
base::MakeRefCounted<base::RefCountedString>());
return;
}
printing_rfh_ = rfh;
page_ranges_ = page_ranges;
ignore_invalid_page_ranges_ = ignore_invalid_page_ranges;
print_pages_params_ = std::move(print_pages_params);
set_cookie(print_pages_params_->params->document_cookie);
callback_ = std::move(callback);
GetPrintRenderFrame(rfh)->PrintRequestedPages();
}
void PdfPrintManager::GetDefaultPrintSettings(
GetDefaultPrintSettingsCallback callback) {
if (!printing_rfh_) {
DLOG(ERROR) << "Unexpected message received before PrintToPdf is "
"called: GetDefaultPrintSettings";
std::move(callback).Run(printing::mojom::PrintParams::New());
return;
}
std::move(callback).Run(print_pages_params_->params->Clone());
}
void PdfPrintManager::ScriptedPrint(
printing::mojom::ScriptedPrintParamsPtr params,
ScriptedPrintCallback callback) {
auto default_param = printing::mojom::PrintPagesParams::New();
default_param->params = printing::mojom::PrintParams::New();
if (!printing_rfh_) {
DLOG(ERROR) << "Unexpected message received before PrintToPdf is "
"called: ScriptedPrint";
std::move(callback).Run(std::move(default_param));
return;
}
if (params->is_scripted &&
GetCurrentTargetFrame()->IsNestedWithinFencedFrame()) {
DLOG(ERROR) << "Unexpected message received. Script Print is not allowed"
" in a fenced frame.";
std::move(callback).Run(std::move(default_param));
return;
}
absl::variant<printing::PageRanges, PageRangeError> page_ranges =
TextPageRangesToPageRanges(page_ranges_, ignore_invalid_page_ranges_,
params->expected_pages_count);
if (absl::holds_alternative<PageRangeError>(page_ranges)) {
PrintResult print_result;
switch (absl::get<PageRangeError>(page_ranges)) {
case PageRangeError::SYNTAX_ERROR:
print_result = PAGE_RANGE_SYNTAX_ERROR;
break;
case PageRangeError::LIMIT_ERROR:
print_result = PAGE_COUNT_EXCEEDED;
break;
}
ReleaseJob(print_result);
std::move(callback).Run(std::move(default_param));
return;
}
DCHECK(absl::holds_alternative<printing::PageRanges>(page_ranges));
print_pages_params_->pages = printing::PageRange::GetPages(
absl::get<printing::PageRanges>(page_ranges));
std::move(callback).Run(print_pages_params_->Clone());
}
void PdfPrintManager::ShowInvalidPrinterSettingsError() {
ReleaseJob(INVALID_PRINTER_SETTINGS);
}
void PdfPrintManager::PrintingFailed(int32_t cookie) {
ReleaseJob(PRINTING_FAILED);
}
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
void PdfPrintManager::UpdatePrintSettings(
int32_t cookie,
base::Value job_settings,
UpdatePrintSettingsCallback callback) {
// UpdatePrintSettingsCallback() should never be called on
// PdfPrintManager, since it is only triggered by Print Preview.
mojo::ReportBadMessage(kInvalidUpdatePrintSettingsCall);
}
void PdfPrintManager::SetupScriptedPrintPreview(
SetupScriptedPrintPreviewCallback callback) {
// SetupScriptedPrintPreview() should never be called on
// PdfPrintManager, since it is only triggered by Print Preview.
mojo::ReportBadMessage(kInvalidSetupScriptedPrintPreviewCall);
}
void PdfPrintManager::ShowScriptedPrintPreview(bool source_is_modifiable) {
// ShowScriptedPrintPreview() should never be called on
// PdfPrintManager, since it is only triggered by Print Preview.
mojo::ReportBadMessage(kInvalidShowScriptedPrintPreviewCall);
}
void PdfPrintManager::RequestPrintPreview(
printing::mojom::RequestPrintPreviewParamsPtr params) {
// RequestPrintPreview() should never be called on PdfPrintManager,
// since it is only triggered by Print Preview.
mojo::ReportBadMessage(kInvalidRequestPrintPreviewCall);
}
void PdfPrintManager::CheckForCancel(int32_t preview_ui_id,
int32_t request_id,
CheckForCancelCallback callback) {
// CheckForCancel() should never be called on PdfPrintManager, since it
// is only triggered by Print Preview.
mojo::ReportBadMessage(kInvalidCheckForCancelCall);
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
#if BUILDFLAG(ENABLE_TAGGED_PDF)
void PdfPrintManager::SetAccessibilityTree(
int32_t cookie,
const ui::AXTreeUpdate& accessibility_tree) {
// SetAccessibilityTree() should never be called on PdfPrintManager,
// since it is only triggered by Print Preview.
mojo::ReportBadMessage(kInvalidSetAccessibilityTreeCall);
}
#endif
#if defined(OS_ANDROID)
void PdfPrintManager::PdfWritingDone(int page_count) {}
#endif
void PdfPrintManager::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
PrintManager::RenderFrameDeleted(render_frame_host);
if (printing_rfh_ != render_frame_host) {
return;
}
if (callback_) {
std::move(callback_).Run(PRINTING_FAILED,
base::MakeRefCounted<base::RefCountedString>());
}
Reset();
}
void PdfPrintManager::DidPrintDocument(
printing::mojom::DidPrintDocumentParamsPtr params,
DidPrintDocumentCallback callback) {
auto& content = *params->content;
if (!content.metafile_data_region.IsValid()) {
ReleaseJob(INVALID_MEMORY_HANDLE);
std::move(callback).Run(false);
return;
}
base::ReadOnlySharedMemoryMapping map = content.metafile_data_region.Map();
if (!map.IsValid()) {
ReleaseJob(METAFILE_MAP_ERROR);
std::move(callback).Run(false);
return;
}
data_ = std::string(static_cast<const char*>(map.memory()), map.size());
std::move(callback).Run(true);
ReleaseJob(PRINT_SUCCESS);
}
void PdfPrintManager::Reset() {
printing_rfh_ = nullptr;
callback_.Reset();
print_pages_params_.reset();
data_.clear();
}
void PdfPrintManager::ReleaseJob(PrintResult result) {
if (!callback_) {
DLOG(ERROR) << "ReleaseJob is called when callback_ is null. Check whether "
"ReleaseJob is called more than once.";
return;
}
DCHECK(result == PRINT_SUCCESS || data_.empty());
std::move(callback_).Run(result, base::RefCountedString::TakeString(&data_));
// TODO(https://crbug.com/1286556): In theory, this should not be needed. In
// practice, nothing seems to restrict receiving incoming Mojo method calls
// for reporting the printing state to `printing_rfh_`.
//
// This should probably be changed so that the browser pushes endpoints to the
// renderer rather than the renderer connecting on-demand to the browser...
if (printing_rfh_ && printing_rfh_->IsRenderFrameLive()) {
GetPrintRenderFrame(printing_rfh_)->PrintingDone(result == PRINT_SUCCESS);
}
Reset();
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(PdfPrintManager);
} // namespace print_to_pdf