10000 feat: promisify webContents.printToPDF() by miniak · Pull Request #16795 · electron/electron · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: promisify webContents.printToPDF() #16795

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

Merged
merged 1 commit into from
Feb 11, 2019
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.
Loading
Jump to < 10000 div class="select-menu-header"> Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions atom/browser/api/atom_api_web_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1499,11 +1499,12 @@ std::vector<printing::PrinterBasicInfo> WebContents::GetPrinterList() {
return printers;
}

void WebContents::PrintToPDF(
const base::DictionaryValue& settings,
const PrintPreviewMessageHandler::PrintToPDFCallback& callback) {
v8::Local<v8::Promise> WebContents::PrintToPDF(
const base::DictionaryValue& settings) {
scoped_refptr<util::Promise> promise = new util::Promise(isolate());
PrintPreviewMessageHandler::FromWebContents(web_contents())
->PrintToPDF(settings, callback);
->PrintToPDF(settings, promise);
return promise->GetHandle();
}
#endif

Expand Down
4 changes: 1 addition & 3 deletions atom/browser/api/atom_api_web_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
void Print(mate::Arguments* args);
std::vector<printing::PrinterBasicInfo> GetPrinterList();
// Print current page as PDF.
void PrintToPDF(
const base::DictionaryValue& settings,
const PrintPreviewMessageHandler::PrintToPDFCallback& callback);
v8::Local<v8::Promise> PrintToPDF(const base::DictionaryValue& settings);
#endif

// DevTools workspace api.
Expand Down
71 changes: 43 additions & 28 deletions atom/browser/printing/print_preview_message_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
const PrintHostMsg_DidPrintContent_Params& content = params.content;
if (!content.metafile_data_region.IsValid() ||
params.expected_pages_count <= 0) {
RunPrintToPDFCallback(ids.request_id, nullptr);
RejectPromise(ids.request_id);
return;
}

Expand All @@ -102,10 +102,9 @@ void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
base::BindOnce(&PrintPreviewMessageHandler::OnCompositePdfDocumentDone,
weak_ptr_factory_.GetWeakPtr(), ids));
} else {
RunPrintToPDFCallback(
ids.request_id,
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(
content.metafile_data_region));
ResolvePromise(ids.request_id,
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(
content.metafile_data_region));
}
}

