Merge pull request #14710 from Snuffleupagus/overlays-dialog

Convert the existing overlays to use `<dialog>` elements (issue 14698)
This commit is contained in:
Jonas Jenwald 2022-03-30 11:47:36 +02:00 committed by GitHub
commit d6592b5e37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 270 additions and 302 deletions

13
package-lock.json generated
View File

@ -21,6 +21,7 @@
"canvas": "^2.9.1", "canvas": "^2.9.1",
"core-js": "^3.21.1", "core-js": "^3.21.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dialog-polyfill": "^0.5.6",
"dommatrix": "^0.0.24", "dommatrix": "^0.0.24",
"es-module-shims": "1.4.7", "es-module-shims": "1.4.7",
"eslint": "^8.11.0", "eslint": "^8.11.0",
@ -4350,6 +4351,12 @@
"kuler": "1.0.x" "kuler": "1.0.x"
} }
}, },
"node_modules/dialog-polyfill": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.5.6.tgz",
"integrity": "sha512-ZbVDJI9uvxPAKze6z146rmfUZjBqNEwcnFTVamQzXH+svluiV7swmVIGr7miwADgfgt1G2JQIytypM9fbyhX4w==",
"dev": true
},
"node_modules/dir-glob": { "node_modules/dir-glob": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@ -21951,6 +21958,12 @@
"kuler": "1.0.x" "kuler": "1.0.x"
} }
}, },
"dialog-polyfill": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.5.6.tgz",
"integrity": "sha512-ZbVDJI9uvxPAKze6z146rmfUZjBqNEwcnFTVamQzXH+svluiV7swmVIGr7miwADgfgt1G2JQIytypM9fbyhX4w==",
"dev": true
},
"dir-glob": { "dir-glob": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",

View File

@ -14,6 +14,7 @@
"canvas": "^2.9.1", "canvas": "^2.9.1",
"core-js": "^3.21.1", "core-js": "^3.21.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dialog-polyfill": "^0.5.6",
"dommatrix": "^0.0.24", "dommatrix": "^0.0.24",
"es-module-shims": "1.4.7", "es-module-shims": "1.4.7",
"eslint": "^8.11.0", "eslint": "^8.11.0",

View File

@ -147,7 +147,7 @@ function reloadIfRuntimeIsUnavailable() {
let chromeFileAccessOverlayPromise; let chromeFileAccessOverlayPromise;
function requestAccessToLocalFile(fileUrl, overlayManager, callback) { function requestAccessToLocalFile(fileUrl, overlayManager, callback) {
let onCloseOverlay = null; const dialog = document.getElementById("chromeFileAccessDialog");
if (top !== window) { if (top !== window) {
// When the extension reloads after receiving new permissions, the pages // When the extension reloads after receiving new permissions, the pages
// have to be reloaded to restore the extension runtime. Auto-reload // have to be reloaded to restore the extension runtime. Auto-reload
@ -157,20 +157,16 @@ function requestAccessToLocalFile(fileUrl, overlayManager, callback) {
// for detecting unload of the top-level frame. Should this ever change // for detecting unload of the top-level frame. Should this ever change
// (crbug.com/511670), then the user can just reload the tab. // (crbug.com/511670), then the user can just reload the tab.
window.addEventListener("focus", reloadIfRuntimeIsUnavailable); window.addEventListener("focus", reloadIfRuntimeIsUnavailable);
onCloseOverlay = function () { dialog.addEventListener("close", function () {
window.removeEventListener("focus", reloadIfRuntimeIsUnavailable); window.removeEventListener("focus", reloadIfRuntimeIsUnavailable);
reloadIfRuntimeIsUnavailable(); reloadIfRuntimeIsUnavailable();
overlayManager.close("chromeFileAccessOverlay"); });
};
}
if (!chromeFileAccessOverlayPromise) {
chromeFileAccessOverlayPromise = overlayManager.register(
"chromeFileAccessOverlay",
document.getElementById("chromeFileAccessOverlay"),
onCloseOverlay,
true
);
} }
chromeFileAccessOverlayPromise ||= overlayManager.register(
dialog,
/* canForceClose = */ true
);
chromeFileAccessOverlayPromise.then(function () { chromeFileAccessOverlayPromise.then(function () {
const iconPath = chrome.runtime.getManifest().icons[48]; const iconPath = chrome.runtime.getManifest().icons[48];
document.getElementById("chrome-pdfjs-logo-bg").style.backgroundImage = document.getElementById("chrome-pdfjs-logo-bg").style.backgroundImage =
@ -229,11 +225,11 @@ function requestAccessToLocalFile(fileUrl, overlayManager, callback) {
originalUrl = "file:///fakepath/to/" + encodeURIComponent(file.name); originalUrl = "file:///fakepath/to/" + encodeURIComponent(file.name);
} }
callback(URL.createObjectURL(file), file.size, originalUrl); callback(URL.createObjectURL(file), file.size, originalUrl);
overlayManager.close("chromeFileAccessOverlay"); overlayManager.close(dialog);
} }
}; };
overlayManager.open("chromeFileAccessOverlay"); overlayManager.open(dialog);
}); });
} }

