Update the events, used with scripting, to use lower-case names and avoid using DOM events internally in the viewer

For DOM events all event names are lower-case, and the newly added PDF.js scripting-events thus "stick out" quite a bit. Even more so, considering that our internal `eventBus`-events follow the same naming convention.
Hence this patch, which changes the "updateFromSandbox"/"dispatchEventInSandbox" events to be lower-case instead.

Furthermore, using DOM events for communication *within* the PDF.js code itself (i.e. between code in `web/app.js` and `src/display/annotation_layer.js/`) feels *really* out of place.
That's exactly the reason that we have the `EventBus` abstraction, since it allowed us to remove prior use of DOM events, and this patch thus re-factors the code to make use of the `EventBus` instead for scripting-related events.
Obviously for events targeting a *specific element* using DOM events is still fine, but the "updatefromsandbox"/"dispatcheventinsandbox" ones should be using the `EventBus` internally.

*Drive-by change:* Use the `BaseViewer.currentScaleValue` setter unconditionally in `PDFViewerApplication._initializeJavaScript`, since it accepts either a string or a number.
This commit is contained in:
Jonas Jenwald 2020-12-17 14:10:56 +01:00
parent e2b6d79dee
commit eff4d8182d
4 changed files with 136 additions and 130 deletions

View File

