diff --git a/browser/locales/en-US/pdfviewer/viewer.properties b/browser/locales/en-US/pdfviewer/viewer.properties --- a/browser/locales/en-US/pdfviewer/viewer.properties +++ b/browser/locales/en-US/pdfviewer/viewer.properties @@ -205,11 +205,10 @@ # LOCALIZATION NOTE (page_scale_percent): "{{scale}}" will be replaced by a # numerical scale value. page_scale_percent={{scale}}% # Loading indicator messages -loading=Loading… loading_error=An error occurred while loading the PDF. invalid_file_error=Invalid or corrupted PDF file. missing_file_error=Missing PDF file. unexpected_response_error=Unexpected server response. rendering_error=An error occurred while rendering the page. diff --git a/toolkit/components/pdfjs/content/build/pdf.js b/toolkit/components/pdfjs/content/build/pdf.js --- a/toolkit/components/pdfjs/content/build/pdf.js +++ b/toolkit/components/pdfjs/content/build/pdf.js @@ -894,47 +894,32 @@ exports.DefaultStandardFontDataFactory = DefaultStandardFontDataFactory; ; let createPDFNetworkStream; ; function getDocument(src) { + if (typeof src !== "object") { + throw new Error("Invalid parameter in getDocument, need parameter object."); + } + if (!src.url && !src.data && !src.range) { + throw new Error("Invalid parameter object: need either .data, .range or .url"); + } const task = new PDFDocumentLoadingTask(); - let source; - if (typeof src === "string" || src instanceof URL) { - source = { - url: src - }; - } else if ((0, _util.isArrayBuffer)(src)) { - source = { - data: src - }; - } else { - if (typeof src !== "object") { - throw new Error("Invalid parameter in getDocument, " + "need either string, URL, TypedArray, or parameter object."); - } - if (!src.url && !src.data && !src.range) { - throw new Error("Invalid parameter object: need either .data, .range or .url"); - } - source = src; - } const params = Object.create(null); let rangeTransport = null, worker = null; - for (const key in source) { - const val = source[key]; + for (const key in src) { + const val = src[key]; switch (key) { case "url": - if (typeof window !== "undefined") { - try { - params[key] = new URL(val, window.location).href; - continue; - } catch (ex) { - (0, _util.warn)(`Cannot create valid URL: "${ex}".`); - } - } else if (typeof val === "string" || val instanceof URL) { - params[key] = val.toString(); + if (val instanceof URL) { + params[key] = val.href; continue; } + try { + params[key] = new URL(val, window.location).href; + continue; + } catch (ex) {} throw new Error("Invalid PDF url data: " + "either string or URL-object is expected in the url property."); case "range": rangeTransport = val; continue; case "worker": @@ -1056,11 +1041,11 @@ source.contentDispositionFilename = pdfDataRangeTransport.contentDispositionFilename; } const transfers = source.data ? [source.data.buffer] : null; const workerId = await worker.messageHandler.sendWithPromise("GetDocRequest", { docId, - apiVersion: '3.3.97', + apiVersion: '3.4.14', data: source.data, password: source.password, disableAutoFetch: source.disableAutoFetch, rangeChunkSize: source.rangeChunkSize, length: source.length, @@ -2655,13 +2640,13 @@ this.callback(); } } } } -const version = '3.3.97'; +const version = '3.4.14'; exports.version = version; -const build = 'edfdb693e'; +const build = '9261daa23'; exports.build = build; /***/ }), /* 3 */ /***/ ((__unused_webpack_module, exports, __w_pdfjs_require__) => { @@ -4684,11 +4669,10 @@ exports.CanvasGraphics = void 0; var _util = __w_pdfjs_require__(1); var _display_utils = __w_pdfjs_require__(6); var _pattern_helper = __w_pdfjs_require__(12); var _image_utils = __w_pdfjs_require__(13); -var _is_node = __w_pdfjs_require__(10); const MIN_FONT_SIZE = 16; const MAX_FONT_SIZE = 100; const MAX_GROUP_SIZE = 4096; const EXECUTION_TIME = 15; const EXECUTION_STEPS = 10; @@ -6923,19 +6907,18 @@ })); exports.TilingPattern = exports.PathType = void 0; exports.getShadingPattern = getShadingPattern; var _util = __w_pdfjs_require__(1); var _display_utils = __w_pdfjs_require__(6); -var _is_node = __w_pdfjs_require__(10); const PathType = { FILL: "Fill", STROKE: "Stroke", SHADING: "Shading" }; exports.PathType = PathType; function applyBoundingBox(ctx, bbox) { - if (!bbox || _is_node.isNodeJS) { + if (!bbox) { return; } const width = bbox[2] - bbox[0]; const height = bbox[3] - bbox[1]; const region = new Path2D(); @@ -7434,12 +7417,12 @@ value: true })); exports.GlobalWorkerOptions = void 0; const GlobalWorkerOptions = Object.create(null); exports.GlobalWorkerOptions = GlobalWorkerOptions; -GlobalWorkerOptions.workerPort = GlobalWorkerOptions.workerPort === undefined ? null : GlobalWorkerOptions.workerPort; -GlobalWorkerOptions.workerSrc = GlobalWorkerOptions.workerSrc === undefined ? "" : GlobalWorkerOptions.workerSrc; +GlobalWorkerOptions.workerPort = null; +GlobalWorkerOptions.workerSrc = ""; /***/ }), /* 15 */ /***/ ((__unused_webpack_module, exports, __w_pdfjs_require__) => { @@ -8819,10 +8802,13 @@ this.pageIndex = options.pageIndex; this.div = options.div; this.#accessibilityManager = options.accessibilityManager; this.#uiManager.addLayer(this); } + get isEmpty() { + return this.#editors.size === 0; + } updateToolbar(mode) { this.#uiManager.updateToolbar(mode); } updateMode(mode = this.#uiManager.getMode()) { this.#cleanup(); @@ -8831,12 +8817,15 @@ this.disableClick(); } else { this.enableClick(); } this.#uiManager.unselectAll(); - this.div.classList.toggle("freeTextEditing", mode === _util.AnnotationEditorType.FREETEXT); - this.div.classList.toggle("inkEditing", mode === _util.AnnotationEditorType.INK); + if (mode !== _util.AnnotationEditorType.NONE) { + this.div.classList.toggle("freeTextEditing", mode === _util.AnnotationEditorType.FREETEXT); + this.div.classList.toggle("inkEditing", mode === _util.AnnotationEditorType.INK); + this.div.hidden = false; + } } addInkEditorIfNeeded(isCommitting) { if (!isCommitting && this.#uiManager.getMode() !== _util.AnnotationEditorType.INK) { return; } @@ -8869,10 +8858,14 @@ disable() { this.div.style.pointerEvents = "none"; for (const editor of this.#editors.values()) { editor.disableEditing(); } + this.#cleanup(); + if (this.isEmpty) { + this.div.hidden = true; + } } setActiveEditor(editor) { const currentActive = this.#uiManager.getActive(); if (currentActive === editor) { return; @@ -13056,13 +13049,13 @@ var _tools = __w_pdfjs_require__(5); var _annotation_layer = __w_pdfjs_require__(26); var _worker_options = __w_pdfjs_require__(14); var _svg = __w_pdfjs_require__(29); var _xfa_layer = __w_pdfjs_require__(28); -const pdfjsVersion = '3.3.97'; -const pdfjsBuild = 'edfdb693e'; +const pdfjsVersion = '3.4.14'; +const pdfjsBuild = '9261daa23'; })(); /******/ return __webpack_exports__; /******/ })() ; }); \ No newline at end of file diff --git a/toolkit/components/pdfjs/content/build/pdf.scripting.js b/toolkit/components/pdfjs/content/build/pdf.scripting.js --- a/toolkit/components/pdfjs/content/build/pdf.scripting.js +++ b/toolkit/components/pdfjs/content/build/pdf.scripting.js @@ -580,24 +580,34 @@ if (value === "") { this._value = ""; } else if (typeof value === "string") { switch (this._fieldType) { case _common.FieldType.none: - this._value = !isNaN(value) ? parseFloat(value) : value; - break; + { + this._originalValue = value; + const _value = value.trim().replace(",", "."); + this._value = !isNaN(_value) ? parseFloat(_value) : value; + break; + } case _common.FieldType.number: case _common.FieldType.percent: - const number = parseFloat(value); - this._value = !isNaN(number) ? number : 0; - break; + { + const _value = value.trim().replace(",", "."); + const number = parseFloat(_value); + this._value = !isNaN(number) ? number : 0; + break; + } default: this._value = value; } } else { this._value = value; } } + _getValue() { + return this._originalValue ?? this.value; + } _setChoiceValue(value) { if (this.multipleSelection) { if (!Array.isArray(value)) { value = [value]; } @@ -2391,11 +2401,12 @@ runValidation(source, event) { const didValidateRun = this.runActions(source, source, event, "Validate"); if (event.rc) { source.obj.value = event.value; this.runCalculate(source, event); - const savedValue = event.value = source.obj.value; + const savedValue = source.obj._getValue(); + event.value = source.obj.value; let formattedValue = null; if (this.runActions(source, source, event, "Format")) { formattedValue = event.value?.toString?.(); } source.obj._send({ @@ -3655,11 +3666,11 @@ obj[prop] = value; if (!this.nosend.has(prop) && obj._send && obj._id !== null && typeof old !== "function") { const data = { id: obj._id }; - data[prop] = obj[prop]; + data[prop] = prop === "value" ? obj._getValue() : obj[prop]; if (!obj._siblings) { obj._send(data); } else { data.siblings = obj._siblings; obj._send(data); @@ -4245,13 +4256,13 @@ get: function () { return _initialization.initSandbox; } })); var _initialization = __w_pdfjs_require__(1); -const pdfjsVersion = '3.3.97'; -const pdfjsBuild = 'edfdb693e'; +const pdfjsVersion = '3.4.14'; +const pdfjsBuild = '9261daa23'; })(); /******/ return __webpack_exports__; /******/ })() ; }); \ No newline at end of file diff --git a/toolkit/components/pdfjs/content/build/pdf.worker.js b/toolkit/components/pdfjs/content/build/pdf.worker.js --- a/toolkit/components/pdfjs/content/build/pdf.worker.js +++ b/toolkit/components/pdfjs/content/build/pdf.worker.js @@ -99,11 +99,11 @@ const verbosity = (0, _util.getVerbosityLevel)(); const { docId, apiVersion } = docParams; - const workerVersion = '3.3.97'; + const workerVersion = '3.4.14'; if (apiVersion !== workerVersion) { throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`); } const workerHandlerName = docId + "_worker"; let handler = new _message_handler.MessageHandler(workerHandlerName, docId, port); @@ -25603,10 +25603,11 @@ t[180] = 8221; t[181] = 8216; t[182] = 8217; t[200] = 193; t[203] = 205; + t[207] = 211; t[210] = 218; t[223] = 711; t[224] = 321; t[225] = 322; t[226] = 352; @@ -52415,13 +52416,13 @@ get: function () { return _worker.WorkerMessageHandler; } })); var _worker = __w_pdfjs_require__(1); -const pdfjsVersion = '3.3.97'; -const pdfjsBuild = 'edfdb693e'; +const pdfjsVersion = '3.4.14'; +const pdfjsBuild = '9261daa23'; })(); /******/ return __webpack_exports__; /******/ })() ; }); \ No newline at end of file diff --git a/toolkit/components/pdfjs/content/web/viewer-geckoview.css b/toolkit/components/pdfjs/content/web/viewer-geckoview.css --- a/toolkit/components/pdfjs/content/web/viewer-geckoview.css +++ b/toolkit/components/pdfjs/content/web/viewer-geckoview.css @@ -957,19 +957,10 @@ .pdfViewer.removePageBorders .page { margin: 0 auto 10px; border: none; } -.pdfViewer.singlePageView { - display: inline-block; -} - -.pdfViewer.singlePageView .page { - margin: 0; - border: none; -} - .pdfViewer.scrollHorizontal, .pdfViewer.scrollWrapped, .spread { margin-left: 3.5px; margin-right: 3.5px; @@ -1024,26 +1015,32 @@ .pdfViewer .page canvas[zooming] { width: 100%; height: 100%; } -.pdfViewer .page .loadingIcon { +.pdfViewer .page.loadingIcon:after { position: absolute; - display: block; - left: 0; top: 0; - right: 0; - bottom: 0; + left: 0; + content: ""; + width: 100%; + height: 100%; background: url("images/loading-icon.gif") center no-repeat; - visibility: visible; + visibility: hidden; /* Using a delay with background-image doesn't work, consequently we use the visibility. */ transition-property: visibility; transition-delay: var(--loading-icon-delay); z-index: 5; + contain: strict; } -.pdfViewer .page .loadingIcon.notVisible { + +.pdfViewer .page.loading:after { + visibility: visible; +} + +.pdfViewer .page:not(.loading):after { transition-property: none; visibility: hidden; } .pdfViewer.enablePermissions .textLayer span { diff --git a/toolkit/components/pdfjs/content/web/viewer-geckoview.js b/toolkit/components/pdfjs/content/web/viewer-geckoview.js --- a/toolkit/components/pdfjs/content/web/viewer-geckoview.js +++ b/toolkit/components/pdfjs/content/web/viewer-geckoview.js @@ -396,18 +396,17 @@ delta = -delta; } return delta; } function normalizeWheelEventDelta(evt) { + const deltaMode = evt.deltaMode; let delta = normalizeWheelEventDirection(evt); - const MOUSE_DOM_DELTA_PIXEL_MODE = 0; - const MOUSE_DOM_DELTA_LINE_MODE = 1; const MOUSE_PIXELS_PER_LINE = 30; const MOUSE_LINES_PER_PAGE = 30; - if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) { + if (deltaMode === WheelEvent.DOM_DELTA_PIXEL) { delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE; - } else if (evt.deltaMode === MOUSE_DOM_DELTA_LINE_MODE) { + } else if (deltaMode === WheelEvent.DOM_DELTA_LINE) { delta /= MOUSE_LINES_PER_PAGE; } return delta; } function isValidRotation(angle) { @@ -433,13 +432,15 @@ } class ProgressBar { #classList = null; #disableAutoFetchTimeout = null; #percent = 0; + #style = null; #visible = true; constructor(bar) { this.#classList = bar.classList; + this.#style = bar.style; } get percent() { return this.#percent; } set percent(val) { @@ -447,20 +448,20 @@ if (isNaN(val)) { this.#classList.add("indeterminate"); return; } this.#classList.remove("indeterminate"); - docStyle.setProperty("--progressBar-percent", `${this.#percent}%`); + this.#style.setProperty("--progressBar-percent", `${this.#percent}%`); } setWidth(viewer) { if (!viewer) { return; } const container = viewer.parentNode; const scrollbarWidth = container.offsetWidth - viewer.offsetWidth; if (scrollbarWidth > 0) { - docStyle.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`); + this.#style.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`); } } setDisableAutoFetch(delay = 5000) { if (isNaN(this.#percent)) { return; @@ -1757,30 +1758,30 @@ return this.externalServices.supportedMouseWheelZoomModifierKeys; }, initPassiveLoading() { this.externalServices.initPassiveLoading({ onOpenWithTransport: (url, length, transport) => { - this.open(url, { + this.open({ + url, length, range: transport }); }, onOpenWithData: (data, contentDispositionFilename) => { if ((0, _pdfjsLib.isPdfFile)(contentDispositionFilename)) { this._contentDispositionFilename = contentDispositionFilename; } - this.open(data); + this.open({ + data + }); }, onOpenWithURL: (url, length, originalUrl) => { - const file = originalUrl !== undefined ? { + this.open({ url, + length, originalUrl - } : url; - const args = length !== undefined ? { - length - } : null; - this.open(file, args); + }); }, onError: err => { this.l10n.get("loading_error").then(msg => { this._documentError(msg, err); }); @@ -1869,40 +1870,29 @@ this.toolbar?.reset(); this.secondaryToolbar?.reset(); this._PDFBug?.cleanup(); await Promise.all(promises); }, - async open(file, args) { + async open(args) { if (this.pdfLoadingTask) { await this.close(); } const workerParameters = _app_options.AppOptions.getAll(_app_options.OptionKind.WORKER); for (const key in workerParameters) { _pdfjsLib.GlobalWorkerOptions[key] = workerParameters[key]; } const parameters = Object.create(null); - if (typeof file === "string") { - this.setTitleUsingUrl(file, file); - parameters.url = file; - } else if (file && "byteLength" in file) { - parameters.data = file; - } else if (file.url && file.originalUrl) { - this.setTitleUsingUrl(file.originalUrl, file.url); - parameters.url = file.url; - } const apiParameters = _app_options.AppOptions.getAll(_app_options.OptionKind.API); for (const key in apiParameters) { let value = apiParameters[key]; - if (key === "docBaseUrl" && !value) { - value = this.baseUrl; + if (key === "docBaseUrl") { + value ||= this.baseUrl; } parameters[key] = value; } - if (args) { - for (const key in args) { - parameters[key] = args[key]; - } + for (const key in args) { + parameters[key] = args[key]; } const loadingTask = (0, _pdfjsLib.getDocument)(parameters); this.pdfLoadingTask = loadingTask; loadingTask.onPassword = (updateCallback, reason) => { if (this.isViewerEmbedded) { @@ -3016,29 +3006,29 @@ supportsPinchToZoom } = PDFViewerApplication; if (pdfViewer.isInPresentationMode) { return; } - const isPinchToZoom = evt.ctrlKey && !PDFViewerApplication._isCtrlKeyDown; + const deltaMode = evt.deltaMode; + let scaleFactor = Math.exp(-evt.deltaY / 100); + const isPinchToZoom = evt.ctrlKey && !PDFViewerApplication._isCtrlKeyDown && deltaMode === WheelEvent.DOM_DELTA_PIXEL && evt.deltaX === 0 && Math.abs(scaleFactor - 1) < 0.05 && evt.deltaZ === 0; if (isPinchToZoom || evt.ctrlKey && supportedMouseWheelZoomModifierKeys.ctrlKey || evt.metaKey && supportedMouseWheelZoomModifierKeys.metaKey) { evt.preventDefault(); if (zoomDisabledTimeout || document.visibilityState === "hidden") { return; } const previousScale = pdfViewer.currentScale; if (isPinchToZoom && supportsPinchToZoom) { - let scaleFactor = Math.exp(-evt.deltaY / 100); scaleFactor = PDFViewerApplication._accumulateFactor(previousScale, scaleFactor, "_wheelUnusedFactor"); if (scaleFactor < 1) { PDFViewerApplication.zoomOut(null, scaleFactor); } else if (scaleFactor > 1) { PDFViewerApplication.zoomIn(null, scaleFactor); } else { return; } } else { - const deltaMode = evt.deltaMode; const delta = (0, _ui_utils.normalizeWheelEventDirection)(evt); let ticks = 0; if (deltaMode === WheelEvent.DOM_DELTA_LINE || deltaMode === WheelEvent.DOM_DELTA_PAGE) { if (Math.abs(delta) >= 1) { ticks = Math.sign(delta); @@ -8178,11 +8168,11 @@ #resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this)); #scrollModePageState = null; #onVisibilityChange = null; #scaleTimeoutId = null; constructor(options) { - const viewerVersion = '3.3.97'; + const viewerVersion = '3.4.14'; if (_pdfjsLib.version !== viewerVersion) { throw new Error(`The API version "${_pdfjsLib.version}" does not match the Viewer version "${viewerVersion}".`); } this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; @@ -9567,11 +9557,10 @@ page_scale_width: "Page Width", page_scale_fit: "Page Fit", page_scale_auto: "Automatic Zoom", page_scale_actual: "Actual Size", page_scale_percent: "{{scale}}%", - loading: "Loading…", loading_error: "An error occurred while loading the PDF.", invalid_file_error: "Invalid or corrupted PDF file.", missing_file_error: "Missing PDF file.", unexpected_response_error: "Unexpected server response.", rendering_error: "An error occurred while rendering the page.", @@ -9662,10 +9651,11 @@ return null; }; class PDFPageView { #annotationMode = _pdfjsLib.AnnotationMode.ENABLE_FORMS; #layerProperties = null; + #loadingId = null; #previousRotation = null; #renderingState = _ui_utils.RenderingStates.INITIAL; #useThumbnailCanvas = { initialOptionalContent: true, regularAnnotations: true @@ -9720,24 +9710,32 @@ } get renderingState() { return this.#renderingState; } set renderingState(state) { + if (state === this.#renderingState) { + return; + } this.#renderingState = state; + if (this.#loadingId) { + clearTimeout(this.#loadingId); + this.#loadingId = null; + } switch (state) { case _ui_utils.RenderingStates.INITIAL: case _ui_utils.RenderingStates.PAUSED: - this.loadingIconDiv?.classList.add("notVisible"); + this.div.classList.remove("loading"); break; case _ui_utils.RenderingStates.RUNNING: - this.loadingIconDiv?.classList.remove("notVisible"); + this.div.classList.add("loadingIcon"); + this.#loadingId = setTimeout(() => { + this.div.classList.add("loading"); + this.#loadingId = null; + }, 0); break; case _ui_utils.RenderingStates.FINISHED: - if (this.loadingIconDiv) { - this.loadingIconDiv.remove(); - delete this.loadingIconDiv; - } + this.div.classList.remove("loadingIcon", "loading"); break; } } #setDimensions() { const { @@ -9914,11 +9912,10 @@ case zoomLayerNode: case annotationLayerNode: case annotationEditorLayerNode: case xfaLayerNode: case textLayerNode: - case this.loadingIconDiv: continue; } node.remove(); } div.removeAttribute("data-loaded"); @@ -9941,19 +9938,10 @@ this.canvas.height = 0; delete this.canvas; } this._resetZoomLayer(); } - if (!this.loadingIconDiv) { - this.loadingIconDiv = document.createElement("div"); - this.loadingIconDiv.className = "loadingIcon notVisible"; - this.loadingIconDiv.setAttribute("role", "img"); - this.l10n.get("loading").then(msg => { - this.loadingIconDiv?.setAttribute("aria-label", msg); - }); - div.append(this.loadingIconDiv); - } } update({ scale = 0, rotation = null, optionalContentConfigPromise = null, @@ -10436,10 +10424,11 @@ return; } const div = this.div = document.createElement("div"); div.className = "annotationEditorLayer"; div.tabIndex = 0; + div.hidden = true; this.pageDiv.append(div); this.annotationEditorLayer = new _pdfjsLib.AnnotationEditorLayer({ uiManager: this.#uiManager, div, accessibilityManager: this.accessibilityManager, @@ -10452,10 +10441,11 @@ div, annotations: null, intent }; this.annotationEditorLayer.render(parameters); + this.show(); } cancel() { this._cancelled = true; if (!this.div) { return; @@ -10469,11 +10459,11 @@ return; } this.div.hidden = true; } show() { - if (!this.div) { + if (!this.div || this.annotationEditorLayer.isEmpty) { return; } this.div.hidden = false; } } @@ -12613,12 +12603,12 @@ })); var _ui_utils = __webpack_require__(1); var _app_options = __webpack_require__(2); var _pdf_link_service = __webpack_require__(3); var _app = __webpack_require__(4); -const pdfjsVersion = '3.3.97'; -const pdfjsBuild = 'edfdb693e'; +const pdfjsVersion = '3.4.14'; +const pdfjsBuild = '9261daa23'; const AppConstants = null; exports.PDFViewerApplicationConstants = AppConstants; window.PDFViewerApplication = _app.PDFViewerApplication; window.PDFViewerApplicationConstants = AppConstants; window.PDFViewerApplicationOptions = _app_options.AppOptions; diff --git a/toolkit/components/pdfjs/content/web/viewer.css b/toolkit/components/pdfjs/content/web/viewer.css --- a/toolkit/components/pdfjs/content/web/viewer.css +++ b/toolkit/components/pdfjs/content/web/viewer.css @@ -957,19 +957,10 @@ .pdfViewer.removePageBorders .page { margin: 0 auto 10px; border: none; } -.pdfViewer.singlePageView { - display: inline-block; -} - -.pdfViewer.singlePageView .page { - margin: 0; - border: none; -} - .pdfViewer.scrollHorizontal, .pdfViewer.scrollWrapped, .spread { margin-left: 3.5px; margin-right: 3.5px; @@ -1024,26 +1015,32 @@ .pdfViewer .page canvas[zooming] { width: 100%; height: 100%; } -.pdfViewer .page .loadingIcon { +.pdfViewer .page.loadingIcon:after { position: absolute; - display: block; - left: 0; top: 0; - right: 0; - bottom: 0; + left: 0; + content: ""; + width: 100%; + height: 100%; background: url("images/loading-icon.gif") center no-repeat; - visibility: visible; + visibility: hidden; /* Using a delay with background-image doesn't work, consequently we use the visibility. */ transition-property: visibility; transition-delay: var(--loading-icon-delay); z-index: 5; + contain: strict; } -.pdfViewer .page .loadingIcon.notVisible { + +.pdfViewer .page.loading:after { + visibility: visible; +} + +.pdfViewer .page:not(.loading):after { transition-property: none; visibility: hidden; } .pdfViewer.enablePermissions .textLayer span { @@ -1074,12 +1071,10 @@ --toolbar-icon-opacity: 0.7; --doorhanger-icon-opacity: 0.9; --main-color: rgba(12, 12, 13, 1); --body-bg-color: rgba(212, 212, 215, 1); - --progressBar-percent: 0%; - --progressBar-end-offset: 0; --progressBar-color: rgba(10, 132, 255, 1); --progressBar-bg-color: rgba(221, 221, 222, 1); --progressBar-blend-color: rgba(116, 177, 239, 1); --scrollbar-color: auto; --scrollbar-bg-color: auto; @@ -1235,10 +1230,16 @@ --toolbarSidebar-box-shadow: none; --toolbarSidebar-border-bottom: 1px solid var(--toolbar-border-color); } } +@media screen and (prefers-reduced-motion: reduce) { + :root { + --sidebar-transition-duration: 0; + } +} + * { padding: 0; margin: 0; } @@ -1414,10 +1415,15 @@ #toolbarViewer { height: 32px; } #loadingBar { + /* Define these variables here, and not in :root, to avoid reflowing the + entire viewer when updating progress (see issue 15958). */ + --progressBar-percent: 0%; + --progressBar-end-offset: 0; + position: absolute; inset-inline: 0 var(--progressBar-end-offset); height: 4px; background-color: var(--progressBar-bg-color); border-bottom: 1px solid var(--toolbar-border-color); diff --git a/toolkit/components/pdfjs/content/web/viewer.js b/toolkit/components/pdfjs/content/web/viewer.js --- a/toolkit/components/pdfjs/content/web/viewer.js +++ b/toolkit/components/pdfjs/content/web/viewer.js @@ -396,18 +396,17 @@ delta = -delta; } return delta; } function normalizeWheelEventDelta(evt) { + const deltaMode = evt.deltaMode; let delta = normalizeWheelEventDirection(evt); - const MOUSE_DOM_DELTA_PIXEL_MODE = 0; - const MOUSE_DOM_DELTA_LINE_MODE = 1; const MOUSE_PIXELS_PER_LINE = 30; const MOUSE_LINES_PER_PAGE = 30; - if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) { + if (deltaMode === WheelEvent.DOM_DELTA_PIXEL) { delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE; - } else if (evt.deltaMode === MOUSE_DOM_DELTA_LINE_MODE) { + } else if (deltaMode === WheelEvent.DOM_DELTA_LINE) { delta /= MOUSE_LINES_PER_PAGE; } return delta; } function isValidRotation(angle) { @@ -433,13 +432,15 @@ } class ProgressBar { #classList = null; #disableAutoFetchTimeout = null; #percent = 0; + #style = null; #visible = true; constructor(bar) { this.#classList = bar.classList; + this.#style = bar.style; } get percent() { return this.#percent; } set percent(val) { @@ -447,20 +448,20 @@ if (isNaN(val)) { this.#classList.add("indeterminate"); return; } this.#classList.remove("indeterminate"); - docStyle.setProperty("--progressBar-percent", `${this.#percent}%`); + this.#style.setProperty("--progressBar-percent", `${this.#percent}%`); } setWidth(viewer) { if (!viewer) { return; } const container = viewer.parentNode; const scrollbarWidth = container.offsetWidth - viewer.offsetWidth; if (scrollbarWidth > 0) { - docStyle.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`); + this.#style.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`); } } setDisableAutoFetch(delay = 5000) { if (isNaN(this.#percent)) { return; @@ -1757,30 +1758,30 @@ return this.externalServices.supportedMouseWheelZoomModifierKeys; }, initPassiveLoading() { this.externalServices.initPassiveLoading({ onOpenWithTransport: (url, length, transport) => { - this.open(url, { + this.open({ + url, length, range: transport }); }, onOpenWithData: (data, contentDispositionFilename) => { if ((0, _pdfjsLib.isPdfFile)(contentDispositionFilename)) { this._contentDispositionFilename = contentDispositionFilename; } - this.open(data); + this.open({ + data + }); }, onOpenWithURL: (url, length, originalUrl) => { - const file = originalUrl !== undefined ? { + this.open({ url, + length, originalUrl - } : url; - const args = length !== undefined ? { - length - } : null; - this.open(file, args); + }); }, onError: err => { this.l10n.get("loading_error").then(msg => { this._documentError(msg, err); }); @@ -1869,40 +1870,29 @@ this.toolbar?.reset(); this.secondaryToolbar?.reset(); this._PDFBug?.cleanup(); await Promise.all(promises); }, - async open(file, args) { + async open(args) { if (this.pdfLoadingTask) { await this.close(); } const workerParameters = _app_options.AppOptions.getAll(_app_options.OptionKind.WORKER); for (const key in workerParameters) { _pdfjsLib.GlobalWorkerOptions[key] = workerParameters[key]; } const parameters = Object.create(null); - if (typeof file === "string") { - this.setTitleUsingUrl(file, file); - parameters.url = file; - } else if (file && "byteLength" in file) { - parameters.data = file; - } else if (file.url && file.originalUrl) { - this.setTitleUsingUrl(file.originalUrl, file.url); - parameters.url = file.url; - } const apiParameters = _app_options.AppOptions.getAll(_app_options.OptionKind.API); for (const key in apiParameters) { let value = apiParameters[key]; - if (key === "docBaseUrl" && !value) { - value = this.baseUrl; + if (key === "docBaseUrl") { + value ||= this.baseUrl; } parameters[key] = value; } - if (args) { - for (const key in args) { - parameters[key] = args[key]; - } + for (const key in args) { + parameters[key] = args[key]; } const loadingTask = (0, _pdfjsLib.getDocument)(parameters); this.pdfLoadingTask = loadingTask; loadingTask.onPassword = (updateCallback, reason) => { if (this.isViewerEmbedded) { @@ -3020,29 +3010,29 @@ supportsPinchToZoom } = PDFViewerApplication; if (pdfViewer.isInPresentationMode) { return; } - const isPinchToZoom = evt.ctrlKey && !PDFViewerApplication._isCtrlKeyDown; + const deltaMode = evt.deltaMode; + let scaleFactor = Math.exp(-evt.deltaY / 100); + const isPinchToZoom = evt.ctrlKey && !PDFViewerApplication._isCtrlKeyDown && deltaMode === WheelEvent.DOM_DELTA_PIXEL && evt.deltaX === 0 && Math.abs(scaleFactor - 1) < 0.05 && evt.deltaZ === 0; if (isPinchToZoom || evt.ctrlKey && supportedMouseWheelZoomModifierKeys.ctrlKey || evt.metaKey && supportedMouseWheelZoomModifierKeys.metaKey) { evt.preventDefault(); if (zoomDisabledTimeout || document.visibilityState === "hidden") { return; } const previousScale = pdfViewer.currentScale; if (isPinchToZoom && supportsPinchToZoom) { - let scaleFactor = Math.exp(-evt.deltaY / 100); scaleFactor = PDFViewerApplication._accumulateFactor(previousScale, scaleFactor, "_wheelUnusedFactor"); if (scaleFactor < 1) { PDFViewerApplication.zoomOut(null, scaleFactor); } else if (scaleFactor > 1) { PDFViewerApplication.zoomIn(null, scaleFactor); } else { return; } } else { - const deltaMode = evt.deltaMode; const delta = (0, _ui_utils.normalizeWheelEventDirection)(evt); let ticks = 0; if (deltaMode === WheelEvent.DOM_DELTA_LINE || deltaMode === WheelEvent.DOM_DELTA_PAGE) { if (Math.abs(delta) >= 1) { ticks = Math.sign(delta); @@ -8187,11 +8177,11 @@ #resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this)); #scrollModePageState = null; #onVisibilityChange = null; #scaleTimeoutId = null; constructor(options) { - const viewerVersion = '3.3.97'; + const viewerVersion = '3.4.14'; if (_pdfjsLib.version !== viewerVersion) { throw new Error(`The API version "${_pdfjsLib.version}" does not match the Viewer version "${viewerVersion}".`); } this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; @@ -9576,11 +9566,10 @@ page_scale_width: "Page Width", page_scale_fit: "Page Fit", page_scale_auto: "Automatic Zoom", page_scale_actual: "Actual Size", page_scale_percent: "{{scale}}%", - loading: "Loading…", loading_error: "An error occurred while loading the PDF.", invalid_file_error: "Invalid or corrupted PDF file.", missing_file_error: "Missing PDF file.", unexpected_response_error: "Unexpected server response.", rendering_error: "An error occurred while rendering the page.", @@ -9671,10 +9660,11 @@ return null; }; class PDFPageView { #annotationMode = _pdfjsLib.AnnotationMode.ENABLE_FORMS; #layerProperties = null; + #loadingId = null; #previousRotation = null; #renderingState = _ui_utils.RenderingStates.INITIAL; #useThumbnailCanvas = { initialOptionalContent: true, regularAnnotations: true @@ -9729,24 +9719,32 @@ } get renderingState() { return this.#renderingState; } set renderingState(state) { + if (state === this.#renderingState) { + return; + } this.#renderingState = state; + if (this.#loadingId) { + clearTimeout(this.#loadingId); + this.#loadingId = null; + } switch (state) { case _ui_utils.RenderingStates.INITIAL: case _ui_utils.RenderingStates.PAUSED: - this.loadingIconDiv?.classList.add("notVisible"); + this.div.classList.remove("loading"); break; case _ui_utils.RenderingStates.RUNNING: - this.loadingIconDiv?.classList.remove("notVisible"); + this.div.classList.add("loadingIcon"); + this.#loadingId = setTimeout(() => { + this.div.classList.add("loading"); + this.#loadingId = null; + }, 0); break; case _ui_utils.RenderingStates.FINISHED: - if (this.loadingIconDiv) { - this.loadingIconDiv.remove(); - delete this.loadingIconDiv; - } + this.div.classList.remove("loadingIcon", "loading"); break; } } #setDimensions() { const { @@ -9923,11 +9921,10 @@ case zoomLayerNode: case annotationLayerNode: case annotationEditorLayerNode: case xfaLayerNode: case textLayerNode: - case this.loadingIconDiv: continue; } node.remove(); } div.removeAttribute("data-loaded"); @@ -9950,19 +9947,10 @@ this.canvas.height = 0; delete this.canvas; } this._resetZoomLayer(); } - if (!this.loadingIconDiv) { - this.loadingIconDiv = document.createElement("div"); - this.loadingIconDiv.className = "loadingIcon notVisible"; - this.loadingIconDiv.setAttribute("role", "img"); - this.l10n.get("loading").then(msg => { - this.loadingIconDiv?.setAttribute("aria-label", msg); - }); - div.append(this.loadingIconDiv); - } } update({ scale = 0, rotation = null, optionalContentConfigPromise = null, @@ -10445,10 +10433,11 @@ return; } const div = this.div = document.createElement("div"); div.className = "annotationEditorLayer"; div.tabIndex = 0; + div.hidden = true; this.pageDiv.append(div); this.annotationEditorLayer = new _pdfjsLib.AnnotationEditorLayer({ uiManager: this.#uiManager, div, accessibilityManager: this.accessibilityManager, @@ -10461,10 +10450,11 @@ div, annotations: null, intent }; this.annotationEditorLayer.render(parameters); + this.show(); } cancel() { this._cancelled = true; if (!this.div) { return; @@ -10478,11 +10468,11 @@ return; } this.div.hidden = true; } show() { - if (!this.div) { + if (!this.div || this.annotationEditorLayer.isEmpty) { return; } this.div.hidden = false; } } @@ -12772,12 +12762,12 @@ })); var _ui_utils = __webpack_require__(1); var _app_options = __webpack_require__(2); var _pdf_link_service = __webpack_require__(3); var _app = __webpack_require__(4); -const pdfjsVersion = '3.3.97'; -const pdfjsBuild = 'edfdb693e'; +const pdfjsVersion = '3.4.14'; +const pdfjsBuild = '9261daa23'; const AppConstants = null; exports.PDFViewerApplicationConstants = AppConstants; window.PDFViewerApplication = _app.PDFViewerApplication; window.PDFViewerApplicationConstants = AppConstants; window.PDFViewerApplicationOptions = _app_options.AppOptions; diff --git a/toolkit/components/pdfjs/moz.yaml b/toolkit/components/pdfjs/moz.yaml --- a/toolkit/components/pdfjs/moz.yaml +++ b/toolkit/components/pdfjs/moz.yaml @@ -18,12 +18,12 @@ # Usually different from repository url url: https://github.com/mozilla/pdf.js # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: edfdb693e5b11057252f8cef9388d063678d95cd (2023-01-21T13:02:40Z). - revision: edfdb693e5b11057252f8cef9388d063678d95cd + release: 9261daa2334c46c12735c6492a08e72fca0be560 (2023-02-02T17:13:49Z). + revision: 9261daa2334c46c12735c6492a08e72fca0be560 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ # Multiple licenses can be specified (as a YAML list) # A "LICENSE" file must exist containing the full license text