View File

@ -14,123 +14,93 @@
*/ */
class OverlayManager { class OverlayManager {
#overlays = Object.create(null); #overlays = new WeakMap();
#active = null; #active = null;
#keyDownBound = null;
get active() { get active() {
return this.#active; return this.#active;
} }
/** /**
* @param {string} name - The name of the overlay that is registered. * @param {HTMLDialogElement} dialog - The overlay's DOM element.
* @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 {boolean} [canForceClose] - Indicates if opening the overlay closes * @param {boolean} [canForceClose] - Indicates if opening the overlay closes
* an active overlay. The default is `false`. * an active overlay. The default is `false`.
* @returns {Promise} A promise that is resolved when the overlay has been * @returns {Promise} A promise that is resolved when the overlay has been
* registered. * registered.
*/ */
async register( async register(dialog, canForceClose = false) {
name, if (typeof dialog !== "object") {
element,
callerCloseMethod = null,
canForceClose = false
) {
let container;
if (!name || !element || !(container = element.parentNode)) {
throw new Error("Not enough parameters."); throw new Error("Not enough parameters.");
} else if (this.#overlays[name]) { } else if (this.#overlays.has(dialog)) {
throw new Error("The overlay is already registered."); throw new Error("The overlay is already registered.");
} }
this.#overlays[name] = { this.#overlays.set(dialog, { canForceClose });
element,
container, if (
callerCloseMethod, typeof PDFJSDev !== "undefined" &&
canForceClose, PDFJSDev.test("GENERIC && !SKIP_BABEL") &&
}; !dialog.showModal
) {
const dialogPolyfill = require("dialog-polyfill/dist/dialog-polyfill.js");
dialogPolyfill.registerDialog(dialog);
}
dialog.addEventListener("cancel", evt => {
this.#active = null;
});
} }
/** /**
* @param {string} name - The name of the overlay that is unregistered. * @param {HTMLDialogElement} dialog - The overlay's DOM element.
* @returns {Promise} A promise that is resolved when the overlay has been * @returns {Promise} A promise that is resolved when the overlay has been
* unregistered. * unregistered.
*/ */
async unregister(name) { async unregister(dialog) {
if (!this.#overlays[name]) { if (!this.#overlays.has(dialog)) {
throw new Error("The overlay does not exist."); throw new Error("The overlay does not exist.");
} else if (this.#active === name) { } else if (this.#active === dialog) {
throw new Error("The overlay cannot be removed while it is active."); throw new Error("The overlay cannot be removed while it is active.");
} }
delete this.#overlays[name]; this.#overlays.delete(dialog);
} }
/** /**
* @param {string} name - The name of the overlay that should be opened. * @param {HTMLDialogElement} dialog - The overlay's DOM element.
* @returns {Promise} A promise that is resolved when the overlay has been * @returns {Promise} A promise that is resolved when the overlay has been
* opened. * opened.
*/ */
async open(name) { async open(dialog) {
if (!this.#overlays[name]) { if (!this.#overlays.has(dialog)) {
throw new Error("The overlay does not exist."); throw new Error("The overlay does not exist.");
} else if (this.#active) { } else if (this.#active) {
if (this.#active === name) { if (this.#active === dialog) {
throw new Error("The overlay is already active."); throw new Error("The overlay is already active.");
} else if (this.#overlays[name].canForceClose) { } else if (this.#overlays.get(dialog).canForceClose) {
this.#closeThroughCaller(); await this.close();
} else { } else {
throw new Error("Another overlay is currently active."); throw new Error("Another overlay is currently active.");
} }
} }
this.#active = name; this.#active = dialog;
this.#overlays[this.#active].element.classList.remove("hidden"); dialog.showModal();
this.#overlays[this.#active].container.classList.remove("hidden");
this.#keyDownBound = this.#keyDown.bind(this);
window.addEventListener("keydown", this.#keyDownBound);
} }
/** /**
* @param {string} name - The name of the overlay that should be closed. * @param {HTMLDialogElement} dialog - The overlay's DOM element.
* @returns {Promise} A promise that is resolved when the overlay has been * @returns {Promise} A promise that is resolved when the overlay has been
* closed. * closed.
*/ */
async close(name) { async close(dialog = this.#active) {
if (!this.#overlays[name]) { if (!this.#overlays.has(dialog)) {
throw new Error("The overlay does not exist."); throw new Error("The overlay does not exist.");
} else if (!this.#active) { } else if (!this.#active) {
throw new Error("The overlay is currently not active."); throw new Error("The overlay is currently not active.");
} else if (this.#active !== name) { } else if (this.#active !== dialog) {
throw new Error("Another overlay is currently active."); throw new Error("Another overlay is currently active.");
} }
this.#overlays[this.#active].container.classList.add("hidden"); dialog.close();
this.#overlays[this.#active].element.classList.add("hidden");
this.#active = null; 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);
}
} }
} }

