mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-21 23:58:07 +02:00
Merge pull request #12604 from calixteman/quickjs
JS -- Add a sandbox based on quickjs
This commit is contained in:
commit
c88e805870
15 changed files with 886 additions and 245 deletions
|
@ -32,7 +32,6 @@
|
|||
"pdf_find_utils_spec.js",
|
||||
"pdf_history_spec.js",
|
||||
"primitives_spec.js",
|
||||
"scripting_spec.js",
|
||||
"stream_spec.js",
|
||||
"type1_parser_spec.js",
|
||||
"ui_utils_spec.js",
|
||||
|
|
|
@ -13,258 +13,426 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { initSandbox } from "../../src/scripting_api/initialization.js";
|
||||
import { loadScript } from "../../src/display/display_utils.js";
|
||||
|
||||
describe("Scripting", function () {
|
||||
let sandbox, send_queue, test_id, ref;
|
||||
|
||||
function getId() {
|
||||
const id = `${ref++}R`;
|
||||
return id;
|
||||
}
|
||||
|
||||
beforeAll(function (done) {
|
||||
test_id = 0;
|
||||
ref = 1;
|
||||
send_queue = new Map();
|
||||
window.dispatchEvent = event => {
|
||||
if (send_queue.has(event.detail.id)) {
|
||||
const prev = send_queue.get(event.detail.id);
|
||||
Object.assign(prev, event.detail);
|
||||
} else {
|
||||
send_queue.set(event.detail.id, event.detail);
|
||||
}
|
||||
};
|
||||
const promise = loadScript("../../build/generic/build/pdf.sandbox.js").then(
|
||||
() => {
|
||||
return window.pdfjsSandbox.QuickJSSandbox(true);
|
||||
}
|
||||
);
|
||||
sandbox = {
|
||||
createSandbox(data) {
|
||||
promise.then(sbx => sbx.create(data));
|
||||
},
|
||||
dispatchEventInSandbox(data) {
|
||||
return promise.then(sbx => sbx.dispatchEvent(data));
|
||||
},
|
||||
nukeSandbox() {
|
||||
promise.then(sbx => sbx.nukeSandbox());
|
||||
},
|
||||
eval(code, key) {
|
||||
return promise.then(sbx => sbx.evalForTesting(code, key));
|
||||
},
|
||||
};
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function () {
|
||||
sandbox.nukeSandbox();
|
||||
sandbox = null;
|
||||
send_queue = null;
|
||||
});
|
||||
|
||||
describe("Sandbox", function () {
|
||||
it("should send a value, execute an action and get back a new value", function (done) {
|
||||
function compute(n) {
|
||||
let s = 0;
|
||||
for (let i = 0; i < n; i++) {
|
||||
s += i;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
const number = 123;
|
||||
const expected = ((number - 1) * number) / 2;
|
||||
const refId = getId();
|
||||
|
||||
const data = {
|
||||
objects: {
|
||||
field: [
|
||||
{
|
||||
id: refId,
|
||||
value: "",
|
||||
actions: {
|
||||
Keystroke: [
|
||||
`${compute.toString()}event.value = compute(parseInt(event.value));`,
|
||||
],
|
||||
},
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
calculationOrder: [],
|
||||
dispatchEventName: "_dispatchMe",
|
||||
};
|
||||
sandbox.createSandbox(data);
|
||||
sandbox
|
||||
.dispatchEventInSandbox({
|
||||
id: refId,
|
||||
value: `${number}`,
|
||||
name: "Keystroke",
|
||||
willCommit: true,
|
||||
})
|
||||
.then(() => {
|
||||
expect(send_queue.has(refId)).toEqual(true);
|
||||
expect(send_queue.get(refId)).toEqual({
|
||||
id: refId,
|
||||
valueAsString: expected,
|
||||
});
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Util", function () {
|
||||
let sandbox, util;
|
||||
function myeval(code) {
|
||||
const key = (test_id++).toString();
|
||||
return sandbox.eval(code, key).then(() => {
|
||||
return send_queue.get(key).result;
|
||||
});
|
||||
}
|
||||
|
||||
beforeAll(function (done) {
|
||||
sandbox = Object.create(null);
|
||||
const extra = { send: null, crackURL: null };
|
||||
const data = { objects: {}, calculationOrder: [] };
|
||||
initSandbox({ data, extra, out: sandbox });
|
||||
util = sandbox.util;
|
||||
sandbox.createSandbox({
|
||||
objects: {},
|
||||
calculationOrder: [],
|
||||
dispatchEventName: "_dispatchMe",
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function () {
|
||||
sandbox = util = null;
|
||||
});
|
||||
|
||||
describe("printd", function () {
|
||||
it("should print a date according to a format", function (done) {
|
||||
const date = new Date("April 15, 1707 3:14:15");
|
||||
expect(util.printd(0, date)).toEqual("D:17070415031415");
|
||||
expect(util.printd(1, date)).toEqual("1707.04.15 03:14:15");
|
||||
expect(util.printd(2, date)).toEqual("4/15/07 3:14:15 am");
|
||||
expect(util.printd("mmmm mmm mm m", date)).toEqual("April Apr 04 4");
|
||||
expect(util.printd("dddd ddd dd d", date)).toEqual("Friday Fri 15 15");
|
||||
done();
|
||||
const date = `new Date("Sun Apr 15 2007 03:14:15")`;
|
||||
Promise.all([
|
||||
myeval(`util.printd(0, ${date})`).then(value => {
|
||||
expect(value).toEqual("D:20070415031415");
|
||||
}),
|
||||
myeval(`util.printd(1, ${date})`).then(value => {
|
||||
expect(value).toEqual("2007.04.15 03:14:15");
|
||||
}),
|
||||
myeval(`util.printd(2, ${date})`).then(value => {
|
||||
expect(value).toEqual("4/15/07 3:14:15 am");
|
||||
}),
|
||||
myeval(`util.printd("mmmm mmm mm m", ${date})`).then(value => {
|
||||
expect(value).toEqual("April Apr 04 4");
|
||||
}),
|
||||
myeval(`util.printd("dddd ddd dd d", ${date})`).then(value => {
|
||||
expect(value).toEqual("Sunday Sun 15 15");
|
||||
}),
|
||||
]).then(() => done());
|
||||
});
|
||||
});
|
||||
|
||||
describe("scand", function () {
|
||||
it("should parse a date according to a format", function (done) {
|
||||
const date = new Date("April 15, 1707 3:14:15");
|
||||
expect(util.scand(0, "D:17070415031415")).toEqual(date);
|
||||
expect(util.scand(1, "1707.04.15 03:14:15")).toEqual(date);
|
||||
expect(util.scand(2, "4/15/07 3:14:15 am")).toEqual(
|
||||
new Date("April 15, 2007 3:14:15")
|
||||
);
|
||||
done();
|
||||
const date = new Date("Sun Apr 15 2007 03:14:15");
|
||||
Promise.all([
|
||||
myeval(`util.scand(0, "D:20070415031415").toString()`).then(value => {
|
||||
expect(new Date(value)).toEqual(date);
|
||||
}),
|
||||
myeval(`util.scand(1, "2007.04.15 03:14:15").toString()`).then(
|
||||
value => {
|
||||
expect(new Date(value)).toEqual(date);
|
||||
}
|
||||
),
|
||||
myeval(`util.scand(2, "4/15/07 3:14:15 am").toString()`).then(
|
||||
value => {
|
||||
expect(new Date(value)).toEqual(date);
|
||||
}
|
||||
),
|
||||
]).then(() => done());
|
||||
});
|
||||
});
|
||||
|
||||
describe("printf", function () {
|
||||
it("should print some data according to a format", function (done) {
|
||||
expect(
|
||||
util.printf("Integer numbers: %d, %d,...", 1.234, 56.789)
|
||||
).toEqual("Integer numbers: 1, 56,...");
|
||||
expect(util.printf("Hex numbers: %x, %x,...", 1234, 56789)).toEqual(
|
||||
"Hex numbers: 4D2, DDD5,..."
|
||||
);
|
||||
expect(
|
||||
util.printf("Hex numbers with 0x: %#x, %#x,...", 1234, 56789)
|
||||
).toEqual("Hex numbers with 0x: 0x4D2, 0xDDD5,...");
|
||||
expect(util.printf("Decimal number: %,0+.3f", 1234567.89123)).toEqual(
|
||||
"Decimal number: +1,234,567.891"
|
||||
);
|
||||
expect(util.printf("Decimal number: %,0+8.3f", 1.234567)).toEqual(
|
||||
"Decimal number: + 1.235"
|
||||
);
|
||||
done();
|
||||
Promise.all([
|
||||
myeval(
|
||||
`util.printf("Integer numbers: %d, %d,...", 1.234, 56.789)`
|
||||
).then(value => {
|
||||
expect(value).toEqual("Integer numbers: 1, 56,...");
|
||||
}),
|
||||
myeval(`util.printf("Hex numbers: %x, %x,...", 1234, 56789)`).then(
|
||||
value => {
|
||||
expect(value).toEqual("Hex numbers: 4D2, DDD5,...");
|
||||
}
|
||||
),
|
||||
myeval(
|
||||
`util.printf("Hex numbers with 0x: %#x, %#x,...", 1234, 56789)`
|
||||
).then(value => {
|
||||
expect(value).toEqual("Hex numbers with 0x: 0x4D2, 0xDDD5,...");
|
||||
}),
|
||||
myeval(`util.printf("Decimal number: %,0+.3f", 1234567.89123)`).then(
|
||||
value => {
|
||||
expect(value).toEqual("Decimal number: +1,234,567.891");
|
||||
}
|
||||
),
|
||||
myeval(`util.printf("Decimal number: %,0+8.3f", 1.234567)`).then(
|
||||
value => {
|
||||
expect(value).toEqual("Decimal number: + 1.235");
|
||||
}
|
||||
),
|
||||
]).then(() => done());
|
||||
});
|
||||
|
||||
it("should print a string with no argument", function (done) {
|
||||
expect(util.printf("hello world")).toEqual("hello world");
|
||||
done();
|
||||
myeval(`util.printf("hello world")`)
|
||||
.then(value => {
|
||||
expect(value).toEqual("hello world");
|
||||
})
|
||||
.then(() => done());
|
||||
});
|
||||
|
||||
it("should print a string with a percent", function (done) {
|
||||
expect(util.printf("%%s")).toEqual("%%s");
|
||||
expect(util.printf("%%s", "hello")).toEqual("%%s");
|
||||
done();
|
||||
it(" print a string with a percent", function (done) {
|
||||
myeval(`util.printf("%%s")`)
|
||||
.then(value => {
|
||||
expect(value).toEqual("%%s");
|
||||
})
|
||||
.then(() => done());
|
||||
});
|
||||
});
|
||||
|
||||
describe("printx", function () {
|
||||
it("should print some data according to a format", function (done) {
|
||||
expect(util.printx("9 (999) 999-9999", "aaa14159697489zzz")).toEqual(
|
||||
"1 (415) 969-7489"
|
||||
);
|
||||
done();
|
||||
myeval(`util.printx("9 (999) 999-9999", "aaa14159697489zzz")`)
|
||||
.then(value => {
|
||||
expect(value).toEqual("1 (415) 969-7489");
|
||||
})
|
||||
.then(() => done());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function () {
|
||||
let sandbox, send_queue, _app;
|
||||
|
||||
beforeEach(function (done) {
|
||||
send_queue = [];
|
||||
sandbox = Object.create(null);
|
||||
const extra = {
|
||||
send(data) {
|
||||
send_queue.push(data);
|
||||
},
|
||||
crackURL: null,
|
||||
};
|
||||
it("should trigger an event and modify the source", function (done) {
|
||||
const refId = getId();
|
||||
const data = {
|
||||
objects: {
|
||||
field314R: [
|
||||
field: [
|
||||
{
|
||||
id: "314R",
|
||||
id: refId,
|
||||
value: "",
|
||||
actions: {},
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
field271R: [
|
||||
{
|
||||
id: "271R",
|
||||
value: "",
|
||||
actions: {},
|
||||
actions: {
|
||||
test: [`event.source.value = "123";`],
|
||||
},
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
calculationOrder: ["271R"],
|
||||
calculationOrder: [],
|
||||
dispatchEventName: "_dispatchMe",
|
||||
};
|
||||
|
||||
initSandbox({
|
||||
data,
|
||||
extra,
|
||||
out: sandbox,
|
||||
testMode: true,
|
||||
});
|
||||
|
||||
_app = sandbox._app;
|
||||
send_queue = [];
|
||||
done();
|
||||
});
|
||||
|
||||
afterAll(function () {
|
||||
sandbox = send_queue = _app = null;
|
||||
});
|
||||
|
||||
it("should trigger an event and modify the source", function (done) {
|
||||
_app._objects["314R"].obj._actions.set("test", [
|
||||
event => {
|
||||
event.source.value = "123";
|
||||
},
|
||||
]);
|
||||
|
||||
sandbox.app._dispatchMe({
|
||||
id: "314R",
|
||||
value: "",
|
||||
name: "test",
|
||||
willCommit: true,
|
||||
});
|
||||
|
||||
expect(send_queue.length).toEqual(1);
|
||||
expect(send_queue[0]).toEqual({ id: "314R", value: "123" });
|
||||
|
||||
done();
|
||||
sandbox.createSandbox(data);
|
||||
sandbox
|
||||
.dispatchEventInSandbox({
|
||||
id: refId,
|
||||
value: "",
|
||||
name: "test",
|
||||
willCommit: true,
|
||||
})
|
||||
.then(() => {
|
||||
expect(send_queue.has(refId)).toEqual(true);
|
||||
expect(send_queue.get(refId)).toEqual({
|
||||
id: refId,
|
||||
value: "123",
|
||||
});
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it("should trigger a Keystroke event and invalidate it", function (done) {
|
||||
_app._objects["314R"].obj._actions.set("Keystroke", [
|
||||
event => {
|
||||
event.rc = false;
|
||||
const refId = getId();
|
||||
const data = {
|
||||
objects: {
|
||||
field: [
|
||||
{
|
||||
id: refId,
|
||||
value: "",
|
||||
actions: {
|
||||
Keystroke: [`event.rc = false;`],
|
||||
},
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
sandbox.app._dispatchMe({
|
||||
id: "314R",
|
||||
value: "hell",
|
||||
name: "Keystroke",
|
||||
willCommit: false,
|
||||
change: "o",
|
||||
selStart: 4,
|
||||
selEnd: 4,
|
||||
});
|
||||
expect(send_queue.length).toEqual(1);
|
||||
expect(send_queue[0]).toEqual({
|
||||
id: "314R",
|
||||
value: "hell",
|
||||
selRange: [4, 4],
|
||||
});
|
||||
|
||||
done();
|
||||
calculationOrder: [],
|
||||
dispatchEventName: "_dispatchMe",
|
||||
};
|
||||
sandbox.createSandbox(data);
|
||||
sandbox
|
||||
.dispatchEventInSandbox({
|
||||
id: refId,
|
||||
value: "hell",
|
||||
name: "Keystroke",
|
||||
willCommit: false,
|
||||
change: "o",
|
||||
selStart: 4,
|
||||
selEnd: 4,
|
||||
})
|
||||
.then(() => {
|
||||
expect(send_queue.has(refId)).toEqual(true);
|
||||
expect(send_queue.get(refId)).toEqual({
|
||||
id: refId,
|
||||
value: "hell",
|
||||
selRange: [4, 4],
|
||||
});
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it("should trigger a Keystroke event and change it", function (done) {
|
||||
_app._objects["314R"].obj._actions.set("Keystroke", [
|
||||
event => {
|
||||
event.change = "a";
|
||||
const refId = getId();
|
||||
const data = {
|
||||
objects: {
|
||||
field: [
|
||||
{
|
||||
id: refId,
|
||||
value: "",
|
||||
actions: {
|
||||
Keystroke: [`event.change = "a";`],
|
||||
},
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
sandbox.app._dispatchMe({
|
||||
id: "314R",
|
||||
value: "hell",
|
||||
name: "Keystroke",
|
||||
willCommit: false,
|
||||
change: "o",
|
||||
selStart: 4,
|
||||
selEnd: 4,
|
||||
});
|
||||
expect(send_queue.length).toEqual(1);
|
||||
expect(send_queue[0]).toEqual({ id: "314R", value: "hella" });
|
||||
|
||||
done();
|
||||
calculationOrder: [],
|
||||
dispatchEventName: "_dispatchMe",
|
||||
};
|
||||
sandbox.createSandbox(data);
|
||||
sandbox
|
||||
.dispatchEventInSandbox({
|
||||
id: refId,
|
||||
value: "hell",
|
||||
name: "Keystroke",
|
||||
willCommit: false,
|
||||
change: "o",
|
||||
selStart: 4,
|
||||
selEnd: 4,
|
||||
})
|
||||
.then(() => {
|
||||
expect(send_queue.has(refId)).toEqual(true);
|
||||
expect(send_queue.get(refId)).toEqual({
|
||||
id: refId,
|
||||
value: "hella",
|
||||
});
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it("should trigger an invalid commit Keystroke event", function (done) {
|
||||
_app._objects["314R"].obj._actions.set("Validate", [
|
||||
event => {
|
||||
event.rc = false;
|
||||
const refId = getId();
|
||||
const data = {
|
||||
objects: {
|
||||
field: [
|
||||
{
|
||||
id: refId,
|
||||
value: "",
|
||||
actions: {
|
||||
test: [`event.rc = false;`],
|
||||
},
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
sandbox.app._dispatchMe({
|
||||
id: "314R",
|
||||
value: "hello",
|
||||
name: "Keystroke",
|
||||
willCommit: true,
|
||||
});
|
||||
expect(send_queue.length).toEqual(0);
|
||||
|
||||
done();
|
||||
calculationOrder: [],
|
||||
dispatchEventName: "_dispatchMe",
|
||||
};
|
||||
sandbox.createSandbox(data);
|
||||
sandbox
|
||||
.dispatchEventInSandbox({
|
||||
id: refId,
|
||||
value: "",
|
||||
name: "test",
|
||||
willCommit: true,
|
||||
})
|
||||
.then(() => {
|
||||
expect(send_queue.has(refId)).toEqual(false);
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it("should trigger a valid commit Keystroke event", function (done) {
|
||||
let output = "";
|
||||
_app._objects["314R"].obj._actions.set("Validate", [
|
||||
event => {
|
||||
event.value = "world";
|
||||
output += "foo";
|
||||
const refId1 = getId();
|
||||
const refId2 = getId();
|
||||
const data = {
|
||||
objects: {
|
||||
field1: [
|
||||
{
|
||||
id: refId1,
|
||||
value: "",
|
||||
actions: {
|
||||
Validate: [`event.value = "world";`],
|
||||
},
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
field2: [
|
||||
{
|
||||
id: refId2,
|
||||
value: "",
|
||||
actions: {
|
||||
Calculate: [`event.value = "hello";`],
|
||||
},
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
_app._objects["271R"].obj._actions.set("Calculate", [
|
||||
event => {
|
||||
event.value = "hello";
|
||||
output += "bar";
|
||||
},
|
||||
]);
|
||||
|
||||
sandbox.app._dispatchMe({
|
||||
id: "314R",
|
||||
value: "hello",
|
||||
name: "Keystroke",
|
||||
willCommit: true,
|
||||
});
|
||||
|
||||
expect(send_queue.length).toEqual(4);
|
||||
expect(send_queue[0]).toEqual({ id: "314R", value: "world" });
|
||||
expect(send_queue[1]).toEqual({ id: "271R", value: "hello" });
|
||||
expect(send_queue[2]).toEqual({ id: "271R", valueAsString: "hello" });
|
||||
expect(send_queue[3]).toEqual({ id: "314R", valueAsString: "world" });
|
||||
expect(output).toEqual("foobar");
|
||||
|
||||
done();
|
||||
calculationOrder: [refId2],
|
||||
dispatchEventName: "_dispatchMe",
|
||||
};
|
||||
sandbox.createSandbox(data);
|
||||
sandbox
|
||||
.dispatchEventInSandbox({
|
||||
id: refId1,
|
||||
value: "hello",
|
||||
name: "Keystroke",
|
||||
willCommit: true,
|
||||
})
|
||||
.then(() => {
|
||||
expect(send_queue.has(refId1)).toEqual(true);
|
||||
expect(send_queue.get(refId1)).toEqual({
|
||||
id: refId1,
|
||||
value: "world",
|
||||
valueAsString: "world",
|
||||
});
|
||||
done();
|
||||
})
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue