Some hasJSActions, and general annotation-code, related cleanup in the viewer and API

- Add support for logical assignment operators, i.e. `&&=`, `||=`, and `??=`, with a Babel-plugin. Given that these required incrementing the ECMAScript version in the ESLint and Acorn configurations, and that platform/browser support is still fairly limited, always transpiling them seems appropriate for now.

 - Cache the `hasJSActions` promise in the API, similar to the existing `getAnnotations` caching. With this implemented, the lookup should now be cheap enough that it can be called unconditionally in the viewer.

 - Slightly improve cleanup of resources when destroying the `WorkerTransport`.

 - Remove the `annotationStorage`-property from the `PDFPageView` constructor, since it's not necessary and also brings it more inline with the `BaseViewer`.

 - Update the `BaseViewer.createAnnotationLayerBuilder` method to actaually agree with the `IPDFAnnotationLayerFactory` interface.[1]

 - Slightly tweak a couple of JSDoc comments.

---
[1] We probably ought to re-factor both the `IPDFTextLayerFactory` and `IPDFAnnotationLayerFactory` interfaces to take parameter objects instead, since especially the `IPDFAnnotationLayerFactory` one is becoming quite unwieldy. Given that that would likely be a breaking change for any custom viewer-components implementation, this probably requires careful deprecation.
This commit is contained in:
Jonas Jenwald 2020-11-14 13:38:36 +01:00
parent 59b35600be
commit de628cec59
8 changed files with 30 additions and 19 deletions

View File

