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

100
package-lock.json generated
View File

@ -2178,15 +2178,6 @@
"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",
@ -2196,15 +2187,6 @@
"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",
@ -4028,15 +4010,6 @@
"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",
@ -7073,79 +7046,6 @@
"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",

View File

@ -30,7 +30,6 @@
"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",

View File

@ -13,17 +13,24 @@
* limitations under the License. * 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 { class Sandbox {
constructor(module, testMode) { constructor(module) {
this._evalInSandbox = module.cwrap("evalInSandbox", null, [ this._evalInSandbox = module.cwrap("evalInSandbox", null, [
"string", "string",
"int", "int",
]); ]);
this._dispatchEventName = null; this._dispatchEventName = null;
this._module = module; this._module = module;
this._testMode = testMode;
this._alertOnError = 1; this._alertOnError = 1;
} }
@ -43,14 +50,14 @@ class Sandbox {
"module = Object.create(null);", "module = Object.create(null);",
// Next line is replaced by code from initialization.js // Next line is replaced by code from initialization.js
// when we create the bundle for the sandbox. // when we create the bundle for the sandbox.
"/* INITIALIZATION_CODE */", PDFJSDev.eval("PDF_SCRIPTING_JS_SOURCE"),
`data = ${sandboxData};`, `data = ${sandboxData};`,
`module.exports.initSandbox({ data, extra: {${extraStr}}, out: this});`, `module.exports.initSandbox({ data, extra: {${extraStr}}, out: this});`,
"delete exports;", "delete exports;",
"delete module;", "delete module;",
"delete data;", "delete data;",
]; ];
if (!this._testMode) { if (!TESTING) {
code = code.concat(extra.map(name => `delete ${name};`)); code = code.concat(extra.map(name => `delete ${name};`));
code.push("delete debugMe;"); code.push("delete debugMe;");
} }
@ -81,7 +88,7 @@ class Sandbox {
} }
evalForTesting(code, key) { evalForTesting(code, key) {
if (this._testMode) { if (TESTING) {
this._evalInSandbox( this._evalInSandbox(
`try { `try {
send({ id: "${key}", result: ${code} }); send({ id: "${key}", result: ${code} });
@ -94,13 +101,9 @@ class Sandbox {
} }
} }
function QuickJSSandbox(testMode = false) { function QuickJSSandbox() {
testMode =
testMode &&
(typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || TESTING"));
return ModuleLoader().then(module => { 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"; import { loadScript } from "../../src/display/display_utils.js";
const sandboxBundleSrc = "../../build/generic/build/pdf.sandbox.js";
describe("Scripting", function () { describe("Scripting", function () {
let sandbox, send_queue, test_id, ref; let sandbox, send_queue, test_id, ref;
@ -44,11 +46,9 @@ describe("Scripting", function () {
send_queue.set(event.detail.id, event.detail); send_queue.set(event.detail.id, event.detail);
} }
}; };
const promise = loadScript("../../build/generic/build/pdf.sandbox.js").then( const promise = loadScript(sandboxBundleSrc).then(() => {
() => { return window.pdfjsSandbox.QuickJSSandbox();
return window.pdfjsSandbox.QuickJSSandbox(true); });
}
);
sandbox = { sandbox = {
createSandbox(data) { createSandbox(data) {
promise.then(sbx => sbx.create(data)); promise.then(sbx => sbx.create(data));

View File

@ -248,12 +248,16 @@ const PDFViewerApplication = {
url: "", url: "",
baseUrl: "", baseUrl: "",
externalServices: DefaultExternalServices, externalServices: DefaultExternalServices,
_boundEvents: {}, _boundEvents: Object.create(null),
contentDispositionFilename: null, documentInfo: null,
metadata: null,
_contentDispositionFilename: null,
_contentLength: null,
triggerDelayedFallback: null, triggerDelayedFallback: null,
_saveInProgress: false, _saveInProgress: false,
_wheelUnusedTicks: 0, _wheelUnusedTicks: 0,
_idleCallbacks: new Set(), _idleCallbacks: new Set(),
_scriptingInstance: null,
// Called once when the document is loaded. // Called once when the document is loaded.
async initialize(appConfig) { async initialize(appConfig) {
@ -789,7 +793,10 @@ const PDFViewerApplication = {
this.downloadComplete = false; this.downloadComplete = false;
this.url = ""; this.url = "";
this.baseUrl = ""; this.baseUrl = "";
this.contentDispositionFilename = null; this.documentInfo = null;
this.metadata = null;
this._contentDispositionFilename = null;
this._contentLength = null;
this.triggerDelayedFallback = null; this.triggerDelayedFallback = null;
this._saveInProgress = false; this._saveInProgress = false;
for (const callback of this._idleCallbacks) { for (const callback of this._idleCallbacks) {
@ -797,6 +804,18 @@ const PDFViewerApplication = {
} }
this._idleCallbacks.clear(); 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.pdfSidebar.reset();
this.pdfOutlineViewer.reset(); this.pdfOutlineViewer.reset();
this.pdfAttachmentViewer.reset(); this.pdfAttachmentViewer.reset();
@ -942,7 +961,7 @@ const PDFViewerApplication = {
// Use this.url instead of this.baseUrl to perform filename detection based // Use this.url instead of this.baseUrl to perform filename detection based
// on the reference fragment as ultimate fallback if needed. // on the reference fragment as ultimate fallback if needed.
const filename = const filename =
this.contentDispositionFilename || getPDFFileNameFromURL(this.url); this._contentDispositionFilename || getPDFFileNameFromURL(this.url);
const downloadManager = this.downloadManager; const downloadManager = this.downloadManager;
downloadManager.onerror = err => { downloadManager.onerror = err => {
// This error won't really be helpful because it's likely the // 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 // Use this.url instead of this.baseUrl to perform filename detection based
// on the reference fragment as ultimate fallback if needed. // on the reference fragment as ultimate fallback if needed.
const filename = const filename =
this.contentDispositionFilename || getPDFFileNameFromURL(this.url); this._contentDispositionFilename || getPDFFileNameFromURL(this.url);
const downloadManager = this.downloadManager; const downloadManager = this.downloadManager;
downloadManager.onerror = err => { downloadManager.onerror = err => {
// This error won't really be helpful because it's likely the // This error won't really be helpful because it's likely the
@ -1403,54 +1422,71 @@ const PDFViewerApplication = {
* @private * @private
*/ */
async _initializeJavaScript(pdfDocument) { async _initializeJavaScript(pdfDocument) {
const objects = await pdfDocument.getFieldObjects(); if (!AppOptions.get("enableScripting")) {
if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the JavaScript data resolved.
}
if (!objects || !AppOptions.get("enableScripting")) {
return; return;
} }
const calculationOrder = await pdfDocument.getCalculationOrderIds(); const [objects, calculationOrder] = await Promise.all([
const scripting = this.externalServices.scripting; pdfDocument.getFieldObjects(),
const { pdfDocument.getCalculationOrderIds(),
info, ]);
metadata,
contentDispositionFilename,
} = await pdfDocument.getMetadata();
window.addEventListener("updateFromSandbox", event => { if (!objects || pdfDocument !== this.pdfDocument) {
const detail = event.detail; // No FieldObjects were found in the document,
const id = detail.id; // 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) { if (!id) {
switch (detail.command) { switch (command) {
case "alert": case "alert":
// eslint-disable-next-line no-alert // eslint-disable-next-line no-alert
window.alert(detail.value); window.alert(value);
break; break;
case "clear": case "clear":
console.clear(); console.clear();
break; break;
case "error": case "error":
console.error(detail.value); console.error(value);
break; break;
case "layout": case "layout":
this.pdfViewer.spreadMode = apiPageLayoutToSpreadMode(detail.value); this.pdfViewer.spreadMode = apiPageLayoutToSpreadMode(value);
return; return;
case "page-num": case "page-num":
this.pdfViewer.currentPageNumber = detail.value + 1; this.pdfViewer.currentPageNumber = value + 1;
return; return;
case "print": case "print":
this.triggerPrinting(); this.triggerPrinting();
return; return;
case "println": case "println":
console.log(detail.value); console.log(value);
break; break;
case "zoom": case "zoom":
if (typeof detail.value === "string") { if (typeof value === "string") {
this.pdfViewer.currentScaleValue = detail.value; this.pdfViewer.currentScaleValue = value;
} else { } else {
this.pdfViewer.currentScale = detail.value; this.pdfViewer.currentScale = value;
} }
return; return;
} }
@ -1461,22 +1497,44 @@ const PDFViewerApplication = {
if (element) { if (element) {
element.dispatchEvent(new CustomEvent("updateFromSandbox", { detail })); element.dispatchEvent(new CustomEvent("updateFromSandbox", { detail }));
} else { } else {
const value = detail.value;
if (value !== undefined && value !== null) { if (value !== undefined && value !== null) {
// the element hasn't been rendered yet so use annotation storage // The element hasn't been rendered yet, use the AnnotationStorage.
pdfDocument.annotationStorage.setValue(id, detail.value); 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); 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 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 = const filename =
contentDispositionFilename || getPDFFileNameFromURL(this.url); this._contentDispositionFilename || getPDFFileNameFromURL(this.url);
scripting.createSandbox({ scripting.createSandbox({
objects, objects,
dispatchEventName, dispatchEventName,
@ -1486,11 +1544,11 @@ const PDFViewerApplication = {
language: navigator.language, language: navigator.language,
}, },
docInfo: { docInfo: {
...info, ...this.documentInfo,
baseURL: this.baseUrl, baseURL: this.baseUrl,
filesize: length, filesize: this._contentLength,
filename, filename,
metadata, metadata: this.metadata,
numPages: pdfDocument.numPages, numPages: pdfDocument.numPages,
URL: this.url, URL: this.url,
}, },
@ -1568,6 +1626,7 @@ const PDFViewerApplication = {
info, info,
metadata, metadata,
contentDispositionFilename, contentDispositionFilename,
contentLength,
} = await pdfDocument.getMetadata(); } = await pdfDocument.getMetadata();
if (pdfDocument !== this.pdfDocument) { if (pdfDocument !== this.pdfDocument) {
@ -1575,7 +1634,8 @@ const PDFViewerApplication = {
} }
this.documentInfo = info; this.documentInfo = info;
this.metadata = metadata; this.metadata = metadata;
this.contentDispositionFilename = contentDispositionFilename; this._contentDispositionFilename = contentDispositionFilename;
this._contentLength = contentLength;
// Provides some basic debug information // Provides some basic debug information
console.log( console.log(
@ -1652,6 +1712,8 @@ const PDFViewerApplication = {
generator: generatorId, generator: generatorId,
formType, formType,
}); });
this.eventBus.dispatch("metadataloaded", { source: this });
}, },
/** /**

View File

@ -223,14 +223,6 @@ 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,
@ -265,6 +257,14 @@ if (
value: typeof navigator !== "undefined" ? navigator.language : "en-US", value: typeof navigator !== "undefined" ? navigator.language : "en-US",
kind: OptionKind.VIEWER, 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); 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 { DefaultExternalServices, PDFViewerApplication } from "./app.js";
import { loadScript, shadow } from "pdfjs-lib";
import { AppOptions } from "./app_options.js"; 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";
import { loadScript } from "pdfjs-lib";
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) { if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("GENERIC")) {
throw new Error( 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 { class GenericExternalServices extends DefaultExternalServices {
static createDownloadManager(options) { static createDownloadManager(options) {
return new DownloadManager(); return new DownloadManager();
@ -53,22 +76,7 @@ class GenericExternalServices extends DefaultExternalServices {
} }
static get scripting() { static get scripting() {
const promise = loadScript(AppOptions.get("scriptingSrc")).then(() => { return new GenericScripting();
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;

View File

@ -56,9 +56,6 @@ 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");
} }