JS -- Actions must be evaluated in global scope
* All the public properties of doc are injected into globalThis, in order to make them available through `this` * Put event in the global scope too.
This commit is contained in:
parent
c366390f6b
commit
167ff1a7fc
@ -58,6 +58,9 @@ class Sandbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
create(data) {
|
create(data) {
|
||||||
|
if (TESTING) {
|
||||||
|
this._module.ccall("nukeSandbox", null, []);
|
||||||
|
}
|
||||||
const sandboxData = JSON.stringify(data);
|
const sandboxData = JSON.stringify(data);
|
||||||
const code = [
|
const code = [
|
||||||
// Next line is replaced by code from initialization.js
|
// Next line is replaced by code from initialization.js
|
||||||
|
@ -60,10 +60,7 @@ class AForm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AFMergeChange(event) {
|
AFMergeChange(event = globalThis.event) {
|
||||||
if (!event) {
|
|
||||||
event = this._document._event;
|
|
||||||
}
|
|
||||||
if (event.willCommit) {
|
if (event.willCommit) {
|
||||||
return event.value.toString();
|
return event.value.toString();
|
||||||
}
|
}
|
||||||
@ -128,7 +125,7 @@ class AForm {
|
|||||||
strCurrency,
|
strCurrency,
|
||||||
bCurrencyPrepend
|
bCurrencyPrepend
|
||||||
) {
|
) {
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
if (!event.value) {
|
if (!event.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -193,7 +190,7 @@ class AForm {
|
|||||||
strCurrency /* unused */,
|
strCurrency /* unused */,
|
||||||
bCurrencyPrepend /* unused */
|
bCurrencyPrepend /* unused */
|
||||||
) {
|
) {
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
let value = this.AFMergeChange(event);
|
let value = this.AFMergeChange(event);
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return;
|
return;
|
||||||
@ -236,7 +233,7 @@ class AForm {
|
|||||||
throw new Error("Invalid nDec value in AFPercent_Format");
|
throw new Error("Invalid nDec value in AFPercent_Format");
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
if (nDec > 512) {
|
if (nDec > 512) {
|
||||||
event.value = "%";
|
event.value = "%";
|
||||||
return;
|
return;
|
||||||
@ -268,7 +265,7 @@ class AForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AFDate_FormatEx(cFormat) {
|
AFDate_FormatEx(cFormat) {
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
const value = event.value;
|
const value = event.value;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return;
|
return;
|
||||||
@ -287,7 +284,7 @@ class AForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AFDate_KeystrokeEx(cFormat) {
|
AFDate_KeystrokeEx(cFormat) {
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
if (!event.willCommit) {
|
if (!event.willCommit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -310,7 +307,7 @@ class AForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AFRange_Validate(bGreaterThan, nGreaterThan, bLessThan, nLessThan) {
|
AFRange_Validate(bGreaterThan, nGreaterThan, bLessThan, nLessThan) {
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
if (!event.value) {
|
if (!event.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -397,7 +394,7 @@ class AForm {
|
|||||||
throw new TypeError("Invalid function in AFSimple_Calculate");
|
throw new TypeError("Invalid function in AFSimple_Calculate");
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
const values = [];
|
const values = [];
|
||||||
for (const cField of cFields) {
|
for (const cField of cFields) {
|
||||||
const field = this._document.getField(cField);
|
const field = this._document.getField(cField);
|
||||||
@ -417,7 +414,7 @@ class AForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AFSpecial_Format(psf) {
|
AFSpecial_Format(psf) {
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
if (!event.value) {
|
if (!event.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -457,7 +454,7 @@ class AForm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
const value = this.AFMergeChange(event);
|
const value = this.AFMergeChange(event);
|
||||||
const checkers = new Map([
|
const checkers = new Map([
|
||||||
["9", char => char >= "0" && char <= "9"],
|
["9", char => char >= "0" && char <= "9"],
|
||||||
@ -526,7 +523,7 @@ class AForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AFSpecial_Keystroke(psf) {
|
AFSpecial_Keystroke(psf) {
|
||||||
const event = this._document._event;
|
const event = globalThis.event;
|
||||||
if (!event.value) {
|
if (!event.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -31,17 +31,21 @@ class Doc extends PDFObject {
|
|||||||
constructor(data) {
|
constructor(data) {
|
||||||
super(data);
|
super(data);
|
||||||
|
|
||||||
this.baseURL = data.baseURL || "";
|
// In a script doc === this.
|
||||||
this.calculate = true;
|
// So adding a property to the doc means adding it to this
|
||||||
this.delay = false;
|
this._expandos = globalThis;
|
||||||
this.dirty = false;
|
|
||||||
this.disclosed = false;
|
this._baseURL = data.baseURL || "";
|
||||||
this.media = undefined;
|
this._calculate = true;
|
||||||
this.metadata = data.metadata;
|
this._delay = false;
|
||||||
this.noautocomplete = undefined;
|
this._dirty = false;
|
||||||
this.nocache = undefined;
|
this._disclosed = false;
|
||||||
this.spellDictionaryOrder = [];
|
this._media = undefined;
|
||||||
this.spellLanguageOrder = [];
|
this._metadata = data.metadata;
|
||||||
|
this._noautocomplete = undefined;
|
||||||
|
this._nocache = undefined;
|
||||||
|
this._spellDictionaryOrder = [];
|
||||||
|
this._spellLanguageOrder = [];
|
||||||
|
|
||||||
this._printParams = null;
|
this._printParams = null;
|
||||||
this._fields = new Map();
|
this._fields = new Map();
|
||||||
@ -127,6 +131,14 @@ class Doc extends PDFObject {
|
|||||||
throw new Error("doc.author is read-only");
|
throw new Error("doc.author is read-only");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get baseURL() {
|
||||||
|
return this._baseURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
set baseURL(baseURL) {
|
||||||
|
this._baseURL = baseURL;
|
||||||
|
}
|
||||||
|
|
||||||
get bookmarkRoot() {
|
get bookmarkRoot() {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -135,6 +147,14 @@ class Doc extends PDFObject {
|
|||||||
throw new Error("doc.bookmarkRoot is read-only");
|
throw new Error("doc.bookmarkRoot is read-only");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get calculate() {
|
||||||
|
return this._calculate;
|
||||||
|
}
|
||||||
|
|
||||||
|
set calculate(calculate) {
|
||||||
|
this._calculate = calculate;
|
||||||
|
}
|
||||||
|
|
||||||
get creator() {
|
get creator() {
|
||||||
return this._creator;
|
return this._creator;
|
||||||
}
|
}
|
||||||
@ -151,6 +171,30 @@ class Doc extends PDFObject {
|
|||||||
throw new Error("doc.dataObjects is read-only");
|
throw new Error("doc.dataObjects is read-only");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get delay() {
|
||||||
|
return this._delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
set delay(delay) {
|
||||||
|
this._delay = delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dirty() {
|
||||||
|
return this._dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
set dirty(dirty) {
|
||||||
|
this._dirty = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
get disclosed() {
|
||||||
|
return this._disclosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
set disclosed(disclosed) {
|
||||||
|
this._disclosed = disclosed;
|
||||||
|
}
|
||||||
|
|
||||||
get docID() {
|
get docID() {
|
||||||
return this._docID;
|
return this._docID;
|
||||||
}
|
}
|
||||||
@ -278,6 +322,22 @@ class Doc extends PDFObject {
|
|||||||
this._layout = value;
|
this._layout = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get media() {
|
||||||
|
return this._media;
|
||||||
|
}
|
||||||
|
|
||||||
|
set media(media) {
|
||||||
|
this._media = media;
|
||||||
|
}
|
||||||
|
|
||||||
|
get metadata() {
|
||||||
|
return this._metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
set metadata(metadata) {
|
||||||
|
this._metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
get modDate() {
|
get modDate() {
|
||||||
return this._modDate;
|
return this._modDate;
|
||||||
}
|
}
|
||||||
@ -302,6 +362,22 @@ class Doc extends PDFObject {
|
|||||||
throw new Error("doc.mouseY is read-only");
|
throw new Error("doc.mouseY is read-only");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get noautocomplete() {
|
||||||
|
return this._noautocomplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
set noautocomplete(noautocomplete) {
|
||||||
|
this._noautocomplete = noautocomplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
get nocache() {
|
||||||
|
return this._nocache;
|
||||||
|
}
|
||||||
|
|
||||||
|
set nocache(nocache) {
|
||||||
|
this._nocache = nocache;
|
||||||
|
}
|
||||||
|
|
||||||
get numFields() {
|
get numFields() {
|
||||||
return this._numFields;
|
return this._numFields;
|
||||||
}
|
}
|
||||||
@ -418,6 +494,22 @@ class Doc extends PDFObject {
|
|||||||
throw new Error("doc.sounds is read-only");
|
throw new Error("doc.sounds is read-only");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get spellDictionaryOrder() {
|
||||||
|
return this._spellDictionaryOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
set spellDictionaryOrder(spellDictionaryOrder) {
|
||||||
|
this._spellDictionaryOrder = spellDictionaryOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
get spellLanguageOrder() {
|
||||||
|
return this._spellLanguageOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
set spellLanguageOrder(spellLanguageOrder) {
|
||||||
|
this._spellLanguageOrder = spellLanguageOrder;
|
||||||
|
}
|
||||||
|
|
||||||
get subject() {
|
get subject() {
|
||||||
return this._subject;
|
return this._subject;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ class EventDispatcher {
|
|||||||
|
|
||||||
const name = baseEvent.name.replace(" ", "");
|
const name = baseEvent.name.replace(" ", "");
|
||||||
const source = this._objects[id];
|
const source = this._objects[id];
|
||||||
const event = (this._document.obj._event = new Event(baseEvent));
|
globalThis.event = new Event(baseEvent);
|
||||||
let savedChange;
|
let savedChange;
|
||||||
|
|
||||||
if (source.obj._isButton()) {
|
if (source.obj._isButton()) {
|
||||||
@ -155,7 +155,7 @@ class EventDispatcher {
|
|||||||
}
|
}
|
||||||
const first = this._calculationOrder[0];
|
const first = this._calculationOrder[0];
|
||||||
const source = this._objects[first];
|
const source = this._objects[first];
|
||||||
const event = (this._document.obj._event = new Event({}));
|
globalThis.event = new Event({});
|
||||||
this.runCalculate(source, event);
|
this.runCalculate(source, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +77,8 @@ class Field extends PDFObject {
|
|||||||
this._fillColor = data.fillColor || ["T"];
|
this._fillColor = data.fillColor || ["T"];
|
||||||
this._strokeColor = data.strokeColor || ["G", 0];
|
this._strokeColor = data.strokeColor || ["G", 0];
|
||||||
this._textColor = data.textColor || ["G", 0];
|
this._textColor = data.textColor || ["G", 0];
|
||||||
|
|
||||||
|
this._globalEval = data.globalEval;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fillColor() {
|
get fillColor() {
|
||||||
@ -117,20 +119,6 @@ class Field extends PDFObject {
|
|||||||
this._valueAsString = val ? val.toString() : "";
|
this._valueAsString = val ? val.toString() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
_getFunction(code, actionName) {
|
|
||||||
try {
|
|
||||||
// This eval is running in a sandbox so it's safe to use Function
|
|
||||||
// eslint-disable-next-line no-new-func
|
|
||||||
return Function("event", `with (this) {${code}}`).bind(this._document);
|
|
||||||
} catch (error) {
|
|
||||||
const value =
|
|
||||||
`"${error.toString()}" for action ` +
|
|
||||||
`"${actionName}" in object ${this._id}.`;
|
|
||||||
this._send({ command: "error", value });
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setAction(cTrigger, cScript) {
|
setAction(cTrigger, cScript) {
|
||||||
if (typeof cTrigger !== "string" || typeof cScript !== "string") {
|
if (typeof cTrigger !== "string" || typeof cScript !== "string") {
|
||||||
return;
|
return;
|
||||||
@ -138,10 +126,7 @@ class Field extends PDFObject {
|
|||||||
if (!(cTrigger in this._actions)) {
|
if (!(cTrigger in this._actions)) {
|
||||||
this._actions[cTrigger] = [];
|
this._actions[cTrigger] = [];
|
||||||
}
|
}
|
||||||
const fun = this._getFunction(cScript, cTrigger);
|
this._actions[cTrigger].push(cScript);
|
||||||
if (fun) {
|
|
||||||
this._actions[cTrigger].push(fun);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setFocus() {
|
setFocus() {
|
||||||
@ -152,12 +137,7 @@ class Field extends PDFObject {
|
|||||||
const actionsMap = new Map();
|
const actionsMap = new Map();
|
||||||
if (actions) {
|
if (actions) {
|
||||||
for (const [eventType, actionsForEvent] of Object.entries(actions)) {
|
for (const [eventType, actionsForEvent] of Object.entries(actions)) {
|
||||||
const functions = actionsForEvent
|
actionsMap.set(eventType, actionsForEvent);
|
||||||
.map(action => this._getFunction(action, eventType))
|
|
||||||
.filter(fun => !!fun);
|
|
||||||
if (functions.length > 0) {
|
|
||||||
actionsMap.set(eventType, functions);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return actionsMap;
|
return actionsMap;
|
||||||
@ -176,7 +156,8 @@ class Field extends PDFObject {
|
|||||||
const actions = this._actions.get(eventName);
|
const actions = this._actions.get(eventName);
|
||||||
try {
|
try {
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
action(event);
|
// Action evaluation must happen in the global scope
|
||||||
|
this._globalEval(action);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.rc = false;
|
event.rc = false;
|
||||||
|
@ -72,6 +72,7 @@ function initSandbox(params) {
|
|||||||
obj.send = send;
|
obj.send = send;
|
||||||
obj.globalEval = globalEval;
|
obj.globalEval = globalEval;
|
||||||
obj.doc = _document.wrapped;
|
obj.doc = _document.wrapped;
|
||||||
|
obj.globalEval = globalEval;
|
||||||
const field = new Field(obj);
|
const field = new Field(obj);
|
||||||
const wrapped = new Proxy(field, proxyHandler);
|
const wrapped = new Proxy(field, proxyHandler);
|
||||||
doc._addField(name, wrapped);
|
doc._addField(name, wrapped);
|
||||||
@ -81,7 +82,6 @@ function initSandbox(params) {
|
|||||||
globalThis.event = null;
|
globalThis.event = null;
|
||||||
globalThis.global = Object.create(null);
|
globalThis.global = Object.create(null);
|
||||||
globalThis.app = new Proxy(app, proxyHandler);
|
globalThis.app = new Proxy(app, proxyHandler);
|
||||||
globalThis.doc = _document.wrapped;
|
|
||||||
globalThis.color = new Proxy(new Color(), proxyHandler);
|
globalThis.color = new Proxy(new Color(), proxyHandler);
|
||||||
globalThis.console = new Proxy(new Console({ send }), proxyHandler);
|
globalThis.console = new Proxy(new Console({ send }), proxyHandler);
|
||||||
globalThis.util = new Proxy(util, proxyHandler);
|
globalThis.util = new Proxy(util, proxyHandler);
|
||||||
@ -103,6 +103,26 @@ function initSandbox(params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The doc properties must live in the global scope too
|
||||||
|
const properties = Object.create(null);
|
||||||
|
for (const name of Object.getOwnPropertyNames(Doc.prototype)) {
|
||||||
|
if (name === "constructor" || name.startsWith("_")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const descriptor = Object.getOwnPropertyDescriptor(Doc.prototype, name);
|
||||||
|
if (descriptor.get) {
|
||||||
|
properties[name] = {
|
||||||
|
get: descriptor.get.bind(doc),
|
||||||
|
set: descriptor.set.bind(doc),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
properties[name] = {
|
||||||
|
value: Doc.prototype[name].bind(doc),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object.defineProperties(globalThis, properties);
|
||||||
|
|
||||||
const functions = {
|
const functions = {
|
||||||
dispatchEvent: app._dispatchEvent.bind(app),
|
dispatchEvent: app._dispatchEvent.bind(app),
|
||||||
timeoutCb: app._evalCallback.bind(app),
|
timeoutCb: app._evalCallback.bind(app),
|
||||||
|
@ -129,6 +129,38 @@ describe("Scripting", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Doc", function () {
|
||||||
|
it("should treat globalThis as the doc", async function (done) {
|
||||||
|
const refId = getId();
|
||||||
|
const data = {
|
||||||
|
objects: {
|
||||||
|
field: [
|
||||||
|
{
|
||||||
|
id: refId,
|
||||||
|
value: "",
|
||||||
|
actions: {},
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
appInfo: { language: "en-US", platform: "Linux x86_64" },
|
||||||
|
calculationOrder: [],
|
||||||
|
dispatchEventName: "_dispatchMe",
|
||||||
|
};
|
||||||
|
sandbox.createSandbox(data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await myeval(`(this.foobar = 123456, 0)`);
|
||||||
|
await myeval(`this.getField("field").doc.foobar`).then(value => {
|
||||||
|
expect(value).toEqual(123456);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
} catch (ex) {
|
||||||
|
done.fail(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("Util", function () {
|
describe("Util", function () {
|
||||||
beforeAll(function (done) {
|
beforeAll(function (done) {
|
||||||
sandbox.createSandbox({
|
sandbox.createSandbox({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user