diff --git a/extensions/chromium/preferences_schema.json b/extensions/chromium/preferences_schema.json index add31dae3d1fa5..b5a706c58408ea 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 135882176cb9ba..0de3437262f9d6 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,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 } = {} @@ -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; diff --git a/web/app_options.js b/web/app_options.js index 0f3b5af25942fd..d1e1abd3caa02b 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 757d7771341a06..d49e0f6f5871bb 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;