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:
parent
e2b6d79dee
commit
eff4d8182d
@ -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,30 +550,28 @@ 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,
|
||||||
value: valueGetter(event),
|
value: valueGetter(event),
|
||||||
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,19 +718,18 @@ 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",
|
||||||
value: event.target.value,
|
value: event.target.value,
|
||||||
willCommit: true,
|
willCommit: true,
|
||||||
commitKey,
|
commitKey,
|
||||||
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,19 +737,18 @@ 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",
|
||||||
value: event.target.value,
|
value: event.target.value,
|
||||||
willCommit: true,
|
willCommit: true,
|
||||||
commitKey: 1,
|
commitKey: 1,
|
||||||
selStart: event.target.selectionStart,
|
selStart: event.target.selectionStart,
|
||||||
selEnd: event.target.selectionEnd,
|
selEnd: event.target.selectionEnd,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_blurListener(event);
|
_blurListener(event);
|
||||||
});
|
});
|
||||||
@ -783,19 +778,18 @@ 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",
|
||||||
value: elementData.beforeInputValue,
|
value: elementData.beforeInputValue,
|
||||||
change: event.data,
|
change: event.data,
|
||||||
willCommit: false,
|
willCommit: false,
|
||||||
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,22 +1156,21 @@ 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",
|
||||||
changeEx: value,
|
changeEx: value,
|
||||||
willCommit: true,
|
willCommit: true,
|
||||||
commitKey: 1,
|
commitKey: 1,
|
||||||
keyDown: false,
|
keyDown: false,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this._setEventListeners(
|
this._setEventListeners(
|
||||||
@ -1852,14 +1845,12 @@ 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() {
|
||||||
|
@ -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);
|
||||||
|
85
web/app.js
85
web/app.js
@ -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) {
|
||||||
|
@ -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.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user