@ -1,6 +1,6 @@
{ {
"parserOptions": { "parserOptions": {
"ecmaVersion": 2020, "ecmaVersion": 2021,
"sourceType": "module", "sourceType": "module",
}, },

View File

@ -8,7 +8,7 @@ var path = require("path");
var PDFJS_PREPROCESSOR_NAME = "PDFJSDev"; var PDFJS_PREPROCESSOR_NAME = "PDFJSDev";
var ROOT_PREFIX = "$ROOT/"; var ROOT_PREFIX = "$ROOT/";
const ACORN_ECMA_VERSION = 2020; const ACORN_ECMA_VERSION = 2021;
function isLiteral(obj, value) { function isLiteral(obj, value) {
return obj.type === "Literal" && obj.value === value; return obj.type === "Literal" && obj.value === value;

View File

@ -226,6 +226,7 @@ function createWebpackConfig(defines, output) {
options: { options: {
presets: skipBabel ? undefined : ["@babel/preset-env"], presets: skipBabel ? undefined : ["@babel/preset-env"],
plugins: [ plugins: [
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-transform-modules-commonjs", "@babel/plugin-transform-modules-commonjs",
[ [
"@babel/plugin-transform-runtime", "@babel/plugin-transform-runtime",
@ -570,6 +571,7 @@ gulp.task("default_preferences-pre", function () {
sourceType: "module", sourceType: "module",
presets: undefined, // SKIP_BABEL presets: undefined, // SKIP_BABEL
plugins: [ plugins: [
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-transform-modules-commonjs", "@babel/plugin-transform-modules-commonjs",
babelPluginReplaceNonWebPackRequire, babelPluginReplaceNonWebPackRequire,
], ],
@ -1234,6 +1236,7 @@ function buildLib(defines, dir) {
sourceType: "module", sourceType: "module",
presets: skipBabel ? undefined : ["@babel/preset-env"], presets: skipBabel ? undefined : ["@babel/preset-env"],
plugins: [ plugins: [
"@babel/plugin-proposal-logical-assignment-operators",
"@babel/plugin-transform-modules-commonjs", "@babel/plugin-transform-modules-commonjs",
[ [
"@babel/plugin-transform-runtime", "@babel/plugin-transform-runtime",

View File

@ -3,6 +3,7 @@
"version": "2.0.0", "version": "2.0.0",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.3", "@babel/core": "^7.12.3",
"@babel/plugin-proposal-logical-assignment-operators": "^7.12.1",
"@babel/plugin-transform-modules-commonjs": "^7.12.1", "@babel/plugin-transform-modules-commonjs": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.1", "@babel/plugin-transform-runtime": "^7.12.1",
"@babel/preset-env": "^7.12.1", "@babel/preset-env": "^7.12.1",

View File

@ -905,7 +905,7 @@ class PDFDocumentProxy {
/** /**
* @returns {Promise<boolean>} A promise that is resolved with `true` * @returns {Promise<boolean>} A promise that is resolved with `true`
* if some /AcroForm fields have JavaScript actions. * if some /AcroForm fields have JavaScript actions.
*/ */
hasJSActions() { hasJSActions() {
return this._transport.hasJSActions(); return this._transport.hasJSActions();
@ -2128,7 +2128,10 @@ class WorkerTransport {
const terminated = this.messageHandler.sendWithPromise("Terminate", null); const terminated = this.messageHandler.sendWithPromise("Terminate", null);
waitOn.push(terminated); waitOn.push(terminated);
Promise.all(waitOn).then(() => { Promise.all(waitOn).then(() => {
this.commonObjs.clear();
this.fontLoader.clear(); this.fontLoader.clear();
this._hasJSActionsPromise = null;
if (this._networkStream) { if (this._networkStream) {
this._networkStream.cancelAllRequests( this._networkStream.cancelAllRequests(
new AbortException("Worker was terminated.") new AbortException("Worker was terminated.")
@ -2577,7 +2580,10 @@ class WorkerTransport {
} }
hasJSActions() { hasJSActions() {
return this.messageHandler.sendWithPromise("HasJSActions", null); return (this._hasJSActionsPromise ||= this.messageHandler.sendWithPromise(
"HasJSActions",
null
));
} }
getCalculationOrderIds() { getCalculationOrderIds() {
@ -2679,6 +2685,7 @@ class WorkerTransport {
} }
this.commonObjs.clear(); this.commonObjs.clear();
this.fontLoader.clear(); this.fontLoader.clear();
this._hasJSActionsPromise = null;
}); });
} }

View File

@ -137,8 +137,8 @@ class DefaultAnnotationLayerFactory {
* for annotation icons. Include trailing slash. * for annotation icons. Include trailing slash.
* @param {boolean} renderInteractiveForms * @param {boolean} renderInteractiveForms
* @param {IL10n} l10n * @param {IL10n} l10n
* @param {boolean} enableScripting * @param {boolean} [enableScripting]
* @param {Promise<boolean>} hasJSActionsPromise * @param {Promise<boolean>} [hasJSActionsPromise]
* @returns {AnnotationLayerBuilder} * @returns {AnnotationLayerBuilder}
*/ */
createAnnotationLayerBuilder( createAnnotationLayerBuilder(

View File

@ -469,8 +469,7 @@ class BaseViewer {
} }
const pagesCount = pdfDocument.numPages; const pagesCount = pdfDocument.numPages;
const firstPagePromise = pdfDocument.getPage(1); const firstPagePromise = pdfDocument.getPage(1);
// Rendering (potentially) depends on this, hence fetching it immediately.
const annotationStorage = pdfDocument.annotationStorage;
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig(); const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
this._pagesCapability.promise.then(() => { this._pagesCapability.promise.then(() => {
@ -521,7 +520,6 @@ class BaseViewer {
id: pageNum, id: pageNum,
scale, scale,
defaultViewport: viewport.clone(), defaultViewport: viewport.clone(),
annotationStorage,
optionalContentConfigPromise, optionalContentConfigPromise,
renderingQueue: this.renderingQueue, renderingQueue: this.renderingQueue,
textLayerFactory, textLayerFactory,
@ -1244,11 +1242,14 @@ class BaseViewer {
/** /**
* @param {HTMLDivElement} pageDiv * @param {HTMLDivElement} pageDiv
* @param {PDFPage} pdfPage * @param {PDFPage} pdfPage
* @param {AnnotationStorage} [annotationStorage] - Storage for annotation
* data in forms.
* @param {string} [imageResourcesPath] - Path for image resources, mainly * @param {string} [imageResourcesPath] - Path for image resources, mainly
* for annotation icons. Include trailing slash. * for annotation icons. Include trailing slash.
* @param {boolean} renderInteractiveForms * @param {boolean} renderInteractiveForms
* @param {IL10n} l10n * @param {IL10n} l10n
* @param {boolean} [enableScripting] * @param {boolean} [enableScripting]
* @param {Promise<boolean>} [hasJSActionsPromise]
* @returns {AnnotationLayerBuilder} * @returns {AnnotationLayerBuilder}
*/ */
createAnnotationLayerBuilder( createAnnotationLayerBuilder(
@ -1258,21 +1259,22 @@ class BaseViewer {
imageResourcesPath = "", imageResourcesPath = "",
renderInteractiveForms = false, renderInteractiveForms = false,
l10n = NullL10n, l10n = NullL10n,
enableScripting = false enableScripting = false,
hasJSActionsPromise = null
) { ) {
return new AnnotationLayerBuilder({ return new AnnotationLayerBuilder({
pageDiv, pageDiv,
pdfPage, pdfPage,
annotationStorage, annotationStorage:
annotationStorage || this.pdfDocument?.annotationStorage,
imageResourcesPath, imageResourcesPath,
renderInteractiveForms, renderInteractiveForms,
linkService: this.linkService, linkService: this.linkService,
downloadManager: this.downloadManager, downloadManager: this.downloadManager,
l10n, l10n,
enableScripting, enableScripting,
hasJSActionsPromise: enableScripting hasJSActionsPromise:
? this.pdfDocument.hasJSActions() hasJSActionsPromise || this.pdfDocument?.hasJSActions(),
: Promise.resolve(false),
}); });
} }

View File

@ -38,8 +38,6 @@ import { viewerCompatibilityParams } from "./viewer_compatibility.js";
* @property {number} id - The page unique ID (normally its number). * @property {number} id - The page unique ID (normally its number).
* @property {number} scale - The page scale display. * @property {number} scale - The page scale display.
* @property {PageViewport} defaultViewport - The page viewport. * @property {PageViewport} defaultViewport - The page viewport.
* @property {AnnotationStorage} [annotationStorage] - Storage for annotation
* data in forms. The default value is `null`.
* @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] - * @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] -
* A promise that is resolved with an {@link OptionalContentConfig} instance. * A promise that is resolved with an {@link OptionalContentConfig} instance.
* The default value is `null`. * The default value is `null`.
@ -89,7 +87,6 @@ class PDFPageView {
this.scale = options.scale || DEFAULT_SCALE; this.scale = options.scale || DEFAULT_SCALE;
this.viewport = defaultViewport; this.viewport = defaultViewport;
this.pdfPageRotate = defaultViewport.rotation; this.pdfPageRotate = defaultViewport.rotation;
this._annotationStorage = options.annotationStorage || null;
this._optionalContentConfigPromise = this._optionalContentConfigPromise =
options.optionalContentConfigPromise || null; options.optionalContentConfigPromise || null;
this.hasRestrictedScaling = false; this.hasRestrictedScaling = false;
@ -549,11 +546,12 @@ class PDFPageView {
this.annotationLayer = this.annotationLayerFactory.createAnnotationLayerBuilder( this.annotationLayer = this.annotationLayerFactory.createAnnotationLayerBuilder(
div, div,
pdfPage, pdfPage,
this._annotationStorage, /* annotationStorage = */ null,
this.imageResourcesPath, this.imageResourcesPath,
this.renderInteractiveForms, this.renderInteractiveForms,
this.l10n, this.l10n,
this.enableScripting this.enableScripting,
/* hasJSActionsPromise = */ null
); );
} }
this._renderAnnotationLayer(); this._renderAnnotationLayer();