Support disabling of form editing when pdfjs.enablePermissions is set (issue 14356)

For encrypted PDF documents without the required permissions set, this patch adds support for disabling of form editing. However, please note that it also requires that the `pdfjs.enablePermissions` preference is set to `true`[1] (since PDF document permissions could be seen as user hostile).

Based on https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf#G6.1942134, this condition hopefully makes sense.

---
[1] Either manually with `about:config`, or using e.g. a [Group Policy](https://github.com/mozilla/policy-templates).
This commit is contained in:
Jonas Jenwald 2021-12-11 16:53:59 +01:00
parent b03281de18
commit b1d3e7f121
2 changed files with 34 additions and 10 deletions

View File

@ -175,6 +175,10 @@ class PDFPageViewBuffer {
class BaseViewer { class BaseViewer {
#buffer = null; #buffer = null;
#annotationMode = AnnotationMode.ENABLE_FORMS;
#previousAnnotationMode = null;
#enablePermissions = false; #enablePermissions = false;
#previousContainerHeight = 0; #previousContainerHeight = 0;
@ -225,7 +229,7 @@ class BaseViewer {
this._scriptingManager = options.scriptingManager || null; this._scriptingManager = options.scriptingManager || null;
this.removePageBorders = options.removePageBorders || false; this.removePageBorders = options.removePageBorders || false;
this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
this._annotationMode = this.#annotationMode =
options.annotationMode ?? AnnotationMode.ENABLE_FORMS; options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
this.imageResourcesPath = options.imageResourcesPath || ""; this.imageResourcesPath = options.imageResourcesPath || "";
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false; this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
@ -286,7 +290,7 @@ class BaseViewer {
* @type {boolean} * @type {boolean}
*/ */
get renderForms() { get renderForms() {
return this._annotationMode === AnnotationMode.ENABLE_FORMS; return this.#annotationMode === AnnotationMode.ENABLE_FORMS;
} }
/** /**
@ -479,6 +483,9 @@ class BaseViewer {
return this.pdfDocument ? this._pagesCapability.promise : null; return this.pdfDocument ? this._pagesCapability.promise : null;
} }
/**
* Currently only *some* permissions are supported.
*/
#initializePermissions(permissions, pdfDocument) { #initializePermissions(permissions, pdfDocument) {
if (pdfDocument !== this.pdfDocument) { if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the permissions resolved. return; // The document was closed while the permissions resolved.
@ -486,10 +493,20 @@ class BaseViewer {
if (!permissions || !this.#enablePermissions) { if (!permissions || !this.#enablePermissions) {
return; return;
} }
// Currently only the "copy"-permission is supported.
if (!permissions.includes(PermissionFlag.COPY)) { if (!permissions.includes(PermissionFlag.COPY)) {
this.viewer.classList.add(ENABLE_PERMISSIONS_CLASS); this.viewer.classList.add(ENABLE_PERMISSIONS_CLASS);
} }
if (
!permissions.includes(PermissionFlag.MODIFY_ANNOTATIONS) &&
!permissions.includes(PermissionFlag.FILL_INTERACTIVE_FORMS)
) {
if (this.#annotationMode === AnnotationMode.ENABLE_FORMS) {
this.#previousAnnotationMode = this.#annotationMode; // Allow resetting.
this.#annotationMode = AnnotationMode.ENABLE;
}
}
} }
#onePageRenderedOrForceFetch() { #onePageRenderedOrForceFetch() {
@ -599,7 +616,7 @@ class BaseViewer {
? this ? this
: null; : null;
const annotationLayerFactory = const annotationLayerFactory =
this._annotationMode !== AnnotationMode.DISABLE ? this : null; this.#annotationMode !== AnnotationMode.DISABLE ? this : null;
const xfaLayerFactory = isPureXfa ? this : null; const xfaLayerFactory = isPureXfa ? this : null;
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) { for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
@ -614,7 +631,7 @@ class BaseViewer {
textLayerFactory, textLayerFactory,
textLayerMode: this.textLayerMode, textLayerMode: this.textLayerMode,
annotationLayerFactory, annotationLayerFactory,
annotationMode: this._annotationMode, annotationMode: this.#annotationMode,
xfaLayerFactory, xfaLayerFactory,
textHighlighterFactory: this, textHighlighterFactory: this,
structTreeLayerFactory: this, structTreeLayerFactory: this,
@ -772,6 +789,11 @@ class BaseViewer {
// Reset all PDF document permissions. // Reset all PDF document permissions.
this.viewer.classList.remove(ENABLE_PERMISSIONS_CLASS); this.viewer.classList.remove(ENABLE_PERMISSIONS_CLASS);
if (this.#previousAnnotationMode !== null) {
this.#annotationMode = this.#previousAnnotationMode;
this.#previousAnnotationMode = null;
}
} }
#ensurePageViewVisible() { #ensurePageViewVisible() {

View File

@ -76,6 +76,8 @@ const MAX_CANVAS_PIXELS = compatibilityParams.maxCanvasPixels || 16777216;
* @implements {IRenderableView} * @implements {IRenderableView}
*/ */
class PDFPageView { class PDFPageView {
#annotationMode = AnnotationMode.ENABLE_FORMS;
/** /**
* @param {PDFPageViewOptions} options * @param {PDFPageViewOptions} options
*/ */
@ -96,7 +98,7 @@ class PDFPageView {
options.optionalContentConfigPromise || null; options.optionalContentConfigPromise || null;
this.hasRestrictedScaling = false; this.hasRestrictedScaling = false;
this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE; this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
this._annotationMode = this.#annotationMode =
options.annotationMode ?? AnnotationMode.ENABLE_FORMS; options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
this.imageResourcesPath = options.imageResourcesPath || ""; this.imageResourcesPath = options.imageResourcesPath || "";
this.useOnlyCssZoom = options.useOnlyCssZoom || false; this.useOnlyCssZoom = options.useOnlyCssZoom || false;
@ -597,7 +599,7 @@ class PDFPageView {
this.textLayer = textLayer; this.textLayer = textLayer;
if ( if (
this._annotationMode !== AnnotationMode.DISABLE && this.#annotationMode !== AnnotationMode.DISABLE &&
this.annotationLayerFactory this.annotationLayerFactory
) { ) {
this._annotationCanvasMap ||= new Map(); this._annotationCanvasMap ||= new Map();
@ -607,7 +609,7 @@ class PDFPageView {
pdfPage, pdfPage,
/* annotationStorage = */ null, /* annotationStorage = */ null,
this.imageResourcesPath, this.imageResourcesPath,
this._annotationMode === AnnotationMode.ENABLE_FORMS, this.#annotationMode === AnnotationMode.ENABLE_FORMS,
this.l10n, this.l10n,
/* enableScripting = */ null, /* enableScripting = */ null,
/* hasJSActionsPromise = */ null, /* hasJSActionsPromise = */ null,
@ -835,7 +837,7 @@ class PDFPageView {
canvasContext: ctx, canvasContext: ctx,
transform, transform,
viewport: this.viewport, viewport: this.viewport,
annotationMode: this._annotationMode, annotationMode: this.#annotationMode,
optionalContentConfigPromise: this._optionalContentConfigPromise, optionalContentConfigPromise: this._optionalContentConfigPromise,
annotationCanvasMap: this._annotationCanvasMap, annotationCanvasMap: this._annotationCanvasMap,
}; };
@ -892,7 +894,7 @@ class PDFPageView {
}); });
const promise = pdfPage const promise = pdfPage
.getOperatorList({ .getOperatorList({
annotationMode: this._annotationMode, annotationMode: this.#annotationMode,
}) })
.then(opList => { .then(opList => {
ensureNotCancelled(); ensureNotCancelled();