Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: enable OOPIF printing to PDF #36095

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions chromium_src/BUILD.gn
Expand Up @@ -232,6 +232,8 @@ static_library("chrome") {
"//chrome/browser/printing/printing_service.h",
"//components/printing/browser/print_to_pdf/pdf_print_job.cc",
"//components/printing/browser/print_to_pdf/pdf_print_job.h",
"//components/printing/browser/print_to_pdf/pdf_print_result.cc",
"//components/printing/browser/print_to_pdf/pdf_print_result.h",
"//components/printing/browser/print_to_pdf/pdf_print_utils.cc",
"//components/printing/browser/print_to_pdf/pdf_print_utils.h",
]
Expand Down
7 changes: 4 additions & 3 deletions shell/browser/api/electron_api_web_contents.cc
Expand Up @@ -172,6 +172,7 @@
#if BUILDFLAG(ENABLE_PRINTING)
#include "chrome/browser/printing/print_view_manager_base.h"
#include "components/printing/browser/print_manager_utils.h"
#include "components/printing/browser/print_to_pdf/pdf_print_result.h"
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
#include "printing/backend/print_backend.h" // nogncheck
#include "printing/mojom/print.mojom.h" // nogncheck
Expand Down Expand Up @@ -2900,12 +2901,12 @@ v8::Local<v8::Promise> WebContents::PrintToPDF(const base::Value& settings) {

void WebContents::OnPDFCreated(
gin_helper::Promise<v8::Local<v8::Value>> promise,
PrintViewManagerElectron::PrintResult print_result,
print_to_pdf::PdfPrintResult print_result,
scoped_refptr<base::RefCountedMemory> data) {
if (print_result != PrintViewManagerElectron::PrintResult::kPrintSuccess) {
if (print_result != print_to_pdf::PdfPrintResult::kPrintSuccess) {
promise.RejectWithErrorMessage(
"Failed to generate PDF: " +
PrintViewManagerElectron::PrintResultToString(print_result));
print_to_pdf::PdfPrintResultToString(print_result));
return;
}

Expand Down
3 changes: 2 additions & 1 deletion shell/browser/api/electron_api_web_contents.h
Expand Up @@ -46,6 +46,7 @@
#include "ui/gfx/image/image.h"

#if BUILDFLAG(ENABLE_PRINTING)
#include "components/printing/browser/print_to_pdf/pdf_print_result.h"
#include "shell/browser/printing/print_view_manager_electron.h"
#endif

Expand Down Expand Up @@ -227,7 +228,7 @@ class WebContents : public ExclusiveAccessContext,
// Print current page as PDF.
v8::Local<v8::Promise> PrintToPDF(const base::Value& settings);
void OnPDFCreated(gin_helper::Promise<v8::Local<v8::Value>> promise,
PrintViewManagerElectron::PrintResult print_result,
print_to_pdf::PdfPrintResult print_result,
scoped_refptr<base::RefCountedMemory> data);
#endif

Expand Down
174 changes: 25 additions & 149 deletions shell/browser/printing/print_view_manager_electron.cc
Expand Up @@ -60,124 +60,36 @@ void PrintViewManagerElectron::BindPrintManagerHost(
print_manager->BindReceiver(std::move(receiver), rfh);
}

// static
std::string PrintViewManagerElectron::PrintResultToString(PrintResult result) {
switch (result) {
case kPrintSuccess:
return std::string(); // no error message
case kPrintFailure:
return "Printing failed";
case kInvalidPrinterSettings:
return "Show invalid printer settings error";
case kInvalidMemoryHandle:
return "Invalid memory handle";
case kMetafileMapError:
return "Map to shared memory error";
case kMetafileInvalidHeader:
return "Invalid metafile header";
case kMetafileGetDataError:
return "Get data from metafile error";
case kSimultaneousPrintActive:
return "The previous printing job hasn't finished";
case kPageRangeSyntaxError:
return "Page range syntax error";
case kPageRangeInvalidRange:
return "Page range is invalid (start > end)";
case kPageCountExceeded:
return "Page range exceeds page count";
case kPrintingInProgress:
return "Page is already being printed";
default:
NOTREACHED();
return "Unknown PrintResult";
}
void PrintViewManagerElectron::DidPrintToPdf(
int cookie,
PrintToPdfCallback callback,
print_to_pdf::PdfPrintResult result,
scoped_refptr<base::RefCountedMemory> memory) {
base::Erase(pdf_jobs_, cookie);
std::move(callback).Run(result, memory);
}

void PrintViewManagerElectron::PrintToPdf(
content::RenderFrameHost* rfh,
const std::string& page_ranges,
printing::mojom::PrintPagesParamsPtr print_pages_params,
PrintToPDFCallback callback) {
DCHECK(callback);

if (callback_) {
std::move(callback).Run(kSimultaneousPrintActive,
base::MakeRefCounted<base::RefCountedString>());
return;
}

if (!rfh->IsRenderFrameLive()) {
std::move(callback).Run(kPrintFailure,
base::MakeRefCounted<base::RefCountedString>());
return;
}

absl::variant<printing::PageRanges, print_to_pdf::PdfPrintResult>
parsed_ranges = print_to_pdf::TextPageRangesToPageRanges(page_ranges);
if (absl::holds_alternative<print_to_pdf::PdfPrintResult>(parsed_ranges)) {
DCHECK_NE(absl::get<print_to_pdf::PdfPrintResult>(parsed_ranges),
print_to_pdf::PdfPrintResult::kPrintSuccess);
std::move(callback).Run(
static_cast<PrintResult>(
absl::get<print_to_pdf::PdfPrintResult>(parsed_ranges)),
base::MakeRefCounted<base::RefCountedString>());
return;
}

printing_rfh_ = rfh;
print_pages_params->pages = absl::get<printing::PageRanges>(parsed_ranges);
headless_jobs_.emplace_back(print_pages_params->params->document_cookie);
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(
PrintToPdfCallback callback) {
// Store cookie in order to track job uniqueness and differentiate
// between regular and headless print jobs.
int cookie = print_pages_params->params->document_cookie;
pdf_jobs_.emplace_back(cookie);

print_to_pdf::PdfPrintJob::StartJob(
web_contents(), rfh, GetPrintRenderFrame(rfh), page_ranges,
std::move(print_pages_params),
base::BindOnce(&PrintViewManagerElectron::OnDidPrintWithParams,
base::Unretained(this)));
}

void PrintViewManagerElectron::OnDidPrintWithParams(
printing::mojom::PrintWithParamsResultPtr result) {
if (result->is_failure_reason()) {
switch (result->get_failure_reason()) {
case printing::mojom::PrintFailureReason::kGeneralFailure:
FailJob(kPrintFailure);
return;
case printing::mojom::PrintFailureReason::kInvalidPageRange:
FailJob(kPageCountExceeded);
return;
case printing::mojom::PrintFailureReason::kPrintingInProgress:
FailJob(kPrintingInProgress);
return;
}
}

printing::mojom::DidPrintDocumentParamsPtr& params = result->get_params();

auto& content = *params->content;
if (!content.metafile_data_region.IsValid()) {
FailJob(kInvalidMemoryHandle);
return;
}

base::ReadOnlySharedMemoryMapping map = content.metafile_data_region.Map();
if (!map.IsValid()) {
FailJob(kMetafileMapError);
return;
}

std::string data =
std::string(static_cast<const char*>(map.memory()), map.size());
std::move(callback_).Run(kPrintSuccess,
base::RefCountedString::TakeString(&data));
base::Erase(headless_jobs_, params->document_cookie);
Reset();
base::BindOnce(&PrintViewManagerElectron::DidPrintToPdf,
weak_factory_.GetWeakPtr(), cookie, std::move(callback)));
}

void PrintViewManagerElectron::GetDefaultPrintSettings(
GetDefaultPrintSettingsCallback callback) {
if (printing_rfh_) {
// This isn't ideal, but we're not able to access the document cookie here.
if (pdf_jobs_.size() > 0) {
LOG(ERROR) << "Scripted print is not supported";
std::move(callback).Run(printing::mojom::PrintParams::New());
} else {
Expand All @@ -188,9 +100,8 @@ void PrintViewManagerElectron::GetDefaultPrintSettings(
void PrintViewManagerElectron::ScriptedPrint(
printing::mojom::ScriptedPrintParamsPtr params,
ScriptedPrintCallback callback) {
auto entry =
std::find(headless_jobs_.begin(), headless_jobs_.end(), params->cookie);
if (entry == headless_jobs_.end()) {
auto entry = std::find(pdf_jobs_.begin(), pdf_jobs_.end(), params->cookie);
if (entry == pdf_jobs_.end()) {
PrintViewManagerBase::ScriptedPrint(std::move(params), std::move(callback));
return;
}
Expand All @@ -201,22 +112,13 @@ void PrintViewManagerElectron::ScriptedPrint(
std::move(callback).Run(std::move(default_param), /*cancelled*/ false);
}

void PrintViewManagerElectron::ShowInvalidPrinterSettingsError() {
if (headless_jobs_.size() == 0) {
PrintViewManagerBase::ShowInvalidPrinterSettingsError();
return;
}

FailJob(kInvalidPrinterSettings);
}

#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
void PrintViewManagerElectron::UpdatePrintSettings(
int32_t cookie,
base::Value::Dict job_settings,
UpdatePrintSettingsCallback callback) {
auto entry = std::find(headless_jobs_.begin(), headless_jobs_.end(), cookie);
if (entry == headless_jobs_.end()) {
auto entry = std::find(pdf_jobs_.begin(), pdf_jobs_.end(), cookie);
if (entry == pdf_jobs_.end()) {
PrintViewManagerBase::UpdatePrintSettings(cookie, std::move(job_settings),
std::move(callback));
return;
Expand Down Expand Up @@ -247,40 +149,14 @@ void PrintViewManagerElectron::CheckForCancel(int32_t preview_ui_id,
}
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)

void PrintViewManagerElectron::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
PrintViewManagerBase::RenderFrameDeleted(render_frame_host);

if (printing_rfh_ != render_frame_host)
return;

FailJob(kPrintFailure);
}

void PrintViewManagerElectron::DidGetPrintedPagesCount(int32_t cookie,
uint32_t number_pages) {
auto entry = std::find(headless_jobs_.begin(), headless_jobs_.end(), cookie);
if (entry == headless_jobs_.end()) {
auto entry = std::find(pdf_jobs_.begin(), pdf_jobs_.end(), cookie);
if (entry == pdf_jobs_.end()) {
PrintViewManagerBase::DidGetPrintedPagesCount(cookie, number_pages);
}
}

void PrintViewManagerElectron::Reset() {
printing_rfh_ = nullptr;
callback_.Reset();
data_.clear();
}

void PrintViewManagerElectron::FailJob(PrintResult result) {
DCHECK_NE(result, kPrintSuccess);
if (callback_) {
std::move(callback_).Run(result,
base::MakeRefCounted<base::RefCountedString>());
}

Reset();
}

WEB_CONTENTS_USER_DATA_KEY_IMPL(PrintViewManagerElectron);

} // namespace electron
45 changes: 10 additions & 35 deletions shell/browser/printing/print_view_manager_electron.h
Expand Up @@ -13,6 +13,7 @@
#include "base/memory/ref_counted_memory.h"
#include "build/build_config.h"
#include "chrome/browser/printing/print_view_manager_base.h"
#include "components/printing/browser/print_to_pdf/pdf_print_job.h"
#include "components/printing/common/print.mojom.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents_observer.h"
Expand All @@ -21,29 +22,12 @@

namespace electron {

using PrintToPdfCallback = print_to_pdf::PdfPrintJob::PrintToPdfCallback;

class PrintViewManagerElectron
: public printing::PrintViewManagerBase,
public content::WebContentsUserData<PrintViewManagerElectron> {
public:
enum PrintResult {
kPrintSuccess,
kPrintFailure,
kInvalidPrinterSettings,
kInvalidMemoryHandle,
kMetafileMapError,
kMetafileInvalidHeader,
kMetafileGetDataError,
kSimultaneousPrintActive,
kPageRangeSyntaxError,
kPageRangeInvalidRange,
kPageCountExceeded,
kPrintingInProgress
};

using PrintToPDFCallback =
base::OnceCallback<void(PrintResult,
scoped_refptr<base::RefCountedMemory>)>;

~PrintViewManagerElectron() override;

PrintViewManagerElectron(const PrintViewManagerElectron&) = delete;
Expand All @@ -54,30 +38,26 @@ class PrintViewManagerElectron
receiver,
content::RenderFrameHost* rfh);

static std::string PrintResultToString(PrintResult result);

void DidPrintToPdf(int cookie,
PrintToPdfCallback callback,
print_to_pdf::PdfPrintResult result,
scoped_refptr<base::RefCountedMemory> memory);
void PrintToPdf(content::RenderFrameHost* rfh,
const std::string& page_ranges,
printing::mojom::PrintPagesParamsPtr print_page_params,
PrintToPDFCallback callback);
PrintToPdfCallback callback);

private:
friend class content::WebContentsUserData<PrintViewManagerElectron>;

explicit PrintViewManagerElectron(content::WebContents* web_contents);

void OnDidPrintWithParams(printing::mojom::PrintWithParamsResultPtr result);

// WebContentsObserver overrides (via PrintManager):
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;

// printing::mojom::PrintManagerHost:
void DidGetPrintedPagesCount(int32_t cookie, uint32_t number_pages) override;
void GetDefaultPrintSettings(
GetDefaultPrintSettingsCallback callback) override;
void ScriptedPrint(printing::mojom::ScriptedPrintParamsPtr params,
ScriptedPrintCallback callback) override;
void ShowInvalidPrinterSettingsError() override;
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
void UpdatePrintSettings(int32_t cookie,
base::Value::Dict job_settings,
Expand All @@ -91,14 +71,9 @@ class PrintViewManagerElectron
int32_t request_id,
CheckForCancelCallback callback) override;
#endif
std::vector<int32_t> pdf_jobs_;

void FailJob(PrintResult result);
void Reset();

raw_ptr<content::RenderFrameHost> printing_rfh_ = nullptr;
PrintToPDFCallback callback_;
std::string data_;
std::vector<int32_t> headless_jobs_;
base::WeakPtrFactory<PrintViewManagerElectron> weak_factory_{this};

WEB_CONTENTS_USER_DATA_KEY_DECL();
};
Expand Down