Expand All @@ -117,11 +116,11 @@ void PrintPreviewMessageHandler::OnCompositePdfDocumentDone(

if (status != printing::mojom::PdfCompositor::Status::SUCCESS) {
DLOG(ERROR) << "Compositing pdf failed with error " << status;
RunPrintToPDFCallback(ids.request_id, nullptr);
RejectPromise(ids.request_id);
return;
}

RunPrintToPDFCallback(
ResolvePromise(
ids.request_id,
base::RefCountedSharedMemoryMapping::CreateFromWholeRegion(region));
}
Expand All @@ -131,23 +130,23 @@ void PrintPreviewMessageHandler::OnPrintPreviewFailed(
const PrintHostMsg_PreviewIds& ids) {
StopWorker(document_cookie);

RunPrintToPDFCallback(ids.request_id, nullptr);
RejectPromise(ids.request_id);
}
10000
void PrintPreviewMessageHandler::OnPrintPreviewCancelled(
int document_cookie,
const PrintHostMsg_PreviewIds& ids) {
StopWorker(document_cookie);

RunPrintToPDFCallback(ids.request_id, nullptr);
RejectPromise(ids.request_id);
}

void PrintPreviewMessageHandler::PrintToPDF(
const base::DictionaryValue& options,
const PrintToPDFCallback& callback) {
scoped_refptr<atom::util::Promise> promise) {
int request_id;
options.GetInteger(printing::kPreviewRequestID, &request_id);
print_to_pdf_callback_map_[request_id] = callback;
promise_map_[request_id] = std::move(promise);

auto* focused_frame = web_contents()->GetFocusedFrame();
auto* rfh = focused_frame && focused_frame->HasSelection()
Expand All @@ -156,28 +155,44 @@ void PrintPreviewMessageHandler::PrintToPDF(
rfh->Send(new PrintMsg_PrintPreview(rfh->GetRoutingID(), options));
}

void PrintPreviewMessageHandler::RunPrintToPDFCallback(
scoped_refptr<atom::util::Promise> PrintPreviewMessageHandler::GetPromise(
int request_id) {
auto it = promise_map_.find(request_id);
DCHECK(it != promise_map_.end());

auto promise = it->second;
promise_map_.erase(it);

return promise;
}

void PrintPreviewMessageHandler::ResolvePromise(
int request_id,
scoped_refptr<base::RefCountedMemory> data_bytes) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);

v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Locker locker(isolate);
auto promise = GetPromise(request_id);

v8::Isolate* isolate = promise->isolate();
mate::Locker locker(isolate);
v8::HandleScope handle_scope(isolate);
if (data_bytes && data_bytes->size()) {
v8::Local<v8::Value> buffer =
node::Buffer::Copy(isolate,
reinterpret_cast<const char*>(data_bytes->front()),
data_bytes->size())
.ToLocalChecked();
print_to_pdf_callback_map_[request_id].Run(v8::Null(isolate), buffer);
} else {
v8::Local<v8::String> error_message =
v8::String::NewFromUtf8(isolate, "Failed to generate PDF");
print_to_pdf_callback_map_[request_id].Run(
v8::Exception::Error(error_message), v8::Null(isolate));
}
print_to_pdf_callback_map_.erase(request_id);
v8::Context::Scope context_scope(
v8::Local<v8::Context>::New(isolate, promise->GetContext()));

v8::Local<v8::Value> buffer =
node::Buffer::Copy(isolate,
reinterpret_cast<const char*>(data_bytes->front()),
data_bytes->size())
.ToLocalChecked();

promise->Resolve(buffer);
}

void PrintPreviewMessageHandler::RejectPromise(int request_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);

auto promise = GetPromise(request_id);
promise->RejectWithErrorMessage("Failed to generate PDF");
}

} // namespace atom
18 changes: 10 additions & 8 deletions atom/browser/printing/print_preview_message_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <map>

#include "atom/common/promise_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/weak_ptr.h"
#include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
Expand All @@ -28,13 +29,10 @@ class PrintPreviewMessageHandler
: public content::WebContentsObserver,
public content::WebContentsUserData<PrintPreviewMessageHandler> {
public:
using PrintToPDFCallback =
base::Callback<void(v8::Local<v8::Value>, v8::Local<v8::Value>)>;

~PrintPreviewMessageHandler() override;

void PrintToPDF(const base::DictionaryValue& options,
const PrintToPDFCallback& callback);
scoped_refptr<atom::util::Promise> promise);

protected:
// content::WebContentsObserver implementation.
Expand All @@ -57,11 +55,15 @@ class PrintPreviewMessageHandler
const PrintHostMsg_PreviewIds& ids);
void OnPrintPreviewCancelled(int document_cookie,
const PrintHostMsg_PreviewIds& ids);
void RunPrintToPDFCallback(int request_id,
scoped_refptr<base::RefCountedMemory> data_bytes);

using PrintToPDFCallbackMap = std::map<int, PrintToPDFCallback>;
PrintToPDFCallbackMap print_to_pdf_callback_map_;
scoped_refptr<atom::util::Promise> GetPromise(int request_id);

void ResolvePromise(int request_id,
scoped_refptr<base::RefCountedMemory> data_bytes);
void RejectPromise(int request_id);

using PromiseMap = std::map<int, scoped_refptr<atom::util::Promise>>;
PromiseMap promise_map_;

base::WeakPtrFactory<PrintPreviewMessageHandler> weak_ptr_factory_;

