mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-07-12 10:05:47 +02:00
Merge pull request #21065 from calixteman/issue19988
Dont revoke blob URLs while printing but do it after
This commit is contained in:
commit
1d48f17b98
@ -1784,4 +1784,113 @@ describe("PDF viewer", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("PDFPrintService", () => {
|
||||||
|
describe("blob URL revocation (issue #19988)", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"basicapi.pdf",
|
||||||
|
".textLayer .endOfContent",
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
earlySetup: () => {
|
||||||
|
// Track blob URLs created during the print phase (between
|
||||||
|
// beforeprint and afterprint).
|
||||||
|
let trackPrintURLs = false;
|
||||||
|
window._printBlobURLs = [];
|
||||||
|
|
||||||
|
const origCreate = URL.createObjectURL.bind(URL);
|
||||||
|
URL.createObjectURL = blob => {
|
||||||
|
const url = origCreate(blob);
|
||||||
|
if (trackPrintURLs) {
|
||||||
|
window._printBlobURLs.push(url);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
// beforeprint fires before renderPages(); start tracking here.
|
||||||
|
window.addEventListener("beforeprint", () => {
|
||||||
|
trackPrintURLs = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// window.print() is called by performPrint() after renderPages()
|
||||||
|
// completes and all images are loaded into #printContainer.
|
||||||
|
window.print = () => {
|
||||||
|
const isFirefox = navigator.userAgent.includes("Firefox");
|
||||||
|
if (isFirefox) {
|
||||||
|
// Firefox re-fetches blob URLs when rendering the print
|
||||||
|
// preview (especially when a service worker is registered).
|
||||||
|
// Verify the URLs are still accessible at this point.
|
||||||
|
window._printImagesAccessible = Promise.all(
|
||||||
|
window._printBlobURLs.map(url =>
|
||||||
|
fetch(url).then(
|
||||||
|
() => true,
|
||||||
|
() => false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Chrome uses the cached decoded data already in the <img>
|
||||||
|
// elements and does not re-fetch blob URLs for printing.
|
||||||
|
// Just verify the images rendered correctly.
|
||||||
|
const imgs = document.querySelectorAll("#printContainer img");
|
||||||
|
window._printImagesAccessible = Promise.resolve(
|
||||||
|
Array.from(imgs).map(
|
||||||
|
img => img.complete && img.naturalWidth > 0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
appSetup: app => {
|
||||||
|
app._testPrintResolver = Promise.withResolvers();
|
||||||
|
},
|
||||||
|
eventBusSetup: eventBus => {
|
||||||
|
eventBus.on(
|
||||||
|
"afterprint",
|
||||||
|
() => {
|
||||||
|
// Wait for the checks initiated in window.print() before
|
||||||
|
// resolving, so the test can assert on them.
|
||||||
|
(window._printImagesAccessible ?? Promise.resolve([])).then(
|
||||||
|
window.PDFViewerApplication._testPrintResolver.resolve
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must keep print image blob URLs accessible until destroy() is called", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await waitAndClick(page, "#printButton");
|
||||||
|
|
||||||
|
// Resolves with an array of booleans, one per print page image.
|
||||||
|
const accessible = await awaitPromise(
|
||||||
|
await page.evaluateHandle(() => [
|
||||||
|
window.PDFViewerApplication._testPrintResolver.promise,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(accessible.length)
|
||||||
|
.withContext(`In ${browserName}: print pages were rendered`)
|
||||||
|
.toBeGreaterThan(0);
|
||||||
|
expect(accessible.every(v => v))
|
||||||
|
.withContext(
|
||||||
|
`In ${browserName}: all print images accessible at print time`
|
||||||
|
)
|
||||||
|
.toBeTrue();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -144,6 +144,12 @@ class PDFPrintService {
|
|||||||
this.pageStyleSheet.remove();
|
this.pageStyleSheet.remove();
|
||||||
this.pageStyleSheet = null;
|
this.pageStyleSheet = null;
|
||||||
}
|
}
|
||||||
|
if (this._blobURLs) {
|
||||||
|
for (const url of this._blobURLs) {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
this._blobURLs = null;
|
||||||
|
}
|
||||||
this.scratchCanvas.width = this.scratchCanvas.height = 0;
|
this.scratchCanvas.width = this.scratchCanvas.height = 0;
|
||||||
this.scratchCanvas = null;
|
this.scratchCanvas = null;
|
||||||
activeService = null;
|
activeService = null;
|
||||||
@ -189,7 +195,13 @@ class PDFPrintService {
|
|||||||
this.throwIfInactive();
|
this.throwIfInactive();
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
this.scratchCanvas.toBlob(blob => {
|
this.scratchCanvas.toBlob(blob => {
|
||||||
img.src = URL.createObjectURL(blob);
|
const blobURL = URL.createObjectURL(blob);
|
||||||
|
img.src = blobURL;
|
||||||
|
// Defer revocation until after printing completes (in destroy()) to avoid
|
||||||
|
// broken print images in Firefox when a service worker is registered,
|
||||||
|
// since Firefox re-fetches blob URLs when rendering the print dialog.
|
||||||
|
// See https://github.com/mozilla/pdf.js/issues/19988
|
||||||
|
(this._blobURLs ??= []).push(blobURL);
|
||||||
});
|
});
|
||||||
|
|
||||||
const wrapper = document.createElement("div");
|
const wrapper = document.createElement("div");
|
||||||
@ -201,13 +213,9 @@ class PDFPrintService {
|
|||||||
img.onload = resolve;
|
img.onload = resolve;
|
||||||
img.onerror = reject;
|
img.onerror = reject;
|
||||||
|
|
||||||
promise
|
promise.catch(() => {
|
||||||
.catch(() => {
|
// Avoid "Uncaught promise" messages in the console.
|
||||||
// Avoid "Uncaught promise" messages in the console.
|
});
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
URL.revokeObjectURL(img.src);
|
|
||||||
});
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user