fix: reset printToPDF queue after a rejection (#51222)

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 <shelley.vohr@gmail.com>
This commit is contained in:
trop[bot]
2026-04-22 12:32:49 +02:00
committed by GitHub
parent da44f3c693
commit d6ea51b456
2 changed files with 22 additions and 9 deletions

View File

@@ -212,7 +212,7 @@ function parsePageSize (pageSize: string | ElectronInternal.PageSize) {
// Translate the options of printToPDF.
let pendingPromise: Promise<any> | undefined;
const printToPDFQueues = new WeakMap<Electron.WebContents, Promise<unknown>>();
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

View File

@@ -3080,6 +3080,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,<h1>Hello, World!</h1>');
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'));