Merge pull request #12695 from Snuffleupagus/sandbox-build

Various clean-up and improvements related to `pdf.sandbox.js` building, and the related default-viewer functionality
This commit is contained in:
Tim van der Meij 2020-12-06 16:51:41 +01:00 committed by GitHub
commit 3df72c3fac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 376 additions and 408 deletions

View File

@ -33,7 +33,6 @@ var stream = require("stream");
var exec = require("child_process").exec;
var spawn = require("child_process").spawn;
var spawnSync = require("child_process").spawnSync;
var stripComments = require("gulp-strip-comments");
var streamqueue = require("streamqueue");
var merge = require("merge-stream");
var zip = require("gulp-zip");
@ -65,6 +64,7 @@ var SRC_DIR = "src/";
var LIB_DIR = BUILD_DIR + "lib/";
var DIST_DIR = BUILD_DIR + "dist/";
var TYPES_DIR = BUILD_DIR + "types/";
const TMP_DIR = BUILD_DIR + "tmp/";
var TYPESTEST_DIR = BUILD_DIR + "typestest/";
var COMMON_WEB_FILES = ["web/images/*.{png,svg,gif,cur}", "web/debugger.js"];
var MOZCENTRAL_DIFF_FILE = "mozcentral.diff";
@ -106,7 +106,6 @@ const DEFINES = Object.freeze({
COMPONENTS: false,
LIB: false,
IMAGE_DECODERS: false,
NO_SOURCE_MAP: false,
});
function transform(charEncoding, transformFunction) {
@ -171,8 +170,18 @@ function createStringSource(filename, content) {
return source;
}
function createWebpackConfig(defines, output) {
var versionInfo = getVersionJSON();
function createWebpackConfig(
defines,
output,
{
disableVersionInfo = false,
disableSourceMaps = false,
disableLicenseHeader = false,
} = {}
) {
const versionInfo = !disableVersionInfo
? getVersionJSON()
: { version: 0, commit: 0 };
var bundleDefines = builder.merge(defines, {
BUNDLE_VERSION: versionInfo.version,
BUNDLE_BUILD: versionInfo.commit,
@ -184,8 +193,9 @@ function createWebpackConfig(defines, output) {
var enableSourceMaps =
!bundleDefines.MOZCENTRAL &&
!bundleDefines.CHROME &&
!bundleDefines.LIB &&
!bundleDefines.TESTING &&
!bundleDefines.NO_SOURCE_MAP;
!disableSourceMaps;
var skipBabel = bundleDefines.SKIP_BABEL;
// `core-js` (see https://github.com/zloirock/core-js/issues/514),
@ -201,6 +211,13 @@ function createWebpackConfig(defines, output) {
}
const babelExcludeRegExp = new RegExp(`(${babelExcludes.join("|")})`);
const plugins = [];
if (!disableLicenseHeader) {
plugins.push(
new webpack2.BannerPlugin({ banner: licenseHeaderLibre, raw: true })
);
}
// Required to expose e.g., the `window` object.
output.globalObject = "this";
@ -210,9 +227,7 @@ function createWebpackConfig(defines, output) {
performance: {
hints: false, // Disable messages about larger file sizes.
},
plugins: [
new webpack2.BannerPlugin({ banner: licenseHeaderLibre, raw: true }),
],
plugins,
resolve: {
alias: {
pdfjs: path.join(__dirname, "src"),
@ -329,16 +344,20 @@ function createMainBundle(defines) {
.pipe(replaceJSRootName(mainAMDName, "pdfjsLib"));
}
function createScriptingBundle(defines) {
function createScriptingBundle(defines, extraOptions = undefined) {
var scriptingAMDName = "pdfjs-dist/build/pdf.scripting";
var scriptingOutputName = "pdf.scripting.js";
var scriptingFileConfig = createWebpackConfig(defines, {
filename: scriptingOutputName,
library: scriptingAMDName,
libraryTarget: "umd",
umdNamedDefine: true,
});
var scriptingFileConfig = createWebpackConfig(
defines,
{
filename: scriptingOutputName,
library: scriptingAMDName,
libraryTarget: "umd",
umdNamedDefine: true,
},
extraOptions
);
return gulp
.src("./src/pdf.scripting.js")
.pipe(webpack2Stream(scriptingFileConfig))
@ -346,51 +365,41 @@ function createScriptingBundle(defines) {
.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 createTemporaryScriptingBundle(defines, extraOptions = undefined) {
return createScriptingBundle(defines, {
disableVersionInfo: !!(extraOptions && extraOptions.disableVersionInfo),
disableSourceMaps: true,
disableLicenseHeader: true,
}).pipe(gulp.dest(TMP_DIR));
}
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 createSandboxBundle(defines, extraOptions = undefined) {
var sandboxAMDName = "pdfjs-dist/build/pdf.sandbox";
var sandboxOutputName = "pdf.sandbox.js";
const scriptingPath = TMP_DIR + "pdf.scripting.js";
// Insert the source as a string to be `eval`-ed in the sandbox.
const sandboxDefines = builder.merge(defines, {
PDF_SCRIPTING_JS_SOURCE: fs.readFileSync(scriptingPath).toString(),
});
fs.unlinkSync(scriptingPath);
var sandboxFileConfig = createWebpackConfig(
sandboxDefines,
{
filename: sandboxOutputName,
library: sandboxAMDName,
libraryTarget: "umd",
umdNamedDefine: true,
},
extraOptions
);
return gulp
.src("./src/pdf.sandbox.js")
.pipe(webpack2Stream(sandboxFileConfig))
.pipe(replaceWebpackRequire())
.pipe(replaceJSRootName(sandboxAMDName, "pdfjsSandbox"));
}
function createWorkerBundle(defines) {
@ -544,25 +553,6 @@ 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) {
console.log("Available tasks:");
var tasks = Object.keys(gulp.registry().tasks());
@ -798,6 +788,7 @@ function buildGeneric(defines, dir) {
return merge([
createMainBundle(defines).pipe(gulp.dest(dir + "build")),
createWorkerBundle(defines).pipe(gulp.dest(dir + "build")),
createSandboxBundle(defines).pipe(gulp.dest(dir + "build")),
createWebBundle(defines).pipe(gulp.dest(dir + "web")),
gulp.src(COMMON_WEB_FILES, { base: "web/" }).pipe(gulp.dest(dir + "web")),
gulp.src("LICENSE").pipe(gulp.dest(dir)),
@ -836,14 +827,17 @@ gulp.task(
"buildnumber",
"default_preferences",
"locale",
function scripting() {
var defines = builder.merge(DEFINES, { GENERIC: true });
return createTemporaryScriptingBundle(defines);
},
function () {
console.log();
console.log("### Creating generic viewer");
var defines = builder.merge(DEFINES, { GENERIC: true });
return buildGeneric(defines, GENERIC_DIR);
},
"sandbox"
}
)
);
@ -855,6 +849,13 @@ gulp.task(
"buildnumber",
"default_preferences",
"locale",
function scripting() {
var defines = builder.merge(DEFINES, {
GENERIC: true,
SKIP_BABEL: false,
});
return createTemporaryScriptingBundle(defines);
},
function () {
console.log();
console.log("### Creating generic (ES5) viewer");
@ -864,13 +865,6 @@ gulp.task(
});
return buildGeneric(defines, GENERIC_ES5_DIR);
},
function () {
const defines = builder.merge(DEFINES, {
GENERIC: true,
SKIP_BABEL: false,
});
return buildSandbox(defines, GENERIC_ES5_DIR);
}
)
);
@ -964,6 +958,7 @@ function buildMinified(defines, dir) {
return merge([
createMainBundle(defines).pipe(gulp.dest(dir + "build")),
createWorkerBundle(defines).pipe(gulp.dest(dir + "build")),
createSandboxBundle(defines).pipe(gulp.dest(dir + "build")),
createWebBundle(defines).pipe(gulp.dest(dir + "web")),
createImageDecodersBundle(
builder.merge(defines, { IMAGE_DECODERS: true })
@ -1003,16 +998,15 @@ gulp.task(
"buildnumber",
"default_preferences",
"locale",
function scripting() {
var defines = builder.merge(DEFINES, { MINIFIED: true, GENERIC: true });
return createTemporaryScriptingBundle(defines);
},
function () {
console.log();
console.log("### Creating minified viewer");
var defines = builder.merge(DEFINES, { MINIFIED: true, GENERIC: true });
return buildSandbox(defines, MINIFIED_DIR);
},
function () {
var defines = builder.merge(DEFINES, { MINIFIED: true, GENERIC: true });
return buildMinified(defines, MINIFIED_DIR);
}
)
@ -1024,19 +1018,17 @@ gulp.task(
"buildnumber",
"default_preferences",
"locale",
function () {
console.log();
console.log("### Creating minified (ES5) viewer");
function scripting() {
var defines = builder.merge(DEFINES, {
MINIFIED: true,
GENERIC: true,
SKIP_BABEL: false,
});
return buildSandbox(defines, MINIFIED_ES5_DIR);
return createTemporaryScriptingBundle(defines);
},
function () {
console.log();
console.log("### Creating minified (ES5) viewer");
var defines = builder.merge(DEFINES, {
MINIFIED: true,
GENERIC: true,
@ -1238,80 +1230,85 @@ gulp.task("mozcentral", gulp.series("mozcentral-pre"));
gulp.task(
"chromium-pre",
gulp.series("buildnumber", "default_preferences", "locale", function () {
console.log();
console.log("### Building Chromium extension");
var defines = builder.merge(DEFINES, { CHROME: true, SKIP_BABEL: false });
gulp.series(
"buildnumber",
"default_preferences",
"locale",
function scripting() {
var defines = builder.merge(DEFINES, { CHROME: true, SKIP_BABEL: false });
return createTemporaryScriptingBundle(defines);
},
function () {
console.log();
console.log("### Building Chromium extension");
var defines = builder.merge(DEFINES, { CHROME: true, SKIP_BABEL: false });
var CHROME_BUILD_DIR = BUILD_DIR + "/chromium/",
CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + "/content/";
var CHROME_BUILD_DIR = BUILD_DIR + "/chromium/",
CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + "/content/";
// Clear out everything in the chrome extension build directory
rimraf.sync(CHROME_BUILD_DIR);
// Clear out everything in the chrome extension build directory
rimraf.sync(CHROME_BUILD_DIR);
var version = getVersionJSON().version;
var version = getVersionJSON().version;
return merge([
createMainBundle(defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
),
createWorkerBundle(defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
),
createWebBundle(defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
),
gulp
.src(COMMON_WEB_FILES, { base: "web/" })
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
return merge([
createMainBundle(defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
),
createWorkerBundle(defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
),
createSandboxBundle(defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
),
createWebBundle(defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
),
gulp
.src(COMMON_WEB_FILES, { base: "web/" })
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
gulp
.src(
["web/locale/*/viewer.properties", "web/locale/locale.properties"],
{ base: "web/" }
)
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
gulp
.src(["external/bcmaps/*.bcmap", "external/bcmaps/LICENSE"], {
base: "external/bcmaps",
})
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/cmaps")),
gulp
.src(
["web/locale/*/viewer.properties", "web/locale/locale.properties"],
{ base: "web/" }
)
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
gulp
.src(["external/bcmaps/*.bcmap", "external/bcmaps/LICENSE"], {
base: "external/bcmaps",
})
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/cmaps")),
preprocessHTML("web/viewer.html", defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
),
preprocessCSS("web/viewer.css", "chrome", defines, true)
.pipe(
postcss([autoprefixer({ overrideBrowserslist: ["chrome >= 49"] })])
)
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
preprocessHTML("web/viewer.html", defines).pipe(
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
),
preprocessCSS("web/viewer.css", "chrome", defines, true)
.pipe(
postcss([autoprefixer({ overrideBrowserslist: ["chrome >= 49"] })])
)
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
gulp.src("LICENSE").pipe(gulp.dest(CHROME_BUILD_DIR)),
gulp
.src("extensions/chromium/manifest.json")
.pipe(replace(/\bPDFJSSCRIPT_VERSION\b/g, version))
.pipe(gulp.dest(CHROME_BUILD_DIR)),
gulp
.src(
[
"extensions/chromium/**/*.{html,js,css,png}",
"extensions/chromium/preferences_schema.json",
],
{ base: "extensions/chromium/" }
)
.pipe(gulp.dest(CHROME_BUILD_DIR)),
]);
})
gulp.src("LICENSE").pipe(gulp.dest(CHROME_BUILD_DIR)),
gulp
.src("extensions/chromium/manifest.json")
.pipe(replace(/\bPDFJSSCRIPT_VERSION\b/g, version))
.pipe(gulp.dest(CHROME_BUILD_DIR)),
gulp
.src(
[
"extensions/chromium/**/*.{html,js,css,png}",
"extensions/chromium/preferences_schema.json",
],
{ base: "extensions/chromium/" }
)
.pipe(gulp.dest(CHROME_BUILD_DIR)),
]);
}
)
);
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("chromium", gulp.series("chromium-pre"));
gulp.task("jsdoc", function (done) {
console.log();
@ -1432,15 +1429,17 @@ gulp.task(
gulp.series(
"buildnumber",
"default_preferences",
function () {
function scripting() {
var defines = builder.merge(DEFINES, { GENERIC: true, LIB: true });
return buildLib(defines, "build/lib/");
return createTemporaryScriptingBundle(defines);
},
function () {
var defines = builder.merge(DEFINES, { GENERIC: true, LIB: true });
return buildSandbox(defines, "build/lib/");
return merge([
buildLib(defines, "build/lib/"),
createSandboxBundle(defines).pipe(gulp.dest("build/lib/")),
]);
}
)
);
@ -1450,14 +1449,13 @@ gulp.task(
gulp.series(
"buildnumber",
"default_preferences",
function () {
function scripting() {
var defines = builder.merge(DEFINES, {
GENERIC: true,
LIB: true,
SKIP_BABEL: false,
});
return buildLib(defines, "build/lib-es5/");
return createTemporaryScriptingBundle(defines);
},
function () {
var defines = builder.merge(DEFINES, {
@ -1466,7 +1464,10 @@ gulp.task(
SKIP_BABEL: false,
});
return buildSandbox(defines, "build/lib-es5/");
return merge([
buildLib(defines, "build/lib-es5/"),
createSandboxBundle(defines).pipe(gulp.dest("build/lib-es5/")),
]);
}
)
);
@ -1717,16 +1718,57 @@ gulp.task(
})
);
gulp.task("server", function () {
console.log();
console.log("### Starting local server");
gulp.task(
"dev-sandbox",
gulp.series(
function scripting() {
const defines = builder.merge(DEFINES, { GENERIC: true, TESTING: true });
return createTemporaryScriptingBundle(defines, {
disableVersionInfo: true,
});
},
function () {
console.log();
console.log("### Building development sandbox");
var WebServer = require("./test/webserver.js").WebServer;
var server = new WebServer();
server.port = 8888;
server.start();
const defines = builder.merge(DEFINES, { GENERIC: true, TESTING: true });
const sandboxDir = BUILD_DIR + "dev-sandbox/";
rimraf.sync(sandboxDir);
return createSandboxBundle(defines, {
disableVersionInfo: true,
}).pipe(gulp.dest(sandboxDir));
}
)
);
gulp.task("watch-dev-sandbox", function () {
gulp.watch(
[
"src/pdf.{sandbox,scripting}.js",
"src/scripting_api/*.js",
"src/shared/scripting_utils.js",
"external/quickjs/*.js",
],
{ ignoreInitial: false },
gulp.series("dev-sandbox")
);
});
gulp.task(
"server",
gulp.parallel("watch-dev-sandbox", function () {
console.log();
console.log("### Starting local server");
var WebServer = require("./test/webserver.js").WebServer;
var server = new WebServer();
server.port = 8888;
server.start();
})
);
gulp.task("clean", function (done) {
console.log();
console.log("### Cleaning up project builds");

100
package-lock.json generated
View File

@ -2178,15 +2178,6 @@
"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": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
@ -2196,15 +2187,6 @@
"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": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
@ -4028,15 +4010,6 @@
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"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": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
@ -7073,79 +7046,6 @@
"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": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/gulp-zip/-/gulp-zip-5.0.2.tgz",

View File

@ -30,7 +30,6 @@
"gulp-postcss": "^9.0.0",
"gulp-rename": "^2.0.0",
"gulp-replace": "^1.0.0",
"gulp-strip-comments": "^2.5.2",
"gulp-zip": "^5.0.2",
"jasmine": "^3.6.3",
"jsdoc": "^3.6.6",

View File

@ -13,17 +13,24 @@
* limitations under the License.
*/
import ModuleLoader from "../../external/quickjs/quickjs-eval.js";
import ModuleLoader from "../external/quickjs/quickjs-eval.js";
/* eslint-disable-next-line no-unused-vars */
const pdfjsVersion = PDFJSDev.eval("BUNDLE_VERSION");
/* eslint-disable-next-line no-unused-vars */
const pdfjsBuild = PDFJSDev.eval("BUNDLE_BUILD");
const TESTING =
typeof PDFJSDev === "undefined" || PDFJSDev.test("!PRODUCTION || TESTING");
class Sandbox {
constructor(module, testMode) {
constructor(module) {
this._evalInSandbox = module.cwrap("evalInSandbox", null, [
"string",
"int",
]);
this._dispatchEventName = null;
this._module = module;
this._testMode = testMode;
this._alertOnError = 1;
}
@ -43,14 +50,14 @@ class Sandbox {
"module = Object.create(null);",
// Next line is replaced by code from initialization.js
// when we create the bundle for the sandbox.
"/* INITIALIZATION_CODE */",
PDFJSDev.eval("PDF_SCRIPTING_JS_SOURCE"),
`data = ${sandboxData};`,
`module.exports.initSandbox({ data, extra: {${extraStr}}, out: this});`,
"delete exports;",
"delete module;",
"delete data;",
];
if (!this._testMode) {
if (!TESTING) {
code = code.concat(extra.map(name => `delete ${name};`));
code.push("delete debugMe;");
}
@ -81,7 +88,7 @@ class Sandbox {
}
evalForTesting(code, key) {
if (this._testMode) {
if (TESTING) {
this._evalInSandbox(
`try {
send({ id: "${key}", result: ${code} });
@ -94,13 +101,9 @@ class Sandbox {
}
}
function QuickJSSandbox(testMode = false) {
testMode =
testMode &&
(typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || TESTING"));
function QuickJSSandbox() {
return ModuleLoader().then(module => {
return new Sandbox(module, testMode);
return new Sandbox(module);
});
}

View File

@ -15,6 +15,8 @@
import { loadScript } from "../../src/display/display_utils.js";
const sandboxBundleSrc = "../../build/generic/build/pdf.sandbox.js";
describe("Scripting", function () {
let sandbox, send_queue, test_id, ref;
@ -44,11 +46,9 @@ describe("Scripting", function () {
send_queue.set(event.detail.id, event.detail);
}
};
const promise = loadScript("../../build/generic/build/pdf.sandbox.js").then(
() => {
return window.pdfjsSandbox.QuickJSSandbox(true);
}
);
const promise = loadScript(sandboxBundleSrc).then(() => {
return window.pdfjsSandbox.QuickJSSandbox();
});
sandbox = {
createSandbox(data) {
promise.then(sbx => sbx.create(data));

View File

@ -248,12 +248,16 @@ const PDFViewerApplication = {
url: "",
baseUrl: "",
externalServices: DefaultExternalServices,
_boundEvents: {},
contentDispositionFilename: null,
_boundEvents: Object.create(null),
documentInfo: null,
metadata: null,
_contentDispositionFilename: null,
_contentLength: null,
triggerDelayedFallback: null,
_saveInProgress: false,
_wheelUnusedTicks: 0,
_idleCallbacks: new Set(),
_scriptingInstance: null,
// Called once when the document is loaded.
async initialize(appConfig) {
@ -789,7 +793,10 @@ const PDFViewerApplication = {
this.downloadComplete = false;
this.url = "";
this.baseUrl = "";
this.contentDispositionFilename = null;
this.documentInfo = null;
this.metadata = null;
this._contentDispositionFilename = null;
this._contentLength = null;
this.triggerDelayedFallback = null;
this._saveInProgress = false;
for (const callback of this._idleCallbacks) {
@ -797,6 +804,18 @@ const PDFViewerApplication = {
}
this._idleCallbacks.clear();
if (this._scriptingInstance) {
const { scripting, events } = this._scriptingInstance;
try {
scripting.destroySandbox();
} catch (ex) {}
for (const [name, listener] of events) {
window.removeEventListener(name, listener);
}
this._scriptingInstance = null;
}
this.pdfSidebar.reset();
this.pdfOutlineViewer.reset();
this.pdfAttachmentViewer.reset();
@ -942,7 +961,7 @@ const PDFViewerApplication = {
// Use this.url instead of this.baseUrl to perform filename detection based
// on the reference fragment as ultimate fallback if needed.
const filename =
this.contentDispositionFilename || getPDFFileNameFromURL(this.url);
this._contentDispositionFilename || getPDFFileNameFromURL(this.url);
const downloadManager = this.downloadManager;
downloadManager.onerror = err => {
// This error won't really be helpful because it's likely the
@ -975,7 +994,7 @@ const PDFViewerApplication = {
// Use this.url instead of this.baseUrl to perform filename detection based
// on the reference fragment as ultimate fallback if needed.
const filename =
this.contentDispositionFilename || getPDFFileNameFromURL(this.url);
this._contentDispositionFilename || getPDFFileNameFromURL(this.url);
const downloadManager = this.downloadManager;
downloadManager.onerror = err => {
// This error won't really be helpful because it's likely the
@ -1403,54 +1422,71 @@ const PDFViewerApplication = {
* @private
*/
async _initializeJavaScript(pdfDocument) {
const objects = await pdfDocument.getFieldObjects();
if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the JavaScript data resolved.
}
if (!objects || !AppOptions.get("enableScripting")) {
if (!AppOptions.get("enableScripting")) {
return;
}
const calculationOrder = await pdfDocument.getCalculationOrderIds();
const scripting = this.externalServices.scripting;
const {
info,
metadata,
contentDispositionFilename,
} = await pdfDocument.getMetadata();
const [objects, calculationOrder] = await Promise.all([
pdfDocument.getFieldObjects(),
pdfDocument.getCalculationOrderIds(),
]);
window.addEventListener("updateFromSandbox", event => {
const detail = event.detail;
const id = detail.id;
if (!objects || pdfDocument !== this.pdfDocument) {
// No FieldObjects were found in the document,
// or the document was closed while the data resolved.
return;
}
const { scripting } = this.externalServices;
// Store a reference to the current scripting-instance, to allow destruction
// of the sandbox and removal of the event listeners at document closing.
this._scriptingInstance = { scripting, events: new Map() };
if (!this.documentInfo) {
// It should be *extremely* rare for metadata to not have been resolved
// when this code runs, but ensure that we handle that case here.
await new Promise(resolve => {
const metadataLoaded = () => {
this.eventBus._off("metadataloaded", metadataLoaded);
resolve();
};
this.eventBus._on("metadataloaded", metadataLoaded);
});
if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the metadata resolved.
}
}
const updateFromSandbox = event => {
const { detail } = event;
const { id, command, value } = detail;
if (!id) {
switch (detail.command) {
switch (command) {
case "alert":
// eslint-disable-next-line no-alert
window.alert(detail.value);
window.alert(value);
break;
case "clear":
console.clear();
break;
case "error":
console.error(detail.value);
console.error(value);
break;
case "layout":
this.pdfViewer.spreadMode = apiPageLayoutToSpreadMode(detail.value);
this.pdfViewer.spreadMode = apiPageLayoutToSpreadMode(value);
return;
case "page-num":
this.pdfViewer.currentPageNumber = detail.value + 1;
this.pdfViewer.currentPageNumber = value + 1;
return;
case "print":
this.triggerPrinting();
return;
case "println":
console.log(detail.value);
console.log(value);
break;
case "zoom":
if (typeof detail.value === "string") {
this.pdfViewer.currentScaleValue = detail.value;
if (typeof value === "string") {
this.pdfViewer.currentScaleValue = value;
} else {
this.pdfViewer.currentScale = detail.value;
this.pdfViewer.currentScale = value;
}
return;
}
@ -1461,22 +1497,44 @@ const PDFViewerApplication = {
if (element) {
element.dispatchEvent(new CustomEvent("updateFromSandbox", { detail }));
} else {
const value = detail.value;
if (value !== undefined && value !== null) {
// the element hasn't been rendered yet so use annotation storage
pdfDocument.annotationStorage.setValue(id, detail.value);
// The element hasn't been rendered yet, use the AnnotationStorage.
pdfDocument.annotationStorage.setValue(id, value);
}
}
});
};
window.addEventListener("updateFromSandbox", updateFromSandbox);
// Ensure that the event listener can be removed at document closing.
this._scriptingInstance.events.set("updateFromSandbox", updateFromSandbox);
window.addEventListener("dispatchEventInSandbox", function (event) {
const dispatchEventInSandbox = event => {
scripting.dispatchEventInSandbox(event.detail);
});
};
window.addEventListener("dispatchEventInSandbox", dispatchEventInSandbox);
// Ensure that the event listener can be removed at document closing.
this._scriptingInstance.events.set(
"dispatchEventInSandbox",
dispatchEventInSandbox
);
const dispatchEventName = generateRandomStringForSandbox(objects);
const { length } = await pdfDocument.getDownloadInfo();
if (!this._contentLength) {
// Always waiting for the entire PDF document to be loaded will, most
// likely, delay sandbox-creation too much in the general case for all
// PDF documents which are not provided as binary data to the API.
// Hence we'll simply have to trust that the `contentLength` (as provided
// by the server), when it exists, is accurate enough here.
const { length } = await pdfDocument.getDownloadInfo();
if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the download info resolved.
}
this._contentLength = length;
}
const filename =
contentDispositionFilename || getPDFFileNameFromURL(this.url);
this._contentDispositionFilename || getPDFFileNameFromURL(this.url);
scripting.createSandbox({
objects,
dispatchEventName,
@ -1486,11 +1544,11 @@ const PDFViewerApplication = {
language: navigator.language,
},
docInfo: {
...info,
...this.documentInfo,
baseURL: this.baseUrl,
filesize: length,
filesize: this._contentLength,
filename,
metadata,
metadata: this.metadata,
numPages: pdfDocument.numPages,
URL: this.url,
},
@ -1568,6 +1626,7 @@ const PDFViewerApplication = {
info,
metadata,
contentDispositionFilename,
contentLength,
} = await pdfDocument.getMetadata();
if (pdfDocument !== this.pdfDocument) {
@ -1575,7 +1634,8 @@ const PDFViewerApplication = {
}
this.documentInfo = info;
this.metadata = metadata;
this.contentDispositionFilename = contentDispositionFilename;
this._contentDispositionFilename = contentDispositionFilename;
this._contentLength = contentLength;
// Provides some basic debug information
console.log(
@ -1652,6 +1712,8 @@ const PDFViewerApplication = {
generator: generatorId,
formType,
});
this.eventBus.dispatch("metadataloaded", { source: this });
},
/**

View File

@ -223,14 +223,6 @@ const defaultOptions = {
value: false,
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: {
/** @type {number} */
value: 1,
@ -265,6 +257,14 @@ if (
value: typeof navigator !== "undefined" ? navigator.language : "en-US",
kind: OptionKind.VIEWER,
};
defaultOptions.sandboxBundleSrc = {
/** @type {string} */
value:
typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
? "../build/dev-sandbox/pdf.sandbox.js"
: "../build/pdf.sandbox.js",
kind: OptionKind.VIEWER,
};
}
const userOptions = Object.create(null);

View File

@ -1,43 +0,0 @@
/* 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 };

View File

@ -14,11 +14,11 @@
*/
import { DefaultExternalServices, PDFViewerApplication } from "./app.js";
import { loadScript, shadow } from "pdfjs-lib";
import { AppOptions } from "./app_options.js";
import { BasePreferences } from "./preferences.js";
import { DownloadManager } from "./download_manager.js";
import { GenericL10n } from "./genericl10n.js";
import { loadScript } from "pdfjs-lib";
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) {
throw new Error(
@ -39,6 +39,29 @@ class GenericPreferences extends BasePreferences {
}
}
class GenericScripting {
constructor() {
this._ready = loadScript(AppOptions.get("sandboxBundleSrc")).then(() => {
return window.pdfjsSandbox.QuickJSSandbox();
});
}
async createSandbox(data) {
const sandbox = await this._ready;
sandbox.create(data);
}
async dispatchEventInSandbox(event) {
const sandbox = await this._ready;
sandbox.dispatchEvent(event);
}
async destroySandbox() {
const sandbox = await this._ready;
sandbox.nukeSandbox();
}
}
class GenericExternalServices extends DefaultExternalServices {
static createDownloadManager(options) {
return new DownloadManager();
@ -53,22 +76,7 @@ class GenericExternalServices extends DefaultExternalServices {
}
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);
return new GenericScripting();
}
}
PDFViewerApplication.externalServices = GenericExternalServices;

View File

@ -56,9 +56,6 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME")) {
require("./chromecom.js");
}
if (typeof PDFJSDev === "undefined") {
import("./devcom.js");
}
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME || GENERIC")) {
require("./pdf_print_service.js");
}