View File

@ -17,8 +17,7 @@ import { PasswordResponses } from "pdfjs-lib";
/** /**
* @typedef {Object} PasswordPromptOptions * @typedef {Object} PasswordPromptOptions
* @property {string} overlayName - Name of the overlay for the overlay manager. * @property {HTMLDialogElement} dialog - The overlay's DOM element.
* @property {HTMLDivElement} container - Div container for the overlay.
* @property {HTMLParagraphElement} label - Label containing instructions for * @property {HTMLParagraphElement} label - Label containing instructions for
* entering the password. * entering the password.
* @property {HTMLInputElement} input - Input field for entering the password. * @property {HTMLInputElement} input - Input field for entering the password.
@ -29,6 +28,10 @@ import { PasswordResponses } from "pdfjs-lib";
*/ */
class PasswordPrompt { class PasswordPrompt {
#updateCallback = null;
#reason = null;
/** /**
* @param {PasswordPromptOptions} options * @param {PasswordPromptOptions} options
* @param {OverlayManager} overlayManager - Manager for the viewer overlays. * @param {OverlayManager} overlayManager - Manager for the viewer overlays.
@ -37,8 +40,7 @@ class PasswordPrompt {
* an <iframe> or an <object>. The default value is `false`. * an <iframe> or an <object>. The default value is `false`.
*/ */
constructor(options, overlayManager, l10n, isViewerEmbedded = false) { constructor(options, overlayManager, l10n, isViewerEmbedded = false) {
this.overlayName = options.overlayName; this.dialog = options.dialog;
this.container = options.container;
this.label = options.label; this.label = options.label;
this.input = options.input; this.input = options.input;
this.submitButton = options.submitButton; this.submitButton = options.submitButton;
@ -47,9 +49,6 @@ class PasswordPrompt {
this.l10n = l10n; this.l10n = l10n;
this._isViewerEmbedded = isViewerEmbedded; this._isViewerEmbedded = isViewerEmbedded;
this.updateCallback = null;
this.reason = null;
// Attach the event listeners. // Attach the event listeners.
this.submitButton.addEventListener("click", this.#verify.bind(this)); this.submitButton.addEventListener("click", this.#verify.bind(this));
this.cancelButton.addEventListener("click", this.#cancel.bind(this)); this.cancelButton.addEventListener("click", this.#cancel.bind(this));
@ -59,19 +58,16 @@ class PasswordPrompt {
} }
}); });
this.overlayManager.register( this.overlayManager.register(this.dialog, /* canForceClose = */ true);
this.overlayName,
this.container, this.dialog.addEventListener("close", this.#cancel.bind(this));
this.#cancel.bind(this),
true
);
} }
async open() { async open() {
await this.overlayManager.open(this.overlayName); await this.overlayManager.open(this.dialog);
const passwordIncorrect = const passwordIncorrect =
this.reason === PasswordResponses.INCORRECT_PASSWORD; this.#reason === PasswordResponses.INCORRECT_PASSWORD;
if (!this._isViewerEmbedded || passwordIncorrect) { if (!this._isViewerEmbedded || passwordIncorrect) {
this.input.focus(); this.input.focus();
@ -82,26 +78,36 @@ class PasswordPrompt {
} }
async close() { async close() {
await this.overlayManager.close(this.overlayName); if (this.overlayManager.active === this.dialog) {
this.input.value = ""; this.overlayManager.close(this.dialog);
}
} }
#verify() { #verify() {
const password = this.input.value; const password = this.input.value;
if (password?.length > 0) { if (password?.length > 0) {
this.close(); this.#invokeCallback(password);
this.updateCallback(password);
} }
} }
#cancel() { #cancel() {
this.#invokeCallback(new Error("PasswordPrompt cancelled."));
}
#invokeCallback(password) {
if (!this.#updateCallback) {
return; // Ensure that the callback is only invoked once.
}
this.close(); this.close();
this.updateCallback(new Error("PasswordPrompt cancelled.")); this.input.value = "";
this.#updateCallback(password);
this.#updateCallback = null;
} }
setUpdateCallback(updateCallback, reason) { setUpdateCallback(updateCallback, reason) {
this.updateCallback = updateCallback; this.#updateCallback = updateCallback;
this.reason = reason; this.#reason = reason;
} }
} }

View File

