From 390afcb6851e0d54820ce205cfe2f43163f18335 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Tue, 18 Jun 2024 21:05:26 +0200 Subject: [PATCH] [Editor] Remove the various listeners when destroying the editor manager --- src/display/editor/alt_text.js | 70 +++++++++++++------ src/display/editor/annotation_editor_layer.js | 18 +++-- src/display/editor/color_picker.js | 18 +++-- src/display/editor/editor.js | 41 ++++++----- src/display/editor/freetext.js | 21 ++++-- src/display/editor/highlight.js | 15 ++-- src/display/editor/ink.js | 35 ++++++++-- src/display/editor/stamp.js | 53 +++++++++----- src/display/editor/toolbar.js | 39 ++++++++--- src/display/editor/tools.js | 63 ++++++++--------- 10 files changed, 244 insertions(+), 129 deletions(-) diff --git a/src/display/editor/alt_text.js b/src/display/editor/alt_text.js index 428f2f20dad7e..fececf29269cf 100644 --- a/src/display/editor/alt_text.js +++ b/src/display/editor/alt_text.js @@ -49,20 +49,27 @@ class AltText { altText.textContent = msg; altText.setAttribute("aria-label", msg); altText.tabIndex = "0"; - altText.addEventListener("contextmenu", noContextMenu); - altText.addEventListener("pointerdown", event => event.stopPropagation()); + const signal = this.#editor._uiManager._signal; + altText.addEventListener("contextmenu", noContextMenu, { signal }); + altText.addEventListener("pointerdown", event => event.stopPropagation(), { + signal, + }); const onClick = event => { event.preventDefault(); this.#editor._uiManager.editAltText(this.#editor); }; - altText.addEventListener("click", onClick, { capture: true }); - altText.addEventListener("keydown", event => { - if (event.target === altText && event.key === "Enter") { - this.#altTextWasFromKeyBoard = true; - onClick(event); - } - }); + altText.addEventListener("click", onClick, { capture: true, signal }); + altText.addEventListener( + "keydown", + event => { + if (event.target === altText && event.key === "Enter") { + this.#altTextWasFromKeyBoard = true; + onClick(event); + } + }, + { signal } + ); await this.#setState(); return altText; @@ -142,22 +149,39 @@ class AltText { button.setAttribute("aria-describedby", id); const DELAY_TO_SHOW_TOOLTIP = 100; - button.addEventListener("mouseenter", () => { - this.#altTextTooltipTimeout = setTimeout(() => { - this.#altTextTooltipTimeout = null; - this.#altTextTooltip.classList.add("show"); - this.#editor._reportTelemetry({ - action: "alt_text_tooltip", - }); - }, DELAY_TO_SHOW_TOOLTIP); - }); - button.addEventListener("mouseleave", () => { - if (this.#altTextTooltipTimeout) { + const signal = this.#editor._uiManager._signal; + signal.addEventListener( + "abort", + () => { clearTimeout(this.#altTextTooltipTimeout); this.#altTextTooltipTimeout = null; - } - this.#altTextTooltip?.classList.remove("show"); - }); + }, + { once: true } + ); + button.addEventListener( + "mouseenter", + () => { + this.#altTextTooltipTimeout = setTimeout(() => { + this.#altTextTooltipTimeout = null; + this.#altTextTooltip.classList.add("show"); + this.#editor._reportTelemetry({ + action: "alt_text_tooltip", + }); + }, DELAY_TO_SHOW_TOOLTIP); + }, + { signal } + ); + button.addEventListener( + "mouseleave", + () => { + if (this.#altTextTooltipTimeout) { + clearTimeout(this.#altTextTooltipTimeout); + this.#altTextTooltipTimeout = null; + } + this.#altTextTooltip?.classList.remove("show"); + }, + { signal } + ); } tooltip.innerText = this.#altTextDecorative ? await AltText._l10nPromise.get( diff --git a/src/display/editor/annotation_editor_layer.js b/src/display/editor/annotation_editor_layer.js index 1e95423d09f15..5c4e9445a613f 100644 --- a/src/display/editor/annotation_editor_layer.js +++ b/src/display/editor/annotation_editor_layer.js @@ -365,7 +365,8 @@ class AnnotationEditorLayer { this.#boundTextLayerPointerDown = this.#textLayerPointerDown.bind(this); this.#textLayer.div.addEventListener( "pointerdown", - this.#boundTextLayerPointerDown + this.#boundTextLayerPointerDown, + { signal: this.#uiManager._signal } ); this.#textLayer.div.classList.add("highlighting"); } @@ -409,7 +410,7 @@ class AnnotationEditorLayer { () => { this.#textLayer.div.classList.remove("free"); }, - { once: true } + { once: true, signal: this.#uiManager._signal } ); event.preventDefault(); } @@ -419,10 +420,13 @@ class AnnotationEditorLayer { if (this.#boundPointerdown) { return; } + const signal = this.#uiManager._signal; this.#boundPointerdown = this.pointerdown.bind(this); this.#boundPointerup = this.pointerup.bind(this); - this.div.addEventListener("pointerdown", this.#boundPointerdown); - this.div.addEventListener("pointerup", this.#boundPointerup); + this.div.addEventListener("pointerdown", this.#boundPointerdown, { + signal, + }); + this.div.addEventListener("pointerup", this.#boundPointerup, { signal }); } disableClick() { @@ -540,7 +544,7 @@ class AnnotationEditorLayer { () => { editor._focusEventsAllowed = true; }, - { once: true } + { once: true, signal: this.#uiManager._signal } ); activeElement.focus(); } else { @@ -596,6 +600,10 @@ class AnnotationEditorLayer { return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode()); } + get _signal() { + return this.#uiManager._signal; + } + /** * Create a new editor * @param {Object} params diff --git a/src/display/editor/color_picker.js b/src/display/editor/color_picker.js index ab69299c7af1d..9550145205674 100644 --- a/src/display/editor/color_picker.js +++ b/src/display/editor/color_picker.js @@ -89,8 +89,9 @@ class ColorPicker { button.tabIndex = "0"; button.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-button"); button.setAttribute("aria-haspopup", true); - button.addEventListener("click", this.#openDropdown.bind(this)); - button.addEventListener("keydown", this.#boundKeyDown); + const signal = this.#uiManager._signal; + button.addEventListener("click", this.#openDropdown.bind(this), { signal }); + button.addEventListener("keydown", this.#boundKeyDown, { signal }); const swatch = (this.#buttonSwatch = document.createElement("span")); swatch.className = "swatch"; swatch.setAttribute("aria-hidden", true); @@ -109,7 +110,8 @@ class ColorPicker { #getDropdownRoot() { const div = document.createElement("div"); - div.addEventListener("contextmenu", noContextMenu); + const signal = this.#uiManager._signal; + div.addEventListener("contextmenu", noContextMenu, { signal }); div.className = "dropdown"; div.role = "listbox"; div.setAttribute("aria-multiselectable", false); @@ -127,11 +129,13 @@ class ColorPicker { swatch.className = "swatch"; swatch.style.backgroundColor = color; button.setAttribute("aria-selected", color === this.#defaultColor); - button.addEventListener("click", this.#colorSelect.bind(this, color)); + button.addEventListener("click", this.#colorSelect.bind(this, color), { + signal, + }); div.append(button); } - div.addEventListener("keydown", this.#boundKeyDown); + div.addEventListener("keydown", this.#boundKeyDown, { signal }); return div; } @@ -211,7 +215,9 @@ class ColorPicker { return; } this.#dropdownWasFromKeyboard = event.detail === 0; - window.addEventListener("pointerdown", this.#boundPointerDown); + window.addEventListener("pointerdown", this.#boundPointerDown, { + signal: this.#uiManager._signal, + }); if (this.#dropdown) { this.#dropdown.classList.remove("hidden"); return; diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 3a42786d3374a..d33a15202aebf 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -715,6 +715,7 @@ class AnnotationEditor { "bottomLeft", "middleLeft", ]; + const signal = this._uiManager._signal; for (const name of classes) { const div = document.createElement("div"); this.#resizersDiv.append(div); @@ -722,9 +723,10 @@ class AnnotationEditor { div.setAttribute("data-resizer-name", name); div.addEventListener( "pointerdown", - this.#resizerPointerdown.bind(this, name) + this.#resizerPointerdown.bind(this, name), + { signal } ); - div.addEventListener("contextmenu", noContextMenu); + div.addEventListener("contextmenu", noContextMenu, { signal }); div.tabIndex = -1; } this.div.prepend(this.#resizersDiv); @@ -742,14 +744,15 @@ class AnnotationEditor { const boundResizerPointermove = this.#resizerPointermove.bind(this, name); const savedDraggable = this._isDraggable; this._isDraggable = false; - const pointerMoveOptions = { passive: true, capture: true }; + const signal = this._uiManager._signal; + const pointerMoveOptions = { passive: true, capture: true, signal }; this.parent.togglePointerEvents(false); window.addEventListener( "pointermove", boundResizerPointermove, pointerMoveOptions ); - window.addEventListener("contextmenu", noContextMenu); + window.addEventListener("contextmenu", noContextMenu, { signal }); const savedX = this.x; const savedY = this.y; const savedWidth = this.width; @@ -776,10 +779,10 @@ class AnnotationEditor { this.#addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight); }; - window.addEventListener("pointerup", pointerUpCallback); + window.addEventListener("pointerup", pointerUpCallback, { signal }); // If the user switches to another window (with alt+tab), then we end the // resize session. - window.addEventListener("blur", pointerUpCallback); + window.addEventListener("blur", pointerUpCallback, { signal }); } #addResizeToUndoStack(savedX, savedY, savedWidth, savedHeight) { @@ -1027,8 +1030,9 @@ class AnnotationEditor { this.setInForeground(); - this.div.addEventListener("focusin", this.#boundFocusin); - this.div.addEventListener("focusout", this.#boundFocusout); + const signal = this._uiManager._signal; + this.div.addEventListener("focusin", this.#boundFocusin, { signal }); + this.div.addEventListener("focusout", this.#boundFocusout, { signal }); const [parentWidth, parentHeight] = this.parentDimensions; if (this.parentRotation % 180 !== 0) { @@ -1089,9 +1093,10 @@ class AnnotationEditor { this._uiManager.setUpDragSession(); let pointerMoveOptions, pointerMoveCallback; + const signal = this._uiManager._signal; if (isSelected) { this.div.classList.add("moving"); - pointerMoveOptions = { passive: true, capture: true }; + pointerMoveOptions = { passive: true, capture: true, signal }; this.#prevDragX = event.clientX; this.#prevDragY = event.clientY; pointerMoveCallback = e => { @@ -1128,11 +1133,11 @@ class AnnotationEditor { this.#selectOnPointerEvent(event); } }; - window.addEventListener("pointerup", pointerUpCallback); + window.addEventListener("pointerup", pointerUpCallback, { signal }); // If the user is using alt+tab during the dragging session, the pointerup // event could be not fired, but a blur event is fired so we can use it in // order to interrupt the dragging session. - window.addEventListener("blur", pointerUpCallback); + window.addEventListener("blur", pointerUpCallback, { signal }); } moveInDOM() { @@ -1284,8 +1289,9 @@ class AnnotationEditor { * To implement in subclasses. */ rebuild() { - this.div?.addEventListener("focusin", this.#boundFocusin); - this.div?.addEventListener("focusout", this.#boundFocusout); + const signal = this._uiManager._signal; + this.div?.addEventListener("focusin", this.#boundFocusin, { signal }); + this.div?.addEventListener("focusout", this.#boundFocusout, { signal }); } /** @@ -1429,12 +1435,15 @@ class AnnotationEditor { this.#allResizerDivs = Array.from(children); const boundResizerKeydown = this.#resizerKeydown.bind(this); const boundResizerBlur = this.#resizerBlur.bind(this); + const signal = this._uiManager._signal; for (const div of this.#allResizerDivs) { const name = div.getAttribute("data-resizer-name"); div.setAttribute("role", "spinbutton"); - div.addEventListener("keydown", boundResizerKeydown); - div.addEventListener("blur", boundResizerBlur); - div.addEventListener("focus", this.#resizerFocus.bind(this, name)); + div.addEventListener("keydown", boundResizerKeydown, { signal }); + div.addEventListener("blur", boundResizerBlur, { signal }); + div.addEventListener("focus", this.#resizerFocus.bind(this, name), { + signal, + }); AnnotationEditor._l10nPromise .get(`pdfjs-editor-resizer-label-${name}`) .then(msg => div.setAttribute("aria-label", msg)); diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index 2280021d8c88c..fcd28f50b286d 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -307,11 +307,22 @@ class FreeTextEditor extends AnnotationEditor { this.editorDiv.contentEditable = true; this._isDraggable = false; this.div.removeAttribute("aria-activedescendant"); - this.editorDiv.addEventListener("keydown", this.#boundEditorDivKeydown); - this.editorDiv.addEventListener("focus", this.#boundEditorDivFocus); - this.editorDiv.addEventListener("blur", this.#boundEditorDivBlur); - this.editorDiv.addEventListener("input", this.#boundEditorDivInput); - this.editorDiv.addEventListener("paste", this.#boundEditorDivPaste); + const signal = this._uiManager._signal; + this.editorDiv.addEventListener("keydown", this.#boundEditorDivKeydown, { + signal, + }); + this.editorDiv.addEventListener("focus", this.#boundEditorDivFocus, { + signal, + }); + this.editorDiv.addEventListener("blur", this.#boundEditorDivBlur, { + signal, + }); + this.editorDiv.addEventListener("input", this.#boundEditorDivInput, { + signal, + }); + this.editorDiv.addEventListener("paste", this.#boundEditorDivPaste, { + signal, + }); } /** @inheritdoc */ diff --git a/src/display/editor/highlight.js b/src/display/editor/highlight.js index 58638d800c18b..9887b0778320f 100644 --- a/src/display/editor/highlight.js +++ b/src/display/editor/highlight.js @@ -568,7 +568,9 @@ class HighlightEditor extends AnnotationEditor { if (this.#isFreeHighlight) { div.classList.add("free"); } else { - this.div.addEventListener("keydown", this.#boundKeydown); + this.div.addEventListener("keydown", this.#boundKeydown, { + signal: this._uiManager._signal, + }); } const highlightDiv = (this.#highlightDiv = document.createElement("div")); div.append(highlightDiv); @@ -702,7 +704,8 @@ class HighlightEditor extends AnnotationEditor { const pointerMove = e => { this.#highlightMove(parent, e); }; - const pointerDownOptions = { capture: true, passive: false }; + const signal = parent._signal; + const pointerDownOptions = { capture: true, passive: false, signal }; const pointerDown = e => { // Avoid to have undesired clicks during the drawing. e.preventDefault(); @@ -720,12 +723,12 @@ class HighlightEditor extends AnnotationEditor { window.removeEventListener("contextmenu", noContextMenu); this.#endHighlight(parent, e); }; - window.addEventListener("blur", pointerUpCallback); - window.addEventListener("pointerup", pointerUpCallback); + window.addEventListener("blur", pointerUpCallback, { signal }); + window.addEventListener("pointerup", pointerUpCallback, { signal }); window.addEventListener("pointerdown", pointerDown, pointerDownOptions); - window.addEventListener("contextmenu", noContextMenu); + window.addEventListener("contextmenu", noContextMenu, { signal }); - textLayer.addEventListener("pointermove", pointerMove); + textLayer.addEventListener("pointermove", pointerMove, { signal }); this._freeHighlight = new FreeOutliner( { x, y }, [layerX, layerY, parentWidth, parentHeight], diff --git a/src/display/editor/ink.js b/src/display/editor/ink.js index f0794763421eb..3db72632eaeb1 100644 --- a/src/display/editor/ink.js +++ b/src/display/editor/ink.js @@ -261,7 +261,7 @@ class InkEditor extends AnnotationEditor { this.#canvasContextMenuTimeoutId = null; } - this.#observer.disconnect(); + this.#observer?.disconnect(); this.#observer = null; super.remove(); @@ -296,7 +296,9 @@ class InkEditor extends AnnotationEditor { super.enableEditMode(); this._isDraggable = false; - this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown); + this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown, { + signal: this._uiManager._signal, + }); } /** @inheritdoc */ @@ -363,10 +365,19 @@ class InkEditor extends AnnotationEditor { * @param {number} y */ #startDrawing(x, y) { - this.canvas.addEventListener("contextmenu", noContextMenu); - this.canvas.addEventListener("pointerleave", this.#boundCanvasPointerleave); - this.canvas.addEventListener("pointermove", this.#boundCanvasPointermove); - this.canvas.addEventListener("pointerup", this.#boundCanvasPointerup); + const signal = this._uiManager._signal; + this.canvas.addEventListener("contextmenu", noContextMenu, { signal }); + this.canvas.addEventListener( + "pointerleave", + this.#boundCanvasPointerleave, + { signal } + ); + this.canvas.addEventListener("pointermove", this.#boundCanvasPointermove, { + signal, + }); + this.canvas.addEventListener("pointerup", this.#boundCanvasPointerup, { + signal, + }); this.canvas.removeEventListener( "pointerdown", this.#boundCanvasPointerdown @@ -706,7 +717,9 @@ class InkEditor extends AnnotationEditor { this.#boundCanvasPointermove ); this.canvas.removeEventListener("pointerup", this.#boundCanvasPointerup); - this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown); + this.canvas.addEventListener("pointerdown", this.#boundCanvasPointerdown, { + signal: this._uiManager._signal, + }); // Slight delay to avoid the context menu to appear (it can happen on a long // tap with a pen). @@ -751,6 +764,14 @@ class InkEditor extends AnnotationEditor { } }); this.#observer.observe(this.div); + this._uiManager._signal.addEventListener( + "abort", + () => { + this.#observer?.disconnect(); + this.#observer = null; + }, + { once: true } + ); } /** @inheritdoc */ diff --git a/src/display/editor/stamp.js b/src/display/editor/stamp.js index a57b522f27b69..7d159357d0251 100644 --- a/src/display/editor/stamp.js +++ b/src/display/editor/stamp.js @@ -160,26 +160,35 @@ class StampEditor extends AnnotationEditor { } input.type = "file"; input.accept = StampEditor.supportedTypesStr; + const signal = this._uiManager._signal; this.#bitmapPromise = new Promise(resolve => { - input.addEventListener("change", async () => { - if (!input.files || input.files.length === 0) { + input.addEventListener( + "change", + async () => { + if (!input.files || input.files.length === 0) { + this.remove(); + } else { + this._uiManager.enableWaiting(true); + const data = await this._uiManager.imageManager.getFromFile( + input.files[0] + ); + this.#getBitmapFetched(data); + } + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { + input.remove(); + } + resolve(); + }, + { signal } + ); + input.addEventListener( + "cancel", + () => { this.remove(); - } else { - this._uiManager.enableWaiting(true); - const data = await this._uiManager.imageManager.getFromFile( - input.files[0] - ); - this.#getBitmapFetched(data); - } - if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING")) { - input.remove(); - } - resolve(); - }); - input.addEventListener("cancel", () => { - this.remove(); - resolve(); - }); + resolve(); + }, + { signal } + ); }).finally(() => this.#getBitmapDone()); if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("TESTING")) { input.click(); @@ -536,6 +545,14 @@ class StampEditor extends AnnotationEditor { } }); this.#observer.observe(this.div); + this._uiManager._signal.addEventListener( + "abort", + () => { + this.#observer?.disconnect(); + this.#observer = null; + }, + { once: true } + ); } /** @inheritdoc */ diff --git a/src/display/editor/toolbar.js b/src/display/editor/toolbar.js index ea493004537c1..66c0b93ede31b 100644 --- a/src/display/editor/toolbar.js +++ b/src/display/editor/toolbar.js @@ -32,8 +32,11 @@ class EditorToolbar { const editToolbar = (this.#toolbar = document.createElement("div")); editToolbar.className = "editToolbar"; editToolbar.setAttribute("role", "toolbar"); - editToolbar.addEventListener("contextmenu", noContextMenu); - editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown); + const signal = this.#editor._uiManager._signal; + editToolbar.addEventListener("contextmenu", noContextMenu, { signal }); + editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown, { + signal, + }); const buttons = (this.#buttons = document.createElement("div")); buttons.className = "buttons"; @@ -77,13 +80,16 @@ class EditorToolbar { // If we're clicking on a button with the keyboard or with // the mouse, we don't want to trigger any focus events on // the editor. + const signal = this.#editor._uiManager._signal; element.addEventListener("focusin", this.#focusIn.bind(this), { capture: true, + signal, }); element.addEventListener("focusout", this.#focusOut.bind(this), { capture: true, + signal, }); - element.addEventListener("contextmenu", noContextMenu); + element.addEventListener("contextmenu", noContextMenu, { signal }); } hide() { @@ -104,9 +110,13 @@ class EditorToolbar { `pdfjs-editor-remove-${this.#editor.editorType}-button` ); this.#addListenersToElement(button); - button.addEventListener("click", e => { - this.#editor._uiManager.delete(); - }); + button.addEventListener( + "click", + e => { + this.#editor._uiManager.delete(); + }, + { signal: this.#editor._uiManager._signal } + ); this.#buttons.append(button); } @@ -150,7 +160,9 @@ class HighlightToolbar { const editToolbar = (this.#toolbar = document.createElement("div")); editToolbar.className = "editToolbar"; editToolbar.setAttribute("role", "toolbar"); - editToolbar.addEventListener("contextmenu", noContextMenu); + editToolbar.addEventListener("contextmenu", noContextMenu, { + signal: this.#uiManager._signal, + }); const buttons = (this.#buttons = document.createElement("div")); buttons.className = "buttons"; @@ -207,10 +219,15 @@ class HighlightToolbar { button.append(span); span.className = "visuallyHidden"; span.setAttribute("data-l10n-id", "pdfjs-highlight-floating-button-label"); - button.addEventListener("contextmenu", noContextMenu); - button.addEventListener("click", () => { - this.#uiManager.highlightSelection("floating_button"); - }); + const signal = this.#uiManager._signal; + button.addEventListener("contextmenu", noContextMenu, { signal }); + button.addEventListener( + "click", + () => { + this.#uiManager.highlightSelection("floating_button"); + }, + { signal } + ); this.#buttons.append(button); } } diff --git a/src/display/editor/tools.js b/src/display/editor/tools.js index 14444cf9461fa..2fb916c72969d 100644 --- a/src/display/editor/tools.js +++ b/src/display/editor/tools.js @@ -534,6 +534,8 @@ class ColorManager { * some action like copy/paste, undo/redo, ... */ class AnnotationEditorUIManager { + #abortController = new AbortController(); + #activeEditor = null; #allEditors = new Map(); @@ -600,10 +602,6 @@ class AnnotationEditorUIManager { #boundCut = this.cut.bind(this); - #boundDragOver = this.dragOver.bind(this); - - #boundDrop = this.drop.bind(this); - #boundPaste = this.paste.bind(this); #boundKeydown = this.keydown.bind(this); @@ -616,8 +614,6 @@ class AnnotationEditorUIManager { #boundOnScaleChanging = this.onScaleChanging.bind(this); - #boundSelectionChange = this.#selectionChange.bind(this); - #boundOnRotationChanging = this.onRotationChanging.bind(this); #previousStates = { @@ -785,6 +781,7 @@ class AnnotationEditorUIManager { enableHighlightFloatingButton, mlManager ) { + this._signal = this.#abortController.signal; this.#container = container; this.#viewer = viewer; this.#altTextManager = altTextManager; @@ -820,9 +817,10 @@ class AnnotationEditorUIManager { } destroy() { - this.#removeDragAndDropListeners(); - this.#removeKeyboardManager(); - this.#removeFocusManager(); + this.#abortController?.abort(); + this.#abortController = null; + this._signal = null; + this._eventBus._off("editingaction", this.#boundOnEditingAction); this._eventBus._off("pagechanging", this.#boundOnPageChanging); this._eventBus._off("scalechanging", this.#boundOnScaleChanging); @@ -847,7 +845,6 @@ class AnnotationEditorUIManager { clearTimeout(this.#translationTimeoutId); this.#translationTimeoutId = null; } - this.#removeSelectionListener(); } async mlGuess(data) { @@ -1084,6 +1081,7 @@ class AnnotationEditorUIManager { this.#highlightWhenShiftUp = this.isShiftKeyDown; if (!this.isShiftKeyDown) { + const signal = this._signal; const pointerup = e => { if (e.type === "pointerup" && e.button !== 0) { // Do nothing on right click. @@ -1095,8 +1093,8 @@ class AnnotationEditorUIManager { this.#onSelectEnd("main_toolbar"); } }; - window.addEventListener("pointerup", pointerup); - window.addEventListener("blur", pointerup); + window.addEventListener("pointerup", pointerup, { signal }); + window.addEventListener("blur", pointerup, { signal }); } } @@ -1109,16 +1107,19 @@ class AnnotationEditorUIManager { } #addSelectionListener() { - document.addEventListener("selectionchange", this.#boundSelectionChange); - } - - #removeSelectionListener() { - document.removeEventListener("selectionchange", this.#boundSelectionChange); + document.addEventListener( + "selectionchange", + this.#selectionChange.bind(this), + { + signal: this._signal, + } + ); } #addFocusManager() { - window.addEventListener("focus", this.#boundFocus); - window.addEventListener("blur", this.#boundBlur); + const signal = this._signal; + window.addEventListener("focus", this.#boundFocus, { signal }); + window.addEventListener("blur", this.#boundBlur, { signal }); } #removeFocusManager() { @@ -1160,16 +1161,17 @@ class AnnotationEditorUIManager { () => { lastEditor._focusEventsAllowed = true; }, - { once: true } + { once: true, signal: this._signal } ); lastActiveElement.focus(); } #addKeyboardManager() { + const signal = this._signal; // The keyboard events are caught at the container level in order to be able // to execute some callbacks even if the current page doesn't have focus. - window.addEventListener("keydown", this.#boundKeydown); - window.addEventListener("keyup", this.#boundKeyup); + window.addEventListener("keydown", this.#boundKeydown, { signal }); + window.addEventListener("keyup", this.#boundKeyup, { signal }); } #removeKeyboardManager() { @@ -1178,9 +1180,10 @@ class AnnotationEditorUIManager { } #addCopyPasteListeners() { - document.addEventListener("copy", this.#boundCopy); - document.addEventListener("cut", this.#boundCut); - document.addEventListener("paste", this.#boundPaste); + const signal = this._signal; + document.addEventListener("copy", this.#boundCopy, { signal }); + document.addEventListener("cut", this.#boundCut, { signal }); + document.addEventListener("paste", this.#boundPaste, { signal }); } #removeCopyPasteListeners() { @@ -1190,13 +1193,9 @@ class AnnotationEditorUIManager { } #addDragAndDropListeners() { - document.addEventListener("dragover", this.#boundDragOver); - document.addEventListener("drop", this.#boundDrop); - } - - #removeDragAndDropListeners() { - document.removeEventListener("dragover", this.#boundDragOver); - document.removeEventListener("drop", this.#boundDrop); + const signal = this._signal; + document.addEventListener("dragover", this.dragOver.bind(this), { signal }); + document.addEventListener("drop", this.drop.bind(this), { signal }); } addEditListeners() {