Merge pull request #12748 from Snuffleupagus/scripting-misc-fixes
Update the events, used with scripting, to use lower-case names and avoid using DOM events internally in the viewer + misc scripting-related tweaks
This commit is contained in:
commit
af52c5fd17
55
gulpfile.js
55
gulpfile.js
@ -97,7 +97,6 @@ const DEFINES = Object.freeze({
|
|||||||
PRODUCTION: true,
|
PRODUCTION: true,
|
||||||
SKIP_BABEL: true,
|
SKIP_BABEL: true,
|
||||||
TESTING: false,
|
TESTING: false,
|
||||||
ENABLE_SCRIPTING: false,
|
|
||||||
// The main build targets:
|
// The main build targets:
|
||||||
GENERIC: false,
|
GENERIC: false,
|
||||||
MOZCENTRAL: false,
|
MOZCENTRAL: false,
|
||||||
@ -682,7 +681,6 @@ gulp.task("default_preferences-pre", function () {
|
|||||||
LIB: true,
|
LIB: true,
|
||||||
BUNDLE_VERSION: 0, // Dummy version
|
BUNDLE_VERSION: 0, // Dummy version
|
||||||
BUNDLE_BUILD: 0, // Dummy build
|
BUNDLE_BUILD: 0, // Dummy build
|
||||||
ENABLE_SCRIPTING: process.env.ENABLE_SCRIPTING === "true",
|
|
||||||
}),
|
}),
|
||||||
map: {
|
map: {
|
||||||
"pdfjs-lib": "../pdf",
|
"pdfjs-lib": "../pdf",
|
||||||
@ -1551,46 +1549,29 @@ gulp.task("testing-pre", function (done) {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task("enable-scripting", function (done) {
|
|
||||||
process.env.ENABLE_SCRIPTING = "true";
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"test",
|
"test",
|
||||||
gulp.series(
|
gulp.series("testing-pre", "generic", "components", function () {
|
||||||
"enable-scripting",
|
return streamqueue(
|
||||||
"testing-pre",
|
{ objectMode: true },
|
||||||
"generic",
|
createTestSource("unit"),
|
||||||
"components",
|
createTestSource("browser"),
|
||||||
function () {
|
createTestSource("integration")
|
||||||
return streamqueue(
|
);
|
||||||
{ objectMode: true },
|
})
|
||||||
createTestSource("unit"),
|
|
||||||
createTestSource("browser"),
|
|
||||||
createTestSource("integration")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"bottest",
|
"bottest",
|
||||||
gulp.series(
|
gulp.series("testing-pre", "generic", "components", function () {
|
||||||
"enable-scripting",
|
return streamqueue(
|
||||||
"testing-pre",
|
{ objectMode: true },
|
||||||
"generic",
|
createTestSource("unit", true),
|
||||||
"components",
|
createTestSource("font", true),
|
||||||
function () {
|
createTestSource("browser (no reftest)", true),
|
||||||
return streamqueue(
|
createTestSource("integration")
|
||||||
{ objectMode: true },
|
);
|
||||||
createTestSource("unit", true),
|
})
|
||||||
createTestSource("font", true),
|
|
||||||
createTestSource("browser (no reftest)", true),
|
|
||||||
createTestSource("integration")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
@ -1609,7 +1590,7 @@ gulp.task(
|
|||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"integrationtest",
|
"integrationtest",
|
||||||
gulp.series("enable-scripting", "testing-pre", "generic", function () {
|
gulp.series("testing-pre", "generic", function () {
|
||||||
return createTestSource("integration");
|
return createTestSource("integration");
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -47,6 +47,7 @@ import { ColorConverters } from "../shared/scripting_utils.js";
|
|||||||
* @property {Object} svgFactory
|
* @property {Object} svgFactory
|
||||||
* @property {boolean} [enableScripting]
|
* @property {boolean} [enableScripting]
|
||||||
* @property {boolean} [hasJSActions]
|
* @property {boolean} [hasJSActions]
|
||||||
|
* @property {Object} [mouseState]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AnnotationElementFactory {
|
class AnnotationElementFactory {
|
||||||
@ -155,6 +156,7 @@ class AnnotationElement {
|
|||||||
this.annotationStorage = parameters.annotationStorage;
|
this.annotationStorage = parameters.annotationStorage;
|
||||||
this.enableScripting = parameters.enableScripting;
|
this.enableScripting = parameters.enableScripting;
|
||||||
this.hasJSActions = parameters.hasJSActions;
|
this.hasJSActions = parameters.hasJSActions;
|
||||||
|
this._mouseState = parameters.mouseState;
|
||||||
|
|
||||||
if (isRenderable) {
|
if (isRenderable) {
|
||||||
this.container = this._createContainer(ignoreBorder);
|
this.container = this._createContainer(ignoreBorder);
|
||||||
@ -397,7 +399,7 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||||||
this.enableScripting &&
|
this.enableScripting &&
|
||||||
this.hasJSActions
|
this.hasJSActions
|
||||||
) {
|
) {
|
||||||
this._bindJSAction(link);
|
this._bindJSAction(link, data);
|
||||||
} else {
|
} else {
|
||||||
this._bindLink(link, "");
|
this._bindLink(link, "");
|
||||||
}
|
}
|
||||||
@ -463,9 +465,8 @@ class LinkAnnotationElement extends AnnotationElement {
|
|||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
* @memberof LinkAnnotationElement
|
* @memberof LinkAnnotationElement
|
||||||
*/
|
*/
|
||||||
_bindJSAction(link) {
|
_bindJSAction(link, data) {
|
||||||
link.href = this.linkService.getAnchorUrl("#");
|
link.href = this.linkService.getAnchorUrl("");
|
||||||
const { data } = this;
|
|
||||||
const map = new Map([
|
const map = new Map([
|
||||||
["Action", "onclick"],
|
["Action", "onclick"],
|
||||||
["MouseUp", "onmouseup"],
|
["MouseUp", "onmouseup"],
|
||||||
@ -477,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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -544,40 +544,42 @@ class WidgetAnnotationElement extends AnnotationElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_setEventListener(element, baseName, eventName, valueGetter) {
|
_setEventListener(element, baseName, eventName, valueGetter) {
|
||||||
if (this.data.actions && eventName.replace(" ", "") in this.data.actions) {
|
if (this.data.actions[eventName.replace(" ", "")] === undefined) {
|
||||||
if (baseName.includes("mouse")) {
|
return;
|
||||||
// Mouse events
|
}
|
||||||
element.addEventListener(baseName, event => {
|
if (baseName.includes("mouse")) {
|
||||||
window.dispatchEvent(
|
// Mouse events
|
||||||
new CustomEvent("dispatchEventInSandbox", {
|
element.addEventListener(baseName, event => {
|
||||||
detail: {
|
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
|
||||||
id: this.data.id,
|
source: this,
|
||||||
name: eventName,
|
detail: {
|
||||||
value: valueGetter(event),
|
id: this.data.id,
|
||||||
shift: event.shiftKey,
|
name: eventName,
|
||||||
modifier: this._getKeyModifier(event),
|
value: valueGetter(event),
|
||||||
},
|
shift: event.shiftKey,
|
||||||
})
|
modifier: this._getKeyModifier(event),
|
||||||
);
|
},
|
||||||
});
|
});
|
||||||
} else {
|
});
|
||||||
// Non mouse event
|
} else {
|
||||||
element.addEventListener(baseName, event => {
|
// Non mouse event
|
||||||
window.dispatchEvent(
|
element.addEventListener(baseName, event => {
|
||||||
new CustomEvent("dispatchEventInSandbox", {
|
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
|
||||||
detail: {
|
source: this,
|
||||||
id: this.data.id,
|
detail: {
|
||||||
name: eventName,
|
id: this.data.id,
|
||||||
value: event.target.checked,
|
name: eventName,
|
||||||
},
|
value: event.target.checked,
|
||||||
})
|
},
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setEventListeners(element, names, getter) {
|
_setEventListeners(element, names, getter) {
|
||||||
|
if (!this.data.actions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (const [baseName, eventName] of names) {
|
for (const [baseName, eventName] of names) {
|
||||||
this._setEventListener(element, baseName, eventName, getter);
|
this._setEventListener(element, baseName, eventName, getter);
|
||||||
}
|
}
|
||||||
@ -590,7 +592,6 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
parameters.renderInteractiveForms ||
|
parameters.renderInteractiveForms ||
|
||||||
(!parameters.data.hasAppearance && !!parameters.data.fieldValue);
|
(!parameters.data.hasAppearance && !!parameters.data.fieldValue);
|
||||||
super(parameters, { isRenderable });
|
super(parameters, { isRenderable });
|
||||||
this.mouseState = parameters.mouseState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -646,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() {
|
||||||
@ -717,39 +718,37 @@ 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: {
|
||||||
|
id,
|
||||||
|
name: "Keystroke",
|
||||||
|
value: event.target.value,
|
||||||
|
willCommit: true,
|
||||||
|
commitKey,
|
||||||
|
selStart: event.target.selectionStart,
|
||||||
|
selEnd: event.target.selectionEnd,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const _blurListener = blurListener;
|
||||||
|
blurListener = null;
|
||||||
|
element.addEventListener("blur", event => {
|
||||||
|
if (this._mouseState.isDown) {
|
||||||
|
// Focus out using the mouse: data are committed
|
||||||
|
elementData.userValue = event.target.value;
|
||||||
|
this.linkService.eventBus?.dispatch("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: 1,
|
||||||
selStart: event.target.selectionStart,
|
selStart: event.target.selectionStart,
|
||||||
selEnd: event.target.selectionEnd,
|
selEnd: event.target.selectionEnd,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
);
|
|
||||||
});
|
|
||||||
const _blurListener = blurListener;
|
|
||||||
blurListener = null;
|
|
||||||
element.addEventListener("blur", event => {
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_blurListener(event);
|
_blurListener(event);
|
||||||
});
|
});
|
||||||
@ -779,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,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -925,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() {
|
||||||
@ -996,8 +994,8 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
element.setAttribute("id", id);
|
element.setAttribute("id", id);
|
||||||
|
|
||||||
element.addEventListener("change", function (event) {
|
element.addEventListener("change", function (event) {
|
||||||
const target = event.target;
|
const { target } = event;
|
||||||
for (const radio of document.getElementsByName(event.target.name)) {
|
for (const radio of document.getElementsByName(target.name)) {
|
||||||
if (radio !== target) {
|
if (radio !== target) {
|
||||||
storage.setValue(radio.getAttribute("id"), { value: false });
|
storage.setValue(radio.getAttribute("id"), { value: false });
|
||||||
}
|
}
|
||||||
@ -1006,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() {
|
||||||
@ -1128,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() {
|
||||||
@ -1158,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(
|
||||||
@ -1848,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() {
|
||||||
@ -1951,7 +1946,7 @@ class AnnotationLayer {
|
|||||||
parameters.annotationStorage || new AnnotationStorage(),
|
parameters.annotationStorage || new AnnotationStorage(),
|
||||||
enableScripting: parameters.enableScripting,
|
enableScripting: parameters.enableScripting,
|
||||||
hasJSActions: parameters.hasJSActions,
|
hasJSActions: parameters.hasJSActions,
|
||||||
mouseState: parameters.mouseState,
|
mouseState: parameters.mouseState || { isDown: false },
|
||||||
});
|
});
|
||||||
if (element.isRenderable) {
|
if (element.isRenderable) {
|
||||||
const rendered = element.render();
|
const rendered = element.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);
|
||||||
|
@ -63,8 +63,6 @@ class Sandbox {
|
|||||||
}
|
}
|
||||||
const sandboxData = JSON.stringify(data);
|
const sandboxData = JSON.stringify(data);
|
||||||
const code = [
|
const code = [
|
||||||
// Next line is replaced by code from initialization.js
|
|
||||||
// when we create the bundle for the sandbox.
|
|
||||||
PDFJSDev.eval("PDF_SCRIPTING_JS_SOURCE"),
|
PDFJSDev.eval("PDF_SCRIPTING_JS_SOURCE"),
|
||||||
`pdfjsScripting.initSandbox({ data: ${sandboxData} })`,
|
`pdfjsScripting.initSandbox({ data: ${sandboxData} })`,
|
||||||
];
|
];
|
||||||
|
@ -30,9 +30,12 @@ describe("Interaction", () => {
|
|||||||
it("must check that first text field has focus", async () => {
|
it("must check that first text field has focus", async () => {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
pages.map(async ([browserName, page]) => {
|
pages.map(async ([browserName, page]) => {
|
||||||
|
await page.waitForFunction(
|
||||||
|
"window.PDFViewerApplication.scriptingReady === true"
|
||||||
|
);
|
||||||
|
|
||||||
// The document has an open action in order to give
|
// The document has an open action in order to give
|
||||||
// the focus to 401R.
|
// the focus to 401R.
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
const id = await page.evaluate(
|
const id = await page.evaluate(
|
||||||
() => window.document.activeElement.id
|
() => window.document.activeElement.id
|
||||||
);
|
);
|
||||||
|
@ -30,6 +30,7 @@ import { SimpleLinkService } from "./pdf_link_service.js";
|
|||||||
* @property {IL10n} l10n - Localization service.
|
* @property {IL10n} l10n - Localization service.
|
||||||
* @property {boolean} [enableScripting]
|
* @property {boolean} [enableScripting]
|
||||||
* @property {Promise<boolean>} [hasJSActionsPromise]
|
* @property {Promise<boolean>} [hasJSActionsPromise]
|
||||||
|
* @property {Object} [mouseState]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AnnotationLayerBuilder {
|
class AnnotationLayerBuilder {
|
||||||
|
157
web/app.js
157
web/app.js
@ -784,16 +784,22 @@ 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;
|
||||||
this._scriptingInstance = null;
|
this._scriptingInstance = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1030,13 +1036,10 @@ const PDFViewerApplication = {
|
|||||||
this.download({ sourceEventType });
|
this.download({ sourceEventType });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this._scriptingInstance?.scripting.dispatchEventInSandbox({
|
||||||
if (this._scriptingInstance) {
|
id: "doc",
|
||||||
this._scriptingInstance.scripting.dispatchEventInSandbox({
|
name: "WillSave",
|
||||||
id: "doc",
|
});
|
||||||
name: "WillSave",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._saveInProgress = true;
|
this._saveInProgress = true;
|
||||||
this.pdfDocument
|
this.pdfDocument
|
||||||
@ -1045,12 +1048,10 @@ const PDFViewerApplication = {
|
|||||||
const blob = new Blob([data], { type: "application/pdf" });
|
const blob = new Blob([data], { type: "application/pdf" });
|
||||||
downloadManager.download(blob, url, filename, sourceEventType);
|
downloadManager.download(blob, url, filename, sourceEventType);
|
||||||
|
|
||||||
if (this._scriptingInstance) {
|
this._scriptingInstance?.scripting.dispatchEventInSandbox({
|
||||||
this._scriptingInstance.scripting.dispatchEventInSandbox({
|
id: "doc",
|
||||||
id: "doc",
|
name: "DidSave",
|
||||||
name: "DidSave",
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.download({ sourceEventType });
|
this.download({ sourceEventType });
|
||||||
@ -1467,16 +1468,24 @@ 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,
|
||||||
|
ready: false,
|
||||||
|
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
|
||||||
@ -1493,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) {
|
||||||
@ -1506,24 +1514,20 @@ const PDFViewerApplication = {
|
|||||||
break;
|
break;
|
||||||
case "layout":
|
case "layout":
|
||||||
this.pdfViewer.spreadMode = apiPageLayoutToSpreadMode(value);
|
this.pdfViewer.spreadMode = apiPageLayoutToSpreadMode(value);
|
||||||
return;
|
break;
|
||||||
case "page-num":
|
case "page-num":
|
||||||
this.pdfViewer.currentPageNumber = value + 1;
|
this.pdfViewer.currentPageNumber = value + 1;
|
||||||
return;
|
break;
|
||||||
case "print":
|
case "print":
|
||||||
this.pdfViewer.pagesPromise.then(() => {
|
this.pdfViewer.pagesPromise.then(() => {
|
||||||
this.triggerPrinting();
|
this.triggerPrinting();
|
||||||
});
|
});
|
||||||
return;
|
break;
|
||||||
case "println":
|
case "println":
|
||||||
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;
|
||||||
@ -1531,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.
|
||||||
@ -1539,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
|
||||||
@ -1601,19 +1604,28 @@ 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();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scripting.dispatchEventInSandbox({
|
scripting.dispatchEventInSandbox({
|
||||||
id: "doc",
|
id: "doc",
|
||||||
name: "Open",
|
name: "Open",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Used together with the integration-tests, see the `scriptingReady`
|
||||||
|
// getter, to enable awaiting full initialization of the scripting/sandbox.
|
||||||
|
// (Defer this slightly, to make absolutely sure that everything is done.)
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
if (this._scriptingInstance) {
|
||||||
|
this._scriptingInstance.ready = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2032,21 +2044,17 @@ const PDFViewerApplication = {
|
|||||||
if (!this.supportsPrinting) {
|
if (!this.supportsPrinting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._scriptingInstance) {
|
this._scriptingInstance?.scripting.dispatchEventInSandbox({
|
||||||
this._scriptingInstance.scripting.dispatchEventInSandbox({
|
id: "doc",
|
||||||
id: "doc",
|
name: "WillPrint",
|
||||||
name: "WillPrint",
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.print();
|
window.print();
|
||||||
|
|
||||||
if (this._scriptingInstance) {
|
this._scriptingInstance?.scripting.dispatchEventInSandbox({
|
||||||
this._scriptingInstance.scripting.dispatchEventInSandbox({
|
id: "doc",
|
||||||
id: "doc",
|
name: "DidPrint",
|
||||||
name: "DidPrint",
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
@ -2124,6 +2132,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 });
|
||||||
@ -2137,6 +2151,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() {
|
||||||
@ -2211,11 +2229,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) {
|
||||||
@ -2233,6 +2256,14 @@ const PDFViewerApplication = {
|
|||||||
this._wheelUnusedTicks -= wholeTicks;
|
this._wheelUnusedTicks -= wholeTicks;
|
||||||
return wholeTicks;
|
return wholeTicks;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used together with the integration-tests, to enable awaiting full
|
||||||
|
* initialization of the scripting/sandbox.
|
||||||
|
*/
|
||||||
|
get scriptingReady() {
|
||||||
|
return this._scriptingInstance?.ready || false;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let validateFileURL;
|
let validateFileURL;
|
||||||
|
@ -67,7 +67,7 @@ const defaultOptions = {
|
|||||||
},
|
},
|
||||||
enableScripting: {
|
enableScripting: {
|
||||||
/** @type {boolean} */
|
/** @type {boolean} */
|
||||||
value: typeof PDFJSDev !== "undefined" && PDFJSDev.test("ENABLE_SCRIPTING"),
|
value: typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING"),
|
||||||
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
||||||
},
|
},
|
||||||
enableWebGL: {
|
enableWebGL: {
|
||||||
@ -249,7 +249,7 @@ if (
|
|||||||
) {
|
) {
|
||||||
defaultOptions.disablePreferences = {
|
defaultOptions.disablePreferences = {
|
||||||
/** @type {boolean} */
|
/** @type {boolean} */
|
||||||
value: false,
|
value: typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING"),
|
||||||
kind: OptionKind.VIEWER,
|
kind: OptionKind.VIEWER,
|
||||||
};
|
};
|
||||||
defaultOptions.locale = {
|
defaultOptions.locale = {
|
||||||
@ -260,8 +260,7 @@ if (
|
|||||||
defaultOptions.sandboxBundleSrc = {
|
defaultOptions.sandboxBundleSrc = {
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
value:
|
value:
|
||||||
typeof PDFJSDev === "undefined" ||
|
typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
|
||||||
PDFJSDev.test("!PRODUCTION && !ENABLE_SCRIPTING")
|
|
||||||
? "../build/dev-sandbox/pdf.sandbox.js"
|
? "../build/dev-sandbox/pdf.sandbox.js"
|
||||||
: "../build/pdf.sandbox.js",
|
: "../build/pdf.sandbox.js",
|
||||||
kind: OptionKind.VIEWER,
|
kind: OptionKind.VIEWER,
|
||||||
|
@ -79,7 +79,8 @@ const DEFAULT_CACHE_SIZE = 10;
|
|||||||
* @property {IL10n} l10n - Localization service.
|
* @property {IL10n} l10n - Localization service.
|
||||||
* @property {boolean} [enableScripting] - Enable embedded script execution.
|
* @property {boolean} [enableScripting] - Enable embedded script execution.
|
||||||
* The default value is `false`.
|
* The default value is `false`.
|
||||||
* @property {Object} [mouseState] - The mouse button state.
|
* @property {Object} [mouseState] - The mouse button state. The default value
|
||||||
|
* is `null`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function PDFPageViewBuffer(size) {
|
function PDFPageViewBuffer(size) {
|
||||||
@ -195,7 +196,7 @@ class BaseViewer {
|
|||||||
this.maxCanvasPixels = options.maxCanvasPixels;
|
this.maxCanvasPixels = options.maxCanvasPixels;
|
||||||
this.l10n = options.l10n || NullL10n;
|
this.l10n = options.l10n || NullL10n;
|
||||||
this.enableScripting = options.enableScripting || false;
|
this.enableScripting = options.enableScripting || false;
|
||||||
this.mouseState = options.mouseState || null;
|
this._mouseState = options.mouseState || null;
|
||||||
|
|
||||||
this.defaultRenderingQueue = !options.renderingQueue;
|
this.defaultRenderingQueue = !options.renderingQueue;
|
||||||
if (this.defaultRenderingQueue) {
|
if (this.defaultRenderingQueue) {
|
||||||
@ -540,7 +541,6 @@ class BaseViewer {
|
|||||||
maxCanvasPixels: this.maxCanvasPixels,
|
maxCanvasPixels: this.maxCanvasPixels,
|
||||||
l10n: this.l10n,
|
l10n: this.l10n,
|
||||||
enableScripting: this.enableScripting,
|
enableScripting: this.enableScripting,
|
||||||
mouseState: this.mouseState,
|
|
||||||
});
|
});
|
||||||
this._pages.push(pageView);
|
this._pages.push(pageView);
|
||||||
}
|
}
|
||||||
@ -1301,7 +1301,7 @@ class BaseViewer {
|
|||||||
enableScripting,
|
enableScripting,
|
||||||
hasJSActionsPromise:
|
hasJSActionsPromise:
|
||||||
hasJSActionsPromise || this.pdfDocument?.hasJSActions(),
|
hasJSActionsPromise || this.pdfDocument?.hasJSActions(),
|
||||||
mouseState,
|
mouseState: mouseState || this._mouseState,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,6 @@ import { viewerCompatibilityParams } from "./viewer_compatibility.js";
|
|||||||
* @property {IL10n} l10n - Localization service.
|
* @property {IL10n} l10n - Localization service.
|
||||||
* @property {boolean} [enableScripting] - Enable embedded script execution.
|
* @property {boolean} [enableScripting] - Enable embedded script execution.
|
||||||
* The default value is `false`.
|
* The default value is `false`.
|
||||||
* @property {Object} [mouseState] - The mouse button state.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const MAX_CANVAS_PIXELS = viewerCompatibilityParams.maxCanvasPixels || 16777216;
|
const MAX_CANVAS_PIXELS = viewerCompatibilityParams.maxCanvasPixels || 16777216;
|
||||||
@ -110,7 +109,6 @@ class PDFPageView {
|
|||||||
this.enableWebGL = options.enableWebGL || false;
|
this.enableWebGL = options.enableWebGL || false;
|
||||||
this.l10n = options.l10n || NullL10n;
|
this.l10n = options.l10n || NullL10n;
|
||||||
this.enableScripting = options.enableScripting || false;
|
this.enableScripting = options.enableScripting || false;
|
||||||
this.mouseState = options.mouseState || null;
|
|
||||||
|
|
||||||
this.paintTask = null;
|
this.paintTask = null;
|
||||||
this.paintedViewportMap = new WeakMap();
|
this.paintedViewportMap = new WeakMap();
|
||||||
@ -554,7 +552,7 @@ class PDFPageView {
|
|||||||
this.l10n,
|
this.l10n,
|
||||||
this.enableScripting,
|
this.enableScripting,
|
||||||
/* hasJSActionsPromise = */ null,
|
/* hasJSActionsPromise = */ null,
|
||||||
this.mouseState
|
/* mouseState = */ null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this._renderAnnotationLayer();
|
this._renderAnnotationLayer();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user