@ -45,9 +45,8 @@ function getPageName(size, isPortrait, pageNames) {
/** /**
* @typedef {Object} PDFDocumentPropertiesOptions * @typedef {Object} PDFDocumentPropertiesOptions
* @property {string} overlayName - Name/identifier for the overlay. * @property {HTMLDialogElement} dialog - The overlay's DOM element.
* @property {Object} fields - Names and elements of the overlay's fields. * @property {Object} fields - Names and elements of the overlay's fields.
* @property {HTMLDivElement} container - Div container for the overlay.
* @property {HTMLButtonElement} closeButton - Button for closing the overlay. * @property {HTMLButtonElement} closeButton - Button for closing the overlay.
*/ */
@ -60,15 +59,9 @@ class PDFDocumentProperties {
* @param {EventBus} eventBus - The application event bus. * @param {EventBus} eventBus - The application event bus.
* @param {IL10n} l10n - Localization service. * @param {IL10n} l10n - Localization service.
*/ */
constructor( constructor({ dialog, fields, closeButton }, overlayManager, eventBus, l10n) {
{ overlayName, fields, container, closeButton }, this.dialog = dialog;
overlayManager,
eventBus,
l10n
) {
this.overlayName = overlayName;
this.fields = fields; this.fields = fields;
this.container = container;
this.overlayManager = overlayManager; this.overlayManager = overlayManager;
this.l10n = l10n; this.l10n = l10n;
@ -76,11 +69,7 @@ class PDFDocumentProperties {
// Bind the event listener for the Close button. // Bind the event listener for the Close button.
closeButton.addEventListener("click", this.close.bind(this)); closeButton.addEventListener("click", this.close.bind(this));
this.overlayManager.register( this.overlayManager.register(this.dialog);
this.overlayName,
this.container,
this.close.bind(this)
);
eventBus._on("pagechanging", evt => { eventBus._on("pagechanging", evt => {
this._currentPageNumber = evt.pageNumber; this._currentPageNumber = evt.pageNumber;
@ -100,7 +89,7 @@ class PDFDocumentProperties {
*/ */
async open() { async open() {
await Promise.all([ await Promise.all([
this.overlayManager.open(this.overlayName), this.overlayManager.open(this.dialog),
this._dataAvailableCapability.promise, this._dataAvailableCapability.promise,
]); ]);
const currentPageNumber = this._currentPageNumber; const currentPageNumber = this._currentPageNumber;
@ -179,8 +168,8 @@ class PDFDocumentProperties {
/** /**
* Close the document properties overlay. * Close the document properties overlay.
*/ */
close() { async close() {
this.overlayManager.close(this.overlayName); this.overlayManager.close(this.dialog);
} }
/** /**
@ -228,7 +217,7 @@ class PDFDocumentProperties {
} }
return; return;
} }
if (this.overlayManager.active !== this.overlayName) { if (this.overlayManager.active !== this.dialog) {
// Don't bother updating the dialog if has already been closed, // Don't bother updating the dialog if has already been closed,
// since it will be updated the next time `this.open` is called. // since it will be updated the next time `this.open` is called.
return; return;

View File

@ -18,6 +18,7 @@ import { PDFPrintServiceFactory, PDFViewerApplication } from "./app.js";
import { getXfaHtmlForPrinting } from "./print_utils.js"; import { getXfaHtmlForPrinting } from "./print_utils.js";
let activeService = null; let activeService = null;
let dialog = null;
let overlayManager = null; let overlayManager = null;
// Renders the page to the canvas of the given print service, and returns // Renders the page to the canvas of the given print service, and returns
@ -115,8 +116,7 @@ PDFPrintService.prototype = {
destroy() { destroy() {
if (activeService !== this) { if (activeService !== this) {
// |activeService| cannot be replaced without calling destroy() first, // |activeService| cannot be replaced without calling destroy() first,
// so if it differs then an external consumer has a stale reference to // so if it differs then an external consumer has a stale reference to us.
// us.
return; return;
} }
this.printContainer.textContent = ""; this.printContainer.textContent = "";
@ -132,10 +132,9 @@ PDFPrintService.prototype = {
this.scratchCanvas = null; this.scratchCanvas = null;
activeService = null; activeService = null;
ensureOverlay().then(function () { ensureOverlay().then(function () {
if (overlayManager.active !== "printServiceOverlay") { if (overlayManager.active === dialog) {
return; // overlay was already closed overlayManager.close(dialog);
} }
overlayManager.close("printServiceOverlay");
}); });
}, },
@ -231,7 +230,7 @@ window.print = function () {
} }
ensureOverlay().then(function () { ensureOverlay().then(function () {
if (activeService) { if (activeService) {
overlayManager.open("printServiceOverlay"); overlayManager.open(dialog);
} }
}); });
@ -241,8 +240,8 @@ window.print = function () {
if (!activeService) { if (!activeService) {
console.error("Expected print service to be initialized."); console.error("Expected print service to be initialized.");
ensureOverlay().then(function () { ensureOverlay().then(function () {
if (overlayManager.active === "printServiceOverlay") { if (overlayManager.active === dialog) {
overlayManager.close("printServiceOverlay"); overlayManager.close(dialog);
} }
}); });
return; // eslint-disable-line no-unsafe-finally return; // eslint-disable-line no-unsafe-finally
@ -283,10 +282,10 @@ function abort() {
} }
function renderProgress(index, total, l10n) { function renderProgress(index, total, l10n) {
const progressContainer = document.getElementById("printServiceOverlay"); dialog ||= document.getElementById("printServiceDialog");
const progress = Math.round((100 * index) / total); const progress = Math.round((100 * index) / total);
const progressBar = progressContainer.querySelector("progress"); const progressBar = dialog.querySelector("progress");
const progressPerc = progressContainer.querySelector(".relative-progress"); const progressPerc = dialog.querySelector(".relative-progress");
progressBar.value = progress; progressBar.value = progress;
l10n.get("print_progress_percent", { progress }).then(msg => { l10n.get("print_progress_percent", { progress }).then(msg => {
progressPerc.textContent = msg; progressPerc.textContent = msg;
@ -338,14 +337,15 @@ function ensureOverlay() {
if (!overlayManager) { if (!overlayManager) {
throw new Error("The overlay manager has not yet been initialized."); throw new Error("The overlay manager has not yet been initialized.");
} }
dialog ||= document.getElementById("printServiceDialog");
overlayPromise = overlayManager.register( overlayPromise = overlayManager.register(
"printServiceOverlay", dialog,
document.getElementById("printServiceOverlay"), /* canForceClose = */ true
abort,
true
); );
document.getElementById("printCancel").onclick = abort; document.getElementById("printCancel").onclick = abort;
dialog.addEventListener("close", abort);
} }
return overlayPromise; return overlayPromise;
} }

View File

@ -1,32 +1,30 @@
<div id="chromeFileAccessOverlay" class="container hidden"> <dialog id="chromeFileAccessDialog">
<div class="dialog"> <div class="row">
<div class="row"> <!-- The extension icon (PDF.js logo) will be shown at the left, to help
<!-- The extension icon (PDF.js logo) will be shown at the left, to help users with recognizing which checkbox they have to click when they
users with recognizing which checkbox they have to click when they visit chrome://extensions.
visit chrome://extensions. -->
--> <p id="chrome-pdfjs-logo-bg" style="
<p id="chrome-pdfjs-logo-bg" style=" display: block;
display: block; padding-left: 60px;
padding-left: 60px; min-height: 48px;
min-height: 48px; background-size: 48px;
background-size: 48px; background-repeat: no-repeat;
background-repeat: no-repeat; font-size: 14px;
font-size: 14px; line-height: 1.8em;
line-height: 1.8em; word-break: break-all;">
word-break: break-all;"> Click on
Click on "<span id="chrome-file-access-label">Allow access to file URLs</span>"
"<span id="chrome-file-access-label">Allow access to file URLs</span>" at
at <a id="chrome-link-to-extensions-page">chrome://extensions</a>
<a id="chrome-link-to-extensions-page">chrome://extensions</a> <br>
<br> to view <span id="chrome-url-of-local-file">this PDF file.</span>
to view <span id="chrome-url-of-local-file">this PDF file.</span> </p>
</p>
</div>
<div class="row">
<p>
or select the file again:
<input type="file" id="chrome-file-fallback" accept=".pdf">
</p>
</div>
</div> </div>
</div> <div class="row">
<p>
or select the file again:
<input type="file" id="chrome-file-fallback" accept=".pdf">
</p>
</div>
</dialog>

View File

@ -62,9 +62,9 @@
--doorhanger-hover-color: rgba(12, 12, 13, 1); --doorhanger-hover-color: rgba(12, 12, 13, 1);
--doorhanger-hover-bg-color: rgba(237, 237, 237, 1); --doorhanger-hover-bg-color: rgba(237, 237, 237, 1);
--doorhanger-separator-color: rgba(222, 222, 222, 1); --doorhanger-separator-color: rgba(222, 222, 222, 1);
--overlay-button-border: 0 none; --dialog-button-border: 0 none;
--overlay-button-bg-color: rgba(12, 12, 13, 0.1); --dialog-button-bg-color: rgba(12, 12, 13, 0.1);
--overlay-button-hover-bg-color: rgba(12, 12, 13, 0.3); --dialog-button-hover-bg-color: rgba(12, 12, 13, 0.3);
--loading-icon: url(images/loading.svg); --loading-icon: url(images/loading.svg);
--treeitem-expanded-icon: url(images/treeitem-expanded.svg); --treeitem-expanded-icon: url(images/treeitem-expanded.svg);
@ -146,8 +146,8 @@
--doorhanger-hover-color: rgba(249, 249, 250, 1); --doorhanger-hover-color: rgba(249, 249, 250, 1);
--doorhanger-hover-bg-color: rgba(93, 94, 98, 1); --doorhanger-hover-bg-color: rgba(93, 94, 98, 1);
--doorhanger-separator-color: rgba(92, 92, 97, 1); --doorhanger-separator-color: rgba(92, 92, 97, 1);
--overlay-button-bg-color: rgba(92, 92, 97, 1); --dialog-button-bg-color: rgba(92, 92, 97, 1);
--overlay-button-hover-bg-color: rgba(115, 115, 115, 1); --dialog-button-hover-bg-color: rgba(115, 115, 115, 1);
/* This image is used in <input> elements, which unfortunately means that /* This image is used in <input> elements, which unfortunately means that
* the `mask-image` approach used with all of the other images doesn't work * the `mask-image` approach used with all of the other images doesn't work
@ -168,9 +168,9 @@
--doorhanger-hover-color: ButtonFace; --doorhanger-hover-color: ButtonFace;
--doorhanger-border-color-whcm: 1px solid ButtonText; --doorhanger-border-color-whcm: 1px solid ButtonText;
--doorhanger-triangle-opacity-whcm: 0; --doorhanger-triangle-opacity-whcm: 0;
--overlay-button-border: 1px solid Highlight; --dialog-button-border: 1px solid Highlight;
--overlay-button-hover-bg-color: Highlight; --dialog-button-hover-bg-color: Highlight;
--overlay-button-hover-color: ButtonFace; --dialog-button-hover-color: ButtonFace;
--field-border-color: ButtonText; --field-border-color: ButtonText;
} }
} }
@ -628,21 +628,21 @@ select {
.toolbarButton, .toolbarButton,
.secondaryToolbarButton, .secondaryToolbarButton,
.overlayButton { .dialogButton {
border: 0 none; border: 0 none;
background: none; background: none;
width: 28px; width: 28px;
height: 28px; height: 28px;
} }
.overlayButton:hover, .dialogButton:hover,
.overlayButton:focus-visible { .dialogButton:focus-visible {
background-color: var(--overlay-button-hover-bg-color); background-color: var(--dialog-button-hover-bg-color);
} }
.overlayButton:hover > span, .dialogButton:hover > span,
.overlayButton:focus-visible > span { .dialogButton:focus-visible > span {
color: var(--overlay-button-hover-color); color: var(--dialog-button-hover-color);
} }
.toolbarButton > span { .toolbarButton > span {
@ -654,7 +654,7 @@ select {
.toolbarButton[disabled], .toolbarButton[disabled],
.secondaryToolbarButton[disabled], .secondaryToolbarButton[disabled],
.overlayButton[disabled] { .dialogButton[disabled] {
opacity: 0.5; opacity: 0.5;
} }
@ -697,7 +697,7 @@ select {
.toolbarButton, .toolbarButton,
.dropdownToolbarButton, .dropdownToolbarButton,
.secondaryToolbarButton, .secondaryToolbarButton,
.overlayButton { .dialogButton {
min-width: 16px; min-width: 16px;
margin: 2px 1px; margin: 2px 1px;
padding: 2px 6px 0; padding: 2px 6px 0;
@ -1278,32 +1278,17 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
width: 98%; width: 98%;
} }
.overlayButton { .dialogButton {
width: auto; width: auto;
margin: 3px 4px 2px !important; margin: 3px 4px 2px !important;
padding: 2px 11px; padding: 2px 11px;
color: var(--main-color); color: var(--main-color);
background-color: var(--overlay-button-bg-color); background-color: var(--dialog-button-bg-color);
border: var(--overlay-button-border) !important; border: var(--dialog-button-border) !important;
} }
#overlayContainer { dialog {
display: table; margin: auto;
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.2);
z-index: 40000;
}
#overlayContainer > .container {
display: table-cell;
vertical-align: middle;
text-align: center;
}
#overlayContainer > .container > .dialog {
display: inline-block;
padding: 15px; padding: 15px;
border-spacing: 4px; border-spacing: 4px;
color: var(--main-color); color: var(--main-color);
@ -1314,20 +1299,24 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
border-radius: 4px; border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
} }
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.2);
user-select: none;
}
.dialog > .row { dialog > .row {
display: table-row; display: table-row;
} }
.dialog > .row > * { dialog > .row > * {
display: table-cell; display: table-cell;
} }
.dialog .toolbarField { dialog .toolbarField {
margin: 5px 0; margin: 5px 0;
} }
.dialog .separator { dialog .separator {
display: block; display: block;
margin: 4px 0; margin: 4px 0;
height: 1px; height: 1px;
@ -1335,38 +1324,38 @@ a:focus > .thumbnail > .thumbnailSelectionRing,
background-color: var(--separator-color); background-color: var(--separator-color);
} }
.dialog .buttonRow { dialog .buttonRow {
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
} }
.dialog :link { dialog :link {
color: rgba(255, 255, 255, 1); color: rgba(255, 255, 255, 1);
} }
#passwordOverlay > .dialog { #passwordDialog {
text-align: center; text-align: center;
} }
#passwordOverlay .toolbarField { #passwordDialog .toolbarField {
width: 200px; width: 200px;
} }
#documentPropertiesOverlay > .dialog { #documentPropertiesDialog {
text-align: left; text-align: left;
} }
#documentPropertiesOverlay .row > * { #documentPropertiesDialog .row > * {
min-width: 100px; min-width: 100px;
text-align: start; text-align: start;
} }
#documentPropertiesOverlay .row > span { #documentPropertiesDialog .row > span {
width: 125px; width: 125px;
word-wrap: break-word; word-wrap: break-word;
} }
#documentPropertiesOverlay .row > p { #documentPropertiesDialog .row > p {
max-width: 225px; max-width: 225px;
word-wrap: break-word; word-wrap: break-word;
} }
#documentPropertiesOverlay .buttonRow { #documentPropertiesDialog .buttonRow {
margin-top: 10px; margin-top: 10px;
} }

View File

@ -233,7 +233,7 @@ See https://github.com/adobe-type-tools/cmap-resources
<div class="horizontalToolbarSeparator"></div> <div class="horizontalToolbarSeparator"></div>
<button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="69" data-l10n-id="document_properties"> <button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="69" data-l10n-id="document_properties" aria-controls="documentPropertiesDialog">
<span data-l10n-id="document_properties_label">Document Properties…</span> <span data-l10n-id="document_properties_label">Document Properties…</span>
</button> </button>
</div> </div>
@ -352,93 +352,101 @@ See https://github.com/adobe-type-tools/cmap-resources
<!--#endif--> <!--#endif-->
</div> <!-- mainContainer --> </div> <!-- mainContainer -->
<div id="overlayContainer" class="hidden"> <div id="dialogContainer">
<div id="passwordOverlay" class="container hidden"> <dialog id="passwordDialog">
<div class="dialog"> <div class="row">
<div class="row"> <label for="password" id="passwordText" data-l10n-id="password_label">Enter the password to open this PDF file:</label>
<p id="passwordText" data-l10n-id="password_label">Enter the password to open this PDF file:</p>
</div>
<div class="row">
<input type="password" id="password" class="toolbarField">
</div>
<div class="buttonRow">
<button id="passwordCancel" class="overlayButton"><span data-l10n-id="password_cancel">Cancel</span></button>
<button id="passwordSubmit" class="overlayButton"><span data-l10n-id="password_ok">OK</span></button>
</div>
</div> </div>
</div> <div class="row">
<div id="documentPropertiesOverlay" class="container hidden"> <input type="password" id="password" class="toolbarField">
<div class="dialog">
<div class="row">
<span data-l10n-id="document_properties_file_name">File name:</span> <p id="fileNameField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_file_size">File size:</span> <p id="fileSizeField">-</p>
</div>
<div class="separator"></div>
<div class="row">
<span data-l10n-id="document_properties_title">Title:</span> <p id="titleField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_author">Author:</span> <p id="authorField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_subject">Subject:</span> <p id="subjectField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_keywords">Keywords:</span> <p id="keywordsField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_creation_date">Creation Date:</span> <p id="creationDateField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_modification_date">Modification Date:</span> <p id="modificationDateField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_creator">Creator:</span> <p id="creatorField">-</p>
</div>
<div class="separator"></div>
<div class="row">
<span data-l10n-id="document_properties_producer">PDF Producer:</span> <p id="producerField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_version">PDF Version:</span> <p id="versionField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_page_count">Page Count:</span> <p id="pageCountField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_page_size">Page Size:</span> <p id="pageSizeField">-</p>
</div>
<div class="separator"></div>
<div class="row">
<span data-l10n-id="document_properties_linearized">Fast Web View:</span> <p id="linearizedField">-</p>
</div>
<div class="buttonRow">
<button id="documentPropertiesClose" class="overlayButton"><span data-l10n-id="document_properties_close">Close</span></button>
</div>
</div> </div>
</div> <div class="buttonRow">
<button id="passwordCancel" class="dialogButton"><span data-l10n-id="password_cancel">Cancel</span></button>
<button id="passwordSubmit" class="dialogButton"><span data-l10n-id="password_ok">OK</span></button>
</div>
</dialog>
<dialog id="documentPropertiesDialog">
<div class="row">
<span id="fileNameLabel" data-l10n-id="document_properties_file_name">File name:</span>
<p id="fileNameField" aria-labelledby="fileNameLabel">-</p>
</div>
<div class="row">
<span id="fileSizeLabel" data-l10n-id="document_properties_file_size">File size:</span>
<p id="fileSizeField" aria-labelledby="fileSizeLabel">-</p>
</div>
<div class="separator"></div>
<div class="row">
<span id="titleLabel" data-l10n-id="document_properties_title">Title:</span>
<p id="titleField" aria-labelledby="titleLabel">-</p>
</div>
<div class="row">
<span id="authorLabel" data-l10n-id="document_properties_author">Author:</span>
<p id="authorField" aria-labelledby="authorLabel">-</p>
</div>
<div class="row">
<span id="subjectLabel" data-l10n-id="document_properties_subject">Subject:</span>
<p id="subjectField" aria-labelledby="subjectLabel">-</p>
</div>
<div class="row">
<span id="keywordsLabel" data-l10n-id="document_properties_keywords">Keywords:</span>
<p id="keywordsField" aria-labelledby="keywordsLabel">-</p>
</div>
<div class="row">
<span id="creationDateLabel" data-l10n-id="document_properties_creation_date">Creation Date:</span>
<p id="creationDateField" aria-labelledby="creationDateLabel">-</p>
</div>
<div class="row">
<span id="modificationDateLabel" data-l10n-id="document_properties_modification_date">Modification Date:</span>
<p id="modificationDateField" aria-labelledby="modificationDateLabel">-</p>
</div>
<div class="row">
<span id="creatorLabel" data-l10n-id="document_properties_creator">Creator:</span>
<p id="creatorField" aria-labelledby="creatorLabel">-</p>
</div>
<div class="separator"></div>
<div class="row">
<span id="producerLabel" data-l10n-id="document_properties_producer">PDF Producer:</span>
<p id="producerField" aria-labelledby="producerLabel">-</p>
</div>
<div class="row">
<span id="versionLabel" data-l10n-id="document_properties_version">PDF Version:</span>
<p id="versionField" aria-labelledby="versionLabel">-</p>
</div>
<div class="row">
<span id="pageCountLabel" data-l10n-id="document_properties_page_count">Page Count:</span>
<p id="pageCountField" aria-labelledby="pageCountLabel">-</p>
</div>
<div class="row">
<span id="pageSizeLabel" data-l10n-id="document_properties_page_size">Page Size:</span>
<p id="pageSizeField" aria-labelledby="pageSizeLabel">-</p>
</div>
<div class="separator"></div>
<div class="row">
<span id="linearizedLabel" data-l10n-id="document_properties_linearized">Fast Web View:</span>
<p id="linearizedField" aria-labelledby="linearizedLabel">-</p>
</div>
<div class="buttonRow">
<button id="documentPropertiesClose" class="dialogButton"><span data-l10n-id="document_properties_close">Close</span></button>
</div>
</dialog>
<!--#if !MOZCENTRAL--> <!--#if !MOZCENTRAL-->
<div id="printServiceOverlay" class="container hidden"> <dialog id="printServiceDialog" style="min-width: 200px;">
<div class="dialog"> <div class="row">
<div class="row"> <span data-l10n-id="print_progress_message">Preparing document for printing…</span>
<span data-l10n-id="print_progress_message">Preparing document for printing…</span>
</div>
<div class="row">
<progress value="0" max="100"></progress>
<span data-l10n-id="print_progress_percent" data-l10n-args='{ "progress": 0 }' class="relative-progress">0%</span>
</div>
<div class="buttonRow">
<button id="printCancel" class="overlayButton"><span data-l10n-id="print_progress_close">Cancel</span></button>
</div>
</div> </div>
</div> <div class="row">
<progress value="0" max="100"></progress>
<span data-l10n-id="print_progress_percent" data-l10n-args='{ "progress": 0 }' class="relative-progress">0%</span>
</div>
<div class="buttonRow">
<button id="printCancel" class="dialogButton"><span data-l10n-id="print_progress_close">Cancel</span></button>
</div>
</dialog>
<!--#endif--> <!--#endif-->
<!--#if CHROME--> <!--#if CHROME-->
<!--#include viewer-snippet-chrome-overlays.html--> <!--#include viewer-snippet-chrome-overlays.html-->
<!--#endif--> <!--#endif-->
</div> <!-- overlayContainer --> </div> <!-- dialogContainer -->
</div> <!-- outerContainer --> </div> <!-- outerContainer -->
<div id="printContainer"></div> <div id="printContainer"></div>

View File

@ -161,16 +161,14 @@ function getViewerConfiguration() {
findNextButton: document.getElementById("findNext"), findNextButton: document.getElementById("findNext"),
}, },
passwordOverlay: { passwordOverlay: {
overlayName: "passwordOverlay", dialog: document.getElementById("passwordDialog"),
container: document.getElementById("passwordOverlay"),
label: document.getElementById("passwordText"), label: document.getElementById("passwordText"),
input: document.getElementById("password"), input: document.getElementById("password"),
submitButton: document.getElementById("passwordSubmit"), submitButton: document.getElementById("passwordSubmit"),
cancelButton: document.getElementById("passwordCancel"), cancelButton: document.getElementById("passwordCancel"),
}, },
documentProperties: { documentProperties: {
overlayName: "documentPropertiesOverlay", dialog: document.getElementById("documentPropertiesDialog"),
container: document.getElementById("documentPropertiesOverlay"),
closeButton: document.getElementById("documentPropertiesClose"), closeButton: document.getElementById("documentPropertiesClose"),
fields: { fields: {
fileName: document.getElementById("fileNameField"), fileName: document.getElementById("fileNameField"),