From f0811a4a3c49e2b75c142576d739ff361f3756c2 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 30 Oct 2022 16:11:31 +0100 Subject: [PATCH 1/2] Prevent mouse interaction with form elements in PresentationMode (issue 12232) --- src/display/annotation_layer.js | 16 ++++++++++------ web/pdf_presentation_mode.js | 31 +++++++++++++++++-------------- web/viewer.css | 4 ++-- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 495071dff..c8a156fc1 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -645,6 +645,10 @@ class LinkAnnotationElement extends AnnotationElement { return this.container; } + #setInternalLink() { + this.container.setAttribute("data-internal-link", ""); + } + /** * Bind internal links to the link element. * @@ -662,7 +666,7 @@ class LinkAnnotationElement extends AnnotationElement { return false; }; if (destination || destination === /* isTooltipOnly = */ "") { - link.className = "internalLink"; + this.#setInternalLink(); } } @@ -680,7 +684,7 @@ class LinkAnnotationElement extends AnnotationElement { this.linkService.executeNamedAction(action); return false; }; - link.className = "internalLink"; + this.#setInternalLink(); } /** @@ -698,7 +702,7 @@ class LinkAnnotationElement extends AnnotationElement { ); return false; }; - link.className = "internalLink"; + this.#setInternalLink(); } /** @@ -712,7 +716,7 @@ class LinkAnnotationElement extends AnnotationElement { this.linkService.executeSetOCGState(action); return false; }; - link.className = "internalLink"; + this.#setInternalLink(); } /** @@ -750,7 +754,7 @@ class LinkAnnotationElement extends AnnotationElement { if (!link.onclick) { link.onclick = () => false; } - link.className = "internalLink"; + this.#setInternalLink(); } _bindResetFormAction(link, resetForm) { @@ -758,7 +762,7 @@ class LinkAnnotationElement extends AnnotationElement { if (!otherClickAction) { link.href = this.linkService.getAnchorUrl(""); } - link.className = "internalLink"; + this.#setInternalLink(); if (!this._fieldObjects) { warn( diff --git a/web/pdf_presentation_mode.js b/web/pdf_presentation_mode.js index 07ff6125e..a3658f101 100644 --- a/web/pdf_presentation_mode.js +++ b/web/pdf_presentation_mode.js @@ -224,21 +224,24 @@ class PDFPresentationMode { evt.preventDefault(); return; } - if (evt.button === 0) { - // Enable clicking of links in presentation mode. Note: only links - // pointing to destinations in the current PDF document work. - const isInternalLink = - evt.target.href && evt.target.classList.contains("internalLink"); - if (!isInternalLink) { - // Unless an internal link was clicked, advance one page. - evt.preventDefault(); + if (evt.button !== 0) { + return; + } + // Enable clicking of links in presentation mode. Note: only links + // pointing to destinations in the current PDF document work. + if ( + evt.target.href && + evt.target.parentNode?.hasAttribute("data-internal-link") + ) { + return; + } + // Unless an internal link was clicked, advance one page. + evt.preventDefault(); - if (evt.shiftKey) { - this.pdfViewer.previousPage(); - } else { - this.pdfViewer.nextPage(); - } - } + if (evt.shiftKey) { + this.pdfViewer.previousPage(); + } else { + this.pdfViewer.nextPage(); } } diff --git a/web/viewer.css b/web/viewer.css index 064e1a872..7770b6883 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -222,8 +222,8 @@ body { user-select: none; } -.pdfPresentationMode:fullscreen a:not(.internalLink) { - display: none; +.pdfPresentationMode:fullscreen section:not([data-internal-link]) { + pointer-events: none; } .pdfPresentationMode:fullscreen .textLayer span { From 547556b5b2940fb26c8d3faed131d1a2005abab2 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 30 Oct 2022 17:36:20 +0100 Subject: [PATCH 2/2] Prevent keyboard interaction with form elements in PresentationMode (issue 12232) This uses the relatively new `HTMLElement.inert` property, see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert for additional information. The only "problem" is that this isn't yet available in all Firefox channels, but until https://bugzilla.mozilla.org/show_bug.cgi?id=1764263 is fixed we're no worse off than before. --- web/annotation_layer_builder.js | 50 +++++++++++++++++++++++++++++++++ web/interfaces.js | 5 ++++ web/pdf_link_service.js | 14 +++++++++ 3 files changed, 69 insertions(+) diff --git a/web/annotation_layer_builder.js b/web/annotation_layer_builder.js index 25dd70021..efbf308bf 100644 --- a/web/annotation_layer_builder.js +++ b/web/annotation_layer_builder.js @@ -24,6 +24,7 @@ import { AnnotationLayer } from "pdfjs-lib"; import { NullL10n } from "./l10n_utils.js"; +import { PresentationModeState } from "./ui_utils.js"; /** * @typedef {Object} AnnotationLayerBuilderOptions @@ -46,6 +47,8 @@ import { NullL10n } from "./l10n_utils.js"; */ class AnnotationLayerBuilder { + #onPresentationModeChanged = null; + /** * @param {AnnotationLayerBuilderOptions} options */ @@ -82,6 +85,7 @@ class AnnotationLayerBuilder { this.div = null; this._cancelled = false; + this._eventBus = linkService.eventBus; } /** @@ -134,11 +138,34 @@ class AnnotationLayerBuilder { AnnotationLayer.render(parameters); this.l10n.translate(this.div); + + // Ensure that interactive form elements in the annotationLayer are + // disabled while PresentationMode is active (see issue 12232). + if (this.linkService.isInPresentationMode) { + this.#updatePresentationModeState(PresentationModeState.FULLSCREEN); + } + if (!this.#onPresentationModeChanged) { + this.#onPresentationModeChanged = evt => { + this.#updatePresentationModeState(evt.state); + }; + this._eventBus?._on( + "presentationmodechanged", + this.#onPresentationModeChanged + ); + } } } cancel() { this._cancelled = true; + + if (this.#onPresentationModeChanged) { + this._eventBus?._off( + "presentationmodechanged", + this.#onPresentationModeChanged + ); + this.#onPresentationModeChanged = null; + } } hide() { @@ -147,6 +174,29 @@ class AnnotationLayerBuilder { } this.div.hidden = true; } + + #updatePresentationModeState(state) { + if (!this.div) { + return; + } + let disableFormElements = false; + + switch (state) { + case PresentationModeState.FULLSCREEN: + disableFormElements = true; + break; + case PresentationModeState.NORMAL: + break; + default: + return; + } + for (const section of this.div.childNodes) { + if (section.hasAttribute("data-internal-link")) { + continue; + } + section.inert = disableFormElements; + } + } } export { AnnotationLayerBuilder }; diff --git a/web/interfaces.js b/web/interfaces.js index 7cce362fc..2331511a1 100644 --- a/web/interfaces.js +++ b/web/interfaces.js @@ -61,6 +61,11 @@ class IPDFLinkService { */ set rotation(value) {} + /** + * @type {boolean} + */ + get isInPresentationMode() {} + /** * @type {boolean} */ diff --git a/web/pdf_link_service.js b/web/pdf_link_service.js index 52cba0ec2..de8172e6b 100644 --- a/web/pdf_link_service.js +++ b/web/pdf_link_service.js @@ -173,6 +173,13 @@ class PDFLinkService { this.pdfViewer.pagesRotation = value; } + /** + * @type {boolean} + */ + get isInPresentationMode() { + return this.pdfViewer.isInPresentationMode; + } + #goToDestinationHelper(rawDest, namedDest = null, explicitDest) { // Dest array looks like that: const destRef = explicitDest[0]; @@ -673,6 +680,13 @@ class SimpleLinkService { */ set rotation(value) {} + /** + * @type {boolean} + */ + get isInPresentationMode() { + return false; + } + /** * @param {string|Array} dest - The named, or explicit, PDF destination. */