diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 025043ae1..b51d14d8d 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -478,14 +478,13 @@ class LinkAnnotationElement extends AnnotationElement { continue; } link[jsName] = () => { - window.dispatchEvent( - new CustomEvent("dispatchEventInSandbox", { - detail: { - id: data.id, - name, - }, - }) - ); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id: data.id, + name, + }, + }); return false; }; } @@ -551,30 +550,28 @@ class WidgetAnnotationElement extends AnnotationElement { if (baseName.includes("mouse")) { // Mouse events element.addEventListener(baseName, event => { - window.dispatchEvent( - new CustomEvent("dispatchEventInSandbox", { - detail: { - id: this.data.id, - name: eventName, - value: valueGetter(event), - shift: event.shiftKey, - modifier: this._getKeyModifier(event), - }, - }) - ); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id: this.data.id, + name: eventName, + value: valueGetter(event), + shift: event.shiftKey, + modifier: this._getKeyModifier(event), + }, + }); }); } else { // Non mouse event element.addEventListener(baseName, event => { - window.dispatchEvent( - new CustomEvent("dispatchEventInSandbox", { - detail: { - id: this.data.id, - name: eventName, - value: event.target.checked, - }, - }) - ); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id: this.data.id, + name: eventName, + value: event.target.checked, + }, + }); }); } } @@ -650,7 +647,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { } }); - element.addEventListener("updateFromSandbox", function (event) { + element.addEventListener("updatefromsandbox", function (event) { const { detail } = event; const actions = { value() { @@ -721,19 +718,18 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { } // Save the entered value elementData.userValue = event.target.value; - window.dispatchEvent( - new CustomEvent("dispatchEventInSandbox", { - detail: { - id, - name: "Keystroke", - value: event.target.value, - willCommit: true, - commitKey, - selStart: event.target.selectionStart, - selEnd: event.target.selectionEnd, - }, - }) - ); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + value: event.target.value, + willCommit: true, + commitKey, + selStart: event.target.selectionStart, + selEnd: event.target.selectionEnd, + }, + }); }); const _blurListener = blurListener; blurListener = null; @@ -741,19 +737,18 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { if (this._mouseState.isDown) { // Focus out using the mouse: data are committed elementData.userValue = event.target.value; - window.dispatchEvent( - new CustomEvent("dispatchEventInSandbox", { - detail: { - id, - name: "Keystroke", - value: event.target.value, - willCommit: true, - commitKey: 1, - selStart: event.target.selectionStart, - selEnd: event.target.selectionEnd, - }, - }) - ); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + value: event.target.value, + willCommit: true, + commitKey: 1, + selStart: event.target.selectionStart, + selEnd: event.target.selectionEnd, + }, + }); } _blurListener(event); }); @@ -783,19 +778,18 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { if (elementData.beforeInputSelectionRange) { [selStart, selEnd] = elementData.beforeInputSelectionRange; } - window.dispatchEvent( - new CustomEvent("dispatchEventInSandbox", { - detail: { - id, - name: "Keystroke", - value: elementData.beforeInputValue, - change: event.data, - willCommit: false, - selStart, - selEnd, - }, - }) - ); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + value: elementData.beforeInputValue, + change: event.data, + willCommit: false, + selStart, + selEnd, + }, + }); }); } @@ -929,7 +923,7 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { }); if (this.enableScripting && this.hasJSActions) { - element.addEventListener("updateFromSandbox", event => { + element.addEventListener("updatefromsandbox", event => { const { detail } = event; const actions = { value() { @@ -1010,7 +1004,7 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { }); if (this.enableScripting && this.hasJSActions) { - element.addEventListener("updateFromSandbox", event => { + element.addEventListener("updatefromsandbox", event => { const { detail } = event; const actions = { value() { @@ -1132,7 +1126,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { } if (this.enableScripting && this.hasJSActions) { - selectElement.addEventListener("updateFromSandbox", event => { + selectElement.addEventListener("updatefromsandbox", event => { const { detail } = event; const actions = { value() { @@ -1162,22 +1156,21 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { .forEach(name => actions[name]()); }); - selectElement.addEventListener("input", function (event) { + selectElement.addEventListener("input", event => { const value = getValue(event); storage.setValue(id, { value }); - window.dispatchEvent( - new CustomEvent("dispatchEventInSandbox", { - detail: { - id, - name: "Keystroke", - changeEx: value, - willCommit: true, - commitKey: 1, - keyDown: false, - }, - }) - ); + this.linkService.eventBus?.dispatch("dispatcheventinsandbox", { + source: this, + detail: { + id, + name: "Keystroke", + changeEx: value, + willCommit: true, + commitKey: 1, + keyDown: false, + }, + }); }); this._setEventListeners( @@ -1852,14 +1845,12 @@ class FileAttachmentAnnotationElement extends AnnotationElement { this.filename = getFilenameFromUrl(filename); this.content = content; - if (this.linkService.eventBus) { - this.linkService.eventBus.dispatch("fileattachmentannotation", { - source: this, - id: stringToPDFString(filename), - filename, - content, - }); - } + this.linkService.eventBus?.dispatch("fileattachmentannotation", { + source: this, + id: stringToPDFString(filename), + filename, + content, + }); } render() { diff --git a/src/pdf.sandbox.external.js b/src/pdf.sandbox.external.js index 89b2fecf0..38b9faaeb 100644 --- a/src/pdf.sandbox.external.js +++ b/src/pdf.sandbox.external.js @@ -148,7 +148,7 @@ class SandboxSupportBase { if (!data) { return; } - const event = new this.win.CustomEvent("updateFromSandbox", { + const event = new this.win.CustomEvent("updatefromsandbox", { detail: this.importValueFromSandbox(data), }); this.win.dispatchEvent(event); diff --git a/web/app.js b/web/app.js index 0b8de60c1..672d03edb 100644 --- a/web/app.js +++ b/web/app.js @@ -784,15 +784,20 @@ const PDFViewerApplication = { if (!this._scriptingInstance) { return; } - const { scripting, events } = this._scriptingInstance; + const { scripting, internalEvents, domEvents } = this._scriptingInstance; try { await scripting.destroySandbox(); } catch (ex) {} - for (const [name, listener] of events) { + for (const [name, listener] of internalEvents) { + this.eventBus._off(name, listener); + } + internalEvents.clear(); + + for (const [name, listener] of domEvents) { window.removeEventListener(name, listener); } - events.clear(); + domEvents.clear(); delete this._mouseState.isDown; this._scriptingInstance = null; @@ -1468,16 +1473,19 @@ const PDFViewerApplication = { pdfDocument.getJSActions(), ]); - if ((!objects && !docActions) || pdfDocument !== this.pdfDocument) { - // No FieldObjects were found in the document, no JS Actions at doc level - // or the document was closed while the data resolved. + if (!objects && !docActions) { + // No FieldObjects or JavaScript actions were found in the document. return; } - + if (pdfDocument !== this.pdfDocument) { + return; // The document was closed while the data resolved. + } const scripting = this.externalServices.createScripting(); // Store a reference to the current scripting-instance, to allow destruction // of the sandbox and removal of the event listeners at document closing. - this._scriptingInstance = { scripting, events: new Map() }; + const internalEvents = new Map(), + domEvents = new Map(); + this._scriptingInstance = { scripting, internalEvents, domEvents }; if (!this.documentInfo) { // It should be *extremely* rare for metadata to not have been resolved @@ -1494,8 +1502,7 @@ const PDFViewerApplication = { } } - const updateFromSandbox = event => { - const { detail } = event; + const updateFromSandbox = ({ detail }) => { const { id, command, value } = detail; if (!id) { switch (command) { @@ -1520,11 +1527,7 @@ const PDFViewerApplication = { console.log(value); break; case "zoom": - if (typeof value === "string") { - this.pdfViewer.currentScaleValue = value; - } else { - this.pdfViewer.currentScale = value; - } + this.pdfViewer.currentScaleValue = value; break; } return; @@ -1532,7 +1535,7 @@ const PDFViewerApplication = { const element = document.getElementById(id); if (element) { - element.dispatchEvent(new CustomEvent("updateFromSandbox", { detail })); + element.dispatchEvent(new CustomEvent("updatefromsandbox", { detail })); } else { if (value !== undefined && value !== null) { // The element hasn't been rendered yet, use the AnnotationStorage. @@ -1540,30 +1543,29 @@ const PDFViewerApplication = { } } }; - window.addEventListener("updateFromSandbox", updateFromSandbox); - // Ensure that the event listener can be removed at document closing. - this._scriptingInstance.events.set("updateFromSandbox", updateFromSandbox); + internalEvents.set("updatefromsandbox", updateFromSandbox); - const dispatchEventInSandbox = event => { - scripting.dispatchEventInSandbox(event.detail); + const dispatchEventInSandbox = ({ detail }) => { + scripting.dispatchEventInSandbox(detail); }; - window.addEventListener("dispatchEventInSandbox", dispatchEventInSandbox); - // Ensure that the event listener can be removed at document closing. - this._scriptingInstance.events.set( - "dispatchEventInSandbox", - dispatchEventInSandbox - ); + internalEvents.set("dispatcheventinsandbox", dispatchEventInSandbox); const mouseDown = event => { this._mouseState.isDown = true; }; + domEvents.set("mousedown", mouseDown); + const mouseUp = event => { this._mouseState.isDown = false; }; - window.addEventListener("mousedown", mouseDown); - this._scriptingInstance.events.set("mousedown", mouseDown); - window.addEventListener("mouseup", mouseUp); - this._scriptingInstance.events.set("mouseup", mouseUp); + domEvents.set("mouseup", mouseUp); + + for (const [name, listener] of internalEvents) { + this.eventBus._on(name, listener); + } + for (const [name, listener] of domEvents) { + window.addEventListener(name, listener); + } if (!this._contentLength) { // Always waiting for the entire PDF document to be loaded will, most @@ -1602,12 +1604,10 @@ const PDFViewerApplication = { }); if (this.externalServices.isInAutomation) { - this.eventBus.dispatch("sandboxcreated", { - source: this, - }); + this.eventBus.dispatch("sandboxcreated", { source: this }); } } catch (error) { - console.error(error); + console.error(`_initializeJavaScript: "${error?.message}".`); this._destroyScriptingInstance(); } @@ -2125,6 +2125,12 @@ const PDFViewerApplication = { _boundEvents.windowAfterPrint = () => { eventBus.dispatch("afterprint", { source: window }); }; + _boundEvents.windowUpdateFromSandbox = event => { + eventBus.dispatch("updatefromsandbox", { + source: window, + detail: event.detail, + }); + }; window.addEventListener("visibilitychange", webViewerVisibilityChange); window.addEventListener("wheel", webViewerWheel, { passive: false }); @@ -2138,6 +2144,10 @@ const PDFViewerApplication = { window.addEventListener("hashchange", _boundEvents.windowHashChange); window.addEventListener("beforeprint", _boundEvents.windowBeforePrint); window.addEventListener("afterprint", _boundEvents.windowAfterPrint); + window.addEventListener( + "updatefromsandbox", + _boundEvents.windowUpdateFromSandbox + ); }, unbindEvents() { @@ -2212,11 +2222,16 @@ const PDFViewerApplication = { window.removeEventListener("hashchange", _boundEvents.windowHashChange); window.removeEventListener("beforeprint", _boundEvents.windowBeforePrint); window.removeEventListener("afterprint", _boundEvents.windowAfterPrint); + window.removeEventListener( + "updatefromsandbox", + _boundEvents.windowUpdateFromSandbox + ); _boundEvents.windowResize = null; _boundEvents.windowHashChange = null; _boundEvents.windowBeforePrint = null; _boundEvents.windowAfterPrint = null; + _boundEvents.windowUpdateFromSandbox = null; }, accumulateWheelTicks(ticks) { diff --git a/web/firefoxcom.js b/web/firefoxcom.js index f126d40a6..9af65e863 100644 --- a/web/firefoxcom.js +++ b/web/firefoxcom.js @@ -255,12 +255,12 @@ class FirefoxComDataRangeTransport extends PDFDataRangeTransport { } class FirefoxScripting { - static createSandbox(data) { + static async createSandbox(data) { return new Promise(resolve => { FirefoxCom.request("createSandbox", data, resolve); }).then(success => { if (!success) { - throw new Error("Cannot start sandbox"); + throw new Error("Cannot create sandbox."); } }); }