From f19572c4ccbabc4d6c3f0ce7e157d90bfe163e08 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Wed, 14 Dec 2022 16:40:25 +0100 Subject: [PATCH] [GV] Add a viewer for GeckoView --- gulpfile.js | 37 ++++ web/app.js | 292 ++++++++++++++++-------------- web/ui_utils.js | 3 +- web/viewer-geckoview.css | 367 ++++++++++++++++++++++++++++++++++++++ web/viewer-geckoview.html | 96 ++++++++++ web/viewer-geckoview.js | 102 +++++++++++ 6 files changed, 763 insertions(+), 134 deletions(-) create mode 100644 web/viewer-geckoview.css create mode 100644 web/viewer-geckoview.html create mode 100644 web/viewer-geckoview.js diff --git a/gulpfile.js b/gulpfile.js index c252f30ed..aae30d134 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -103,6 +103,7 @@ const DEFINES = Object.freeze({ // The main build targets: GENERIC: false, MOZCENTRAL: false, + GECKOVIEW: false, CHROME: false, MINIFIED: false, COMPONENTS: false, @@ -477,6 +478,25 @@ function createWebBundle(defines, options) { .pipe(replaceNonWebpackImport()); } +function createGVWebBundle(defines, options) { + const viewerOutputName = "viewer-geckoview.js"; + defines = builder.merge(defines, { GECKOVIEW: true }); + + const viewerFileConfig = createWebpackConfig( + defines, + { + filename: viewerOutputName, + }, + { + defaultPreferencesDir: options.defaultPreferencesDir, + } + ); + return gulp + .src("./web/viewer-geckoview.js") + .pipe(webpack2Stream(viewerFileConfig)) + .pipe(replaceNonWebpackImport()); +} + function createComponentsBundle(defines) { const componentsAMDName = "pdfjs-dist/web/pdf_viewer"; const componentsOutputName = "pdf_viewer.js"; @@ -1291,6 +1311,9 @@ gulp.task( createWebBundle(defines, { defaultPreferencesDir: "mozcentral/" }).pipe( gulp.dest(MOZCENTRAL_CONTENT_DIR + "web") ), + createGVWebBundle(defines, { + defaultPreferencesDir: "mozcentral/", + }).pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")), gulp .src(MOZCENTRAL_WEB_FILES, { base: "web/" }) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")), @@ -1304,6 +1327,10 @@ gulp.task( preprocessHTML("web/viewer.html", defines).pipe( gulp.dest(MOZCENTRAL_CONTENT_DIR + "web") ), + preprocessHTML("web/viewer-geckoview.html", defines).pipe( + gulp.dest(MOZCENTRAL_CONTENT_DIR + "web") + ), + preprocessCSS("web/viewer.css", defines) .pipe( postcss([ @@ -1314,6 +1341,16 @@ gulp.task( ) .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")), + preprocessCSS("web/viewer-geckoview.css", defines) + .pipe( + postcss([ + autoprefixer({ + overrideBrowserslist: ["last 1 firefox versions"], + }), + ]) + ) + .pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")), + gulp .src("l10n/en-US/*.properties") .pipe(gulp.dest(MOZCENTRAL_L10N_DIR)), diff --git a/web/app.js b/web/app.js index 2519d96f9..8bd64b036 100644 --- a/web/app.js +++ b/web/app.js @@ -510,15 +510,17 @@ const PDFViewerApplication = { pdfLinkService.setViewer(this.pdfViewer); pdfScriptingManager.setViewer(this.pdfViewer); - this.pdfThumbnailViewer = new PDFThumbnailViewer({ - container: appConfig.sidebar.thumbnailView, - eventBus, - renderingQueue: pdfRenderingQueue, - linkService: pdfLinkService, - l10n: this.l10n, - pageColors, - }); - pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); + if (appConfig.sidebar?.thumbnailView) { + this.pdfThumbnailViewer = new PDFThumbnailViewer({ + container: appConfig.sidebar.thumbnailView, + eventBus, + renderingQueue: pdfRenderingQueue, + linkService: pdfLinkService, + l10n: this.l10n, + pageColors, + }); + pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); + } // The browsing history is only enabled when the viewer is standalone, // i.e. not when it is embedded in a web page. @@ -530,33 +532,37 @@ const PDFViewerApplication = { pdfLinkService.setHistory(this.pdfHistory); } - if (!this.supportsIntegratedFind) { + if (appConfig.findBar && !this.supportsIntegratedFind) { this.findBar = new PDFFindBar(appConfig.findBar, eventBus, this.l10n); } - if (annotationEditorMode !== AnnotationEditorType.DISABLE) { - this.annotationEditorParams = new AnnotationEditorParams( - appConfig.annotationEditorParams, - eventBus - ); - } else { - for (const element of [ - document.getElementById("editorModeButtons"), - document.getElementById("editorModeSeparator"), - ]) { - element.hidden = true; + if (appConfig.annotationEditorParams) { + if (annotationEditorMode !== AnnotationEditorType.DISABLE) { + this.annotationEditorParams = new AnnotationEditorParams( + appConfig.annotationEditorParams, + eventBus + ); + } else { + for (const element of [ + document.getElementById("editorModeButtons"), + document.getElementById("editorModeSeparator"), + ]) { + element.hidden = true; + } } } - this.pdfDocumentProperties = new PDFDocumentProperties( - appConfig.documentProperties, - this.overlayManager, - eventBus, - this.l10n, - /* fileNameLookup = */ () => { - return this._docFilename; - } - ); + if (appConfig.documentProperties) { + this.pdfDocumentProperties = new PDFDocumentProperties( + appConfig.documentProperties, + this.overlayManager, + eventBus, + this.l10n, + /* fileNameLookup = */ () => { + return this._docFilename; + } + ); + } this.pdfCursorTools = new PDFCursorTools({ container, @@ -564,13 +570,17 @@ const PDFViewerApplication = { cursorToolOnLoad: AppOptions.get("cursorToolOnLoad"), }); - this.toolbar = new Toolbar(appConfig.toolbar, eventBus, this.l10n); + if (appConfig.toolbar) { + this.toolbar = new Toolbar(appConfig.toolbar, eventBus, this.l10n); + } - this.secondaryToolbar = new SecondaryToolbar( - appConfig.secondaryToolbar, - eventBus, - this.externalServices - ); + if (appConfig.secondaryToolbar) { + this.secondaryToolbar = new SecondaryToolbar( + appConfig.secondaryToolbar, + eventBus, + this.externalServices + ); + } if (this.supportsFullscreen) { this.pdfPresentationMode = new PDFPresentationMode({ @@ -580,46 +590,56 @@ const PDFViewerApplication = { }); } - this.passwordPrompt = new PasswordPrompt( - appConfig.passwordOverlay, - this.overlayManager, - this.l10n, - this.isViewerEmbedded - ); + if (appConfig.passwordOverlay) { + this.passwordPrompt = new PasswordPrompt( + appConfig.passwordOverlay, + this.overlayManager, + this.l10n, + this.isViewerEmbedded + ); + } - this.pdfOutlineViewer = new PDFOutlineViewer({ - container: appConfig.sidebar.outlineView, - eventBus, - linkService: pdfLinkService, - downloadManager, - }); + if (appConfig.sidebar?.outlineView) { + this.pdfOutlineViewer = new PDFOutlineViewer({ + container: appConfig.sidebar.outlineView, + eventBus, + linkService: pdfLinkService, + downloadManager, + }); + } - this.pdfAttachmentViewer = new PDFAttachmentViewer({ - container: appConfig.sidebar.attachmentsView, - eventBus, - downloadManager, - }); + if (appConfig.sidebar?.attachmentsView) { + this.pdfAttachmentViewer = new PDFAttachmentViewer({ + container: appConfig.sidebar.attachmentsView, + eventBus, + downloadManager, + }); + } - this.pdfLayerViewer = new PDFLayerViewer({ - container: appConfig.sidebar.layersView, - eventBus, - l10n: this.l10n, - }); + if (appConfig.sidebar?.layersView) { + this.pdfLayerViewer = new PDFLayerViewer({ + container: appConfig.sidebar.layersView, + eventBus, + l10n: this.l10n, + }); + } - this.pdfSidebar = new PDFSidebar({ - elements: appConfig.sidebar, - pdfViewer: this.pdfViewer, - pdfThumbnailViewer: this.pdfThumbnailViewer, - eventBus, - l10n: this.l10n, - }); - this.pdfSidebar.onToggled = this.forceRendering.bind(this); + if (appConfig.sidebar) { + this.pdfSidebar = new PDFSidebar({ + elements: appConfig.sidebar, + pdfViewer: this.pdfViewer, + pdfThumbnailViewer: this.pdfThumbnailViewer, + eventBus, + l10n: this.l10n, + }); + this.pdfSidebar.onToggled = this.forceRendering.bind(this); - this.pdfSidebarResizer = new PDFSidebarResizer( - appConfig.sidebarResizer, - eventBus, - this.l10n - ); + this.pdfSidebarResizer = new PDFSidebarResizer( + appConfig.sidebarResizer, + eventBus, + this.l10n + ); + } }, run(config) { @@ -684,7 +704,8 @@ const PDFViewerApplication = { }, get loadingBar() { - const bar = new ProgressBar("loadingBar"); + const barElement = document.getElementById("loadingBar"); + const bar = barElement ? new ProgressBar(barElement) : null; return shadow(this, "loadingBar", bar); }, @@ -816,10 +837,10 @@ const PDFViewerApplication = { if (this.pdfDocument) { this.pdfDocument = null; - this.pdfThumbnailViewer.setDocument(null); + this.pdfThumbnailViewer?.setDocument(null); this.pdfViewer.setDocument(null); this.pdfLinkService.setDocument(null); - this.pdfDocumentProperties.setDocument(null); + this.pdfDocumentProperties?.setDocument(null); } this.pdfLinkService.externalLinkEnabled = true; this.store = null; @@ -838,15 +859,15 @@ const PDFViewerApplication = { promises.push(this.pdfScriptingManager.destroyPromise); this.setTitle(); - this.pdfSidebar.reset(); - this.pdfOutlineViewer.reset(); - this.pdfAttachmentViewer.reset(); - this.pdfLayerViewer.reset(); + this.pdfSidebar?.reset(); + this.pdfOutlineViewer?.reset(); + this.pdfAttachmentViewer?.reset(); + this.pdfLayerViewer?.reset(); this.pdfHistory?.reset(); this.findBar?.reset(); - this.toolbar.reset(); - this.secondaryToolbar.reset(); + this.toolbar?.reset(); + this.secondaryToolbar?.reset(); this._PDFBug?.cleanup(); await Promise.all(promises); @@ -1063,7 +1084,7 @@ const PDFViewerApplication = { }, progress(level) { - if (this.downloadComplete) { + if (!this.loadingBar || this.downloadComplete) { // Don't accidentally show the loading bar again when the entire file has // already been fetched (only an issue when disableAutoFetch is enabled). return; @@ -1108,7 +1129,7 @@ const PDFViewerApplication = { pdfDocument.getDownloadInfo().then(({ length }) => { this._contentLength = length; // Ensure that the correct length is used. this.downloadComplete = true; - this.loadingBar.hide(); + this.loadingBar?.hide(); firstPagePromise.then(() => { this.eventBus.dispatch("documentloaded", { source: this }); @@ -1127,8 +1148,8 @@ const PDFViewerApplication = { /* Avoid breaking initial rendering; ignoring errors. */ }); - this.toolbar.setPagesCount(pdfDocument.numPages, false); - this.secondaryToolbar.setPagesCount(pdfDocument.numPages); + this.toolbar?.setPagesCount(pdfDocument.numPages, false); + this.secondaryToolbar?.setPagesCount(pdfDocument.numPages); let baseDocumentUrl; if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { @@ -1144,14 +1165,13 @@ const PDFViewerApplication = { baseDocumentUrl = null; } this.pdfLinkService.setDocument(pdfDocument, baseDocumentUrl); - this.pdfDocumentProperties.setDocument(pdfDocument); + this.pdfDocumentProperties?.setDocument(pdfDocument); const pdfViewer = this.pdfViewer; pdfViewer.setDocument(pdfDocument); const { firstPagePromise, onePageRendered, pagesPromise } = pdfViewer; - const pdfThumbnailViewer = this.pdfThumbnailViewer; - pdfThumbnailViewer.setDocument(pdfDocument); + this.pdfThumbnailViewer?.setDocument(pdfDocument); const storedPromise = (this.store = new ViewHistory( pdfDocument.fingerprints[0] @@ -1172,7 +1192,7 @@ const PDFViewerApplication = { }); firstPagePromise.then(pdfPage => { - this.loadingBar.setWidth(this.appConfig.viewerContainer); + this.loadingBar?.setWidth(this.appConfig.viewerContainer); this._initializeAnnotationStorageCallbacks(pdfDocument); Promise.all([ @@ -1308,13 +1328,13 @@ const PDFViewerApplication = { if (pdfDocument !== this.pdfDocument) { return; // The document was closed while the outline resolved. } - this.pdfOutlineViewer.render({ outline, pdfDocument }); + this.pdfOutlineViewer?.render({ outline, pdfDocument }); }); pdfDocument.getAttachments().then(attachments => { if (pdfDocument !== this.pdfDocument) { return; // The document was closed while the attachments resolved. } - this.pdfAttachmentViewer.render({ attachments }); + this.pdfAttachmentViewer?.render({ attachments }); }); // Ensure that the layers accurately reflects the current state in the // viewer itself, rather than the default state provided by the API. @@ -1322,7 +1342,7 @@ const PDFViewerApplication = { if (pdfDocument !== this.pdfDocument) { return; // The document was closed while the layers resolved. } - this.pdfLayerViewer.render({ optionalContentConfig, pdfDocument }); + this.pdfLayerViewer?.render({ optionalContentConfig, pdfDocument }); }); }); @@ -1515,12 +1535,12 @@ const PDFViewerApplication = { const { pdfViewer, pdfThumbnailViewer, toolbar } = this; pdfViewer.setPageLabels(labels); - pdfThumbnailViewer.setPageLabels(labels); + pdfThumbnailViewer?.setPageLabels(labels); // Changing toolbar page display to use labels and we need to set // the label of the current page. - toolbar.setPagesCount(numLabels, true); - toolbar.setPageNumber( + toolbar?.setPagesCount(numLabels, true); + toolbar?.setPageNumber( pdfViewer.currentPageNumber, pdfViewer.currentPageLabel ); @@ -1612,7 +1632,7 @@ const PDFViewerApplication = { } }; this.isInitialViewSet = true; - this.pdfSidebar.setInitialView(sidebarView); + this.pdfSidebar?.setInitialView(sidebarView); setViewerModes(scrollMode, spreadMode); @@ -1630,11 +1650,11 @@ const PDFViewerApplication = { // Ensure that the correct page number is displayed in the UI, // even if the active page didn't change during document load. - this.toolbar.setPageNumber( + this.toolbar?.setPageNumber( this.pdfViewer.currentPageNumber, this.pdfViewer.currentPageLabel ); - this.secondaryToolbar.setPageNumber(this.pdfViewer.currentPageNumber); + this.secondaryToolbar?.setPageNumber(this.pdfViewer.currentPageNumber); if (!this.pdfViewer.currentScaleValue) { // Scale was not initialized: invalid bookmark or scale was not specified. @@ -1651,7 +1671,7 @@ const PDFViewerApplication = { return; // run cleanup when document is loaded } this.pdfViewer.cleanup(); - this.pdfThumbnailViewer.cleanup(); + this.pdfThumbnailViewer?.cleanup(); if ( typeof PDFJSDev === "undefined" || @@ -1669,7 +1689,7 @@ const PDFViewerApplication = { forceRendering() { this.pdfRenderingQueue.printing = !!this.printService; this.pdfRenderingQueue.isThumbnailViewEnabled = - this.pdfSidebar.visibleView === SidebarView.THUMBS; + this.pdfSidebar?.visibleView === SidebarView.THUMBS; this.pdfRenderingQueue.renderHighestPriority(); }, @@ -2151,16 +2171,16 @@ function webViewerInitialized() { } if (!PDFViewerApplication.supportsPrinting) { - appConfig.toolbar.print.classList.add("hidden"); - appConfig.secondaryToolbar.printButton.classList.add("hidden"); + appConfig.toolbar?.print.classList.add("hidden"); + appConfig.secondaryToolbar?.printButton.classList.add("hidden"); } - if (!PDFViewerApplication.supportsFullscreen) { + if (appConfig.secondaryToolbar && !PDFViewerApplication.supportsFullscreen) { appConfig.secondaryToolbar.presentationModeButton.hidden = true; } if (PDFViewerApplication.supportsIntegratedFind) { - appConfig.toolbar.viewFind.classList.add("hidden"); + appConfig.toolbar?.viewFind.classList.add("hidden"); } appConfig.mainContainer.addEventListener( @@ -2197,15 +2217,15 @@ function webViewerPageRendered({ pageNumber, error }) { // If the page is still visible when it has finished rendering, // ensure that the page number input loading indicator is hidden. if (pageNumber === PDFViewerApplication.page) { - PDFViewerApplication.toolbar.updateLoadingIndicatorState(false); + PDFViewerApplication.toolbar?.updateLoadingIndicatorState(false); } // Use the rendered page to set the corresponding thumbnail image. - if (PDFViewerApplication.pdfSidebar.visibleView === SidebarView.THUMBS) { + if (PDFViewerApplication.pdfSidebar?.visibleView === SidebarView.THUMBS) { const pageView = PDFViewerApplication.pdfViewer.getPageView( /* index = */ pageNumber - 1 ); - const thumbnailView = PDFViewerApplication.pdfThumbnailViewer.getThumbnail( + const thumbnailView = PDFViewerApplication.pdfThumbnailViewer?.getThumbnail( /* index = */ pageNumber - 1 ); if (pageView && thumbnailView) { @@ -2244,7 +2264,7 @@ function webViewerPageMode({ mode }) { console.error('Invalid "pagemode" hash parameter: ' + mode); return; } - PDFViewerApplication.pdfSidebar.switchView(view, /* forceOpen = */ true); + PDFViewerApplication.pdfSidebar?.switchView(view, /* forceOpen = */ true); } function webViewerNamedAction(evt) { @@ -2252,12 +2272,12 @@ function webViewerNamedAction(evt) { // `PDFLinkService.executeNamedAction`. switch (evt.action) { case "GoToPage": - PDFViewerApplication.appConfig.toolbar.pageNumber.select(); + PDFViewerApplication.appConfig.toolbar?.pageNumber.select(); break; case "Find": if (!PDFViewerApplication.supportsIntegratedFind) { - PDFViewerApplication.findBar.toggle(); + PDFViewerApplication?.findBar.toggle(); } break; @@ -2305,15 +2325,17 @@ function webViewerUpdateViewarea({ location }) { const href = PDFViewerApplication.pdfLinkService.getAnchorUrl( location.pdfOpenParams ); - PDFViewerApplication.appConfig.secondaryToolbar.viewBookmarkButton.href = - href; + if (PDFViewerApplication.appConfig.secondaryToolbar) { + PDFViewerApplication.appConfig.secondaryToolbar.viewBookmarkButton.href = + href; + } // Show/hide the loading indicator in the page number input element. const currentPage = PDFViewerApplication.pdfViewer.getPageView( /* index = */ PDFViewerApplication.page - 1 ); const loading = currentPage?.renderingState !== RenderingStates.FINISHED; - PDFViewerApplication.toolbar.updateLoadingIndicatorState(loading); + PDFViewerApplication.toolbar?.updateLoadingIndicatorState(loading); } function webViewerScrollModeChanged(evt) { @@ -2452,7 +2474,7 @@ function webViewerPageNumberChanged(evt) { evt.value !== pdfViewer.currentPageNumber.toString() && evt.value !== pdfViewer.currentPageLabel ) { - PDFViewerApplication.toolbar.setPageNumber( + PDFViewerApplication.toolbar?.setPageNumber( pdfViewer.currentPageNumber, pdfViewer.currentPageLabel ); @@ -2477,7 +2499,7 @@ function webViewerSwitchSpreadMode(evt) { PDFViewerApplication.pdfViewer.spreadMode = evt.mode; } function webViewerDocumentProperties() { - PDFViewerApplication.pdfDocumentProperties.open(); + PDFViewerApplication.pdfDocumentProperties?.open(); } function webViewerFindFromUrlHash(evt) { @@ -2516,18 +2538,20 @@ function webViewerUpdateFindControlState({ rawQuery, }); } else { - PDFViewerApplication.findBar.updateUIState(state, previous, matchesCount); + PDFViewerApplication.findBar?.updateUIState(state, previous, matchesCount); } } function webViewerScaleChanging(evt) { - PDFViewerApplication.toolbar.setPageScale(evt.presetValue, evt.scale); + PDFViewerApplication.toolbar?.setPageScale(evt.presetValue, evt.scale); PDFViewerApplication.pdfViewer.update(); } function webViewerRotationChanging(evt) { - PDFViewerApplication.pdfThumbnailViewer.pagesRotation = evt.pagesRotation; + if (PDFViewerApplication.pdfThumbnailViewer) { + PDFViewerApplication.pdfThumbnailViewer.pagesRotation = evt.pagesRotation; + } PDFViewerApplication.forceRendering(); // Ensure that the active page doesn't change during rotation. @@ -2535,11 +2559,13 @@ function webViewerRotationChanging(evt) { } function webViewerPageChanging({ pageNumber, pageLabel }) { - PDFViewerApplication.toolbar.setPageNumber(pageNumber, pageLabel); - PDFViewerApplication.secondaryToolbar.setPageNumber(pageNumber); + PDFViewerApplication.toolbar?.setPageNumber(pageNumber, pageLabel); + PDFViewerApplication.secondaryToolbar?.setPageNumber(pageNumber); - if (PDFViewerApplication.pdfSidebar.visibleView === SidebarView.THUMBS) { - PDFViewerApplication.pdfThumbnailViewer.scrollThumbnailIntoView(pageNumber); + if (PDFViewerApplication.pdfSidebar?.visibleView === SidebarView.THUMBS) { + PDFViewerApplication.pdfThumbnailViewer?.scrollThumbnailIntoView( + pageNumber + ); } } @@ -2652,14 +2678,14 @@ function webViewerTouchStart(evt) { } function webViewerClick(evt) { - if (!PDFViewerApplication.secondaryToolbar.isOpen) { + if (!PDFViewerApplication.secondaryToolbar?.isOpen) { return; } const appConfig = PDFViewerApplication.appConfig; if ( PDFViewerApplication.pdfViewer.containsElement(evt.target) || - (appConfig.toolbar.container.contains(evt.target) && - evt.target !== appConfig.secondaryToolbar.toggleButton) + (appConfig.toolbar?.container.contains(evt.target) && + evt.target !== appConfig.secondaryToolbar?.toggleButton) ) { PDFViewerApplication.secondaryToolbar.close(); } @@ -2687,7 +2713,7 @@ function webViewerKeyDown(evt) { switch (evt.keyCode) { case 70: // f if (!PDFViewerApplication.supportsIntegratedFind && !evt.shiftKey) { - PDFViewerApplication.findBar.open(); + PDFViewerApplication.findBar?.open(); handled = true; } break; @@ -2786,8 +2812,10 @@ function webViewerKeyDown(evt) { break; case 71: // g // focuses input#pageNumber field - PDFViewerApplication.appConfig.toolbar.pageNumber.select(); - handled = true; + if (PDFViewerApplication.appConfig.toolbar) { + PDFViewerApplication.appConfig.toolbar.pageNumber.select(); + handled = true; + } break; } } @@ -2846,13 +2874,13 @@ function webViewerKeyDown(evt) { turnPage = -1; break; case 27: // esc key - if (PDFViewerApplication.secondaryToolbar.isOpen) { + if (PDFViewerApplication.secondaryToolbar?.isOpen) { PDFViewerApplication.secondaryToolbar.close(); handled = true; } if ( !PDFViewerApplication.supportsIntegratedFind && - PDFViewerApplication.findBar.opened + PDFViewerApplication.findBar?.opened ) { PDFViewerApplication.findBar.close(); handled = true; @@ -2903,10 +2931,10 @@ function webViewerKeyDown(evt) { break; case 83: // 's' - PDFViewerApplication.pdfCursorTools.switchTool(CursorTool.SELECT); + PDFViewerApplication.pdfCursorTools?.switchTool(CursorTool.SELECT); break; case 72: // 'h' - PDFViewerApplication.pdfCursorTools.switchTool(CursorTool.HAND); + PDFViewerApplication.pdfCursorTools?.switchTool(CursorTool.HAND); break; case 82: // 'r' @@ -2914,7 +2942,7 @@ function webViewerKeyDown(evt) { break; case 115: // F4 - PDFViewerApplication.pdfSidebar.toggle(); + PDFViewerApplication.pdfSidebar?.toggle(); break; } diff --git a/web/ui_utils.js b/web/ui_utils.js index 6a5f84391..7bc602a99 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -697,8 +697,7 @@ class ProgressBar { #visible = true; - constructor(id) { - const bar = document.getElementById(id); + constructor(bar) { this.#classList = bar.classList; } diff --git a/web/viewer-geckoview.css b/web/viewer-geckoview.css new file mode 100644 index 000000000..342860c1e --- /dev/null +++ b/web/viewer-geckoview.css @@ -0,0 +1,367 @@ +/* Copyright 2014 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import url(pdf_viewer.css); + +:root { + --dir-factor: 1; + --scale-select-width: 140px; + + --toolbar-icon-opacity: 0.7; + --doorhanger-icon-opacity: 0.9; + + --main-color: rgba(12, 12, 13, 1); + --body-bg-color: rgba(237, 237, 240, 1); + --scrollbar-color: auto; + --scrollbar-bg-color: auto; + --dialog-button-border: none; + --dialog-button-bg-color: rgba(12, 12, 13, 0.1); + --dialog-button-hover-bg-color: rgba(12, 12, 13, 0.3); +} + +:root:dir(rtl) { + --dir-factor: -1; +} + +@media (prefers-color-scheme: dark) { + :root { + --main-color: rgba(249, 249, 250, 1); + --body-bg-color: rgba(42, 42, 46, 1); + --progressBar-color: rgba(0, 96, 223, 1); + --progressBar-indeterminate-bg-color: rgba(40, 40, 43, 1); + --progressBar-indeterminate-blend-color: rgba(20, 68, 133, 1); + --scrollbar-color: rgba(121, 121, 123, 1); + --scrollbar-bg-color: rgba(35, 35, 39, 1); + + --dialog-button-bg-color: rgba(92, 92, 97, 1); + --dialog-button-hover-bg-color: rgba(115, 115, 115, 1); + } +} + +@media screen and (forced-colors: active) { + :root { + --dialog-button-border: 1px solid Highlight; + --dialog-button-hover-bg-color: Highlight; + --dialog-button-hover-color: ButtonFace; + --main-color: CanvasText; + } +} + +* { + padding: 0; + margin: 0; +} + +html, +body { + height: 100%; + width: 100%; +} + +body { + background-color: var(--body-bg-color); + scrollbar-color: var(--scrollbar-color) var(--scrollbar-bg-color); +} + +.hidden, +[hidden] { + display: none !important; +} + +#outerContainer { + width: 100%; + height: 100%; + position: relative; +} + +#mainContainer { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + min-width: 350px; +} + +#viewerContainer { + overflow: auto; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + outline: none; +} +#viewerContainer { + transition-duration: var(--sidebar-transition-duration); + transition-timing-function: var(--sidebar-transition-timing-function); +} + +.dialogButton { + border: none; + background: none; + width: 28px; + height: 28px; + outline: none; +} + +.dialogButton:hover, +.dialogButton:focus-visible { + background-color: var(--dialog-button-hover-bg-color); +} + +.dialogButton:hover > span, +.dialogButton:focus-visible > span { + color: var(--dialog-button-hover-color); +} + +.dialogButton[disabled] { + opacity: 0.5; +} + +.dialogButton { + min-width: 16px; + margin: 2px 1px; + padding: 2px 6px 0; + border: none; + border-radius: 2px; + color: var(--main-color); + font-size: 12px; + line-height: 14px; + user-select: none; + cursor: default; + box-sizing: border-box; +} + +.toolbarField { + padding: 4px 7px; + margin: 3px 0; + border-radius: 2px; + background-color: var(--field-bg-color); + background-clip: padding-box; + border: 1px solid var(--field-border-color); + box-shadow: none; + color: var(--field-color); + font-size: 12px; + line-height: 16px; + outline: none; +} + +.toolbarField:focus { + border-color: #0a84ff; +} + +.dialogButton { + width: auto; + margin: 3px 4px 2px !important; + padding: 2px 11px; + color: var(--main-color); + background-color: var(--dialog-button-bg-color); + border: var(--dialog-button-border) !important; +} + +dialog { + margin: auto; + padding: 15px; + border-spacing: 4px; + color: var(--main-color); + font: message-box; + font-size: 12px; + line-height: 14px; + background-color: var(--doorhanger-bg-color); + border: 1px solid rgba(0, 0, 0, 0.5); + border-radius: 4px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); +} +dialog::backdrop { + background-color: rgba(0, 0, 0, 0.2); +} + +dialog > .row { + display: table-row; +} + +dialog > .row > * { + display: table-cell; +} + +dialog .toolbarField { + margin: 5px 0; +} + +dialog .separator { + display: block; + margin: 4px 0; + height: 1px; + width: 100%; + background-color: var(--separator-color); +} + +dialog .buttonRow { + text-align: center; + vertical-align: middle; +} + +dialog :link { + color: rgba(255, 255, 255, 1); +} + +#passwordDialog { + text-align: center; +} +#passwordDialog .toolbarField { + width: 200px; +} + +.grab-to-pan-grab { + cursor: grab !important; +} +.grab-to-pan-grab + *:not(input):not(textarea):not(button):not(select):not(:link) { + cursor: inherit !important; +} +.grab-to-pan-grab:active, +.grab-to-pan-grabbing { + cursor: grabbing !important; + position: fixed; + background: rgba(0, 0, 0, 0); + display: block; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; + z-index: 50000; /* should be higher than anything else in PDF.js! */ +} + +@page { + margin: 0; +} + +#printContainer { + display: none; +} + +@media print { + body { + background: rgba(0, 0, 0, 0) none; + } + body[data-pdfjsprinting] #outerContainer { + display: none; + } + body[data-pdfjsprinting] #printContainer { + display: block; + } + #printContainer { + height: 100%; + } + /* wrapper around (scaled) print canvas elements */ + #printContainer > .printedPage { + page-break-after: always; + page-break-inside: avoid; + + /* The wrapper always cover the whole page. */ + height: 100%; + width: 100%; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + + #printContainer > .xfaPrintedPage .xfaPage { + position: absolute; + } + + #printContainer > .xfaPrintedPage { + page-break-after: always; + page-break-inside: avoid; + width: 100%; + height: 100%; + position: relative; + } + + #printContainer > .printedPage canvas, + #printContainer > .printedPage img { + /* The intrinsic canvas / image size will make sure that we fit the page. */ + max-width: 100%; + max-height: 100%; + + direction: ltr; + display: block; + } +} + +.visibleLargeView, +.visibleMediumView { + display: none; +} + +@media all and (max-width: 900px) { + #toolbarViewerMiddle { + display: table; + margin: auto; + left: auto; + position: inherit; + transform: none; + } +} + +@media all and (max-width: 840px) { + #sidebarContainer { + background-color: var(--sidebar-narrow-bg-color); + } + #outerContainer.sidebarOpen #viewerContainer { + inset-inline-start: 0 !important; + } +} + +@media all and (max-width: 820px) { + #outerContainer .hiddenLargeView { + display: none; + } + #outerContainer .visibleLargeView { + display: inherit; + } +} + +@media all and (max-width: 750px) { + #outerContainer .hiddenMediumView { + display: none; + } + #outerContainer .visibleMediumView { + display: inherit; + } +} + +@media all and (max-width: 690px) { + .hiddenSmallView, + .hiddenSmallView * { + display: none; + } + .toolbarButtonSpacer { + width: 0; + } + .findbar { + inset-inline-start: 34px; + } +} + +@media all and (max-width: 560px) { + #scaleSelectContainer { + display: none; + } +} diff --git a/web/viewer-geckoview.html b/web/viewer-geckoview.html new file mode 100644 index 000000000..312a626ba --- /dev/null +++ b/web/viewer-geckoview.html @@ -0,0 +1,96 @@ + + + + + + + PDF.js viewer + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+ + +
+
+
+ +
+
+ + + + + + + diff --git a/web/viewer-geckoview.js b/web/viewer-geckoview.js new file mode 100644 index 000000000..d14aaee0e --- /dev/null +++ b/web/viewer-geckoview.js @@ -0,0 +1,102 @@ +/* Copyright 2016 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RenderingStates, ScrollMode, SpreadMode } from "./ui_utils.js"; +import { AppOptions } from "./app_options.js"; +import { LinkTarget } from "./pdf_link_service.js"; +import { PDFViewerApplication } from "./app.js"; + +/* eslint-disable-next-line no-unused-vars */ +const pdfjsVersion = + typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_VERSION") : void 0; +/* eslint-disable-next-line no-unused-vars */ +const pdfjsBuild = + typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_BUILD") : void 0; + +const AppConstants = + typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC") + ? { LinkTarget, RenderingStates, ScrollMode, SpreadMode } + : null; + +window.PDFViewerApplication = PDFViewerApplication; +window.PDFViewerApplicationConstants = AppConstants; +window.PDFViewerApplicationOptions = AppOptions; + +if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { + require("./firefoxcom.js"); + require("./firefox_print_service.js"); +} + +function getViewerConfiguration() { + return { + appContainer: document.body, + mainContainer: document.getElementById("viewerContainer"), + viewerContainer: document.getElementById("viewer"), + + passwordOverlay: { + dialog: document.getElementById("passwordDialog"), + label: document.getElementById("passwordText"), + input: document.getElementById("password"), + submitButton: document.getElementById("passwordSubmit"), + cancelButton: document.getElementById("passwordCancel"), + }, + printContainer: document.getElementById("printContainer"), + openFileInput: + typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC") + ? document.getElementById("fileInput") + : null, + }; +} + +function webViewerLoad() { + const config = getViewerConfiguration(); + if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) { + if (window.chrome) { + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = "../build/dev-css/viewer.css"; + + document.head.append(link); + } + + Promise.all([ + import("pdfjs-web/genericcom.js"), + import("pdfjs-web/pdf_print_service.js"), + ]).then(function ([genericCom, pdfPrintService]) { + PDFViewerApplication.run(config); + }); + } else { + PDFViewerApplication.run(config); + } +} + +// Block the "load" event until all pages are loaded, to ensure that printing +// works in Firefox; see https://bugzilla.mozilla.org/show_bug.cgi?id=1618553 +document.blockUnblockOnload?.(true); + +if ( + document.readyState === "interactive" || + document.readyState === "complete" +) { + webViewerLoad(); +} else { + document.addEventListener("DOMContentLoaded", webViewerLoad, true); +} + +export { + PDFViewerApplication, + AppConstants as PDFViewerApplicationConstants, + AppOptions as PDFViewerApplicationOptions, +};