Merge pull request #12546 from calixteman/hasjs
[api-minor] JS -- Add listener for sandbox events only if there are some actions
This commit is contained in:
commit
1c17e078ec
@ -1020,6 +1020,21 @@ class PDFDocument {
|
||||
);
|
||||
}
|
||||
|
||||
get hasJSActions() {
|
||||
return shadow(
|
||||
this,
|
||||
"hasJSActions",
|
||||
this.fieldObjects.then(fieldObjects => {
|
||||
return (
|
||||
fieldObjects !== null &&
|
||||
Object.values(fieldObjects).some(fieldObject =>
|
||||
fieldObject.some(object => object.actions !== null)
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get calculationOrderIds() {
|
||||
const acroForm = this.catalog.acroForm;
|
||||
if (!acroForm || !acroForm.has("CO")) {
|
||||
|
@ -525,6 +525,10 @@ class WorkerMessageHandler {
|
||||
return pdfManager.ensureDoc("fieldObjects");
|
||||
});
|
||||
|
||||
handler.on("HasJSActions", function (data) {
|
||||
return pdfManager.ensureDoc("hasJSActions");
|
||||
});
|
||||
|
||||
handler.on("GetCalculationOrderIds", function (data) {
|
||||
return pdfManager.ensureDoc("calculationOrderIds");
|
||||
});
|
||||
|
@ -43,6 +43,8 @@ import { AnnotationStorage } from "./annotation_storage.js";
|
||||
* for annotation icons. Include trailing slash.
|
||||
* @property {boolean} renderInteractiveForms
|
||||
* @property {Object} svgFactory
|
||||
* @property {boolean} [enableScripting]
|
||||
* @property {boolean} [hasJSActions]
|
||||
*/
|
||||
|
||||
class AnnotationElementFactory {
|
||||
@ -142,6 +144,8 @@ class AnnotationElement {
|
||||
this.renderInteractiveForms = parameters.renderInteractiveForms;
|
||||
this.svgFactory = parameters.svgFactory;
|
||||
this.annotationStorage = parameters.annotationStorage;
|
||||
this.enableScripting = parameters.enableScripting;
|
||||
this.hasJSActions = parameters.hasJSActions;
|
||||
|
||||
if (isRenderable) {
|
||||
this.container = this._createContainer(ignoreBorder);
|
||||
@ -507,7 +511,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
|
||||
event.target.setSelectionRange(0, 0);
|
||||
});
|
||||
|
||||
if (this.data.actions) {
|
||||
if (this.enableScripting && this.hasJSActions) {
|
||||
element.addEventListener("updateFromSandbox", function (event) {
|
||||
const data = event.detail;
|
||||
if ("value" in data) {
|
||||
@ -517,21 +521,23 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement {
|
||||
}
|
||||
});
|
||||
|
||||
for (const eventType of Object.keys(this.data.actions)) {
|
||||
switch (eventType) {
|
||||
case "Format":
|
||||
element.addEventListener("blur", function (event) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("dispatchEventInSandbox", {
|
||||
detail: {
|
||||
id,
|
||||
name: "Format",
|
||||
value: event.target.value,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
break;
|
||||
if (this.data.actions !== null) {
|
||||
for (const eventType of Object.keys(this.data.actions)) {
|
||||
switch (eventType) {
|
||||
case "Format":
|
||||
element.addEventListener("blur", function (event) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("dispatchEventInSandbox", {
|
||||
detail: {
|
||||
id,
|
||||
name: "Format",
|
||||
value: event.target.value,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1562,6 +1568,9 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
|
||||
* @property {string} [imageResourcesPath] - Path for image resources, mainly
|
||||
* for annotation icons. Include trailing slash.
|
||||
* @property {boolean} renderInteractiveForms
|
||||
* @property {boolean} [enableScripting] - Enable embedded script execution.
|
||||
* @property {boolean} [hasJSActions] - Some fields have JS actions.
|
||||
* The default value is `false`.
|
||||
*/
|
||||
|
||||
class AnnotationLayer {
|
||||
@ -1608,6 +1617,8 @@ class AnnotationLayer {
|
||||
svgFactory: new DOMSVGFactory(),
|
||||
annotationStorage:
|
||||
parameters.annotationStorage || new AnnotationStorage(),
|
||||
enableScripting: parameters.enableScripting,
|
||||
hasJSActions: parameters.hasJSActions,
|
||||
});
|
||||
if (element.isRenderable) {
|
||||
const rendered = element.render();
|
||||
|
@ -903,6 +903,14 @@ class PDFDocumentProxy {
|
||||
return this._transport.getFieldObjects();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<boolean>} A promise that is resolved with `true`
|
||||
* if some /AcroForm fields have JavaScript actions.
|
||||
*/
|
||||
hasJSActions() {
|
||||
return this._transport.hasJSActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<Array<string> | null>} A promise that is resolved with an
|
||||
* {Array<string>} containing IDs of annotations that have a calculation
|
||||
@ -2568,6 +2576,10 @@ class WorkerTransport {
|
||||
return this.messageHandler.sendWithPromise("GetFieldObjects", null);
|
||||
}
|
||||
|
||||
hasJSActions() {
|
||||
return this.messageHandler.sendWithPromise("HasJSActions", null);
|
||||
}
|
||||
|
||||
getCalculationOrderIds() {
|
||||
return this.messageHandler.sendWithPromise("GetCalculationOrderIds", null);
|
||||
}
|
||||
|
@ -73,12 +73,14 @@ class Field extends PDFObject {
|
||||
// Private
|
||||
this._actions = Object.create(null);
|
||||
const doc = (this._document = data.doc);
|
||||
for (const [eventType, actions] of Object.entries(data.actions)) {
|
||||
// This code is running in a sandbox so it's safe to use Function
|
||||
this._actions[eventType] = actions.map(action =>
|
||||
// eslint-disable-next-line no-new-func
|
||||
Function("event", `with (this) {${action}}`).bind(doc)
|
||||
);
|
||||
if (data.actions !== null) {
|
||||
for (const [eventType, actions] of Object.entries(data.actions)) {
|
||||
// This code is running in a sandbox so it's safe to use Function
|
||||
this._actions[eventType] = actions.map(action =>
|
||||
// eslint-disable-next-line no-new-func
|
||||
Function("event", `with (this) {${action}}`).bind(doc)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,10 @@ describe("annotation", function () {
|
||||
ensureCatalog(prop, args) {
|
||||
return this.ensure(this.pdfDocument.catalog, prop, args);
|
||||
}
|
||||
|
||||
ensureDoc(prop, args) {
|
||||
return this.ensure(this.pdfDocument, prop, args);
|
||||
}
|
||||
}
|
||||
|
||||
function HandlerMock() {
|
||||
|
@ -239,5 +239,60 @@ describe("document", function () {
|
||||
expect(fields["parent.kid2"]).toEqual(["265R"]);
|
||||
expect(fields.parent).toEqual(["358R"]);
|
||||
});
|
||||
|
||||
it("should check if fields have any actions", async function () {
|
||||
const acroForm = new Dict();
|
||||
|
||||
let pdfDocument = getDocument(acroForm);
|
||||
let hasJSActions = await pdfDocument.hasJSActions;
|
||||
expect(hasJSActions).toEqual(false);
|
||||
|
||||
acroForm.set("Fields", []);
|
||||
pdfDocument = getDocument(acroForm);
|
||||
hasJSActions = await pdfDocument.hasJSActions;
|
||||
expect(hasJSActions).toEqual(false);
|
||||
|
||||
const kid1Ref = Ref.get(314, 0);
|
||||
const kid11Ref = Ref.get(159, 0);
|
||||
const kid2Ref = Ref.get(265, 0);
|
||||
const parentRef = Ref.get(358, 0);
|
||||
|
||||
const allFields = Object.create(null);
|
||||
for (const name of ["parent", "kid1", "kid2", "kid11"]) {
|
||||
const buttonWidgetDict = new Dict();
|
||||
buttonWidgetDict.set("Type", Name.get("Annot"));
|
||||
buttonWidgetDict.set("Subtype", Name.get("Widget"));
|
||||
buttonWidgetDict.set("FT", Name.get("Btn"));
|
||||
buttonWidgetDict.set("T", name);
|
||||
allFields[name] = buttonWidgetDict;
|
||||
}
|
||||
|
||||
allFields.kid1.set("Kids", [kid11Ref]);
|
||||
allFields.parent.set("Kids", [kid1Ref, kid2Ref]);
|
||||
|
||||
const xref = new XRefMock([
|
||||
{ ref: parentRef, data: allFields.parent },
|
||||
{ ref: kid1Ref, data: allFields.kid1 },
|
||||
{ ref: kid11Ref, data: allFields.kid11 },
|
||||
{ ref: kid2Ref, data: allFields.kid2 },
|
||||
]);
|
||||
|
||||
acroForm.set("Fields", [parentRef]);
|
||||
pdfDocument = getDocument(acroForm, xref);
|
||||
hasJSActions = await pdfDocument.hasJSActions;
|
||||
expect(hasJSActions).toEqual(false);
|
||||
|
||||
const JS = Name.get("JavaScript");
|
||||
const additionalActionsDict = new Dict();
|
||||
const eDict = new Dict();
|
||||
eDict.set("JS", "hello()");
|
||||
eDict.set("S", JS);
|
||||
additionalActionsDict.set("E", eDict);
|
||||
allFields.kid2.set("AA", additionalActionsDict);
|
||||
|
||||
pdfDocument = getDocument(acroForm, xref);
|
||||
hasJSActions = await pdfDocument.hasJSActions;
|
||||
expect(hasJSActions).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -28,6 +28,8 @@ import { SimpleLinkService } from "./pdf_link_service.js";
|
||||
* @property {IPDFLinkService} linkService
|
||||
* @property {DownloadManager} downloadManager
|
||||
* @property {IL10n} l10n - Localization service.
|
||||
* @property {boolean} [enableScripting]
|
||||
* @property {Promise<boolean>} [hasJSActionsPromise]
|
||||
*/
|
||||
|
||||
class AnnotationLayerBuilder {
|
||||
@ -43,6 +45,8 @@ class AnnotationLayerBuilder {
|
||||
imageResourcesPath = "",
|
||||
renderInteractiveForms = true,
|
||||
l10n = NullL10n,
|
||||
enableScripting = false,
|
||||
hasJSActionsPromise = null,
|
||||
}) {
|
||||
this.pageDiv = pageDiv;
|
||||
this.pdfPage = pdfPage;
|
||||
@ -52,6 +56,8 @@ class AnnotationLayerBuilder {
|
||||
this.renderInteractiveForms = renderInteractiveForms;
|
||||
this.l10n = l10n;
|
||||
this.annotationStorage = annotationStorage;
|
||||
this.enableScripting = enableScripting;
|
||||
this._hasJSActionsPromise = hasJSActionsPromise;
|
||||
|
||||
this.div = null;
|
||||
this._cancelled = false;
|
||||
@ -64,7 +70,10 @@ class AnnotationLayerBuilder {
|
||||
* annotations is complete.
|
||||
*/
|
||||
render(viewport, intent = "display") {
|
||||
return this.pdfPage.getAnnotations({ intent }).then(annotations => {
|
||||
return Promise.all([
|
||||
this.pdfPage.getAnnotations({ intent }),
|
||||
this._hasJSActionsPromise,
|
||||
]).then(([annotations, hasJSActions = false]) => {
|
||||
if (this._cancelled) {
|
||||
return;
|
||||
}
|
||||
@ -82,6 +91,8 @@ class AnnotationLayerBuilder {
|
||||
linkService: this.linkService,
|
||||
downloadManager: this.downloadManager,
|
||||
annotationStorage: this.annotationStorage,
|
||||
enableScripting: this.enableScripting,
|
||||
hasJSActions,
|
||||
};
|
||||
|
||||
if (this.div) {
|
||||
@ -126,6 +137,8 @@ class DefaultAnnotationLayerFactory {
|
||||
* for annotation icons. Include trailing slash.
|
||||
* @param {boolean} renderInteractiveForms
|
||||
* @param {IL10n} l10n
|
||||
* @param {boolean} enableScripting
|
||||
* @param {Promise<boolean>} hasJSActionsPromise
|
||||
* @returns {AnnotationLayerBuilder}
|
||||
*/
|
||||
createAnnotationLayerBuilder(
|
||||
@ -134,7 +147,9 @@ class DefaultAnnotationLayerFactory {
|
||||
annotationStorage = null,
|
||||
imageResourcesPath = "",
|
||||
renderInteractiveForms = true,
|
||||
l10n = NullL10n
|
||||
l10n = NullL10n,
|
||||
enableScripting = false,
|
||||
hasJSActionsPromise = null
|
||||
) {
|
||||
return new AnnotationLayerBuilder({
|
||||
pageDiv,
|
||||
@ -144,6 +159,8 @@ class DefaultAnnotationLayerFactory {
|
||||
linkService: new SimpleLinkService(),
|
||||
l10n,
|
||||
annotationStorage,
|
||||
enableScripting,
|
||||
hasJSActionsPromise,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -449,6 +449,7 @@ const PDFViewerApplication = {
|
||||
enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
|
||||
useOnlyCssZoom: AppOptions.get("useOnlyCssZoom"),
|
||||
maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
|
||||
enableScripting: AppOptions.get("enableScripting"),
|
||||
});
|
||||
pdfRenderingQueue.setViewer(this.pdfViewer);
|
||||
pdfLinkService.setViewer(this.pdfViewer);
|
||||
|
@ -77,6 +77,8 @@ const DEFAULT_CACHE_SIZE = 10;
|
||||
* total pixels, i.e. width * height. Use -1 for no limit. The default value
|
||||
* is 4096 * 4096 (16 mega-pixels).
|
||||
* @property {IL10n} l10n - Localization service.
|
||||
* @property {boolean} [enableScripting] - Enable embedded script execution.
|
||||
* The default value is `false`.
|
||||
*/
|
||||
|
||||
function PDFPageViewBuffer(size) {
|
||||
@ -187,6 +189,7 @@ class BaseViewer {
|
||||
this.useOnlyCssZoom = options.useOnlyCssZoom || false;
|
||||
this.maxCanvasPixels = options.maxCanvasPixels;
|
||||
this.l10n = options.l10n || NullL10n;
|
||||
this.enableScripting = options.enableScripting || false;
|
||||
|
||||
this.defaultRenderingQueue = !options.renderingQueue;
|
||||
if (this.defaultRenderingQueue) {
|
||||
@ -527,6 +530,7 @@ class BaseViewer {
|
||||
useOnlyCssZoom: this.useOnlyCssZoom,
|
||||
maxCanvasPixels: this.maxCanvasPixels,
|
||||
l10n: this.l10n,
|
||||
enableScripting: this.enableScripting,
|
||||
});
|
||||
this._pages.push(pageView);
|
||||
}
|
||||
@ -1208,6 +1212,7 @@ class BaseViewer {
|
||||
* for annotation icons. Include trailing slash.
|
||||
* @param {boolean} renderInteractiveForms
|
||||
* @param {IL10n} l10n
|
||||
* @param {boolean} [enableScripting]
|
||||
* @returns {AnnotationLayerBuilder}
|
||||
*/
|
||||
createAnnotationLayerBuilder(
|
||||
@ -1216,7 +1221,8 @@ class BaseViewer {
|
||||
annotationStorage = null,
|
||||
imageResourcesPath = "",
|
||||
renderInteractiveForms = false,
|
||||
l10n = NullL10n
|
||||
l10n = NullL10n,
|
||||
enableScripting = false
|
||||
) {
|
||||
return new AnnotationLayerBuilder({
|
||||
pageDiv,
|
||||
@ -1227,6 +1233,10 @@ class BaseViewer {
|
||||
linkService: this.linkService,
|
||||
downloadManager: this.downloadManager,
|
||||
l10n,
|
||||
enableScripting,
|
||||
hasJSActionsPromise: enableScripting
|
||||
? this.pdfDocument.hasJSActions()
|
||||
: Promise.resolve(false),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,8 @@ class IPDFAnnotationLayerFactory {
|
||||
* for annotation icons. Include trailing slash.
|
||||
* @param {boolean} renderInteractiveForms
|
||||
* @param {IL10n} l10n
|
||||
* @param {boolean} [enableScripting]
|
||||
* @param {Promise<boolean>} [hasJSActionsPromise]
|
||||
* @returns {AnnotationLayerBuilder}
|
||||
*/
|
||||
createAnnotationLayerBuilder(
|
||||
@ -189,7 +191,9 @@ class IPDFAnnotationLayerFactory {
|
||||
annotationStorage = null,
|
||||
imageResourcesPath = "",
|
||||
renderInteractiveForms = true,
|
||||
l10n = undefined
|
||||
l10n = undefined,
|
||||
enableScripting = false,
|
||||
hasJSActionsPromise = null
|
||||
) {}
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,8 @@ import { viewerCompatibilityParams } from "./viewer_compatibility.js";
|
||||
* total pixels, i.e. width * height. Use -1 for no limit. The default value
|
||||
* is 4096 * 4096 (16 mega-pixels).
|
||||
* @property {IL10n} l10n - Localization service.
|
||||
* @property {boolean} [enableScripting] - Enable embedded script execution.
|
||||
* The default value is `false`.
|
||||
*/
|
||||
|
||||
const MAX_CANVAS_PIXELS = viewerCompatibilityParams.maxCanvasPixels || 16777216;
|
||||
@ -109,6 +111,7 @@ class PDFPageView {
|
||||
this.renderer = options.renderer || RendererType.CANVAS;
|
||||
this.enableWebGL = options.enableWebGL || false;
|
||||
this.l10n = options.l10n || NullL10n;
|
||||
this.enableScripting = options.enableScripting || false;
|
||||
|
||||
this.paintTask = null;
|
||||
this.paintedViewportMap = new WeakMap();
|
||||
@ -549,7 +552,8 @@ class PDFPageView {
|
||||
this._annotationStorage,
|
||||
this.imageResourcesPath,
|
||||
this.renderInteractiveForms,
|
||||
this.l10n
|
||||
this.l10n,
|
||||
this.enableScripting
|
||||
);
|
||||
}
|
||||
this._renderAnnotationLayer();
|
||||
|
@ -1023,7 +1023,10 @@ function getActiveOrFocusedElement() {
|
||||
*/
|
||||
function generateRandomStringForSandbox(objects) {
|
||||
const allObjects = Object.values(objects).flat(2);
|
||||
const actions = allObjects.map(obj => Object.values(obj.actions)).flat(2);
|
||||
const actions = allObjects
|
||||
.filter(obj => !!obj.actions)
|
||||
.map(obj => Object.values(obj.actions))
|
||||
.flat(2);
|
||||
|
||||
while (true) {
|
||||
const name = new Uint8Array(64);
|
||||
|
Loading…
x
Reference in New Issue
Block a user