Expand Down
4 changes: 2 additions & 2 deletions docs/api/promisification.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,16 @@ When a majority of affected functions are migrated, this flag will be enabled by
- [ses.clearAuthCache(options[, callback])](https://github.com/electron/electron/blob/master/docs/api/session.md#clearAuthCache)
- [contents.executeJavaScript(code[, userGesture, callback])](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#executeJavaScript)
- [contents.print([options], [callback])](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#print)
- [contents.printToPDF(options, callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#printToPDF)
- [contents.savePage(fullPath, saveType, callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#savePage)
- [webFrame.executeJavaScript(code[, userGesture, callback])](https://github.com/electron/electron/blob/master/docs/api/web-frame.md#executeJavaScript)
- [webFrame.executeJavaScriptInIsolatedWorld(worldId, scripts[, userGesture, callback])](https://github.com/electron/electron/blob/master/docs/api/web-frame.md#executeJavaScriptInIsolatedWorld)
- [webviewTag.executeJavaScript(code[, userGesture, callback])](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#executeJavaScript)
- [webviewTag.printToPDF(options, callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#printToPDF)

### Converted Functions

- [app.getFileIcon(path[, options], callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#getFileIcon)
- [contents.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#capturePage)
- [contents.printToPDF(options, callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#printToPDF)
- [contentTracing.getCategories(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#getCategories)
- [contentTracing.startRecording(options, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#startRecording)
- [contentTracing.stopRecording(resultFilePath, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#stopRecording)
Expand All @@ -50,5 +49,6 @@ When a majority of affected functions are migrated, this flag will be enabled by
- [protocol.isProtocolHandled(scheme, callback)](https://github.com/electron/electron/blob/master/docs/api/protocol.md#isProtocolHandled)
- [shell.openExternal(url[, options, callback])](https://github.com/electron/electron/blob/master/docs/api/shell.md#openExternal)
- [webviewTag.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#capturePage)
- [webviewTag.printToPDF(options, callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#printToPDF)
- [win.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/browser-window.md#capturePage)
- [desktopCapturer.getSources(options, callback)](https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md#getSources)
19 changes: 19 additions & 0 deletions docs/api/web-contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,25 @@ settings.
The `callback` will be called with `callback(error, data)` on completion. The
`data` is a `Buffer` that contains the generated PDF data.

**[Deprecated Soon](promisification.md)**

#### `contents.printToPDF(options)`

* `options` Object
* `marginsType` Integer (optional) - Specifies the type of margins to use. Uses 0 for
default margin, 1 for no margin, and 2 for minimum margin.
* `pageSize` String | Size (optional) - Specify page size of the generated PDF. Can be `A3`,
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height`
and `width` in microns.
* `printBackground` Boolean (optional) - Whether to print CSS backgrounds.
* `printSelectionOnly` Boolean (optional) - Whether to print selection only.
* `landscape` Boolean (optional) - `true` for landscape, `false` for portrait.

* Returns `Promise<Buffer>` - Resolves with the generated PDF data.

Prints window's web page as PDF with Chromium's preview printing custom
settings.

The `landscape` will be ignored if `@page` CSS at-rule is used in the web page.

By default, an empty `options` will be regarded as:
Expand Down
18 changes: 18 additions & 0 deletions docs/api/webview-tag.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,24 @@ Prints `webview`'s web page. Same as `webContents.print([options])`.

Prints `webview`'s web page as PDF, Same as `webContents.printToPDF(options, callback)`.

**[Deprecated Soon](promisification.md)**

### `<webview>.printToPDF(options)`

* `options` Object
* `marginsType` Integer (optional) - Specifies the type of margins to use. Uses 0 for
default margin, 1 for no margin, and 2 for minimum margin.
* `pageSize` String | Size (optional) - Specify page size of the generated PDF. Can be `A3`,
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height`
and `width` in microns.
* `printBackground` Boolean (optional) - Whether to print CSS backgrounds.
* `printSelectionOnly` Boolean (optional) - Whether to print selection only.
* `landscape` Boolean (optional) - `true` for landscape, `false` for portrait.

* Returns `Promise<Buffer>` - Resolves with the generated PDF data.

Prints `webview`'s web page as PDF, Same as `webContents.printToPDF(options)`.

### `<webview>.capturePage([rect, ]callback)`

* `rect` [Rectangle](structures/rectangle.md) (optional) - The bounds to capture
Expand Down
15 changes: 8 additions & 7 deletions lib/browser/api/web-contents.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ WebContents.prototype.takeHeapSnapshot = function (filePath) {
}

// Translate the options of printToPDF.
WebContents.prototype.printToPDF = function (options, callback) {
WebContents.prototype.printToPDF = function (options) {
const printingSetting = Object.assign({}, defaultPrintingSetting)
if (options.landscape) {
printingSetting.landscape = options.landscape
Expand All @@ -282,7 +282,7 @@ WebContents.prototype.printToPDF = function (options, callback) {
const pageSize = options.pageSize
if (typeof pageSize === 'object') {
if (!pageSize.height || !pageSize.width) {
return callback(new Error('Must define height and width for pageSize'))
return Promise.reject(new Error('Must define height and width for pageSize'))
}
// Dimensions in Microns
// 1 meter = 10^6 microns
Expand All @@ -295,7 +295,7 @@ WebContents.prototype.printToPDF = function (options, callback) {
} else if (PDFPageSizes[pageSize]) {
printingSetting.mediaSize = PDFPageSizes[pageSize]
} else {
return callback(new Error(`Does not support pageSize with ${pageSize}`))
return Promise.reject(new Error(`Does not support pageSize with ${pageSize}`))
}
} else {
printingSetting.mediaSize = PDFPageSizes['A4']
Expand All @@ -304,9 +304,9 @@ WebContents.prototype.printToPDF = function (options, callback) {
// Chromium expects this in a 0-100 range number, not as float
printingSetting.scaleFactor *= 100
if (features.isPrintingEnabled()) {
this._printToPDF(printingSetting, callback)
return this._printToPDF(printingSetting)
} else {
console.error('Error: Printing feature is disabled.')
return Promise.reject(new Error('Printing feature is disabled'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

}
}

Expand Down Expand Up @@ -342,6 +342,9 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
}))
}

WebContents.prototype.capturePage = deprecate.promisify(WebContents.prototype.capturePage)
WebContents.prototype.printToPDF = deprecate.promisify(WebContents.prototype.printToPDF)

const addReplyToEvent = (event) => {
event.reply = (...args) => {
event.sender.sendToFrame(event.frameId, ...args)
Expand Down Expand Up @@ -385,8 +388,6 @@ WebContents.prototype._init = function () {
// render-view-deleted event, so ignore the listeners warning.
this.setMaxListeners(0)

this.capturePage = deprecate.promisify(this.capturePage)

// Dispatch IPC messages to the ipc module.
this.on('-ipc-message', function (event, internal, channel, args) {
if (internal) {
Expand Down
6 changes: 3 additions & 3 deletions lib/common/web-view-methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ exports.asyncCallbackMethods = new Set([
'sendInputEvent',
'setLayoutZoomLevelLimits',
'setVisualZoomLevelLimits',
'print',
'printToPDF'
'print'
])

exports.asyncPromiseMethods = new Set([
'capturePage',
'executeJavaScript'
'executeJavaScript',
'printToPDF'
])
4 changes: 3 additions & 1 deletion lib/renderer/web-view/web-view-impl.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { webFrame } = require('electron')
const { webFrame, deprecate } = require('electron')

const v8Util = process.atomBinding('v8_util')
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
Expand Down Expand Up @@ -287,6 +287,8 @@ const setupMethods = (WebViewElement) => {
for (const method of asyncPromiseMethods) {
WebViewElement.prototype[method] = createPromiseHandler(method)
}

WebViewElement.prototype.printToPDF = deprecate.promisify(WebViewElement.prototype.printToPDF)
}

module.exports = { setupAttributes, setupMethods, guestViewInternal, webFrame, WebViewImpl }
17 changes: 16 additions & 1 deletion spec/api-web-contents-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1268,7 +1268,22 @@ describe('webContents module', () => {
}
})

it('can print to PDF', (done) => {
it('can print to PDF', async () => {
w.destroy()
w = new BrowserWindow({
show: false,
webPreferences: {
sandbox: true
}
})
await w.loadURL('data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E')
const data = await w.webContents.printToPDF({})
assert.strictEqual(data instanceof Buffer, true)
assert.notStrictEqual(data.length, 0)
})

// TODO(miniak): remove when promisification is complete
it('can print to PDF (callback)', (done) => {
w.destroy()
w = new BrowserWindow({
show: false,
Expand Down
Loading
0