@ -478,14 +478,13 @@ class LinkAnnotationElement extends AnnotationElement {
continue; continue;
} }
link[jsName] = () => { link[jsName] = () => {
window.dispatchEvent( this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
new CustomEvent("dispatchEventInSandbox", { source: this,
detail: { detail: {
id: data.id, id: data.id,
name, name,
}, },
}) });
);
return false; return false;
}; };
} }
@ -551,8 +550,8 @@ class WidgetAnnotationElement extends AnnotationElement {
if (baseName.includes("mouse")) { if (baseName.includes("mouse")) {
// Mouse events // Mouse events
element.addEventListener(baseName, event => { element.addEventListener(baseName, event => {
window.dispatchEvent( this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
new CustomEvent("dispatchEventInSandbox", { source: this,
detail: { detail: {
id: this.data.id, id: this.data.id,
name: eventName, name: eventName,
@ -560,21 +559,19 @@ class WidgetAnnotationElement extends AnnotationElement {
shift: event.shiftKey, shift: event.shiftKey,
modifier: this._getKeyModifier(event), modifier: this._getKeyModifier(event),
}, },
}) });
);
}); });
} else { } else {
// Non mouse event // Non mouse event
element.addEventListener(baseName, event => { element.addEventListener(baseName, event => {
window.dispatchEvent( this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
new CustomEvent("dispatchEventInSandbox", { source: this,
detail: { detail: {
id: this.data.id, id: this.data.id,
name: eventName, name: eventName,
value: event.target.checked, 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 { detail } = event;
const actions = { const actions = {
value() { value() {
@ -721,8 +718,8 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
} }
// Save the entered value // Save the entered value
elementData.userValue = event.target.value; elementData.userValue = event.target.value;
window.dispatchEvent( this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
new CustomEvent("dispatchEventInSandbox", { source: this,
detail: { detail: {
id, id,
name: "Keystroke", name: "Keystroke",
@ -732,8 +729,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
selStart: event.target.selectionStart, selStart: event.target.selectionStart,
selEnd: event.target.selectionEnd, selEnd: event.target.selectionEnd,
}, },
}) });
);
}); });
const _blurListener = blurListener; const _blurListener = blurListener;
blurListener = null; blurListener = null;
@ -741,8 +737,8 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
if (this._mouseState.isDown) { if (this._mouseState.isDown) {
// Focus out using the mouse: data are committed // Focus out using the mouse: data are committed
elementData.userValue = event.target.value; elementData.userValue = event.target.value;
window.dispatchEvent( this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
new CustomEvent("dispatchEventInSandbox", { source: this,
detail: { detail: {
id, id,
name: "Keystroke", name: "Keystroke",
@ -752,8 +748,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
selStart: event.target.selectionStart, selStart: event.target.selectionStart,
selEnd: event.target.selectionEnd, selEnd: event.target.selectionEnd,
}, },
}) });
);
} }
_blurListener(event); _blurListener(event);
}); });
@ -783,8 +778,8 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
if (elementData.beforeInputSelectionRange) { if (elementData.beforeInputSelectionRange) {
[selStart, selEnd] = elementData.beforeInputSelectionRange; [selStart, selEnd] = elementData.beforeInputSelectionRange;
} }
window.dispatchEvent( this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
new CustomEvent("dispatchEventInSandbox", { source: this,
detail: { detail: {
id, id,
name: "Keystroke", name: "Keystroke",
@ -794,8 +789,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
selStart, selStart,
selEnd, selEnd,
}, },
}) });
);
}); });
} }
@ -929,7 +923,7 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
}); });
if (this.enableScripting && this.hasJSActions) { if (this.enableScripting && this.hasJSActions) {
element.addEventListener("updateFromSandbox", event => { element.addEventListener("updatefromsandbox", event => {
const { detail } = event; const { detail } = event;
const actions = { const actions = {
value() { value() {
@ -1010,7 +1004,7 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
}); });
if (this.enableScripting && this.hasJSActions) { if (this.enableScripting && this.hasJSActions) {
element.addEventListener("updateFromSandbox", event => { element.addEventListener("updatefromsandbox", event => {
const { detail } = event; const { detail } = event;
const actions = { const actions = {
value() { value() {
@ -1132,7 +1126,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
} }
if (this.enableScripting && this.hasJSActions) { if (this.enableScripting && this.hasJSActions) {
selectElement.addEventListener("updateFromSandbox", event => { selectElement.addEventListener("updatefromsandbox", event => {
const { detail } = event; const { detail } = event;
const actions = { const actions = {
value() { value() {
@ -1162,12 +1156,12 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
.forEach(name => actions[name]()); .forEach(name => actions[name]());
}); });
selectElement.addEventListener("input", function (event) { selectElement.addEventListener("input", event => {
const value = getValue(event); const value = getValue(event);
storage.setValue(id, { value }); storage.setValue(id, { value });
window.dispatchEvent( this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
new CustomEvent("dispatchEventInSandbox", { source: this,
detail: { detail: {
id, id,
name: "Keystroke", name: "Keystroke",
@ -1176,8 +1170,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
commitKey: 1, commitKey: 1,
keyDown: false, keyDown: false,
}, },
}) });
);
}); });
this._setEventListeners( this._setEventListeners(
@ -1852,15 +1845,13 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
this.filename = getFilenameFromUrl(filename); this.filename = getFilenameFromUrl(filename);
this.content = content; this.content = content;
if (this.linkService.eventBus) { this.linkService.eventBus?.dispatch("fileattachmentannotation", {
this.linkService.eventBus.dispatch("fileattachmentannotation", {
source: this, source: this,
id: stringToPDFString(filename), id: stringToPDFString(filename),
filename, filename,
content, content,
}); });
} }
}
render() { render() {
this.container.className = "fileAttachmentAnnotation"; this.container.className = "fileAttachmentAnnotation";

View File

@ -148,7 +148,7 @@ class SandboxSupportBase {
if (!data) { if (!data) {
return; return;
} }
const event = new this.win.CustomEvent("updateFromSandbox", { const event = new this.win.CustomEvent("updatefromsandbox", {
detail: this.importValueFromSandbox(data), detail: this.importValueFromSandbox(data),
}); });
this.win.dispatchEvent(event); this.win.dispatchEvent(event);

View File

@ -784,15 +784,20 @@ const PDFViewerApplication = {
if (!this._scriptingInstance) { if (!this._scriptingInstance) {
return; return;
} }
const { scripting, events } = this._scriptingInstance; const { scripting, internalEvents, domEvents } = this._scriptingInstance;
try { try {
await scripting.destroySandbox(); await scripting.destroySandbox();
} catch (ex) {} } 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); window.removeEventListener(name, listener);
} }
events.clear(); domEvents.clear();
delete this._mouseState.isDown; delete this._mouseState.isDown;
this._scriptingInstance = null; this._scriptingInstance = null;
@ -1468,16 +1473,19 @@ const PDFViewerApplication = {
pdfDocument.getJSActions(), pdfDocument.getJSActions(),
]); ]);
if ((!objects && !docActions) || pdfDocument !== this.pdfDocument) { if (!objects && !docActions) {
// No FieldObjects were found in the document, no JS Actions at doc level // No FieldObjects or JavaScript actions were found in the document.
// or the document was closed while the data resolved.
return; return;
} }
if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the data resolved.
}
const scripting = this.externalServices.createScripting(); const scripting = this.externalServices.createScripting();
// Store a reference to the current scripting-instance, to allow destruction // Store a reference to the current scripting-instance, to allow destruction
// of the sandbox and removal of the event listeners at document closing. // 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) { if (!this.documentInfo) {
// It should be *extremely* rare for metadata to not have been resolved // It should be *extremely* rare for metadata to not have been resolved
@ -1494,8 +1502,7 @@ const PDFViewerApplication = {
} }
} }
const updateFromSandbox = event => { const updateFromSandbox = ({ detail }) => {
const { detail } = event;
const { id, command, value } = detail; const { id, command, value } = detail;
if (!id) { if (!id) {
switch (command) { switch (command) {
@ -1520,11 +1527,7 @@ const PDFViewerApplication = {
console.log(value); console.log(value);
break; break;
case "zoom": case "zoom":
if (typeof value === "string") {
this.pdfViewer.currentScaleValue = value; this.pdfViewer.currentScaleValue = value;
} else {
this.pdfViewer.currentScale = value;
}
break; break;
} }
return; return;
@ -1532,7 +1535,7 @@ const PDFViewerApplication = {
const element = document.getElementById(id); const element = document.getElementById(id);
if (element) { if (element) {
element.dispatchEvent(new CustomEvent("updateFromSandbox", { detail })); element.dispatchEvent(new CustomEvent("updatefromsandbox", { detail }));
} else { } else {
if (value !== undefined && value !== null) { if (value !== undefined && value !== null) {
// The element hasn't been rendered yet, use the AnnotationStorage. // The element hasn't been rendered yet, use the AnnotationStorage.
@ -1540,30 +1543,29 @@ const PDFViewerApplication = {
} }
} }
}; };
window.addEventListener("updateFromSandbox", updateFromSandbox); internalEvents.set("updatefromsandbox", updateFromSandbox);
// Ensure that the event listener can be removed at document closing.
this._scriptingInstance.events.set("updateFromSandbox", updateFromSandbox);
const dispatchEventInSandbox = event => { const dispatchEventInSandbox = ({ detail }) => {
scripting.dispatchEventInSandbox(event.detail); scripting.dispatchEventInSandbox(detail);
}; };
window.addEventListener("dispatchEventInSandbox", dispatchEventInSandbox); internalEvents.set("dispatcheventinsandbox", dispatchEventInSandbox);
// Ensure that the event listener can be removed at document closing.
this._scriptingInstance.events.set(
"dispatchEventInSandbox",
dispatchEventInSandbox
);
const mouseDown = event => { const mouseDown = event => {
this._mouseState.isDown = true; this._mouseState.isDown = true;
}; };
domEvents.set("mousedown", mouseDown);
const mouseUp = event => { const mouseUp = event => {
this._mouseState.isDown = false; this._mouseState.isDown = false;
}; };
window.addEventListener("mousedown", mouseDown); domEvents.set("mouseup", mouseUp);
this._scriptingInstance.events.set("mousedown", mouseDown);
window.addEventListener("mouseup", mouseUp); for (const [name, listener] of internalEvents) {
this._scriptingInstance.events.set("mouseup", mouseUp); this.eventBus._on(name, listener);
}
for (const [name, listener] of domEvents) {
window.addEventListener(name, listener);
}
if (!this._contentLength) { if (!this._contentLength) {
// Always waiting for the entire PDF document to be loaded will, most // Always waiting for the entire PDF document to be loaded will, most
@ -1602,12 +1604,10 @@ const PDFViewerApplication = {
}); });
if (this.externalServices.isInAutomation) { if (this.externalServices.isInAutomation) {
this.eventBus.dispatch("sandboxcreated", { this.eventBus.dispatch("sandboxcreated", { source: this });
source: this,
});
} }
} catch (error) { } catch (error) {
console.error(error); console.error(`_initializeJavaScript: "${error?.message}".`);
this._destroyScriptingInstance(); this._destroyScriptingInstance();
} }
@ -2125,6 +2125,12 @@ const PDFViewerApplication = {
_boundEvents.windowAfterPrint = () => { _boundEvents.windowAfterPrint = () => {
eventBus.dispatch("afterprint", { source: window }); eventBus.dispatch("afterprint", { source: window });
}; };
_boundEvents.windowUpdateFromSandbox = event => {
eventBus.dispatch("updatefromsandbox", {
source: window,
detail: event.detail,
});
};
window.addEventListener("visibilitychange", webViewerVisibilityChange); window.addEventListener("visibilitychange", webViewerVisibilityChange);
window.addEventListener("wheel", webViewerWheel, { passive: false }); window.addEventListener("wheel", webViewerWheel, { passive: false });
@ -2138,6 +2144,10 @@ const PDFViewerApplication = {
window.addEventListener("hashchange", _boundEvents.windowHashChange); window.addEventListener("hashchange", _boundEvents.windowHashChange);
window.addEventListener("beforeprint", _boundEvents.windowBeforePrint); window.addEventListener("beforeprint", _boundEvents.windowBeforePrint);
window.addEventListener("afterprint", _boundEvents.windowAfterPrint); window.addEventListener("afterprint", _boundEvents.windowAfterPrint);
window.addEventListener(
"updatefromsandbox",
_boundEvents.windowUpdateFromSandbox
);
}, },
unbindEvents() { unbindEvents() {
@ -2212,11 +2222,16 @@ const PDFViewerApplication = {
window.removeEventListener("hashchange", _boundEvents.windowHashChange); window.removeEventListener("hashchange", _boundEvents.windowHashChange);
window.removeEventListener("beforeprint", _boundEvents.windowBeforePrint); window.removeEventListener("beforeprint", _boundEvents.windowBeforePrint);
window.removeEventListener("afterprint", _boundEvents.windowAfterPrint); window.removeEventListener("afterprint", _boundEvents.windowAfterPrint);
window.removeEventListener(
"updatefromsandbox",
_boundEvents.windowUpdateFromSandbox
);
_boundEvents.windowResize = null; _boundEvents.windowResize = null;
_boundEvents.windowHashChange = null; _boundEvents.windowHashChange = null;
_boundEvents.windowBeforePrint = null; _boundEvents.windowBeforePrint = null;
_boundEvents.windowAfterPrint = null; _boundEvents.windowAfterPrint = null;
_boundEvents.windowUpdateFromSandbox = null;
}, },
accumulateWheelTicks(ticks) { accumulateWheelTicks(ticks) {

View File

@ -255,12 +255,12 @@ class FirefoxComDataRangeTransport extends PDFDataRangeTransport {
} }
class FirefoxScripting { class FirefoxScripting {
static createSandbox(data) { static async createSandbox(data) {
return new Promise(resolve => { return new Promise(resolve => {
FirefoxCom.request("createSandbox", data, resolve); FirefoxCom.request("createSandbox", data, resolve);
}).then(success => { }).then(success => {
if (!success) { if (!success) {
throw new Error("Cannot start sandbox"); throw new Error("Cannot create sandbox.");
} }
}); });
} }