From f0aa08b46454494f4a0936d2291214d2db206758 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 25 Mar 2022 14:10:13 +0100 Subject: [PATCH] Convert the existing overlays to use `` elements (issue 14698) This replaces our *custom* overlays with standard `` DOM elements, see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog, thus simplifying the related CSS, HTML, and JavaScript code. With these changes, some of the functionality of the `OverlayManager` class is now handled natively (e.g. `Esc` to close the dialog). However, since we still need to be able to prevent dialogs from overlaying one another, it still makes sense to keep this functionality (as far as I'm concerned). --- web/chromecom.js | 16 ++- web/overlay_manager.js | 62 ++-------- web/password_prompt.js | 52 ++++---- web/pdf_document_properties.js | 24 ++-- web/pdf_print_service.js | 30 ++--- web/viewer-snippet-chrome-overlays.html | 60 +++++----- web/viewer.css | 89 ++++++-------- web/viewer.html | 152 ++++++++++++------------ web/viewer.js | 8 +- 9 files changed, 221 insertions(+), 272 deletions(-) diff --git a/web/chromecom.js b/web/chromecom.js index 823251221..61c2cb636 100644 --- a/web/chromecom.js +++ b/web/chromecom.js @@ -147,7 +147,7 @@ function reloadIfRuntimeIsUnavailable() { let chromeFileAccessOverlayPromise; function requestAccessToLocalFile(fileUrl, overlayManager, callback) { - let onCloseOverlay = null; + const dialog = document.getElementById("chromeFileAccessDialog"); if (top !== window) { // When the extension reloads after receiving new permissions, the pages // have to be reloaded to restore the extension runtime. Auto-reload @@ -157,18 +157,16 @@ function requestAccessToLocalFile(fileUrl, overlayManager, callback) { // for detecting unload of the top-level frame. Should this ever change // (crbug.com/511670), then the user can just reload the tab. window.addEventListener("focus", reloadIfRuntimeIsUnavailable); - onCloseOverlay = function () { + dialog.addEventListener("close", function () { window.removeEventListener("focus", reloadIfRuntimeIsUnavailable); reloadIfRuntimeIsUnavailable(); - overlayManager.close("chromeFileAccessOverlay"); - }; + }); } if (!chromeFileAccessOverlayPromise) { chromeFileAccessOverlayPromise = overlayManager.register( - "chromeFileAccessOverlay", - document.getElementById("chromeFileAccessOverlay"), - onCloseOverlay, - true + "chromeFileAccessDialog", + dialog, + /* canForceClose = */ true ); } chromeFileAccessOverlayPromise.then(function () { @@ -233,7 +231,7 @@ function requestAccessToLocalFile(fileUrl, overlayManager, callback) { } }; - overlayManager.open("chromeFileAccessOverlay"); + overlayManager.open("chromeFileAccessDialog"); }); } diff --git a/web/overlay_manager.js b/web/overlay_manager.js index b77f4a61a..d7d8ee2c8 100644 --- a/web/overlay_manager.js +++ b/web/overlay_manager.js @@ -18,43 +18,29 @@ class OverlayManager { #active = null; - #keyDownBound = null; - get active() { return this.#active; } /** * @param {string} name - The name of the overlay that is registered. - * @param {HTMLDivElement} element - The overlay's DOM element. - * @param {function} [callerCloseMethod] - The method that, if present, calls - * `OverlayManager.close` from the object registering the - * overlay. Access to this method is necessary in order to - * run cleanup code when e.g. the overlay is force closed. - * The default is `null`. + * @param {HTMLDialogElement} dialog - The overlay's DOM element. * @param {boolean} [canForceClose] - Indicates if opening the overlay closes * an active overlay. The default is `false`. * @returns {Promise} A promise that is resolved when the overlay has been * registered. */ - async register( - name, - element, - callerCloseMethod = null, - canForceClose = false - ) { - let container; - if (!name || !element || !(container = element.parentNode)) { + async register(name, dialog, canForceClose = false) { + if (!name || !dialog) { throw new Error("Not enough parameters."); } else if (this.#overlays[name]) { throw new Error("The overlay is already registered."); } - this.#overlays[name] = { - element, - container, - callerCloseMethod, - canForceClose, - }; + this.#overlays[name] = { dialog, canForceClose }; + + dialog.addEventListener("cancel", evt => { + this.#active = null; + }); } /** @@ -83,17 +69,13 @@ class OverlayManager { if (this.#active === name) { throw new Error("The overlay is already active."); } else if (this.#overlays[name].canForceClose) { - this.#closeThroughCaller(); + await this.close(); } else { throw new Error("Another overlay is currently active."); } } this.#active = name; - this.#overlays[this.#active].element.classList.remove("hidden"); - this.#overlays[this.#active].container.classList.remove("hidden"); - - this.#keyDownBound = this.#keyDown.bind(this); - window.addEventListener("keydown", this.#keyDownBound); + this.#overlays[this.#active].dialog.showModal(); } /** @@ -101,7 +83,7 @@ class OverlayManager { * @returns {Promise} A promise that is resolved when the overlay has been * closed. */ - async close(name) { + async close(name = this.#active) { if (!this.#overlays[name]) { throw new Error("The overlay does not exist."); } else if (!this.#active) { @@ -109,28 +91,8 @@ class OverlayManager { } else if (this.#active !== name) { throw new Error("Another overlay is currently active."); } - this.#overlays[this.#active].container.classList.add("hidden"); - this.#overlays[this.#active].element.classList.add("hidden"); + this.#overlays[this.#active].dialog.close(); this.#active = null; - - window.removeEventListener("keydown", this.#keyDownBound); - this.#keyDownBound = null; - } - - #keyDown(evt) { - if (this.#active && evt.keyCode === /* Esc = */ 27) { - this.#closeThroughCaller(); - evt.preventDefault(); - } - } - - #closeThroughCaller() { - if (this.#overlays[this.#active].callerCloseMethod) { - this.#overlays[this.#active].callerCloseMethod(); - } - if (this.#active) { - this.close(this.#active); - } } } diff --git a/web/password_prompt.js b/web/password_prompt.js index 6631783c7..1a8728553 100644 --- a/web/password_prompt.js +++ b/web/password_prompt.js @@ -17,8 +17,8 @@ import { PasswordResponses } from "pdfjs-lib"; /** * @typedef {Object} PasswordPromptOptions - * @property {string} overlayName - Name of the overlay for the overlay manager. - * @property {HTMLDivElement} container - Div container for the overlay. + * @property {string} dialogName - Name/identifier for the dialog. + * @property {HTMLDialogElement} dialog - The overlay's DOM element. * @property {HTMLParagraphElement} label - Label containing instructions for * entering the password. * @property {HTMLInputElement} input - Input field for entering the password. @@ -29,6 +29,10 @@ import { PasswordResponses } from "pdfjs-lib"; */ class PasswordPrompt { + #updateCallback = null; + + #reason = null; + /** * @param {PasswordPromptOptions} options * @param {OverlayManager} overlayManager - Manager for the viewer overlays. @@ -37,8 +41,8 @@ class PasswordPrompt { * an