JS -- Add a sandbox based on quickjs
* quickjs-eval.js has been generated using https://github.com/mozilla/pdf.js.quickjs/ * lazy load of sandbox code * Rewrite tests to use the sandbox * Add a task `watch-sandbox` which update bundle pdf.sandbox.js on change in the sandbox code
This commit is contained in:
parent
d3936ac9d2
commit
c7974e9996
@ -7,6 +7,7 @@ external/webL10n/
|
|||||||
external/cmapscompress/
|
external/cmapscompress/
|
||||||
external/builder/fixtures/
|
external/builder/fixtures/
|
||||||
external/builder/fixtures_esprima/
|
external/builder/fixtures_esprima/
|
||||||
|
external/quickjs/quickjs-eval.js
|
||||||
src/shared/cffStandardStrings.js
|
src/shared/cffStandardStrings.js
|
||||||
src/shared/fonts_utils.js
|
src/shared/fonts_utils.js
|
||||||
test/tmp/
|
test/tmp/
|
||||||
|
42
external/quickjs/quickjs-eval.js
vendored
Normal file
42
external/quickjs/quickjs-eval.js
vendored
Normal file
File diff suppressed because one or more lines are too long
243
gulpfile.js
243
gulpfile.js
@ -33,6 +33,7 @@ var stream = require("stream");
|
|||||||
var exec = require("child_process").exec;
|
var exec = require("child_process").exec;
|
||||||
var spawn = require("child_process").spawn;
|
var spawn = require("child_process").spawn;
|
||||||
var spawnSync = require("child_process").spawnSync;
|
var spawnSync = require("child_process").spawnSync;
|
||||||
|
var stripComments = require("gulp-strip-comments");
|
||||||
var streamqueue = require("streamqueue");
|
var streamqueue = require("streamqueue");
|
||||||
var merge = require("merge-stream");
|
var merge = require("merge-stream");
|
||||||
var zip = require("gulp-zip");
|
var zip = require("gulp-zip");
|
||||||
@ -105,6 +106,7 @@ const DEFINES = Object.freeze({
|
|||||||
COMPONENTS: false,
|
COMPONENTS: false,
|
||||||
LIB: false,
|
LIB: false,
|
||||||
IMAGE_DECODERS: false,
|
IMAGE_DECODERS: false,
|
||||||
|
NO_SOURCE_MAP: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
function transform(charEncoding, transformFunction) {
|
function transform(charEncoding, transformFunction) {
|
||||||
@ -182,7 +184,8 @@ function createWebpackConfig(defines, output) {
|
|||||||
var enableSourceMaps =
|
var enableSourceMaps =
|
||||||
!bundleDefines.MOZCENTRAL &&
|
!bundleDefines.MOZCENTRAL &&
|
||||||
!bundleDefines.CHROME &&
|
!bundleDefines.CHROME &&
|
||||||
!bundleDefines.TESTING;
|
!bundleDefines.TESTING &&
|
||||||
|
!bundleDefines.NO_SOURCE_MAP;
|
||||||
var skipBabel = bundleDefines.SKIP_BABEL;
|
var skipBabel = bundleDefines.SKIP_BABEL;
|
||||||
|
|
||||||
// `core-js` (see https://github.com/zloirock/core-js/issues/514),
|
// `core-js` (see https://github.com/zloirock/core-js/issues/514),
|
||||||
@ -343,6 +346,53 @@ function createScriptingBundle(defines) {
|
|||||||
.pipe(replaceJSRootName(scriptingAMDName, "pdfjsScripting"));
|
.pipe(replaceJSRootName(scriptingAMDName, "pdfjsScripting"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createSandboxBundle(defines, code) {
|
||||||
|
var sandboxAMDName = "pdfjs-dist/build/pdf.sandbox";
|
||||||
|
var sandboxOutputName = "pdf.sandbox.js";
|
||||||
|
var sandboxFileConfig = createWebpackConfig(defines, {
|
||||||
|
filename: sandboxOutputName,
|
||||||
|
library: sandboxAMDName,
|
||||||
|
libraryTarget: "umd",
|
||||||
|
umdNamedDefine: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// The code is the one from the bundle pdf.scripting.js
|
||||||
|
// so in order to have it in a string (which will be eval-ed
|
||||||
|
// in the sandbox) we must escape some chars.
|
||||||
|
// This way we've all the code (initialization+sandbox) in
|
||||||
|
// the same bundle.
|
||||||
|
code = code.replace(/["\\\n\t]/g, match => {
|
||||||
|
if (match === "\n") {
|
||||||
|
return "\\n";
|
||||||
|
}
|
||||||
|
if (match === "\t") {
|
||||||
|
return "\\t";
|
||||||
|
}
|
||||||
|
return `\\${match}`;
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
gulp
|
||||||
|
.src("./src/scripting_api/quickjs-sandbox.js")
|
||||||
|
.pipe(webpack2Stream(sandboxFileConfig))
|
||||||
|
.pipe(replaceWebpackRequire())
|
||||||
|
.pipe(replaceJSRootName(sandboxAMDName, "pdfjsSandbox"))
|
||||||
|
// put the code in a string to be eval-ed in the sandbox
|
||||||
|
.pipe(replace("/* INITIALIZATION_CODE */", `${code}`))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSandbox(defines, dir) {
|
||||||
|
const scriptingDefines = builder.merge(defines, { NO_SOURCE_MAP: true });
|
||||||
|
return createScriptingBundle(scriptingDefines)
|
||||||
|
.pipe(stripComments())
|
||||||
|
.pipe(gulp.dest(dir + "build"))
|
||||||
|
.on("data", file => {
|
||||||
|
const content = file.contents.toString();
|
||||||
|
createSandboxBundle(defines, content).pipe(gulp.dest(dir + "build"));
|
||||||
|
fs.unlinkSync(dir + "build/pdf.scripting.js");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function createWorkerBundle(defines) {
|
function createWorkerBundle(defines) {
|
||||||
var workerAMDName = "pdfjs-dist/build/pdf.worker";
|
var workerAMDName = "pdfjs-dist/build/pdf.worker";
|
||||||
var workerOutputName = "pdf.worker.js";
|
var workerOutputName = "pdf.worker.js";
|
||||||
@ -494,6 +544,25 @@ function makeRef(done, bot) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gulp.task("sandbox", function (done) {
|
||||||
|
const defines = builder.merge(DEFINES, { GENERIC: true });
|
||||||
|
buildSandbox(defines, GENERIC_DIR);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task("watch-sandbox", function (done) {
|
||||||
|
const defines = builder.merge(DEFINES, { GENERIC: true });
|
||||||
|
buildSandbox(defines, GENERIC_DIR);
|
||||||
|
const watcher = gulp.watch([
|
||||||
|
"src/scripting_api/*.js",
|
||||||
|
"external/quickjs/*.js",
|
||||||
|
]);
|
||||||
|
watcher.on("change", function () {
|
||||||
|
buildSandbox(defines, GENERIC_DIR);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
gulp.task("default", function (done) {
|
gulp.task("default", function (done) {
|
||||||
console.log("Available tasks:");
|
console.log("Available tasks:");
|
||||||
var tasks = Object.keys(gulp.registry().tasks());
|
var tasks = Object.keys(gulp.registry().tasks());
|
||||||
@ -762,26 +831,47 @@ function buildGeneric(defines, dir) {
|
|||||||
// HTML5 browsers, which implement modern ECMAScript features.
|
// HTML5 browsers, which implement modern ECMAScript features.
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"generic",
|
"generic",
|
||||||
gulp.series("buildnumber", "default_preferences", "locale", function () {
|
gulp.series(
|
||||||
console.log();
|
"buildnumber",
|
||||||
console.log("### Creating generic viewer");
|
"default_preferences",
|
||||||
var defines = builder.merge(DEFINES, { GENERIC: true });
|
"locale",
|
||||||
|
function () {
|
||||||
|
console.log();
|
||||||
|
console.log("### Creating generic viewer");
|
||||||
|
var defines = builder.merge(DEFINES, { GENERIC: true });
|
||||||
|
|
||||||
return buildGeneric(defines, GENERIC_DIR);
|
return buildGeneric(defines, GENERIC_DIR);
|
||||||
})
|
},
|
||||||
|
"sandbox"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Builds the generic production viewer that should be compatible with most
|
// Builds the generic production viewer that should be compatible with most
|
||||||
// older HTML5 browsers.
|
// older HTML5 browsers.
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"generic-es5",
|
"generic-es5",
|
||||||
gulp.series("buildnumber", "default_preferences", "locale", function () {
|
gulp.series(
|
||||||
console.log();
|
"buildnumber",
|
||||||
console.log("### Creating generic (ES5) viewer");
|
"default_preferences",
|
||||||
var defines = builder.merge(DEFINES, { GENERIC: true, SKIP_BABEL: false });
|
"locale",
|
||||||
|
function () {
|
||||||
|
console.log();
|
||||||
|
console.log("### Creating generic (ES5) viewer");
|
||||||
|
var defines = builder.merge(DEFINES, {
|
||||||
|
GENERIC: true,
|
||||||
|
SKIP_BABEL: false,
|
||||||
|
});
|
||||||
|
|
||||||
return buildGeneric(defines, GENERIC_ES5_DIR);
|
return buildGeneric(defines, GENERIC_ES5_DIR);
|
||||||
})
|
},
|
||||||
|
function () {
|
||||||
|
const defines = builder.merge(DEFINES, {
|
||||||
|
GENERIC: true,
|
||||||
|
SKIP_BABEL: false,
|
||||||
|
});
|
||||||
|
return buildSandbox(defines, GENERIC_ES5_DIR);
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
function buildComponents(defines, dir) {
|
function buildComponents(defines, dir) {
|
||||||
@ -908,33 +998,61 @@ function buildMinified(defines, dir) {
|
|||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"minified-pre",
|
"minified-pre",
|
||||||
gulp.series("buildnumber", "default_preferences", "locale", function () {
|
gulp.series(
|
||||||
console.log();
|
"buildnumber",
|
||||||
console.log("### Creating minified viewer");
|
"default_preferences",
|
||||||
var defines = builder.merge(DEFINES, { MINIFIED: true, GENERIC: true });
|
"locale",
|
||||||
|
function () {
|
||||||
|
console.log();
|
||||||
|
console.log("### Creating minified viewer");
|
||||||
|
var defines = builder.merge(DEFINES, { MINIFIED: true, GENERIC: true });
|
||||||
|
|
||||||
return buildMinified(defines, MINIFIED_DIR);
|
return buildSandbox(defines, MINIFIED_DIR);
|
||||||
})
|
},
|
||||||
|
function () {
|
||||||
|
var defines = builder.merge(DEFINES, { MINIFIED: true, GENERIC: true });
|
||||||
|
|
||||||
|
return buildMinified(defines, MINIFIED_DIR);
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"minified-es5-pre",
|
"minified-es5-pre",
|
||||||
gulp.series("buildnumber", "default_preferences", "locale", function () {
|
gulp.series(
|
||||||
console.log();
|
"buildnumber",
|
||||||
console.log("### Creating minified (ES5) viewer");
|
"default_preferences",
|
||||||
var defines = builder.merge(DEFINES, {
|
"locale",
|
||||||
MINIFIED: true,
|
function () {
|
||||||
GENERIC: true,
|
console.log();
|
||||||
SKIP_BABEL: false,
|
console.log("### Creating minified (ES5) viewer");
|
||||||
});
|
var defines = builder.merge(DEFINES, {
|
||||||
|
MINIFIED: true,
|
||||||
|
GENERIC: true,
|
||||||
|
SKIP_BABEL: false,
|
||||||
|
});
|
||||||
|
|
||||||
return buildMinified(defines, MINIFIED_ES5_DIR);
|
return buildSandbox(defines, MINIFIED_ES5_DIR);
|
||||||
})
|
},
|
||||||
|
|
||||||
|
function () {
|
||||||
|
var defines = builder.merge(DEFINES, {
|
||||||
|
MINIFIED: true,
|
||||||
|
GENERIC: true,
|
||||||
|
SKIP_BABEL: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return buildMinified(defines, MINIFIED_ES5_DIR);
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
async function parseMinified(dir) {
|
async function parseMinified(dir) {
|
||||||
var pdfFile = fs.readFileSync(dir + "/build/pdf.js").toString();
|
var pdfFile = fs.readFileSync(dir + "/build/pdf.js").toString();
|
||||||
var pdfWorkerFile = fs.readFileSync(dir + "/build/pdf.worker.js").toString();
|
var pdfWorkerFile = fs.readFileSync(dir + "/build/pdf.worker.js").toString();
|
||||||
|
var pdfSandboxFile = fs
|
||||||
|
.readFileSync(dir + "/build/pdf.sandbox.js")
|
||||||
|
.toString();
|
||||||
var pdfImageDecodersFile = fs
|
var pdfImageDecodersFile = fs
|
||||||
.readFileSync(dir + "/image_decoders/pdf.image_decoders.js")
|
.readFileSync(dir + "/image_decoders/pdf.image_decoders.js")
|
||||||
.toString();
|
.toString();
|
||||||
@ -968,6 +1086,10 @@ async function parseMinified(dir) {
|
|||||||
dir + "/build/pdf.worker.min.js",
|
dir + "/build/pdf.worker.min.js",
|
||||||
(await Terser.minify(pdfWorkerFile, options)).code
|
(await Terser.minify(pdfWorkerFile, options)).code
|
||||||
);
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
dir + "/build/pdf.sandbox.min.js",
|
||||||
|
(await Terser.minify(pdfSandboxFile, options)).code
|
||||||
|
);
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
dir + "image_decoders/pdf.image_decoders.min.js",
|
dir + "image_decoders/pdf.image_decoders.min.js",
|
||||||
(await Terser.minify(pdfImageDecodersFile, options)).code
|
(await Terser.minify(pdfImageDecodersFile, options)).code
|
||||||
@ -980,9 +1102,14 @@ async function parseMinified(dir) {
|
|||||||
fs.unlinkSync(dir + "/web/debugger.js");
|
fs.unlinkSync(dir + "/web/debugger.js");
|
||||||
fs.unlinkSync(dir + "/build/pdf.js");
|
fs.unlinkSync(dir + "/build/pdf.js");
|
||||||
fs.unlinkSync(dir + "/build/pdf.worker.js");
|
fs.unlinkSync(dir + "/build/pdf.worker.js");
|
||||||
|
fs.unlinkSync(dir + "/build/pdf.sandbox.js");
|
||||||
|
|
||||||
fs.renameSync(dir + "/build/pdf.min.js", dir + "/build/pdf.js");
|
fs.renameSync(dir + "/build/pdf.min.js", dir + "/build/pdf.js");
|
||||||
fs.renameSync(dir + "/build/pdf.worker.min.js", dir + "/build/pdf.worker.js");
|
fs.renameSync(dir + "/build/pdf.worker.min.js", dir + "/build/pdf.worker.js");
|
||||||
|
fs.renameSync(
|
||||||
|
dir + "/build/pdf.sandbox.min.js",
|
||||||
|
dir + "/build/pdf.sandbox.js"
|
||||||
|
);
|
||||||
fs.renameSync(
|
fs.renameSync(
|
||||||
dir + "/image_decoders/pdf.image_decoders.min.js",
|
dir + "/image_decoders/pdf.image_decoders.min.js",
|
||||||
dir + "/image_decoders/pdf.image_decoders.js"
|
dir + "/image_decoders/pdf.image_decoders.js"
|
||||||
@ -1176,7 +1303,14 @@ gulp.task(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task("chromium", gulp.series("chromium-pre"));
|
gulp.task(
|
||||||
|
"chromium",
|
||||||
|
gulp.series("chromium-pre", function () {
|
||||||
|
var defines = builder.merge(DEFINES, { CHROME: true, SKIP_BABEL: false });
|
||||||
|
var CHROME_BUILD_CONTENT_DIR = BUILD_DIR + "/chromium/content/";
|
||||||
|
return buildSandbox(defines, CHROME_BUILD_CONTENT_DIR);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
gulp.task("jsdoc", function (done) {
|
gulp.task("jsdoc", function (done) {
|
||||||
console.log();
|
console.log();
|
||||||
@ -1276,7 +1410,7 @@ function buildLib(defines, dir) {
|
|||||||
return merge([
|
return merge([
|
||||||
gulp.src(
|
gulp.src(
|
||||||
[
|
[
|
||||||
"src/{core,display,scripting_api,shared}/*.js",
|
"src/{core,display,shared}/*.js",
|
||||||
"!src/shared/{cffStandardStrings,fonts_utils}.js",
|
"!src/shared/{cffStandardStrings,fonts_utils}.js",
|
||||||
"src/{pdf,pdf.worker}.js",
|
"src/{pdf,pdf.worker}.js",
|
||||||
],
|
],
|
||||||
@ -1294,24 +1428,46 @@ function buildLib(defines, dir) {
|
|||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"lib",
|
"lib",
|
||||||
gulp.series("buildnumber", "default_preferences", function () {
|
gulp.series(
|
||||||
var defines = builder.merge(DEFINES, { GENERIC: true, LIB: true });
|
"buildnumber",
|
||||||
|
"default_preferences",
|
||||||
|
function () {
|
||||||
|
var defines = builder.merge(DEFINES, { GENERIC: true, LIB: true });
|
||||||
|
|
||||||
return buildLib(defines, "build/lib/");
|
return buildLib(defines, "build/lib/");
|
||||||
})
|
},
|
||||||
|
function () {
|
||||||
|
var defines = builder.merge(DEFINES, { GENERIC: true, LIB: true });
|
||||||
|
|
||||||
|
return buildSandbox(defines, "build/lib/");
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
gulp.task(
|
gulp.task(
|
||||||
"lib-es5",
|
"lib-es5",
|
||||||
gulp.series("buildnumber", "default_preferences", function () {
|
gulp.series(
|
||||||
var defines = builder.merge(DEFINES, {
|
"buildnumber",
|
||||||
GENERIC: true,
|
"default_preferences",
|
||||||
LIB: true,
|
function () {
|
||||||
SKIP_BABEL: false,
|
var defines = builder.merge(DEFINES, {
|
||||||
});
|
GENERIC: true,
|
||||||
|
LIB: true,
|
||||||
|
SKIP_BABEL: false,
|
||||||
|
});
|
||||||
|
|
||||||
return buildLib(defines, "build/lib-es5/");
|
return buildLib(defines, "build/lib-es5/");
|
||||||
})
|
},
|
||||||
|
function () {
|
||||||
|
var defines = builder.merge(DEFINES, {
|
||||||
|
GENERIC: true,
|
||||||
|
LIB: true,
|
||||||
|
SKIP_BABEL: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return buildSandbox(defines, "build/lib-es5/");
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
function compressPublish(targetName, dir) {
|
function compressPublish(targetName, dir) {
|
||||||
@ -1382,6 +1538,7 @@ gulp.task(
|
|||||||
gulp.task(
|
gulp.task(
|
||||||
"unittest",
|
"unittest",
|
||||||
gulp.series("testing-pre", "generic", "components", function () {
|
gulp.series("testing-pre", "generic", "components", function () {
|
||||||
|
process.env.TZ = "UTC";
|
||||||
return createTestSource("unit");
|
return createTestSource("unit");
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
100
package-lock.json
generated
100
package-lock.json
generated
@ -2079,6 +2079,15 @@
|
|||||||
"ansi-wrap": "^0.1.0"
|
"ansi-wrap": "^0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ansi-cyan": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
|
||||||
|
"integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-wrap": "0.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ansi-gray": {
|
"ansi-gray": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
|
||||||
@ -2088,6 +2097,15 @@
|
|||||||
"ansi-wrap": "0.1.0"
|
"ansi-wrap": "0.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ansi-red": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
|
||||||
|
"integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-wrap": "0.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
@ -3882,6 +3900,15 @@
|
|||||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
|
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"decomment": {
|
||||||
|
"version": "0.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/decomment/-/decomment-0.9.3.tgz",
|
||||||
|
"integrity": "sha512-5skH5BfUL3n09RDmMVaHS1QGCiZRnl2nArUwmsE9JRY93Ueh3tihYl5wIrDdAuXnoFhxVis/DmRWREO2c6DG3w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"esprima": "4.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"decompress-response": {
|
"decompress-response": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
||||||
@ -6920,6 +6947,79 @@
|
|||||||
"replacestream": "^4.0.0"
|
"replacestream": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"gulp-strip-comments": {
|
||||||
|
"version": "2.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/gulp-strip-comments/-/gulp-strip-comments-2.5.2.tgz",
|
||||||
|
"integrity": "sha512-lb1bW7rsPWDD8f4ZPSguDvmCdjKmjr5HR4yZb9ros3sLl5AfW7oUj8KzY9/VRisT7dG8dL7hVHzNpQEVxfwZGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"decomment": "^0.9.0",
|
||||||
|
"plugin-error": "^0.1.2",
|
||||||
|
"through2": "^2.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"arr-diff": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"arr-flatten": "^1.0.1",
|
||||||
|
"array-slice": "^0.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"arr-union": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"array-slice": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
|
||||||
|
"integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"extend-shallow": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
|
||||||
|
"integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"kind-of": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kind-of": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"plugin-error": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-cyan": "^0.1.1",
|
||||||
|
"ansi-red": "^0.1.1",
|
||||||
|
"arr-diff": "^1.0.1",
|
||||||
|
"arr-union": "^2.0.1",
|
||||||
|
"extend-shallow": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"through2": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"xtend": "~4.0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"gulp-zip": {
|
"gulp-zip": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.0.2.tgz",
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"gulp-postcss": "^9.0.0",
|
"gulp-postcss": "^9.0.0",
|
||||||
"gulp-rename": "^2.0.0",
|
"gulp-rename": "^2.0.0",
|
||||||
"gulp-replace": "^1.0.0",
|
"gulp-replace": "^1.0.0",
|
||||||
|
"gulp-strip-comments": "^2.5.2",
|
||||||
"gulp-zip": "^5.0.2",
|
"gulp-zip": "^5.0.2",
|
||||||
"jasmine": "^3.6.3",
|
"jasmine": "^3.6.3",
|
||||||
"jsdoc": "^3.6.6",
|
"jsdoc": "^3.6.6",
|
||||||
|
@ -22,7 +22,7 @@ import { ProxyHandler } from "./proxy.js";
|
|||||||
import { Util } from "./util.js";
|
import { Util } from "./util.js";
|
||||||
import { ZoomType } from "./constants.js";
|
import { ZoomType } from "./constants.js";
|
||||||
|
|
||||||
function initSandbox({ data, extra, out, testMode = false }) {
|
function initSandbox({ data, extra, out }) {
|
||||||
const proxyHandler = new ProxyHandler(data.dispatchEventName);
|
const proxyHandler = new ProxyHandler(data.dispatchEventName);
|
||||||
const { send, crackURL } = extra;
|
const { send, crackURL } = extra;
|
||||||
const doc = new Doc({
|
const doc = new Doc({
|
||||||
@ -58,14 +58,6 @@ function initSandbox({ data, extra, out, testMode = false }) {
|
|||||||
out[name] = aform[name].bind(aform);
|
out[name] = aform[name].bind(aform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
(typeof PDFJSDev === "undefined" ||
|
|
||||||
PDFJSDev.test("!PRODUCTION || TESTING")) &&
|
|
||||||
testMode
|
|
||||||
) {
|
|
||||||
out._app = app;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { initSandbox };
|
export { initSandbox };
|
||||||
|
103
src/scripting_api/quickjs-sandbox.js
Normal file
103
src/scripting_api/quickjs-sandbox.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/* Copyright 2020 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ModuleLoader from "../../external/quickjs/quickjs-eval.js";
|
||||||
|
|
||||||
|
class Sandbox {
|
||||||
|
constructor(module, testMode) {
|
||||||
|
this._evalInSandbox = module.cwrap("evalInSandbox", null, [
|
||||||
|
"string",
|
||||||
|
"int",
|
||||||
|
]);
|
||||||
|
this._dispatchEventName = null;
|
||||||
|
this._module = module;
|
||||||
|
this._testMode = testMode;
|
||||||
|
this._alertOnError = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
create(data) {
|
||||||
|
const sandboxData = JSON.stringify(data);
|
||||||
|
const extra = [
|
||||||
|
"send",
|
||||||
|
"setTimeout",
|
||||||
|
"clearTimeout",
|
||||||
|
"setInterval",
|
||||||
|
"clearInterval",
|
||||||
|
"crackURL",
|
||||||
|
];
|
||||||
|
const extraStr = extra.join(",");
|
||||||
|
let code = [
|
||||||
|
"exports = Object.create(null);",
|
||||||
|
"module = Object.create(null);",
|
||||||
|
// Next line is replaced by code from initialization.js
|
||||||
|
// when we create the bundle for the sandbox.
|
||||||
|
"/* INITIALIZATION_CODE */",
|
||||||
|
`data = ${sandboxData};`,
|
||||||
|
`module.exports.initSandbox({ data, extra: {${extraStr}}, out: this});`,
|
||||||
|
"delete exports;",
|
||||||
|
"delete module;",
|
||||||
|
"delete data;",
|
||||||
|
];
|
||||||
|
if (!this._testMode) {
|
||||||
|
code = code.concat(extra.map(name => `delete ${name};`));
|
||||||
|
code.push("delete debugMe;");
|
||||||
|
}
|
||||||
|
this._evalInSandbox(code.join("\n"), this._alertOnError);
|
||||||
|
this._dispatchEventName = data.dispatchEventName;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchEvent(event) {
|
||||||
|
if (this._dispatchEventName === null) {
|
||||||
|
throw new Error("Sandbox must have been initialized");
|
||||||
|
}
|
||||||
|
event = JSON.stringify(event);
|
||||||
|
this._evalInSandbox(
|
||||||
|
`app["${this._dispatchEventName}"](${event});`,
|
||||||
|
this._alertOnError
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
dumpMemoryUse() {
|
||||||
|
this._module.ccall("dumpMemoryUse", null, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
nukeSandbox() {
|
||||||
|
this._dispatchEventName = null;
|
||||||
|
this._module.ccall("nukeSandbox", null, []);
|
||||||
|
this._module = null;
|
||||||
|
this._evalInSandbox = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
evalForTesting(code, key) {
|
||||||
|
if (this._testMode) {
|
||||||
|
this._evalInSandbox(
|
||||||
|
`send({ id: "${key}", result: ${code} });`,
|
||||||
|
this._alertOnError
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function QuickJSSandbox(testMode = false) {
|
||||||
|
testMode =
|
||||||
|
testMode &&
|
||||||
|
(typeof PDFJSDev === "undefined" ||
|
||||||
|
PDFJSDev.test("!PRODUCTION || TESTING"));
|
||||||
|
return ModuleLoader().then(module => {
|
||||||
|
return new Sandbox(module, testMode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { QuickJSSandbox };
|
@ -49,6 +49,9 @@ class Util extends PDFObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
crackURL(cURL) {
|
crackURL(cURL) {
|
||||||
|
if (typeof cURL !== "string") {
|
||||||
|
throw new TypeError("First argument of util.crackURL must be a string");
|
||||||
|
}
|
||||||
return this._crackURL(cURL);
|
return this._crackURL(cURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
"pdf_find_utils_spec.js",
|
"pdf_find_utils_spec.js",
|
||||||
"pdf_history_spec.js",
|
"pdf_history_spec.js",
|
||||||
"primitives_spec.js",
|
"primitives_spec.js",
|
||||||
"scripting_spec.js",
|
|
||||||
"stream_spec.js",
|
"stream_spec.js",
|
||||||
"type1_parser_spec.js",
|
"type1_parser_spec.js",
|
||||||
"ui_utils_spec.js",
|
"ui_utils_spec.js",
|
||||||
|
@ -13,258 +13,426 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { initSandbox } from "../../src/scripting_api/initialization.js";
|
import { loadScript } from "../../src/display/display_utils.js";
|
||||||
|
|
||||||
describe("Scripting", function () {
|
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 () {
|
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) {
|
beforeAll(function (done) {
|
||||||
sandbox = Object.create(null);
|
sandbox.createSandbox({
|
||||||
const extra = { send: null, crackURL: null };
|
objects: {},
|
||||||
const data = { objects: {}, calculationOrder: [] };
|
calculationOrder: [],
|
||||||
initSandbox({ data, extra, out: sandbox });
|
dispatchEventName: "_dispatchMe",
|
||||||
util = sandbox.util;
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(function () {
|
|
||||||
sandbox = util = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("printd", function () {
|
describe("printd", function () {
|
||||||
it("should print a date according to a format", function (done) {
|
it("should print a date according to a format", function (done) {
|
||||||
const date = new Date("April 15, 1707 3:14:15");
|
const date = `new Date("Sun Apr 15 2007 03:14:15")`;
|
||||||
expect(util.printd(0, date)).toEqual("D:17070415031415");
|
Promise.all([
|
||||||
expect(util.printd(1, date)).toEqual("1707.04.15 03:14:15");
|
myeval(`util.printd(0, ${date})`).then(value => {
|
||||||
expect(util.printd(2, date)).toEqual("4/15/07 3:14:15 am");
|
expect(value).toEqual("D:20070415031415");
|
||||||
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");
|
myeval(`util.printd(1, ${date})`).then(value => {
|
||||||
done();
|
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 () {
|
describe("scand", function () {
|
||||||
it("should parse a date according to a format", function (done) {
|
it("should parse a date according to a format", function (done) {
|
||||||
const date = new Date("April 15, 1707 3:14:15");
|
const date = new Date("Sun Apr 15 2007 03:14:15");
|
||||||
expect(util.scand(0, "D:17070415031415")).toEqual(date);
|
Promise.all([
|
||||||
expect(util.scand(1, "1707.04.15 03:14:15")).toEqual(date);
|
myeval(`util.scand(0, "D:20070415031415").toString()`).then(value => {
|
||||||
expect(util.scand(2, "4/15/07 3:14:15 am")).toEqual(
|
expect(new Date(value)).toEqual(date);
|
||||||
new Date("April 15, 2007 3:14:15")
|
}),
|
||||||
);
|
myeval(`util.scand(1, "2007.04.15 03:14:15").toString()`).then(
|
||||||
done();
|
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 () {
|
describe("printf", function () {
|
||||||
it("should print some data according to a format", function (done) {
|
it("should print some data according to a format", function (done) {
|
||||||
expect(
|
Promise.all([
|
||||||
util.printf("Integer numbers: %d, %d,...", 1.234, 56.789)
|
myeval(
|
||||||
).toEqual("Integer numbers: 1, 56,...");
|
`util.printf("Integer numbers: %d, %d,...", 1.234, 56.789)`
|
||||||
expect(util.printf("Hex numbers: %x, %x,...", 1234, 56789)).toEqual(
|
).then(value => {
|
||||||
"Hex numbers: 4D2, DDD5,..."
|
expect(value).toEqual("Integer numbers: 1, 56,...");
|
||||||
);
|
}),
|
||||||
expect(
|
myeval(`util.printf("Hex numbers: %x, %x,...", 1234, 56789)`).then(
|
||||||
util.printf("Hex numbers with 0x: %#x, %#x,...", 1234, 56789)
|
value => {
|
||||||
).toEqual("Hex numbers with 0x: 0x4D2, 0xDDD5,...");
|
expect(value).toEqual("Hex numbers: 4D2, DDD5,...");
|
||||||
expect(util.printf("Decimal number: %,0+.3f", 1234567.89123)).toEqual(
|
}
|
||||||
"Decimal number: +1,234,567.891"
|
),
|
||||||
);
|
myeval(
|
||||||
expect(util.printf("Decimal number: %,0+8.3f", 1.234567)).toEqual(
|
`util.printf("Hex numbers with 0x: %#x, %#x,...", 1234, 56789)`
|
||||||
"Decimal number: + 1.235"
|
).then(value => {
|
||||||
);
|
expect(value).toEqual("Hex numbers with 0x: 0x4D2, 0xDDD5,...");
|
||||||
done();
|
}),
|
||||||
|
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) {
|
it("should print a string with no argument", function (done) {
|
||||||
expect(util.printf("hello world")).toEqual("hello world");
|
myeval(`util.printf("hello world")`)
|
||||||
done();
|
.then(value => {
|
||||||
|
expect(value).toEqual("hello world");
|
||||||
|
})
|
||||||
|
.then(() => done());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should print a string with a percent", function (done) {
|
it(" print a string with a percent", function (done) {
|
||||||
expect(util.printf("%%s")).toEqual("%%s");
|
myeval(`util.printf("%%s")`)
|
||||||
expect(util.printf("%%s", "hello")).toEqual("%%s");
|
.then(value => {
|
||||||
done();
|
expect(value).toEqual("%%s");
|
||||||
|
})
|
||||||
|
.then(() => done());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("printx", function () {
|
describe("printx", function () {
|
||||||
it("should print some data according to a format", function (done) {
|
it("should print some data according to a format", function (done) {
|
||||||
expect(util.printx("9 (999) 999-9999", "aaa14159697489zzz")).toEqual(
|
myeval(`util.printx("9 (999) 999-9999", "aaa14159697489zzz")`)
|
||||||
"1 (415) 969-7489"
|
.then(value => {
|
||||||
);
|
expect(value).toEqual("1 (415) 969-7489");
|
||||||
done();
|
})
|
||||||
|
.then(() => done());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Events", function () {
|
describe("Events", function () {
|
||||||
let sandbox, send_queue, _app;
|
it("should trigger an event and modify the source", function (done) {
|
||||||
|
const refId = getId();
|
||||||
beforeEach(function (done) {
|
|
||||||
send_queue = [];
|
|
||||||
sandbox = Object.create(null);
|
|
||||||
const extra = {
|
|
||||||
send(data) {
|
|
||||||
send_queue.push(data);
|
|
||||||
},
|
|
||||||
crackURL: null,
|
|
||||||
};
|
|
||||||
const data = {
|
const data = {
|
||||||
objects: {
|
objects: {
|
||||||
field314R: [
|
field: [
|
||||||
{
|
{
|
||||||
id: "314R",
|
id: refId,
|
||||||
value: "",
|
value: "",
|
||||||
actions: {},
|
actions: {
|
||||||
type: "text",
|
test: [`event.source.value = "123";`],
|
||||||
},
|
},
|
||||||
],
|
|
||||||
field271R: [
|
|
||||||
{
|
|
||||||
id: "271R",
|
|
||||||
value: "",
|
|
||||||
actions: {},
|
|
||||||
type: "text",
|
type: "text",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
calculationOrder: ["271R"],
|
calculationOrder: [],
|
||||||
dispatchEventName: "_dispatchMe",
|
dispatchEventName: "_dispatchMe",
|
||||||
};
|
};
|
||||||
|
sandbox.createSandbox(data);
|
||||||
initSandbox({
|
sandbox
|
||||||
data,
|
.dispatchEventInSandbox({
|
||||||
extra,
|
id: refId,
|
||||||
out: sandbox,
|
value: "",
|
||||||
testMode: true,
|
name: "test",
|
||||||
});
|
willCommit: true,
|
||||||
|
})
|
||||||
_app = sandbox._app;
|
.then(() => {
|
||||||
send_queue = [];
|
expect(send_queue.has(refId)).toEqual(true);
|
||||||
done();
|
expect(send_queue.get(refId)).toEqual({
|
||||||
});
|
id: refId,
|
||||||
|
value: "123",
|
||||||
afterAll(function () {
|
});
|
||||||
sandbox = send_queue = _app = null;
|
done();
|
||||||
});
|
})
|
||||||
|
.catch(done.fail);
|
||||||
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();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should trigger a Keystroke event and invalidate it", function (done) {
|
it("should trigger a Keystroke event and invalidate it", function (done) {
|
||||||
_app._objects["314R"].obj._actions.set("Keystroke", [
|
const refId = getId();
|
||||||
event => {
|
const data = {
|
||||||
event.rc = false;
|
objects: {
|
||||||
|
field: [
|
||||||
|
{
|
||||||
|
id: refId,
|
||||||
|
value: "",
|
||||||
|
actions: {
|
||||||
|
Keystroke: [`event.rc = false;`],
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]);
|
calculationOrder: [],
|
||||||
|
dispatchEventName: "_dispatchMe",
|
||||||
sandbox.app._dispatchMe({
|
};
|
||||||
id: "314R",
|
sandbox.createSandbox(data);
|
||||||
value: "hell",
|
sandbox
|
||||||
name: "Keystroke",
|
.dispatchEventInSandbox({
|
||||||
willCommit: false,
|
id: refId,
|
||||||
change: "o",
|
value: "hell",
|
||||||
selStart: 4,
|
name: "Keystroke",
|
||||||
selEnd: 4,
|
willCommit: false,
|
||||||
});
|
change: "o",
|
||||||
expect(send_queue.length).toEqual(1);
|
selStart: 4,
|
||||||
expect(send_queue[0]).toEqual({
|
selEnd: 4,
|
||||||
id: "314R",
|
})
|
||||||
value: "hell",
|
.then(() => {
|
||||||
selRange: [4, 4],
|
expect(send_queue.has(refId)).toEqual(true);
|
||||||
});
|
expect(send_queue.get(refId)).toEqual({
|
||||||
|
id: refId,
|
||||||
done();
|
value: "hell",
|
||||||
|
selRange: [4, 4],
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should trigger a Keystroke event and change it", function (done) {
|
it("should trigger a Keystroke event and change it", function (done) {
|
||||||
_app._objects["314R"].obj._actions.set("Keystroke", [
|
const refId = getId();
|
||||||
event => {
|
const data = {
|
||||||
event.change = "a";
|
objects: {
|
||||||
|
field: [
|
||||||
|
{
|
||||||
|
id: refId,
|
||||||
|
value: "",
|
||||||
|
actions: {
|
||||||
|
Keystroke: [`event.change = "a";`],
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]);
|
calculationOrder: [],
|
||||||
|
dispatchEventName: "_dispatchMe",
|
||||||
sandbox.app._dispatchMe({
|
};
|
||||||
id: "314R",
|
sandbox.createSandbox(data);
|
||||||
value: "hell",
|
sandbox
|
||||||
name: "Keystroke",
|
.dispatchEventInSandbox({
|
||||||
willCommit: false,
|
id: refId,
|
||||||
change: "o",
|
value: "hell",
|
||||||
selStart: 4,
|
name: "Keystroke",
|
||||||
selEnd: 4,
|
willCommit: false,
|
||||||
});
|
change: "o",
|
||||||
expect(send_queue.length).toEqual(1);
|
selStart: 4,
|
||||||
expect(send_queue[0]).toEqual({ id: "314R", value: "hella" });
|
selEnd: 4,
|
||||||
|
})
|
||||||
done();
|
.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) {
|
it("should trigger an invalid commit Keystroke event", function (done) {
|
||||||
_app._objects["314R"].obj._actions.set("Validate", [
|
const refId = getId();
|
||||||
event => {
|
const data = {
|
||||||
event.rc = false;
|
objects: {
|
||||||
|
field: [
|
||||||
|
{
|
||||||
|
id: refId,
|
||||||
|
value: "",
|
||||||
|
actions: {
|
||||||
|
test: [`event.rc = false;`],
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]);
|
calculationOrder: [],
|
||||||
|
dispatchEventName: "_dispatchMe",
|
||||||
sandbox.app._dispatchMe({
|
};
|
||||||
id: "314R",
|
sandbox.createSandbox(data);
|
||||||
value: "hello",
|
sandbox
|
||||||
name: "Keystroke",
|
.dispatchEventInSandbox({
|
||||||
willCommit: true,
|
id: refId,
|
||||||
});
|
value: "",
|
||||||
expect(send_queue.length).toEqual(0);
|
name: "test",
|
||||||
|
willCommit: true,
|
||||||
done();
|
})
|
||||||
|
.then(() => {
|
||||||
|
expect(send_queue.has(refId)).toEqual(false);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should trigger a valid commit Keystroke event", function (done) {
|
it("should trigger a valid commit Keystroke event", function (done) {
|
||||||
let output = "";
|
const refId1 = getId();
|
||||||
_app._objects["314R"].obj._actions.set("Validate", [
|
const refId2 = getId();
|
||||||
event => {
|
const data = {
|
||||||
event.value = "world";
|
objects: {
|
||||||
output += "foo";
|
field1: [
|
||||||
|
{
|
||||||
|
id: refId1,
|
||||||
|
value: "",
|
||||||
|
actions: {
|
||||||
|
Validate: [`event.value = "world";`],
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
field2: [
|
||||||
|
{
|
||||||
|
id: refId2,
|
||||||
|
value: "",
|
||||||
|
actions: {
|
||||||
|
Calculate: [`event.value = "hello";`],
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
]);
|
calculationOrder: [refId2],
|
||||||
_app._objects["271R"].obj._actions.set("Calculate", [
|
dispatchEventName: "_dispatchMe",
|
||||||
event => {
|
};
|
||||||
event.value = "hello";
|
sandbox.createSandbox(data);
|
||||||
output += "bar";
|
sandbox
|
||||||
},
|
.dispatchEventInSandbox({
|
||||||
]);
|
id: refId1,
|
||||||
|
value: "hello",
|
||||||
sandbox.app._dispatchMe({
|
name: "Keystroke",
|
||||||
id: "314R",
|
willCommit: true,
|
||||||
value: "hello",
|
})
|
||||||
name: "Keystroke",
|
.then(() => {
|
||||||
willCommit: true,
|
expect(send_queue.has(refId1)).toEqual(true);
|
||||||
});
|
expect(send_queue.get(refId1)).toEqual({
|
||||||
|
id: refId1,
|
||||||
expect(send_queue.length).toEqual(4);
|
value: "world",
|
||||||
expect(send_queue[0]).toEqual({ id: "314R", value: "world" });
|
valueAsString: "world",
|
||||||
expect(send_queue[1]).toEqual({ id: "271R", value: "hello" });
|
});
|
||||||
expect(send_queue[2]).toEqual({ id: "271R", valueAsString: "hello" });
|
done();
|
||||||
expect(send_queue[3]).toEqual({ id: "314R", valueAsString: "world" });
|
})
|
||||||
expect(output).toEqual("foobar");
|
.catch(done.fail);
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -223,6 +223,14 @@ const defaultOptions = {
|
|||||||
value: false,
|
value: false,
|
||||||
kind: OptionKind.API,
|
kind: OptionKind.API,
|
||||||
},
|
},
|
||||||
|
scriptingSrc: {
|
||||||
|
/** @type {string} */
|
||||||
|
value:
|
||||||
|
typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
|
||||||
|
? "../build/generic/build/pdf.sandbox.js"
|
||||||
|
: "../build/pdf.sandbox.js",
|
||||||
|
kind: OptionKind.VIEWER,
|
||||||
|
},
|
||||||
verbosity: {
|
verbosity: {
|
||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
value: 1,
|
value: 1,
|
||||||
|
43
web/devcom.js
Normal file
43
web/devcom.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* Copyright 2017 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DefaultExternalServices, PDFViewerApplication } from "./app.js";
|
||||||
|
import { loadScript, shadow } from "pdfjs-lib";
|
||||||
|
|
||||||
|
const DevCom = {};
|
||||||
|
|
||||||
|
class DevExternalServices extends DefaultExternalServices {
|
||||||
|
static get scripting() {
|
||||||
|
const promise = loadScript("../build/pdf.sandbox.js").then(() => {
|
||||||
|
return window.pdfjsSandbox.QuickJSSandbox();
|
||||||
|
});
|
||||||
|
const sandbox = {
|
||||||
|
createSandbox(data) {
|
||||||
|
promise.then(sbx => sbx.create(data));
|
||||||
|
},
|
||||||
|
dispatchEventInSandbox(event) {
|
||||||
|
promise.then(sbx => sbx.dispatchEvent(event));
|
||||||
|
},
|
||||||
|
destroySandbox() {
|
||||||
|
promise.then(sbx => sbx.nukeSandbox());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return shadow(this, "scripting", sandbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PDFViewerApplication.externalServices = DevExternalServices;
|
||||||
|
|
||||||
|
export { DevCom };
|
@ -259,7 +259,7 @@ class FirefoxScripting {
|
|||||||
FirefoxCom.requestSync("createSandbox", data);
|
FirefoxCom.requestSync("createSandbox", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static dispatchEventInSandbox(event, sandboxID) {
|
static dispatchEventInSandbox(event) {
|
||||||
FirefoxCom.requestSync("dispatchEventInSandbox", event);
|
FirefoxCom.requestSync("dispatchEventInSandbox", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { DefaultExternalServices, PDFViewerApplication } from "./app.js";
|
import { DefaultExternalServices, PDFViewerApplication } from "./app.js";
|
||||||
|
import { loadScript, shadow } from "pdfjs-lib";
|
||||||
|
import { AppOptions } from "./app_options.js";
|
||||||
import { BasePreferences } from "./preferences.js";
|
import { BasePreferences } from "./preferences.js";
|
||||||
import { DownloadManager } from "./download_manager.js";
|
import { DownloadManager } from "./download_manager.js";
|
||||||
import { GenericL10n } from "./genericl10n.js";
|
import { GenericL10n } from "./genericl10n.js";
|
||||||
@ -49,6 +51,25 @@ class GenericExternalServices extends DefaultExternalServices {
|
|||||||
static createL10n({ locale = "en-US" }) {
|
static createL10n({ locale = "en-US" }) {
|
||||||
return new GenericL10n(locale);
|
return new GenericL10n(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get scripting() {
|
||||||
|
const promise = loadScript(AppOptions.get("scriptingSrc")).then(() => {
|
||||||
|
return window.pdfjsSandbox.QuickJSSandbox();
|
||||||
|
});
|
||||||
|
const sandbox = {
|
||||||
|
createSandbox(data) {
|
||||||
|
promise.then(sbx => sbx.create(data));
|
||||||
|
},
|
||||||
|
dispatchEventInSandbox(event) {
|
||||||
|
promise.then(sbx => sbx.dispatchEvent(event));
|
||||||
|
},
|
||||||
|
destroySandbox() {
|
||||||
|
promise.then(sbx => sbx.nukeSandbox());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return shadow(this, "scripting", sandbox);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PDFViewerApplication.externalServices = GenericExternalServices;
|
PDFViewerApplication.externalServices = GenericExternalServices;
|
||||||
|
|
||||||
|
@ -56,6 +56,9 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
|
|||||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME")) {
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME")) {
|
||||||
require("./chromecom.js");
|
require("./chromecom.js");
|
||||||
}
|
}
|
||||||
|
if (typeof PDFJSDev === "undefined") {
|
||||||
|
import("./devcom.js");
|
||||||
|
}
|
||||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME || GENERIC")) {
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME || GENERIC")) {
|
||||||
require("./pdf_print_service.js");
|
require("./pdf_print_service.js");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user