Skip to content

Commit

Permalink
Add a new pdfjs.enablePermissions preference, off by default, to al…
Browse files Browse the repository at this point in the history
…low the PDF documents to disable copying in the viewer (bug 792816)

*Please note:* Most of the necessary API work was done in PR 10033, and the only remaining thing to do here was to implement it in the viewer.

The new preference should thus allow e.g. enterprise users to disable copying in the viewer, for PDF documents whose permissions specify that.

In order to simplify things the "copy"-permission was implemented using CSS, as suggested in https://bugzilla.mozilla.org/show_bug.cgi?id=792816#c55, which should hopefully suffice.[1]
The advantage of this approach, as opposed to e.g. disabling the `textLayer` completely, is first of all that it ensures that searching still works correctly even in copy-protected documents. Secondly this also greatly simplifies the overall implementation, since it doesn't require a lot of code for something that's disabled by default.

---
[1] As the discussion in the bug shows, this kind of copy-protection is not very strong and is also generally easy to remove/circumvent in various ways. Hence a simple solution, targeting "regular"-users rather than "power"-users is hopefully deemed acceptable here.
  • Loading branch information
Snuffleupagus committed Apr 8, 2020
1 parent 4fe9260 commit 0e4daad
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 0 deletions.
4 changes: 4 additions & 0 deletions extensions/chromium/preferences_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@
"type": "boolean",
"default": false
},
"enablePermissions": {
"type": "boolean",
"default": false
},
"historyUpdateUrl": {
"type": "boolean",
"default": false
Expand Down
35 changes: 35 additions & 0 deletions web/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
MissingPDFException,
OPS,
PDFWorker,
PermissionFlag,
shadow,
UnexpectedResponseException,
UNSUPPORTED_FEATURES,
Expand Down Expand Up @@ -77,6 +78,7 @@ const DEFAULT_SCALE_DELTA = 1.1;
const DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000; // ms
const FORCE_PAGES_LOADED_TIMEOUT = 10000; // ms
const WHEEL_ZOOM_DISABLED_TIMEOUT = 1000; // ms
const ENABLE_PERMISSIONS_CLASS = "enablePermissions";

const ViewOnLoad = {
UNKNOWN: -1,
Expand Down Expand Up @@ -679,6 +681,7 @@ const PDFViewerApplication = {
this.pdfLinkService.setDocument(null);
this.pdfDocumentProperties.setDocument(null);
}
webViewerResetPermissions();
this.store = null;
this.isInitialViewSet = false;
this.downloadComplete = false;
Expand Down Expand Up @@ -1149,6 +1152,10 @@ const PDFViewerApplication = {
pdfViewer.focus();
}

// Currently only the "copy"-permission is supported, hence we delay
// the `getPermissions` API call until *after* rendering has started.
this._initializePermissions(pdfDocument);

// For documents with different page sizes, once all pages are
// resolved, ensure that the correct location becomes visible on load.
// (To reduce the risk, in very large and/or slow loading documents,
Expand Down Expand Up @@ -1467,6 +1474,25 @@ const PDFViewerApplication = {
}
},

/**
* @private
*/
async _initializePermissions(pdfDocument) {
const permissions = await pdfDocument.getPermissions();

if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the permissions resolved.
}
if (!permissions || !AppOptions.get("enablePermissions")) {
return;
}

// Currently only the "copy"-permission is supported.
if (!permissions.includes(PermissionFlag.COPY)) {
this.appConfig.viewerContainer.classList.add(ENABLE_PERMISSIONS_CLASS);
}
},

setInitialView(
storedHash,
{ rotation, sidebarView, scrollMode, spreadMode } = {}
Expand Down Expand Up @@ -1979,6 +2005,15 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
};
}

function webViewerResetPermissions() {
const { appConfig } = PDFViewerApplication;
if (!appConfig) {
return;
}
// Currently only the "copy"-permission is supported.
appConfig.viewerContainer.classList.remove(ENABLE_PERMISSIONS_CLASS);
}

function webViewerPageRendered(evt) {
const pageNumber = evt.pageNumber;
const pageIndex = pageNumber - 1;
Expand Down
5 changes: 5 additions & 0 deletions web/app_options.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ const defaultOptions = {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
enablePermissions: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
/**
* The `disablePreferences` is, conditionally, defined below.
*/
Expand Down
5 changes: 5 additions & 0 deletions web/viewer.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ select {
display: none !important;
}

.pdfViewer.enablePermissions .textLayer > span {
user-select: none !important;
cursor: not-allowed;
}

#viewerContainer.pdfPresentationMode:-ms-fullscreen {
top: 0px !important;
overflow: hidden !important;
Expand Down

0 comments on commit 0e4daad

Please sign in to comment.