From 8521f70157769d9691ee41b03d40b663e3c57e16 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Wed, 8 Apr 2020 16:53:31 +0200 Subject: [PATCH] Add a new `pdfjs.enablePermissions` preference, off by default, to allow 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. --- extensions/chromium/preferences_schema.json | 4 +++ web/app.js | 34 +++++++++++++++++++++ web/app_options.js | 5 +++ web/viewer.css | 5 +++ 4 files changed, 48 insertions(+) diff --git a/extensions/chromium/preferences_schema.json b/extensions/chromium/preferences_schema.json index add31dae3..b5a706c58 100644 --- a/extensions/chromium/preferences_schema.json +++ b/extensions/chromium/preferences_schema.json @@ -151,6 +151,10 @@ "type": "boolean", "default": false }, + "enablePermissions": { + "type": "boolean", + "default": false + }, "historyUpdateUrl": { "type": "boolean", "default": false diff --git a/web/app.js b/web/app.js index 135882176..d4cf74c11 100644 --- a/web/app.js +++ b/web/app.js @@ -48,6 +48,7 @@ import { MissingPDFException, OPS, PDFWorker, + PermissionFlag, shadow, UnexpectedResponseException, UNSUPPORTED_FEATURES, @@ -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, @@ -679,6 +681,7 @@ const PDFViewerApplication = { this.pdfLinkService.setDocument(null); this.pdfDocumentProperties.setDocument(null); } + webViewerResetPermissions(); this.store = null; this.isInitialViewSet = false; this.downloadComplete = false; @@ -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, @@ -1467,6 +1474,24 @@ 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 } = {} @@ -1979,6 +2004,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; diff --git a/web/app_options.js b/web/app_options.js index 0f3b5af25..d1e1abd3c 100644 --- a/web/app_options.js +++ b/web/app_options.js @@ -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. */ diff --git a/web/viewer.css b/web/viewer.css index 757d77713..d49e0f6f5 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -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;