From 35aea3afcc6b6ec65a08f4db6ac06cf79e69ec55 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 12:32:47 +0200 Subject: [PATCH] fix: reset `printToPDF` queue after a rejection (#51221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: reset printToPDF queue after a rejection The module-scoped `pendingPromise` in `webContents.printToPDF` was chained with `.then(onFulfilled)` and never cleared. Once a call rejected (e.g. an out-of-range `pageRanges` like `"999"`), subsequent calls chained onto the rejected promise and short-circuited without ever invoking `_printToPDF` — so every following call re-surfaced the original error. Replace the shared variable with a per-`WebContents` `WeakMap` queue that swallows prior rejections before chaining and clears its entry once the tail drains. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr --- lib/browser/api/web-contents.ts | 21 ++++++++++++--------- spec/api-web-contents-spec.ts | 10 ++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/browser/api/web-contents.ts b/lib/browser/api/web-contents.ts index 5ebf6a4509..f067e30cba 100644 --- a/lib/browser/api/web-contents.ts +++ b/lib/browser/api/web-contents.ts @@ -212,7 +212,7 @@ function parsePageSize (pageSize: string | ElectronInternal.PageSize) { // Translate the options of printToPDF. -let pendingPromise: Promise | undefined; +const printToPDFQueues = new WeakMap>(); WebContents.prototype.printToPDF = async function (options) { const margins = checkType(options.margins ?? {}, 'object', 'margins'); const pageSize = parsePageSize(options.pageSize ?? 'letter'); @@ -244,16 +244,19 @@ WebContents.prototype.printToPDF = async function (options) { ...pageSize }; - if (this._printToPDF) { - if (pendingPromise) { - pendingPromise = pendingPromise.then(() => this._printToPDF(printSettings)); - } else { - pendingPromise = this._printToPDF(printSettings); - } - return pendingPromise; - } else { + if (!this._printToPDF) { throw new Error('Printing feature is disabled'); } + + const prev = printToPDFQueues.get(this) ?? Promise.resolve(); + const next = prev.catch(() => {}).then(() => this._printToPDF(printSettings)); + printToPDFQueues.set(this, next); + next + .finally(() => { + if (printToPDFQueues.get(this) === next) printToPDFQueues.delete(this); + }) + .catch(() => {}); + return next; }; // TODO(codebytere): deduplicate argument sanitization by moving rest of diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts index 255e5dc424..3098541792 100644 --- a/spec/api-web-contents-spec.ts +++ b/spec/api-web-contents-spec.ts @@ -2996,6 +2996,16 @@ describe('webContents module', () => { expect(pdfInfo.numPages).to.equal(3); }); + it('recovers after a prior call fails with an invalid page range', async () => { + await w.loadURL('data:text/html,

Hello, World!

'); + + await expect(w.webContents.printToPDF({ pageRanges: '999' })).to.eventually.be.rejected(); + + const data = await w.webContents.printToPDF({}); + const pdfInfo = await readPDF(data); + expect(pdfInfo.numPages).to.equal(1); + }); + it('does not tag PDFs by default', async () => { await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'print-to-pdf-small.html'));