Merge pull request #18055 from Snuffleupagus/PDFViewer-signal

Remove event listeners with `signal` in web/pdf_viewer.js
This commit is contained in:
Tim van der Meij 2024-05-13 15:16:01 +02:00 committed by GitHub
commit 1c25e951a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -211,12 +211,12 @@ class PDFViewer {
#containerTopLeft = null; #containerTopLeft = null;
#copyCallbackBound = null;
#enableHighlightFloatingButton = false; #enableHighlightFloatingButton = false;
#enablePermissions = false; #enablePermissions = false;
#eventAbortController = null;
#mlManager = null; #mlManager = null;
#getAllTextInProgress = false; #getAllTextInProgress = false;
@ -231,8 +231,6 @@ class PDFViewer {
#scrollModePageState = null; #scrollModePageState = null;
#onVisibilityChange = null;
#scaleTimeoutId = null; #scaleTimeoutId = null;
#textLayerMode = TextLayerMode.ENABLE; #textLayerMode = TextLayerMode.ENABLE;
@ -313,7 +311,6 @@ class PDFViewer {
this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this)); this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this));
this.presentationModeState = PresentationModeState.UNKNOWN; this.presentationModeState = PresentationModeState.UNKNOWN;
this._onBeforeDraw = this._onAfterDraw = null;
this._resetView(); this._resetView();
if ( if (
@ -625,7 +622,7 @@ class PDFViewer {
return params; return params;
} }
async #onePageRenderedOrForceFetch() { async #onePageRenderedOrForceFetch(signal) {
// Unless the viewer *and* its pages are visible, rendering won't start and // Unless the viewer *and* its pages are visible, rendering won't start and
// `this._onePageRenderedCapability` thus won't be resolved. // `this._onePageRenderedCapability` thus won't be resolved.
// To ensure that automatic printing, on document load, still works even in // To ensure that automatic printing, on document load, still works even in
@ -646,23 +643,22 @@ class PDFViewer {
// Handle the window/tab becoming inactive *after* rendering has started; // Handle the window/tab becoming inactive *after* rendering has started;
// fixes (another part of) bug 1746213. // fixes (another part of) bug 1746213.
const visibilityChangePromise = new Promise(resolve => { const hiddenCapability = Promise.withResolvers();
this.#onVisibilityChange = () => { function onVisibilityChange() {
if (document.visibilityState !== "hidden") { if (document.visibilityState === "hidden") {
return; hiddenCapability.resolve();
} }
resolve(); }
}; document.addEventListener("visibilitychange", onVisibilityChange, {
document.addEventListener("visibilitychange", this.#onVisibilityChange); signal,
}); });
await Promise.race([ await Promise.race([
this._onePageRenderedCapability.promise, this._onePageRenderedCapability.promise,
visibilityChangePromise, hiddenCapability.promise,
]); ]);
// Ensure that the "visibilitychange" listener is always removed. // Ensure that the "visibilitychange" listener is removed immediately.
document.removeEventListener("visibilitychange", this.#onVisibilityChange); document.removeEventListener("visibilitychange", onVisibilityChange);
this.#onVisibilityChange = null;
} }
async getAllText() { async getAllText() {
@ -788,6 +784,11 @@ class PDFViewer {
? pdfDocument.getPermissions() ? pdfDocument.getPermissions()
: Promise.resolve(); : Promise.resolve();
const { eventBus, pageColors, viewer } = this;
this.#eventAbortController = new AbortController();
const { signal } = this.#eventAbortController;
// Given that browsers don't handle huge amounts of DOM-elements very well, // Given that browsers don't handle huge amounts of DOM-elements very well,
// enforce usage of PAGE-scrolling when loading *very* long/large documents. // enforce usage of PAGE-scrolling when loading *very* long/large documents.
if (pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) { if (pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
@ -795,19 +796,19 @@ class PDFViewer {
"Forcing PAGE-scrolling for performance reasons, given the length of the document." "Forcing PAGE-scrolling for performance reasons, given the length of the document."
); );
const mode = (this._scrollMode = ScrollMode.PAGE); const mode = (this._scrollMode = ScrollMode.PAGE);
this.eventBus.dispatch("scrollmodechanged", { source: this, mode }); eventBus.dispatch("scrollmodechanged", { source: this, mode });
} }
this._pagesCapability.promise.then( this._pagesCapability.promise.then(
() => { () => {
this.eventBus.dispatch("pagesloaded", { source: this, pagesCount }); eventBus.dispatch("pagesloaded", { source: this, pagesCount });
}, },
() => { () => {
/* Prevent "Uncaught (in promise)"-messages in the console. */ /* Prevent "Uncaught (in promise)"-messages in the console. */
} }
); );
this._onBeforeDraw = evt => { const onBeforeDraw = evt => {
const pageView = this._pages[evt.pageNumber - 1]; const pageView = this._pages[evt.pageNumber - 1];
if (!pageView) { if (!pageView) {
return; return;
@ -816,18 +817,17 @@ class PDFViewer {
// evicted from the buffer and destroyed even if we pause its rendering. // evicted from the buffer and destroyed even if we pause its rendering.
this.#buffer.push(pageView); this.#buffer.push(pageView);
}; };
this.eventBus._on("pagerender", this._onBeforeDraw); eventBus._on("pagerender", onBeforeDraw, { signal });
this._onAfterDraw = evt => { const onAfterDraw = evt => {
if (evt.cssTransform) { if (evt.cssTransform) {
return; return;
} }
this._onePageRenderedCapability.resolve({ timestamp: evt.timestamp }); this._onePageRenderedCapability.resolve({ timestamp: evt.timestamp });
this.eventBus._off("pagerendered", this._onAfterDraw); eventBus._off("pagerendered", onAfterDraw); // Remove immediately.
this._onAfterDraw = null;
}; };
this.eventBus._on("pagerendered", this._onAfterDraw); eventBus._on("pagerendered", onAfterDraw, { signal });
// Fetch a single page so we can get a viewport that will be the default // Fetch a single page so we can get a viewport that will be the default
// viewport for all pages // viewport for all pages
@ -846,7 +846,7 @@ class PDFViewer {
const element = (this.#hiddenCopyElement = const element = (this.#hiddenCopyElement =
document.createElement("div")); document.createElement("div"));
element.id = "hiddenCopyElement"; element.id = "hiddenCopyElement";
this.viewer.before(element); viewer.before(element);
} }
if (annotationEditorMode !== AnnotationEditorType.DISABLE) { if (annotationEditorMode !== AnnotationEditorType.DISABLE) {
@ -857,16 +857,16 @@ class PDFViewer {
} else if (isValidAnnotationEditorMode(mode)) { } else if (isValidAnnotationEditorMode(mode)) {
this.#annotationEditorUIManager = new AnnotationEditorUIManager( this.#annotationEditorUIManager = new AnnotationEditorUIManager(
this.container, this.container,
this.viewer, viewer,
this.#altTextManager, this.#altTextManager,
this.eventBus, eventBus,
pdfDocument, pdfDocument,
this.pageColors, pageColors,
this.#annotationEditorHighlightColors, this.#annotationEditorHighlightColors,
this.#enableHighlightFloatingButton, this.#enableHighlightFloatingButton,
this.#mlManager this.#mlManager
); );
this.eventBus.dispatch("annotationeditoruimanager", { eventBus.dispatch("annotationeditoruimanager", {
source: this, source: this,
uiManager: this.#annotationEditorUIManager, uiManager: this.#annotationEditorUIManager,
}); });
@ -879,19 +879,19 @@ class PDFViewer {
} }
const viewerElement = const viewerElement =
this._scrollMode === ScrollMode.PAGE ? null : this.viewer; this._scrollMode === ScrollMode.PAGE ? null : viewer;
const scale = this.currentScale; const scale = this.currentScale;
const viewport = firstPdfPage.getViewport({ const viewport = firstPdfPage.getViewport({
scale: scale * PixelsPerInch.PDF_TO_CSS_UNITS, scale: scale * PixelsPerInch.PDF_TO_CSS_UNITS,
}); });
// Ensure that the various layers always get the correct initial size, // Ensure that the various layers always get the correct initial size,
// see issue 15795. // see issue 15795.
this.viewer.style.setProperty("--scale-factor", viewport.scale); viewer.style.setProperty("--scale-factor", viewport.scale);
if ( if (
this.pageColors?.foreground === "CanvasText" || pageColors?.foreground === "CanvasText" ||
this.pageColors?.background === "Canvas" pageColors?.background === "Canvas"
) { ) {
this.viewer.style.setProperty( viewer.style.setProperty(
"--hcm-highlight-filter", "--hcm-highlight-filter",
pdfDocument.filterFactory.addHighlightHCMFilter( pdfDocument.filterFactory.addHighlightHCMFilter(
"highlight", "highlight",
@ -901,7 +901,7 @@ class PDFViewer {
"Highlight" "Highlight"
) )
); );
this.viewer.style.setProperty( viewer.style.setProperty(
"--hcm-highlight-selected-filter", "--hcm-highlight-selected-filter",
pdfDocument.filterFactory.addHighlightHCMFilter( pdfDocument.filterFactory.addHighlightHCMFilter(
"highlight_selected", "highlight_selected",
@ -916,7 +916,7 @@ class PDFViewer {
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) { for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
const pageView = new PDFPageView({ const pageView = new PDFPageView({
container: viewerElement, container: viewerElement,
eventBus: this.eventBus, eventBus,
id: pageNum, id: pageNum,
scale, scale,
defaultViewport: viewport.clone(), defaultViewport: viewport.clone(),
@ -926,7 +926,7 @@ class PDFViewer {
annotationMode, annotationMode,
imageResourcesPath: this.imageResourcesPath, imageResourcesPath: this.imageResourcesPath,
maxCanvasPixels: this.maxCanvasPixels, maxCanvasPixels: this.maxCanvasPixels,
pageColors: this.pageColors, pageColors,
l10n: this.l10n, l10n: this.l10n,
layerProperties: this._layerProperties, layerProperties: this._layerProperties,
}); });
@ -947,21 +947,24 @@ class PDFViewer {
// Fetch all the pages since the viewport is needed before printing // Fetch all the pages since the viewport is needed before printing
// starts to create the correct size canvas. Wait until one page is // starts to create the correct size canvas. Wait until one page is
// rendered so we don't tie up too many resources early on. // rendered so we don't tie up too many resources early on.
this.#onePageRenderedOrForceFetch().then(async () => { this.#onePageRenderedOrForceFetch(signal).then(async () => {
if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the first page rendered.
}
this.findController?.setDocument(pdfDocument); // Enable searching. this.findController?.setDocument(pdfDocument); // Enable searching.
this._scriptingManager?.setDocument(pdfDocument); // Enable scripting. this._scriptingManager?.setDocument(pdfDocument); // Enable scripting.
if (this.#hiddenCopyElement) { if (this.#hiddenCopyElement) {
this.#copyCallbackBound = this.#copyCallback.bind( document.addEventListener(
this, "copy",
textLayerMode this.#copyCallback.bind(this, textLayerMode),
{ signal }
); );
document.addEventListener("copy", this.#copyCallbackBound);
} }
if (this.#annotationEditorUIManager) { if (this.#annotationEditorUIManager) {
// Ensure that the Editor buttons, in the toolbar, are updated. // Ensure that the Editor buttons, in the toolbar, are updated.
this.eventBus.dispatch("annotationeditormodechanged", { eventBus.dispatch("annotationeditormodechanged", {
source: this, source: this,
mode: this.#annotationEditorMode, mode: this.#annotationEditorMode,
}); });
@ -1011,14 +1014,14 @@ class PDFViewer {
} }
}); });
this.eventBus.dispatch("pagesinit", { source: this }); eventBus.dispatch("pagesinit", { source: this });
pdfDocument.getMetadata().then(({ info }) => { pdfDocument.getMetadata().then(({ info }) => {
if (pdfDocument !== this.pdfDocument) { if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the metadata resolved. return; // The document was closed while the metadata resolved.
} }
if (info.Language) { if (info.Language) {
this.viewer.lang = info.Language; viewer.lang = info.Language;
} }
}); });
@ -1079,21 +1082,9 @@ class PDFViewer {
pages: [], pages: [],
}; };
if (this._onBeforeDraw) { this.#eventAbortController?.abort();
this.eventBus._off("pagerender", this._onBeforeDraw); this.#eventAbortController = null;
this._onBeforeDraw = null;
}
if (this._onAfterDraw) {
this.eventBus._off("pagerendered", this._onAfterDraw);
this._onAfterDraw = null;
}
if (this.#onVisibilityChange) {
document.removeEventListener(
"visibilitychange",
this.#onVisibilityChange
);
this.#onVisibilityChange = null;
}
// Remove the pages from the DOM... // Remove the pages from the DOM...
this.viewer.textContent = ""; this.viewer.textContent = "";
// ... and reset the Scroll mode CSS class(es) afterwards. // ... and reset the Scroll mode CSS class(es) afterwards.
@ -1101,13 +1092,8 @@ class PDFViewer {
this.viewer.removeAttribute("lang"); this.viewer.removeAttribute("lang");
if (this.#hiddenCopyElement) { this.#hiddenCopyElement?.remove();
document.removeEventListener("copy", this.#copyCallbackBound); this.#hiddenCopyElement = null;
this.#copyCallbackBound = null;
this.#hiddenCopyElement.remove();
this.#hiddenCopyElement = null;
}
} }
#ensurePageViewVisible() { #ensurePageViewVisible() {