tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit af2fb08d3e5d7adc666cd8ba947d8b442c2c0819
parent 5a9502cb10e897146a993dd18d7a02ba4cdae295
Author: Updatebot <updatebot@mozilla.com>
Date:   Mon,  8 Dec 2025 16:33:30 +0000

Bug 2004522 - Update PDF.js to 36de2d976d52db5b4045722f7276145986c2b18f r=pdfjs-reviewers,calixte

Differential Revision: https://phabricator.services.mozilla.com/D275349

Diffstat:
Mtoolkit/components/pdfjs/content/build/pdf.mjs | 16+++++++++-------
Mtoolkit/components/pdfjs/content/build/pdf.scripting.mjs | 5++---
Mtoolkit/components/pdfjs/content/build/pdf.worker.mjs | 6+++---
Mtoolkit/components/pdfjs/content/web/viewer-geckoview.css | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/components/pdfjs/content/web/viewer-geckoview.mjs | 160++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mtoolkit/components/pdfjs/content/web/viewer.css | 253++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mtoolkit/components/pdfjs/content/web/viewer.mjs | 279++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtoolkit/components/pdfjs/moz.yaml | 4++--
8 files changed, 688 insertions(+), 201 deletions(-)

diff --git a/toolkit/components/pdfjs/content/build/pdf.mjs b/toolkit/components/pdfjs/content/build/pdf.mjs @@ -21,8 +21,8 @@ */ /** - * pdfjsVersion = 5.4.445 - * pdfjsBuild = ec5330f78 + * pdfjsVersion = 5.4.466 + * pdfjsBuild = 36de2d976 */ /******/ // The require scope /******/ var __webpack_require__ = {}; @@ -3504,7 +3504,7 @@ class AnnotationEditorUIManager { removeLayer(layer) { this.#allLayers.delete(layer.pageIndex); } - async updateMode(mode, editId = null, isFromKeyboard = false, mustEnterInEditMode = false, editComment = false) { + async updateMode(mode, editId = null, isFromUser = false, isFromKeyboard = false, mustEnterInEditMode = false, editComment = false) { if (this.#mode === mode) { return; } @@ -3538,6 +3538,9 @@ class AnnotationEditorUIManager { if (mode === AnnotationEditorType.SIGNATURE) { await this.#signatureManager?.loadSignatures(); } + if (isFromUser) { + CurrentPointers.clearPointerType(); + } this.setEditingState(true); await this.#enableAll(); this.unselectAll(); @@ -13089,7 +13092,7 @@ function getDocument(src = {}) { } const docParams = { docId, - apiVersion: "5.4.445", + apiVersion: "5.4.466", data, password, disableAutoFetch, @@ -14678,8 +14681,8 @@ class InternalRenderTask { } } } -const version = "5.4.445"; -const build = "ec5330f78"; +const version = "5.4.466"; +const build = "36de2d976"; ;// ./src/display/editor/color_picker.js @@ -21192,7 +21195,6 @@ class DrawingEditor extends AnnotationEditor { this._currentParent = null; DrawingEditor.#currentDraw = null; DrawingEditor.#currentDrawingOptions = null; - CurrentPointers.clearPointerType(); CurrentPointers.clearTimeStamp(); } if (DrawingEditor.#currentDrawingAC) { diff --git a/toolkit/components/pdfjs/content/build/pdf.scripting.mjs b/toolkit/components/pdfjs/content/build/pdf.scripting.mjs @@ -21,10 +21,9 @@ */ /** - * pdfjsVersion = 5.4.445 - * pdfjsBuild = ec5330f78 + * pdfjsVersion = 5.4.466 + * pdfjsBuild = 36de2d976 */ -var __webpack_exports__ = {}; ;// ./src/scripting_api/constants.js const Border = Object.freeze({ diff --git a/toolkit/components/pdfjs/content/build/pdf.worker.mjs b/toolkit/components/pdfjs/content/build/pdf.worker.mjs @@ -21,8 +21,8 @@ */ /** - * pdfjsVersion = 5.4.445 - * pdfjsBuild = ec5330f78 + * pdfjsVersion = 5.4.466 + * pdfjsBuild = 36de2d976 */ /******/ // The require scope /******/ var __webpack_require__ = {}; @@ -59757,7 +59757,7 @@ class WorkerMessageHandler { docId, apiVersion } = docParams; - const workerVersion = "5.4.445"; + const workerVersion = "5.4.466"; if (apiVersion !== workerVersion) { throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`); } diff --git a/toolkit/components/pdfjs/content/web/viewer-geckoview.css b/toolkit/components/pdfjs/content/web/viewer-geckoview.css @@ -1464,6 +1464,172 @@ :not(.sidebarResizer){ pointer-events:none; } + + .sidebarResizer{ + background-color:var(--resizer-hover-bg-color); + } + } +} + +.popupMenu{ + --menuitem-checkmark-icon:url(images/checkmark.svg); + --menu-mark-icon-size:0; + --menu-icon-size:16px; + --menuitem-gap:5px; + --menuitem-border-color:transparent; + --menuitem-active-bg:color-mix( + in srgb, + var(--menu-text-color), + transparent 79% + ); + --menuitem-text-active-fg:var(--menu-text-color); + --menuitem-focus-bg:color-mix( + in srgb, + var(--menu-text-color), + transparent 93% + ); + --menuitem-focus-outline-color:light-dark(#0062fa, #00cadb); + --menuitem-focus-border-color:light-dark(white, black); + + @media screen and (forced-colors: active){ + --menu-bg:Canvas; + --menu-background-blend-mode:normal; + --menu-box-shadow:none; + --menu-backdrop-filter:none; + --menu-text-color:ButtonText; + --menu-border-color:CanvasText; + --menuitem-border-color:none; + --menuitem-hover-bg:SelectedItemText; + --menuitem-text-hover-fg:SelectedItem; + --menuitem-active-bg:SelectedItemText; + --menuitem-text-active-fg:SelectedItem; + --menuitem-focus-outline-color:CanvasText; + --menuitem-focus-border-color:none; + } + + display:flex; + flex-direction:column; + width:max-content; + height:auto; + position:relative; + left:0; + top:1px; + margin:0; + padding:5px; + + background:var(--menu-bg); + background-blend-mode:var(--menu-background-blend-mode); + box-shadow:var(--menu-box-shadow); + border-radius:6px; + border:1px solid var(--menu-border-color); + backdrop-filter:var(--menu-backdrop-filter); + + &.withMark{ + --menu-mark-icon-size:16px; + } + + > li{ + display:flex; + align-items:center; + list-style:none; + width:100%; + height:24px; + padding-inline:calc(var(--menu-mark-icon-size) + var(--menuitem-gap)) var(--menuitem-gap); + gap:var(--menuitem-gap); + box-sizing:border-box; + border-radius:var(--menuitem-border-radius); + border:1px solid var(--menuitem-border-color); + background:transparent; + + &:has(button.selected)::before{ + content:""; + display:inline-block; + width:11px; + height:11px; + mask-repeat:no-repeat; + mask-position:center; + mask-image:var(--menuitem-checkmark-icon); + background-color:var(--menu-text-color); + position:absolute; + margin-left:-16px; + } + + &:has(button:disabled){ + opacity:0.62; + pointer-events:none; + } + + &:hover{ + background:var(--menuitem-hover-bg); + background-blend-mode:var(--menuitem-hover-background-blend-mode); + > button{ + &:not(.noIcon)::before{ + background-color:var(--menuitem-text-hover-fg); + } + > span{ + color:var(--menuitem-text-hover-fg); + } + } + &:has(button.selected)::before{ + background-color:var(--menuitem-text-hover-fg); + } + } + + &:active{ + background-color:var(--menuitem-active-bg); + > button > span{ + color:var(--menuitem-text-active-fg); + } + } + + &:has(> button:focus-visible){ + border-color:var(--menuitem-focus-border-color); + background-color:var(--menuitem-focus-bg); + outline:2px solid var(--menuitem-focus-outline-color); + outline-offset:2px; + } + + > button{ + display:flex; + flex-direction:row; + align-items:center; + width:100%; + height:auto; + padding:var(--menuitem-gap); + gap:var(--menuitem-gap); + background:transparent; + border:none; + + &:not(.noIcon)::before{ + display:inline-block; + width:var(--menu-icon-size); + height:var(--menu-icon-size); + content:""; + mask-size:cover; + mask-position:center; + background-color:var(--menu-text-color); + } + + &:focus-visible{ + outline:none; + } + + > span{ + display:inline-block; + width:max-content; + height:auto; + text-align:left; + color:var(--menu-text-color); + user-select:none; + padding-inline-start:6px; + + font:menu; + font-size:13px; + font-style:normal; + font-weight:510; + line-height:normal; + } + } } } diff --git a/toolkit/components/pdfjs/content/web/viewer-geckoview.mjs b/toolkit/components/pdfjs/content/web/viewer-geckoview.mjs @@ -21,8 +21,8 @@ */ /** - * pdfjsVersion = 5.4.445 - * pdfjsBuild = ec5330f78 + * pdfjsVersion = 5.4.466 + * pdfjsBuild = 36de2d976 */ /******/ // The require scope /******/ var __webpack_require__ = {}; @@ -2456,13 +2456,16 @@ class CaretBrowsingMode { ;// ./web/sidebar.js +const RESIZE_TIMEOUT = 400; class Sidebar { - #minWidth = 0; - #maxWidth = 0; #initialWidth = 0; #width = 0; #coefficient; - #visible = false; + #resizeTimeout = null; + #resizer; + #isResizerOnTheLeft; + #isKeyboardResizing = false; + #resizeObserver = null; constructor({ sidebar, resizer, @@ -2470,30 +2473,37 @@ class Sidebar { }, ltr, isResizerOnTheLeft) { this._sidebar = sidebar; this.#coefficient = ltr === isResizerOnTheLeft ? -1 : 1; + this.#resizer = resizer; + this.#isResizerOnTheLeft = isResizerOnTheLeft; const style = window.getComputedStyle(sidebar); - this.#minWidth = parseFloat(style.getPropertyValue("--sidebar-min-width")); - this.#maxWidth = parseFloat(style.getPropertyValue("--sidebar-max-width")); this.#initialWidth = this.#width = parseFloat(style.getPropertyValue("--sidebar-width")); - this.#makeSidebarResizable(resizer, isResizerOnTheLeft); + resizer.ariaValueMin = parseFloat(style.getPropertyValue("--sidebar-min-width")); + resizer.ariaValueMax = parseFloat(style.getPropertyValue("--sidebar-max-width")); + resizer.ariaValueNow = this.#width; + this.#makeSidebarResizable(); toggleButton.addEventListener("click", this.toggle.bind(this)); + this._isOpen = false; sidebar.hidden = true; } - #makeSidebarResizable(resizer, isResizerOnTheLeft) { - resizer.ariaValueMin = this.#minWidth; - resizer.ariaValueMax = this.#maxWidth; - resizer.ariaValueNow = this.#width; + #makeSidebarResizable() { + const sidebarStyle = this._sidebar.style; let pointerMoveAC; const cancelResize = () => { - this.#width = MathClamp(this.#width, this.#minWidth, this.#maxWidth); + this.#resizeTimeout = null; this._sidebar.classList.remove("resizing"); pointerMoveAC?.abort(); pointerMoveAC = null; + this.#resizeObserver?.disconnect(); + this.#resizeObserver = null; + this.#isKeyboardResizing = false; + this.onStopResizing(); }; - resizer.addEventListener("pointerdown", e => { + this.#resizer.addEventListener("pointerdown", e => { if (pointerMoveAC) { cancelResize(); return; } + this.onStartResizing(); const { clientX } = e; @@ -2504,10 +2514,19 @@ class Sidebar { signal } = pointerMoveAC; const sidebar = this._sidebar; - const sidebarStyle = sidebar.style; sidebar.classList.add("resizing"); const parentStyle = sidebar.parentElement.style; parentStyle.minWidth = 0; + this.#resizeObserver?.disconnect(); + this.#resizeObserver = new ResizeObserver(([{ + borderBoxSize: [{ + inlineSize + }] + }]) => { + prevX += this.#width - inlineSize; + this.#setWidth(inlineSize); + }); + this.#resizeObserver.observe(sidebar); window.addEventListener("contextmenu", noContextMenu, { signal }); @@ -2516,11 +2535,7 @@ class Sidebar { return; } stopEvent(ev); - const { - clientX: x - } = ev; - this.#setNewWidth(x - prevX, parentStyle, resizer, sidebarStyle, isResizerOnTheLeft, false); - prevX = x; + sidebarStyle.width = `${Math.round(this.#width + this.#coefficient * (ev.clientX - prevX))}px`; }, { signal, capture: true @@ -2537,39 +2552,69 @@ class Sidebar { signal }); }); - resizer.addEventListener("keydown", e => { + this.#resizer.addEventListener("keydown", e => { const { key } = e; const isArrowLeft = key === "ArrowLeft"; if (isArrowLeft || key === "ArrowRight") { + if (!this.#isKeyboardResizing) { + this._sidebar.classList.add("resizing"); + this.#isKeyboardResizing = true; + this.#resizeObserver?.disconnect(); + this.#resizeObserver = new ResizeObserver(([{ + borderBoxSize: [{ + inlineSize + }] + }]) => { + this.#setWidth(inlineSize); + }); + this.#resizeObserver.observe(this._sidebar); + this.onStartResizing(); + } const base = e.ctrlKey || e.metaKey ? 10 : 1; const dx = base * (isArrowLeft ? -1 : 1); - this.#setNewWidth(dx, this._sidebar.parentElement.style, resizer, this._sidebar.style, isResizerOnTheLeft, true); + clearTimeout(this.#resizeTimeout); + this.#resizeTimeout = setTimeout(cancelResize, RESIZE_TIMEOUT); + sidebarStyle.width = `${Math.round(this.#width + this.#coefficient * dx)}px`; stopEvent(e); } }); } - #setNewWidth(dx, parentStyle, resizer, sidebarStyle, isResizerOnTheLeft, isFromKeyboard) { - let newWidth = this.#width + this.#coefficient * dx; - if (!isFromKeyboard) { - this.#width = newWidth; - } - if ((newWidth > this.#maxWidth || newWidth < this.#minWidth) && (this.#width === this.#maxWidth || this.#width === this.#minWidth)) { - return; - } - newWidth = MathClamp(newWidth, this.#minWidth, this.#maxWidth); - if (isFromKeyboard) { - this.#width = newWidth; + #setWidth(newWidth) { + this.#width = newWidth; + this.#resizer.ariaValueNow = Math.round(newWidth); + if (this.#isResizerOnTheLeft) { + this._sidebar.parentElement.style.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`; } - resizer.ariaValueNow = Math.round(newWidth); - sidebarStyle.width = `${newWidth.toFixed(3)}px`; - if (isResizerOnTheLeft) { - parentStyle.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`; + this.onResizing(newWidth); + } + get width() { + return this.#width; + } + set width(newWidth) { + if (!this.#resizeObserver) { + this.#resizeObserver = new ResizeObserver(([{ + borderBoxSize: [{ + inlineSize + }] + }]) => { + this.#setWidth(inlineSize); + }); + this.#resizeObserver.observe(this._sidebar); } + this._sidebar.style.width = `${newWidth}px`; + clearTimeout(this.#resizeTimeout); + this.#resizeTimeout = setTimeout(() => { + this.#resizeObserver.disconnect(); + this.#resizeObserver = null; + }, RESIZE_TIMEOUT); } - toggle() { - this._sidebar.hidden = !(this.#visible = !this.#visible); + onStartResizing() {} + onStopResizing() {} + onResizing(_newWidth) {} + toggle(visibility = !this._isOpen) { + this._sidebar.hidden = !(this._isOpen = visibility); } } @@ -6926,7 +6971,27 @@ class StructTreeLayerBuilder { const { role } = node; - element = MathMLElements.has(role) ? document.createElementNS(MathMLNamespace, role) : document.createElement("span"); + if (MathMLElements.has(role)) { + element = document.createElementNS(MathMLNamespace, role); + let text = ""; + for (const { + type, + id + } of node.children || []) { + if (type !== "content" || !id) { + continue; + } + const elem = document.getElementById(id); + if (!elem) { + continue; + } + text += elem.textContent.trim() || ""; + elem.ariaHidden = "true"; + } + element.textContent = text; + } else { + element = document.createElement("span"); + } const match = role.match(HEADING_PATTERN); if (match) { element.setAttribute("role", "heading"); @@ -6942,6 +7007,17 @@ class StructTreeLayerBuilder { element.setHTML(node.mathML, { sanitizer: MathMLSanitizer.sanitizer }); + for (const { + id + } of node.children || []) { + if (!id) { + continue; + } + const elem = document.getElementById(id); + if (elem) { + elem.ariaHidden = true; + } + } } if (!node.mathML && node.children.length === 1 && node.children[0].role !== "math") { element = document.createElementNS(MathMLNamespace, "math"); @@ -8366,7 +8442,7 @@ class PDFViewer { #textLayerMode = TextLayerMode.ENABLE; #viewerAlert = null; constructor(options) { - const viewerVersion = "5.4.445"; + const viewerVersion = "5.4.466"; if (version !== viewerVersion) { throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`); } @@ -9865,7 +9941,7 @@ class PDFViewer { const updater = async () => { this.#cleanupSwitchAnnotationEditorMode(); this.#annotationEditorMode = mode; - await this.#annotationEditorUIManager.updateMode(mode, editId, isFromKeyboard, mustEnterInEditMode, editComment); + await this.#annotationEditorUIManager.updateMode(mode, editId, true, isFromKeyboard, mustEnterInEditMode, editComment); if (mode !== this.#annotationEditorMode || pdfDocument !== this.pdfDocument) { return; } diff --git a/toolkit/components/pdfjs/content/web/viewer.css b/toolkit/components/pdfjs/content/web/viewer.css @@ -4601,6 +4601,172 @@ :not(.sidebarResizer){ pointer-events:none; } + + .sidebarResizer{ + background-color:var(--resizer-hover-bg-color); + } + } +} + +.popupMenu{ + --menuitem-checkmark-icon:url(images/checkmark.svg); + --menu-mark-icon-size:0; + --menu-icon-size:16px; + --menuitem-gap:5px; + --menuitem-border-color:transparent; + --menuitem-active-bg:color-mix( + in srgb, + var(--menu-text-color), + transparent 79% + ); + --menuitem-text-active-fg:var(--menu-text-color); + --menuitem-focus-bg:color-mix( + in srgb, + var(--menu-text-color), + transparent 93% + ); + --menuitem-focus-outline-color:light-dark(#0062fa, #00cadb); + --menuitem-focus-border-color:light-dark(white, black); + + @media screen and (forced-colors: active){ + --menu-bg:Canvas; + --menu-background-blend-mode:normal; + --menu-box-shadow:none; + --menu-backdrop-filter:none; + --menu-text-color:ButtonText; + --menu-border-color:CanvasText; + --menuitem-border-color:none; + --menuitem-hover-bg:SelectedItemText; + --menuitem-text-hover-fg:SelectedItem; + --menuitem-active-bg:SelectedItemText; + --menuitem-text-active-fg:SelectedItem; + --menuitem-focus-outline-color:CanvasText; + --menuitem-focus-border-color:none; + } + + display:flex; + flex-direction:column; + width:max-content; + height:auto; + position:relative; + left:0; + top:1px; + margin:0; + padding:5px; + + background:var(--menu-bg); + background-blend-mode:var(--menu-background-blend-mode); + box-shadow:var(--menu-box-shadow); + border-radius:6px; + border:1px solid var(--menu-border-color); + backdrop-filter:var(--menu-backdrop-filter); + + &.withMark{ + --menu-mark-icon-size:16px; + } + + > li{ + display:flex; + align-items:center; + list-style:none; + width:100%; + height:24px; + padding-inline:calc(var(--menu-mark-icon-size) + var(--menuitem-gap)) var(--menuitem-gap); + gap:var(--menuitem-gap); + box-sizing:border-box; + border-radius:var(--menuitem-border-radius); + border:1px solid var(--menuitem-border-color); + background:transparent; + + &:has(button.selected)::before{ + content:""; + display:inline-block; + width:11px; + height:11px; + mask-repeat:no-repeat; + mask-position:center; + mask-image:var(--menuitem-checkmark-icon); + background-color:var(--menu-text-color); + position:absolute; + margin-left:-16px; + } + + &:has(button:disabled){ + opacity:0.62; + pointer-events:none; + } + + &:hover{ + background:var(--menuitem-hover-bg); + background-blend-mode:var(--menuitem-hover-background-blend-mode); + > button{ + &:not(.noIcon)::before{ + background-color:var(--menuitem-text-hover-fg); + } + > span{ + color:var(--menuitem-text-hover-fg); + } + } + &:has(button.selected)::before{ + background-color:var(--menuitem-text-hover-fg); + } + } + + &:active{ + background-color:var(--menuitem-active-bg); + > button > span{ + color:var(--menuitem-text-active-fg); + } + } + + &:has(> button:focus-visible){ + border-color:var(--menuitem-focus-border-color); + background-color:var(--menuitem-focus-bg); + outline:2px solid var(--menuitem-focus-outline-color); + outline-offset:2px; + } + + > button{ + display:flex; + flex-direction:row; + align-items:center; + width:100%; + height:auto; + padding:var(--menuitem-gap); + gap:var(--menuitem-gap); + background:transparent; + border:none; + + &:not(.noIcon)::before{ + display:inline-block; + width:var(--menu-icon-size); + height:var(--menu-icon-size); + content:""; + mask-size:cover; + mask-position:center; + background-color:var(--menu-text-color); + } + + &:focus-visible{ + outline:none; + } + + > span{ + display:inline-block; + width:max-content; + height:auto; + text-align:left; + color:var(--menu-text-color); + user-select:none; + padding-inline-start:6px; + + font:menu; + font-size:13px; + font-style:normal; + font-weight:510; + line-height:normal; + } + } } } @@ -5452,59 +5618,64 @@ body{ } #thumbnailView{ + --thumbnail-width:98px; + + display:flex; + flex-wrap:wrap; width:calc(100% - 60px); padding:10px 30px 0; -} -#thumbnailView > a:is(:active, :focus){ - outline:0; -} + > a{ + width:auto; + height:auto; -.thumbnail{ - --thumbnail-width:0; - --thumbnail-height:0; + > .thumbnail{ + scroll-margin-block:19px; + width:var(--thumbnail-width); + margin:0 10px 5px; + padding:1px; + border:7px solid transparent; + border-radius:2px; - float:inline-start; - width:var(--thumbnail-width); - height:var(--thumbnail-height); - margin:0 10px 5px; - padding:1px; - border:7px solid transparent; - border-radius:2px; -} + &.selected{ + border-color:var(--thumbnail-selected-color) !important; -#thumbnailView > a:last-of-type > .thumbnail{ - margin-bottom:10px; -} + > .thumbnailImage{ + opacity:1 !important; + } + } -a:focus > .thumbnail, -.thumbnail:hover{ - border-color:var(--thumbnail-hover-color); -} + &.missingThumbnailImage{ + border:1px dashed rgb(132 132 132); + padding:7px; + > .thumbnailImage{ + display:none; + } + } -.thumbnail.selected{ - border-color:var(--thumbnail-selected-color) !important; -} + > .thumbnailImage{ + width:100%; + opacity:0.9; + } + } -.thumbnailImage{ - width:var(--thumbnail-width); - height:var(--thumbnail-height); - opacity:0.9; -} + &:is(:active, :focus){ + outline:0; + } -a:focus > .thumbnail > .thumbnailImage, -.thumbnail:hover > .thumbnailImage{ - opacity:0.95; -} + &:last-of-type > .thumbnail{ + margin-bottom:10px; + } -.thumbnail.selected > .thumbnailImage{ - opacity:1 !important; -} + &:focus > .thumbnail, + .thumbnail:hover{ + border-color:var(--thumbnail-hover-color); -.thumbnail:not([data-loaded]) > .thumbnailImage{ - width:calc(var(--thumbnail-width) - 2px); - height:calc(var(--thumbnail-height) - 2px); - border:1px dashed rgb(132 132 132); + > .thumbnailImage{ + opacity:0.95; + } + } + } } .treeWithDeepNesting > .treeItem, diff --git a/toolkit/components/pdfjs/content/web/viewer.mjs b/toolkit/components/pdfjs/content/web/viewer.mjs @@ -21,8 +21,8 @@ */ /** - * pdfjsVersion = 5.4.445 - * pdfjsBuild = ec5330f78 + * pdfjsVersion = 5.4.466 + * pdfjsBuild = 36de2d976 */ /******/ // The require scope /******/ var __webpack_require__ = {}; @@ -3313,13 +3313,16 @@ class CaretBrowsingMode { ;// ./web/sidebar.js +const RESIZE_TIMEOUT = 400; class Sidebar { - #minWidth = 0; - #maxWidth = 0; #initialWidth = 0; #width = 0; #coefficient; - #visible = false; + #resizeTimeout = null; + #resizer; + #isResizerOnTheLeft; + #isKeyboardResizing = false; + #resizeObserver = null; constructor({ sidebar, resizer, @@ -3327,30 +3330,37 @@ class Sidebar { }, ltr, isResizerOnTheLeft) { this._sidebar = sidebar; this.#coefficient = ltr === isResizerOnTheLeft ? -1 : 1; + this.#resizer = resizer; + this.#isResizerOnTheLeft = isResizerOnTheLeft; const style = window.getComputedStyle(sidebar); - this.#minWidth = parseFloat(style.getPropertyValue("--sidebar-min-width")); - this.#maxWidth = parseFloat(style.getPropertyValue("--sidebar-max-width")); this.#initialWidth = this.#width = parseFloat(style.getPropertyValue("--sidebar-width")); - this.#makeSidebarResizable(resizer, isResizerOnTheLeft); + resizer.ariaValueMin = parseFloat(style.getPropertyValue("--sidebar-min-width")); + resizer.ariaValueMax = parseFloat(style.getPropertyValue("--sidebar-max-width")); + resizer.ariaValueNow = this.#width; + this.#makeSidebarResizable(); toggleButton.addEventListener("click", this.toggle.bind(this)); + this._isOpen = false; sidebar.hidden = true; } - #makeSidebarResizable(resizer, isResizerOnTheLeft) { - resizer.ariaValueMin = this.#minWidth; - resizer.ariaValueMax = this.#maxWidth; - resizer.ariaValueNow = this.#width; + #makeSidebarResizable() { + const sidebarStyle = this._sidebar.style; let pointerMoveAC; const cancelResize = () => { - this.#width = MathClamp(this.#width, this.#minWidth, this.#maxWidth); + this.#resizeTimeout = null; this._sidebar.classList.remove("resizing"); pointerMoveAC?.abort(); pointerMoveAC = null; + this.#resizeObserver?.disconnect(); + this.#resizeObserver = null; + this.#isKeyboardResizing = false; + this.onStopResizing(); }; - resizer.addEventListener("pointerdown", e => { + this.#resizer.addEventListener("pointerdown", e => { if (pointerMoveAC) { cancelResize(); return; } + this.onStartResizing(); const { clientX } = e; @@ -3361,10 +3371,19 @@ class Sidebar { signal } = pointerMoveAC; const sidebar = this._sidebar; - const sidebarStyle = sidebar.style; sidebar.classList.add("resizing"); const parentStyle = sidebar.parentElement.style; parentStyle.minWidth = 0; + this.#resizeObserver?.disconnect(); + this.#resizeObserver = new ResizeObserver(([{ + borderBoxSize: [{ + inlineSize + }] + }]) => { + prevX += this.#width - inlineSize; + this.#setWidth(inlineSize); + }); + this.#resizeObserver.observe(sidebar); window.addEventListener("contextmenu", noContextMenu, { signal }); @@ -3373,11 +3392,7 @@ class Sidebar { return; } stopEvent(ev); - const { - clientX: x - } = ev; - this.#setNewWidth(x - prevX, parentStyle, resizer, sidebarStyle, isResizerOnTheLeft, false); - prevX = x; + sidebarStyle.width = `${Math.round(this.#width + this.#coefficient * (ev.clientX - prevX))}px`; }, { signal, capture: true @@ -3394,39 +3409,69 @@ class Sidebar { signal }); }); - resizer.addEventListener("keydown", e => { + this.#resizer.addEventListener("keydown", e => { const { key } = e; const isArrowLeft = key === "ArrowLeft"; if (isArrowLeft || key === "ArrowRight") { + if (!this.#isKeyboardResizing) { + this._sidebar.classList.add("resizing"); + this.#isKeyboardResizing = true; + this.#resizeObserver?.disconnect(); + this.#resizeObserver = new ResizeObserver(([{ + borderBoxSize: [{ + inlineSize + }] + }]) => { + this.#setWidth(inlineSize); + }); + this.#resizeObserver.observe(this._sidebar); + this.onStartResizing(); + } const base = e.ctrlKey || e.metaKey ? 10 : 1; const dx = base * (isArrowLeft ? -1 : 1); - this.#setNewWidth(dx, this._sidebar.parentElement.style, resizer, this._sidebar.style, isResizerOnTheLeft, true); + clearTimeout(this.#resizeTimeout); + this.#resizeTimeout = setTimeout(cancelResize, RESIZE_TIMEOUT); + sidebarStyle.width = `${Math.round(this.#width + this.#coefficient * dx)}px`; stopEvent(e); } }); } - #setNewWidth(dx, parentStyle, resizer, sidebarStyle, isResizerOnTheLeft, isFromKeyboard) { - let newWidth = this.#width + this.#coefficient * dx; - if (!isFromKeyboard) { - this.#width = newWidth; - } - if ((newWidth > this.#maxWidth || newWidth < this.#minWidth) && (this.#width === this.#maxWidth || this.#width === this.#minWidth)) { - return; - } - newWidth = MathClamp(newWidth, this.#minWidth, this.#maxWidth); - if (isFromKeyboard) { - this.#width = newWidth; + #setWidth(newWidth) { + this.#width = newWidth; + this.#resizer.ariaValueNow = Math.round(newWidth); + if (this.#isResizerOnTheLeft) { + this._sidebar.parentElement.style.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`; } - resizer.ariaValueNow = Math.round(newWidth); - sidebarStyle.width = `${newWidth.toFixed(3)}px`; - if (isResizerOnTheLeft) { - parentStyle.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`; + this.onResizing(newWidth); + } + get width() { + return this.#width; + } + set width(newWidth) { + if (!this.#resizeObserver) { + this.#resizeObserver = new ResizeObserver(([{ + borderBoxSize: [{ + inlineSize + }] + }]) => { + this.#setWidth(inlineSize); + }); + this.#resizeObserver.observe(this._sidebar); } + this._sidebar.style.width = `${newWidth}px`; + clearTimeout(this.#resizeTimeout); + this.#resizeTimeout = setTimeout(() => { + this.#resizeObserver.disconnect(); + this.#resizeObserver = null; + }, RESIZE_TIMEOUT); } - toggle() { - this._sidebar.hidden = !(this.#visible = !this.#visible); + onStartResizing() {} + onStopResizing() {} + onResizing(_newWidth) {} + toggle(visibility = !this._isOpen) { + this._sidebar.hidden = !(this._isOpen = visibility); } } @@ -8510,16 +8555,16 @@ class PdfTextExtractor { const DRAW_UPSCALE_FACTOR = 2; const MAX_NUM_SCALING_STEPS = 3; const THUMBNAIL_WIDTH = 98; -function zeroCanvas(c) { - c.width = 0; - c.height = 0; -} class TempImageFactory { - static #tempCanvas = null; static getCanvas(width, height) { - const tempCanvas = this.#tempCanvas ||= document.createElement("canvas"); - tempCanvas.width = width; - tempCanvas.height = height; + let tempCanvas; + if (FeatureTest.isOffscreenCanvasSupported) { + tempCanvas = new OffscreenCanvas(width, height); + } else { + tempCanvas = document.createElement("canvas"); + tempCanvas.width = width; + tempCanvas.height = height; + } const ctx = tempCanvas.getContext("2d", { alpha: false }); @@ -8527,13 +8572,7 @@ class TempImageFactory { ctx.fillStyle = "rgb(255, 255, 255)"; ctx.fillRect(0, 0, width, height); ctx.restore(); - return [tempCanvas, tempCanvas.getContext("2d")]; - } - static destroyCanvas() { - if (this.#tempCanvas) { - zeroCanvas(this.#tempCanvas); - } - this.#tempCanvas = null; + return [tempCanvas, ctx]; } } class PDFThumbnailView { @@ -8566,24 +8605,21 @@ class PDFThumbnailView { this.renderTask = null; this.renderingState = RenderingStates.INITIAL; this.resume = null; - const anchor = document.createElement("a"); - anchor.href = linkService.getAnchorUrl("#page=" + id); + const anchor = this.anchor = document.createElement("a"); + anchor.href = linkService.getAnchorUrl(`#page=${id}`); anchor.setAttribute("data-l10n-id", "pdfjs-thumb-page-title"); anchor.setAttribute("data-l10n-args", this.#pageL10nArgs); - anchor.onclick = function () { + anchor.onclick = () => { linkService.goToPage(id); return false; }; - this.anchor = anchor; - const div = document.createElement("div"); - div.className = "thumbnail"; + const div = this.div = document.createElement("div"); + div.classList.add("thumbnail", "missingThumbnailImage"); div.setAttribute("data-page-number", this.id); - this.div = div; this.#updateDims(); - const img = document.createElement("div"); - img.className = "thumbnailImage"; - this._placeholderImg = img; - div.append(img); + const image = this.image = document.createElement("img"); + image.className = "thumbnailImage"; + div.append(image); anchor.append(div); container.append(anchor); } @@ -8593,14 +8629,10 @@ class PDFThumbnailView { height } = this.viewport; const ratio = width / height; - this.canvasWidth = THUMBNAIL_WIDTH; - this.canvasHeight = this.canvasWidth / ratio | 0; - this.scale = this.canvasWidth / width; - const { - style - } = this.div; - style.setProperty("--thumbnail-width", `${this.canvasWidth}px`); - style.setProperty("--thumbnail-height", `${this.canvasHeight}px`); + const canvasWidth = this.canvasWidth = THUMBNAIL_WIDTH; + const canvasHeight = this.canvasHeight = canvasWidth / ratio | 0; + this.scale = canvasWidth / width; + this.div.style.height = `${canvasHeight}px`; } setPdfPage(pdfPage) { this.pdfPage = pdfPage; @@ -8615,12 +8647,17 @@ class PDFThumbnailView { reset() { this.cancelRendering(); this.renderingState = RenderingStates.INITIAL; - this.div.removeAttribute("data-loaded"); - this.image?.replaceWith(this._placeholderImg); this.#updateDims(); - if (this.image) { - this.image.removeAttribute("src"); - delete this.image; + const { + image + } = this; + const url = image.src; + if (url) { + URL.revokeObjectURL(url); + image.removeAttribute("data-l10n-id"); + image.removeAttribute("data-l10n-args"); + image.src = ""; + this.div.classList.add("missingThumbnailImage"); } } update({ @@ -8644,11 +8681,11 @@ class PDFThumbnailView { this.resume = null; } #getPageDrawContext(upscaleFactor = 1) { - const canvas = document.createElement("canvas"); const outputScale = new OutputScale(); const width = upscaleFactor * this.canvasWidth, height = upscaleFactor * this.canvasHeight; outputScale.limitCanvas(width, height, this.maxCanvasPixels, this.maxCanvasDim); + const canvas = document.createElement("canvas"); canvas.width = width * outputScale.sx | 0; canvas.height = height * outputScale.sy | 0; const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null; @@ -8657,20 +8694,27 @@ class PDFThumbnailView { transform }; } - #convertCanvasToImage(canvas) { + async #convertCanvasToImage(canvas) { if (this.renderingState !== RenderingStates.FINISHED) { throw new Error("#convertCanvasToImage: Rendering has not finished."); } const reducedCanvas = this.#reduceImage(canvas); - const image = document.createElement("img"); - image.className = "thumbnailImage"; + const { + image + } = this; + const { + promise, + resolve + } = Promise.withResolvers(); + reducedCanvas.toBlob(resolve); + const blob = await promise; + image.src = URL.createObjectURL(blob); image.setAttribute("data-l10n-id", "pdfjs-thumb-page-canvas"); image.setAttribute("data-l10n-args", this.#pageL10nArgs); - image.src = reducedCanvas.toDataURL(); - this.image = image; - this.div.setAttribute("data-loaded", true); - this._placeholderImg.replaceWith(image); - zeroCanvas(reducedCanvas); + this.div.classList.remove("missingThumbnailImage"); + if (!FeatureTest.isOffscreenCanvasSupported) { + reducedCanvas.width = reducedCanvas.height = 0; + } } async draw() { if (this.renderingState !== RenderingStates.INITIAL) { @@ -8718,7 +8762,6 @@ class PDFThumbnailView { await renderTask.promise; } catch (e) { if (e instanceof RenderingCancelledException) { - zeroCanvas(canvas); return; } error = e; @@ -8728,8 +8771,7 @@ class PDFThumbnailView { } } this.renderingState = RenderingStates.FINISHED; - this.#convertCanvasToImage(canvas); - zeroCanvas(canvas); + await this.#convertCanvasToImage(canvas); this.eventBus.dispatch("thumbnailrendered", { source: this, pageNumber: this.id, @@ -8803,18 +8845,19 @@ class PDFThumbnailView { setPageLabel(label) { this.pageLabel = typeof label === "string" ? label : null; this.anchor.setAttribute("data-l10n-args", this.#pageL10nArgs); - if (this.renderingState !== RenderingStates.FINISHED) { - return; - } - this.image?.setAttribute("data-l10n-args", this.#pageL10nArgs); + this.image.setAttribute("data-l10n-args", this.#pageL10nArgs); } } ;// ./web/pdf_thumbnail_viewer.js -const THUMBNAIL_SCROLL_MARGIN = -19; const THUMBNAIL_SELECTED_CLASS = "selected"; +const SCROLL_OPTIONS = { + behavior: "instant", + container: "nearest", + block: "nearest" +}; class PDFThumbnailViewer { constructor({ container, @@ -8886,9 +8929,7 @@ class PDFThumbnailViewer { } } if (shouldScroll) { - scrollIntoView(thumbnailView.div, { - top: THUMBNAIL_SCROLL_MARGIN - }); + thumbnailView.div.scrollIntoView(SCROLL_OPTIONS); } } this._currentPageNumber = pageNumber; @@ -8920,7 +8961,6 @@ class PDFThumbnailViewer { thumbnail.reset(); } } - TempImageFactory.destroyCanvas(); } #resetView() { this._thumbnails = []; @@ -8947,9 +8987,10 @@ class PDFThumbnailViewer { const viewport = firstPdfPage.getViewport({ scale: 1 }); + const fragment = document.createDocumentFragment(); for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) { const thumbnail = new PDFThumbnailView({ - container: this.container, + container: fragment, eventBus: this.eventBus, id: pageNum, defaultViewport: viewport.clone(), @@ -8966,6 +9007,7 @@ class PDFThumbnailViewer { this._thumbnails[0]?.setPdfPage(firstPdfPage); const thumbnailView = this._thumbnails[this._currentPageNumber - 1]; thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS); + this.container.append(fragment); }).catch(reason => { console.error("Unable to initialize thumbnail viewer", reason); }); @@ -10121,7 +10163,27 @@ class StructTreeLayerBuilder { const { role } = node; - element = MathMLElements.has(role) ? document.createElementNS(MathMLNamespace, role) : document.createElement("span"); + if (MathMLElements.has(role)) { + element = document.createElementNS(MathMLNamespace, role); + let text = ""; + for (const { + type, + id + } of node.children || []) { + if (type !== "content" || !id) { + continue; + } + const elem = document.getElementById(id); + if (!elem) { + continue; + } + text += elem.textContent.trim() || ""; + elem.ariaHidden = "true"; + } + element.textContent = text; + } else { + element = document.createElement("span"); + } const match = role.match(HEADING_PATTERN); if (match) { element.setAttribute("role", "heading"); @@ -10137,6 +10199,17 @@ class StructTreeLayerBuilder { element.setHTML(node.mathML, { sanitizer: MathMLSanitizer.sanitizer }); + for (const { + id + } of node.children || []) { + if (!id) { + continue; + } + const elem = document.getElementById(id); + if (elem) { + elem.ariaHidden = true; + } + } } if (!node.mathML && node.children.length === 1 && node.children[0].role !== "math") { element = document.createElementNS(MathMLNamespace, "math"); @@ -11561,7 +11634,7 @@ class PDFViewer { #textLayerMode = TextLayerMode.ENABLE; #viewerAlert = null; constructor(options) { - const viewerVersion = "5.4.445"; + const viewerVersion = "5.4.466"; if (version !== viewerVersion) { throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`); } @@ -13090,7 +13163,7 @@ class PDFViewer { const updater = async () => { this.#cleanupSwitchAnnotationEditorMode(); this.#annotationEditorMode = mode; - await this.#annotationEditorUIManager.updateMode(mode, editId, isFromKeyboard, mustEnterInEditMode, editComment); + await this.#annotationEditorUIManager.updateMode(mode, editId, true, isFromKeyboard, mustEnterInEditMode, editComment); if (mode !== this.#annotationEditorMode || pdfDocument !== this.pdfDocument) { return; } diff --git a/toolkit/components/pdfjs/moz.yaml b/toolkit/components/pdfjs/moz.yaml @@ -20,8 +20,8 @@ origin: # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: ec5330f78c1feb384156bdf6c69b18bedf36b4a3 (2025-11-28T15:59:23Z). - revision: ec5330f78c1feb384156bdf6c69b18bedf36b4a3 + release: 36de2d976d52db5b4045722f7276145986c2b18f (2025-12-05T08:59:11Z). + revision: 36de2d976d52db5b4045722f7276145986c2b18f # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/