refactor: match upstream print preview handling (#24452)

This commit is contained in:
Shelley Vohr
2020-07-08 12:29:50 -07:00
committed by GitHub
parent 68447ba08a
commit 004e29ad33
7 changed files with 131 additions and 37 deletions

View File

@@ -1371,6 +1371,8 @@ An example of `webContents.printToPDF`:
```javascript
const { BrowserWindow } = require('electron')
const fs = require('fs')
const path = require('path')
const os = require('os')
let win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL('http://github.com')
@@ -1378,12 +1380,13 @@ win.loadURL('http://github.com')
win.webContents.on('did-finish-load', () => {
// Use default printing options
win.webContents.printToPDF({}).then(data => {
fs.writeFile('/tmp/print.pdf', data, (error) => {
const pdfPath = path.join(os.homedir(), 'Desktop', 'temp.pdf')
fs.writeFile(pdfPath, data, (error) => {
if (error) throw error
console.log('Write PDF successfully.')
console.log(`Wrote PDF successfully to ${pdfPath}`)
})
}).catch(error => {
console.log(error)
console.log(`Failed to write PDF to ${pdfPath}: `, error)
})
})
```

View File

@@ -215,7 +215,9 @@ WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (code, h
};
// Translate the options of printToPDF.
WebContents.prototype.printToPDF = function (options) {
let pendingPromise;
WebContents.prototype.printToPDF = async function (options) {
const printSettings = {
...defaultPrintingSetting,
requestID: getNextId()
@@ -341,7 +343,12 @@ WebContents.prototype.printToPDF = function (options) {
// PrinterType enum from //printing/print_job_constants.h
printSettings.printerType = 2;
if (this._printToPDF) {
return this._printToPDF(printSettings);
if (pendingPromise) {
pendingPromise = pendingPromise.then(() => this._printToPDF(printSettings));
} else {
pendingPromise = this._printToPDF(printSettings);
}
return pendingPromise;
} else {
const error = new Error('Printing feature is disabled');
return Promise.reject(error);

View File

@@ -83,7 +83,6 @@ delay_lock_the_protocol_scheme_registry.patch
gpu_notify_when_dxdiag_request_fails.patch
feat_allow_embedders_to_add_observers_on_created_hunspell.patch
feat_add_onclose_to_messageport.patch
fix_account_for_print_preview_disabled_when_printing_to_pdf.patch
web_contents.patch
ui_gtk_public_header.patch
refactor_expose_cursor_changes_to_the_webcontentsobserver.patch

View File

@@ -1,23 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andy Locascio <andy@slack-corp.com>
Date: Thu, 2 Apr 2020 15:05:00 -0700
Subject: fix: account for print preview disabled when printing to pdf
Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2068936
Avoid an optimization introduced in the above CL. In my limited
understanding, it seems like we slightly misuse the print preview API
and this is fallout from using it in a way the code doesn't expect.
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index fc39f8874402904021e804a5b7fc754709f25353..40c80983f0d2d45058ee49a3542825d3fa822a8b 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -1491,6 +1491,7 @@ PrintRenderFrameHelper::CreatePreviewDocument() {
const std::vector<int>& pages = print_pages_params_->pages;
bool require_document_metafile =
+ !g_is_preview_enabled ||
print_renderer_ ||
print_params.printed_doc_type != mojom::SkiaDocumentType::kMSKP;
if (!print_preview_context_.CreatePreviewDocument(

View File

@@ -67,6 +67,9 @@ bool PrintPreviewMessageHandler::OnMessageReceived(
render_frame_host)
IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting,
OnMetafileReadyForPrinting)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidPreviewPage, OnDidPreviewPage)
IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrepareDocumentForPreview,
OnDidPrepareForDocumentToPdf)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
@@ -79,13 +82,14 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
// Always try to stop the worker.
StopWorker(params.document_cookie);
const base::ReadOnlySharedMemoryRegion& metafile =
params.content->metafile_data_region;
if (!metafile.IsValid() || params.expected_pages_count <= 0) {
if (params.expected_pages_count == 0) {
RejectPromise(ids.request_id);
return;
}
const base::ReadOnlySharedMemoryRegion& metafile =
params.content->metafile_data_region;
if (printing::IsOopifEnabled()) {
auto* client =
printing::PrintCompositeClient::FromWebContents(web_contents());
@@ -94,8 +98,9 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
auto callback = base::BindOnce(
&PrintPreviewMessageHandler::OnCompositeDocumentToPdfDone,
weak_ptr_factory_.GetWeakPtr(), ids);
client->DoCompositeDocumentToPdf(
params.document_cookie, render_frame_host, *(params.content),
client->DoCompleteDocumentToPdf(
params.document_cookie, params.expected_pages_count,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
std::move(callback),
printing::mojom::PrintCompositor::Status::kCompositingFailure,
@@ -107,6 +112,37 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
}
}
void PrintPreviewMessageHandler::OnPrepareForDocumentToPdfDone(
const PrintHostMsg_PreviewIds& ids,
printing::mojom::PrintCompositor::Status status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != printing::mojom::PrintCompositor::Status::kSuccess) {
LOG(ERROR) << "Preparing document for pdf failed with error " << status;
}
}
void PrintPreviewMessageHandler::OnDidPrepareForDocumentToPdf(
content::RenderFrameHost* render_frame_host,
int document_cookie,
const PrintHostMsg_PreviewIds& ids) {
if (printing::IsOopifEnabled()) {
auto* client =
printing::PrintCompositeClient::FromWebContents(web_contents());
DCHECK(client);
if (client->GetIsDocumentConcurrentlyComposited(document_cookie))
return;
client->DoPrepareForDocumentToPdf(
document_cookie, render_frame_host,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(
&PrintPreviewMessageHandler::OnPrepareForDocumentToPdfDone,
weak_ptr_factory_.GetWeakPtr(), ids),
printing::mojom::PrintCompositor::Status::kCompositingFailure));
}
}
void PrintPreviewMessageHandler::OnCompositeDocumentToPdfDone(
const PrintHostMsg_PreviewIds& ids,
printing::mojom::PrintCompositor::Status status,
@@ -114,7 +150,7 @@ void PrintPreviewMessageHandler::OnCompositeDocumentToPdfDone(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != printing::mojom::PrintCompositor::Status::kSuccess) {
DLOG(ERROR) << "Compositing pdf failed with error " << status;
LOG(ERROR) << "Compositing pdf failed with error " << status;
RejectPromise(ids.request_id);
return;
}
@@ -124,6 +160,49 @@ void PrintPreviewMessageHandler::OnCompositeDocumentToPdfDone(
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region));
}
void PrintPreviewMessageHandler::OnCompositePdfPageDone(
int page_number,
int document_cookie,
const PrintHostMsg_PreviewIds& ids,
printing::mojom::PrintCompositor::Status status,
base::ReadOnlySharedMemoryRegion region) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != printing::mojom::PrintCompositor::Status::kSuccess) {
LOG(ERROR) << "Compositing pdf failed on page: " << page_number
<< " with error: " << status;
}
}
void PrintPreviewMessageHandler::OnDidPreviewPage(
content::RenderFrameHost* render_frame_host,
const printing::mojom::DidPreviewPageParams& params,
const PrintHostMsg_PreviewIds& ids) {
int page_number = params.page_number;
const printing::mojom::DidPrintContentParams& content = *params.content;
if (page_number < printing::FIRST_PAGE_INDEX ||
!content.metafile_data_region.IsValid()) {
RejectPromise(ids.request_id);
return;
}
if (printing::IsOopifEnabled()) {
auto* client =
printing::PrintCompositeClient::FromWebContents(web_contents());
DCHECK(client);
// Use utility process to convert skia metafile to pdf.
client->DoCompositePageToPdf(
params.document_cookie, render_frame_host, content,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfPageDone,
weak_ptr_factory_.GetWeakPtr(), page_number,
params.document_cookie, ids),
printing::mojom::PrintCompositor::Status::kCompositingFailure,
base::ReadOnlySharedMemoryRegion()));
}
}
void PrintPreviewMessageHandler::PrintPreviewFailed(int32_t document_cookie,
int32_t request_id) {
StopWorker(document_cookie);

View File

@@ -18,6 +18,8 @@
#include "shell/common/gin_helper/promise.h"
#include "v8/include/v8.h"
struct PrintHostMsg_DidPreviewDocument_Params;
struct PrintHostMsg_DidPreviewPage_Params;
struct PrintHostMsg_PreviewIds;
namespace content {
@@ -55,6 +57,20 @@ class PrintPreviewMessageHandler
const PrintHostMsg_PreviewIds& ids,
printing::mojom::PrintCompositor::Status status,
base::ReadOnlySharedMemoryRegion region);
void OnPrepareForDocumentToPdfDone(
const PrintHostMsg_PreviewIds& ids,
printing::mojom::PrintCompositor::Status status);
void OnDidPrepareForDocumentToPdf(content::RenderFrameHost* render_frame_host,
int document_cookie,
const PrintHostMsg_PreviewIds& ids);
void OnCompositePdfPageDone(int page_number,
int document_cookie,
const PrintHostMsg_PreviewIds& ids,
printing::mojom::PrintCompositor::Status status,
base::ReadOnlySharedMemoryRegion region);
void OnDidPreviewPage(content::RenderFrameHost* render_frame_host,
const printing::mojom::DidPreviewPageParams& params,
const PrintHostMsg_PreviewIds& ids);
// printing::mojo::PrintPreviewUI:
void SetOptionsFromDocument(

View File

@@ -1662,16 +1662,29 @@ describe('webContents module', () => {
expect(width).to.be.greaterThan(height);
});
it('does not crash when called multiple times', async () => {
it('does not crash when called multiple times in parallel', async () => {
const promises = [];
for (let i = 0; i < 2; i++) {
for (let i = 0; i < 3; i++) {
promises.push(w.webContents.printToPDF({}));
}
const results = await Promise.all(promises);
for (const data of results) {
expect(data).to.be.an.instanceof(Buffer).that.is.not.empty();
}
});
it('does not crash when called multiple times in sequence', async () => {
const results = [];
for (let i = 0; i < 3; i++) {
const result = await w.webContents.printToPDF({});
results.push(result);
}
for (const data of results) {
expect(data).to.be.an.instanceof(Buffer).that.is.not.empty();
}
});
});
describe('PictureInPicture video', () => {