tor-browser

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

commit c954fc70457a3fb93f10a89ae71ddb6f944496f0
parent 1e39260629a119a1976c5c6ef8e1cc0176547f4a
Author: Calixte Denizet <calixte.denizet@gmail.com>
Date:   Fri, 28 Nov 2025 18:27:05 +0000

Bug 2003036 - Update PDF.js to new version ec5330f78c1feb384156bdf6c69b18bedf36b4a3 r=pdfjs-reviewers,marco

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

Diffstat:
Mtoolkit/components/pdfjs/content/build/pdf.mjs | 47+++++++++++++++++++++++++++--------------------
Mtoolkit/components/pdfjs/content/build/pdf.scripting.mjs | 4++--
Mtoolkit/components/pdfjs/content/build/pdf.worker.mjs | 773+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mtoolkit/components/pdfjs/content/web/viewer-geckoview.css | 6++++++
Mtoolkit/components/pdfjs/content/web/viewer-geckoview.html | 29++++++++++++++++-------------
Mtoolkit/components/pdfjs/content/web/viewer-geckoview.mjs | 253++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtoolkit/components/pdfjs/content/web/viewer.css | 6++++++
Mtoolkit/components/pdfjs/content/web/viewer.html | 617++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mtoolkit/components/pdfjs/content/web/viewer.mjs | 267+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mtoolkit/components/pdfjs/moz.yaml | 4++--
10 files changed, 1641 insertions(+), 365 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.402 - * pdfjsBuild = 57334bd20 + * pdfjsVersion = 5.4.445 + * pdfjsBuild = ec5330f78 */ /******/ // The require scope /******/ var __webpack_require__ = {}; @@ -1816,7 +1816,7 @@ function renderRichText({ intent: "richText" }); } - fragment.firstChild.classList.add("richText", className); + fragment.firstElementChild.classList.add("richText", className); container.append(fragment); } function makePathFromDrawOPS(data) { @@ -6292,11 +6292,11 @@ class AnnotationEditor { if (nextFirstPosition !== firstPosition) { if (nextFirstPosition < firstPosition) { for (let i = 0; i < firstPosition - nextFirstPosition; i++) { - this.#resizersDiv.append(this.#resizersDiv.firstChild); + this.#resizersDiv.append(this.#resizersDiv.firstElementChild); } } else if (nextFirstPosition > firstPosition) { for (let i = 0; i < nextFirstPosition - firstPosition; i++) { - this.#resizersDiv.firstChild.before(this.#resizersDiv.lastChild); + this.#resizersDiv.firstElementChild.before(this.#resizersDiv.lastElementChild); } } let i = 0; @@ -6308,7 +6308,7 @@ class AnnotationEditor { } this.#setResizerTabIndex(0); this.#isResizerEnabledForKeyboard = true; - this.#resizersDiv.firstChild.focus({ + this.#resizersDiv.firstElementChild.focus({ focusVisible: true }); event.preventDefault(); @@ -6539,10 +6539,10 @@ class AnnotationEditor { } resetAnnotationElement(annotation) { const { - firstChild + firstElementChild } = annotation.container; - if (firstChild?.nodeName === "DIV" && firstChild.classList.contains("annotationContent")) { - firstChild.remove(); + if (firstElementChild?.nodeName === "DIV" && firstElementChild.classList.contains("annotationContent")) { + firstElementChild.remove(); } } } @@ -7102,6 +7102,9 @@ class FontFaceObject { get disableFontFace() { return this.#fontData.disableFontFace ?? false; } + set disableFontFace(value) { + shadow(this, "disableFontFace", !!value); + } get fontExtraProperties() { return this.#fontData.fontExtraProperties ?? false; } @@ -7135,6 +7138,9 @@ class FontFaceObject { get bbox() { return this.#fontData.bbox; } + set bbox(bbox) { + shadow(this, "bbox", bbox); + } get fontMatrix() { return this.#fontData.fontMatrix; } @@ -13083,7 +13089,7 @@ function getDocument(src = {}) { } const docParams = { docId, - apiVersion: "5.4.402", + apiVersion: "5.4.445", data, password, disableAutoFetch, @@ -14457,7 +14463,8 @@ class WorkerTransport { info: results[0], metadata: results[1] ? new Metadata(results[1]) : null, contentDispositionFilename: this._fullReader?.filename ?? null, - contentLength: this._fullReader?.contentLength ?? null + contentLength: this._fullReader?.contentLength ?? null, + hasStructTree: results[2] })); this.#methodPromises.set(name, promise); return promise; @@ -14671,8 +14678,8 @@ class InternalRenderTask { } } } -const version = "5.4.402"; -const build = "57334bd20"; +const version = "5.4.445"; +const build = "ec5330f78"; ;// ./src/display/editor/color_picker.js @@ -14805,13 +14812,13 @@ class ColorPicker { return; } if (event.target === this.#button) { - this.#dropdown.firstChild?.focus(); + this.#dropdown.firstElementChild?.focus(); return; } event.target.nextSibling?.focus(); } _moveToPrevious(event) { - if (event.target === this.#dropdown?.firstChild || event.target === this.#button) { + if (event.target === this.#dropdown?.firstElementChild || event.target === this.#button) { if (this.#isDropdownVisible) { this._hideDropdownFromKeyboard(); } @@ -14827,14 +14834,14 @@ class ColorPicker { this.#openDropdown(event); return; } - this.#dropdown.firstChild?.focus(); + this.#dropdown.firstElementChild?.focus(); } _moveToEnd(event) { if (!this.#isDropdownVisible) { this.#openDropdown(event); return; } - this.#dropdown.lastChild?.focus(); + this.#dropdown.lastElementChild?.focus(); } #keyDown(event) { ColorPicker._keyboardManager.exec(this, event); @@ -24270,7 +24277,7 @@ class AnnotationEditorLayer { return; } if (editor.parent && editor.annotationElementId) { - this.#uiManager.addDeletedAnnotationElement(editor.annotationElementId); + this.#uiManager.addDeletedAnnotationElement(editor); AnnotationEditor.deleteAnnotationElement(editor); editor.annotationElementId = null; } @@ -24802,8 +24809,8 @@ class DrawLayer { } } if (path) { - const defs = element.firstChild; - const pathElement = defs.firstChild; + const defs = element.firstElementChild; + const pathElement = defs.firstElementChild; this.#updateProperties(pathElement, path); } } diff --git a/toolkit/components/pdfjs/content/build/pdf.scripting.mjs b/toolkit/components/pdfjs/content/build/pdf.scripting.mjs @@ -21,8 +21,8 @@ */ /** - * pdfjsVersion = 5.4.402 - * pdfjsBuild = 57334bd20 + * pdfjsVersion = 5.4.445 + * pdfjsBuild = ec5330f78 */ var __webpack_exports__ = {}; 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.402 - * pdfjsBuild = 57334bd20 + * pdfjsVersion = 5.4.445 + * pdfjsBuild = ec5330f78 */ /******/ // The require scope /******/ var __webpack_require__ = {}; @@ -1210,6 +1210,11 @@ class RefSetCache { yield [Ref.fromString(ref), value]; } } + *keys() { + for (const ref of this._map.keys()) { + yield Ref.fromString(ref); + } + } } function isName(v, name) { return v instanceof Name && (name === undefined || v.name === name); @@ -36343,7 +36348,7 @@ class NameOrNumberTree { this.xref = xref; this._type = type; } - getAll() { + getAll(isRaw = false) { const map = new Map(); if (!this.root) { return map; @@ -36376,7 +36381,7 @@ class NameOrNumberTree { continue; } for (let i = 0, ii = entries.length; i < ii; i += 2) { - map.set(xref.fetchIfRef(entries[i]), xref.fetchIfRef(entries[i + 1])); + map.set(xref.fetchIfRef(entries[i]), isRaw ? entries[i + 1] : xref.fetchIfRef(entries[i + 1])); } } return map; @@ -38084,6 +38089,9 @@ class Catalog { } return markInfo; } + get hasStructTree() { + return this.#catDict.has("StructTreeRoot"); + } get structTreeRoot() { let structTree = null; try { @@ -44754,7 +44762,7 @@ class Occur extends XFAObject { }) : ""; this.max = attributes.max !== "" ? getInteger({ data: attributes.max, - defaultValue: 1, + defaultValue: -1, validate: x => true }) : ""; this.min = attributes.min !== "" ? getInteger({ @@ -58282,6 +58290,7 @@ async function incrementalUpdate({ + const MAX_LEAVES_PER_PAGES_NODE = 16; const MAX_IN_NAME_TREE_NODE = 64; class PageData { @@ -58289,15 +58298,38 @@ class PageData { this.page = page; this.documentData = documentData; this.annotations = null; + this.pointingNamedDestinations = null; documentData.pagesMap.put(page.ref, this); } } class DocumentData { constructor(document) { this.document = document; + this.destinations = null; this.pageLabels = null; this.pagesMap = new RefSetCache(); this.oldRefMapping = new RefSetCache(); + this.dedupNamedDestinations = new Map(); + this.usedNamedDestinations = new Set(); + this.postponedRefCopies = new RefSetCache(); + this.usedStructParents = new Set(); + this.oldStructParentMapping = new Map(); + this.structTreeRoot = null; + this.parentTree = null; + this.idTree = null; + this.roleMap = null; + this.classMap = null; + this.namespaces = null; + this.structTreeAF = null; + this.structTreePronunciationLexicon = []; + } +} +class XRefWrapper { + constructor(entries) { + this.entries = entries; + } + fetch(ref) { + return ref instanceof Ref ? this.entries[ref.num] : ref; } } class PDFEditor { @@ -58311,6 +58343,7 @@ class PDFEditor { this.oldPages = []; this.newPages = []; this.xref = [null]; + this.xrefWrapper = new XRefWrapper(this.xref); this.newRefCount = 1; [this.rootRef, this.rootDict] = this.newDict; [this.infoRef, this.infoDict] = this.newDict; @@ -58322,6 +58355,15 @@ class PDFEditor { this.title = title; this.author = author; this.pageLabels = null; + this.namedDestinations = new Map(); + this.parentTree = new Map(); + this.structTreeKids = []; + this.idTree = new Map(); + this.classMap = new Dict(); + this.roleMap = new Dict(); + this.namespaces = new Map(); + this.structTreeAF = []; + this.structTreePronunciationLexicon = []; } get newRef() { const ref = Ref.get(this.newRefCount++, 0); @@ -58337,6 +58379,11 @@ class PDFEditor { this.xref[ref.num] = await this.#collectDependencies(obj, true, xref); return ref; } + cloneDict(dict) { + const newDict = dict.clone(); + newDict.xref = this.xrefWrapper; + return newDict; + } async #collectDependencies(obj, mustClone, xref) { if (obj instanceof Ref) { const { @@ -58348,18 +58395,32 @@ class PDFEditor { if (newRef) { return newRef; } + const oldRef = obj; + obj = await xref.fetchAsync(oldRef); + if (typeof obj === "number") { + return obj; + } newRef = this.newRef; - oldRefMapping.put(obj, newRef); - obj = await xref.fetchAsync(obj); + oldRefMapping.put(oldRef, newRef); this.xref[newRef.num] = await this.#collectDependencies(obj, true, xref); return newRef; } const promises = []; + const { + currentDocument: { + postponedRefCopies + } + } = this; if (Array.isArray(obj)) { if (mustClone) { obj = obj.slice(); } for (let i = 0, ii = obj.length; i < ii; i++) { + const postponedActions = postponedRefCopies.get(obj[i]); + if (postponedActions) { + postponedActions.push(ref => obj[i] = ref); + continue; + } promises.push(this.#collectDependencies(obj[i], true, xref).then(newObj => obj[i] = newObj)); } await Promise.all(promises); @@ -58370,24 +58431,214 @@ class PDFEditor { ({ dict } = obj = obj.getOriginalStream().clone()); + dict.xref = this.xrefWrapper; } else if (obj instanceof Dict) { if (mustClone) { obj = obj.clone(); + obj.xref = this.xrefWrapper; } dict = obj; } if (dict) { for (const [key, rawObj] of dict.getRawEntries()) { + const postponedActions = postponedRefCopies.get(rawObj); + if (postponedActions) { + postponedActions.push(ref => dict.set(key, ref)); + continue; + } promises.push(this.#collectDependencies(rawObj, true, xref).then(newObj => dict.set(key, newObj))); } await Promise.all(promises); } return obj; } + async #cloneStructTreeNode(parentStructRef, node, xref, removedStructElements, dedupIDs, dedupClasses, dedupRoles, visited = new RefSet()) { + const { + currentDocument: { + pagesMap, + oldRefMapping + } + } = this; + const pg = node.getRaw("Pg"); + if (pg instanceof Ref && !pagesMap.has(pg)) { + return null; + } + let kids; + const k = kids = node.getRaw("K"); + if (k instanceof Ref) { + if (visited.has(k)) { + return null; + } + kids = await xref.fetchAsync(k); + if (!Array.isArray(kids)) { + kids = [k]; + } + } + kids = Array.isArray(kids) ? kids : [kids]; + const newKids = []; + const structElemIndices = []; + for (let kid of kids) { + const kidRef = kid instanceof Ref ? kid : null; + if (kidRef) { + if (visited.has(kidRef)) { + continue; + } + visited.put(kidRef); + kid = await xref.fetchAsync(kidRef); + } + if (typeof kid === "number") { + newKids.push(kid); + continue; + } + if (!(kid instanceof Dict)) { + continue; + } + const pgRef = kid.getRaw("Pg"); + if (pgRef instanceof Ref && !pagesMap.has(pgRef)) { + continue; + } + const type = kid.get("Type"); + if (!type || isName(type, "StructElem")) { + let setAsSpan = false; + if (kidRef && removedStructElements.has(kidRef)) { + if (!isName(kid.get("S"), "Link")) { + continue; + } + setAsSpan = true; + } + const newKidRef = await this.#cloneStructTreeNode(kidRef, kid, xref, removedStructElements, dedupIDs, dedupClasses, dedupRoles, visited); + if (newKidRef) { + structElemIndices.push(newKids.length); + newKids.push(newKidRef); + if (kidRef) { + oldRefMapping.put(kidRef, newKidRef); + } + if (setAsSpan) { + this.xref[newKidRef.num].setIfName("S", "Span"); + } + } + continue; + } + if (isName(type, "OBJR")) { + if (!kidRef) { + continue; + } + const newKidRef = oldRefMapping.get(kidRef); + if (!newKidRef) { + continue; + } + const newKid = this.xref[newKidRef.num]; + const objRef = newKid.getRaw("Obj"); + if (objRef instanceof Ref) { + const obj = this.xref[objRef.num]; + if (obj instanceof Dict && !obj.has("StructParent") && parentStructRef) { + const structParent = this.parentTree.size; + this.parentTree.set(structParent, [oldRefMapping, parentStructRef]); + obj.set("StructParent", structParent); + } + } + newKids.push(newKidRef); + continue; + } + if (isName(type, "MCR")) { + const newKid = await this.#collectDependencies(kidRef || kid, true, xref); + newKids.push(newKid); + continue; + } + if (kidRef) { + const newKidRef = await this.#collectDependencies(kidRef, true, xref); + newKids.push(newKidRef); + } + } + if (kids.length !== 0 && newKids.length === 0) { + return null; + } + const newNodeRef = this.newRef; + const newNode = this.xref[newNodeRef.num] = this.cloneDict(node); + newNode.delete("ID"); + newNode.delete("C"); + newNode.delete("K"); + newNode.delete("P"); + newNode.delete("S"); + await this.#collectDependencies(newNode, false, xref); + const classNames = node.get("C"); + if (classNames instanceof Name) { + const newClassName = dedupClasses.get(classNames.name); + if (newClassName) { + newNode.set("C", Name.get(newClassName)); + } else { + newNode.set("C", classNames); + } + } else if (Array.isArray(classNames)) { + const newClassNames = []; + for (const className of classNames) { + if (className instanceof Name) { + const newClassName = dedupClasses.get(className.name); + if (newClassName) { + newClassNames.push(Name.get(newClassName)); + } else { + newClassNames.push(className); + } + } + } + newNode.set("C", newClassNames); + } + const roleName = node.get("S"); + if (roleName instanceof Name) { + const newRoleName = dedupRoles.get(roleName.name); + if (newRoleName) { + newNode.set("S", Name.get(newRoleName)); + } else { + newNode.set("S", roleName); + } + } + const id = node.get("ID"); + if (typeof id === "string") { + const stringId = stringToPDFString(id, false); + const newId = dedupIDs.get(stringId); + if (newId) { + newNode.set("ID", stringToAsciiOrUTF16BE(newId)); + } else { + newNode.set("ID", id); + } + } + let attributes = newNode.get("A"); + if (attributes) { + if (!Array.isArray(attributes)) { + attributes = [attributes]; + } + for (let attr of attributes) { + attr = this.xrefWrapper.fetch(attr); + if (isName(attr.get("O"), "Table") && attr.has("Headers")) { + const headers = this.xrefWrapper.fetch(attr.getRaw("Headers")); + if (Array.isArray(headers)) { + for (let i = 0, ii = headers.length; i < ii; i++) { + const newId = dedupIDs.get(stringToPDFString(headers[i], false)); + if (newId) { + headers[i] = newId; + } + } + } + } + } + } + for (const index of structElemIndices) { + const structElemRef = newKids[index]; + const structElem = this.xref[structElemRef.num]; + structElem.set("P", newNodeRef); + } + if (newKids.length === 1) { + newNode.set("K", newKids[0]); + } else if (newKids.length > 1) { + newNode.set("K", newKids); + } + return newNodeRef; + } async extractPages(pageInfos) { const promises = []; let newIndex = 0; this.hasSingleFile = pageInfos.length === 1; + const allDocumentData = []; for (const { document, includePages, @@ -58397,6 +58648,7 @@ class PDFEditor { continue; } const documentData = new DocumentData(document); + allDocumentData.push(documentData); promises.push(this.#collectDocumentData(documentData)); let keptIndices, keptRanges, deletedIndices, deletedRanges; for (const page of includePages || []) { @@ -58455,27 +58707,63 @@ class PDFEditor { } await Promise.all(promises); promises.length = 0; + this.#collectValidDestinations(allDocumentData); this.#collectPageLabels(); for (const page of this.oldPages) { promises.push(this.#postCollectPageData(page)); } await Promise.all(promises); + this.#findDuplicateNamedDestinations(); + this.#setPostponedRefCopies(allDocumentData); for (let i = 0, ii = this.oldPages.length; i < ii; i++) { this.newPages[i] = await this.#makePageCopy(i, null); } + this.#fixPostponedRefCopies(allDocumentData); + await this.#mergeStructTrees(allDocumentData); return this.writePDF(); } async #collectDocumentData(documentData) { const { - document + document: { + pdfManager, + xref + } } = documentData; - await document.pdfManager.ensureCatalog("rawPageLabels").then(pageLabels => documentData.pageLabels = pageLabels); + await Promise.all([pdfManager.ensureCatalog("destinations").then(destinations => documentData.destinations = destinations), pdfManager.ensureCatalog("rawPageLabels").then(pageLabels => documentData.pageLabels = pageLabels), pdfManager.ensureCatalog("structTreeRoot").then(structTreeRoot => documentData.structTreeRoot = structTreeRoot)]); + const structTreeRoot = documentData.structTreeRoot; + if (structTreeRoot) { + const rootDict = structTreeRoot.dict; + const parentTree = rootDict.get("ParentTree"); + if (parentTree) { + const numberTree = new NumberTree(parentTree, xref); + documentData.parentTree = numberTree.getAll(true); + } + const idTree = rootDict.get("IDTree"); + if (idTree) { + const nameTree = new NameTree(idTree, xref); + documentData.idTree = nameTree.getAll(true); + } + documentData.roleMap = rootDict.get("RoleMap") || null; + documentData.classMap = rootDict.get("ClassMap") || null; + let namespaces = rootDict.get("Namespaces") || null; + if (namespaces && !Array.isArray(namespaces)) { + namespaces = [namespaces]; + } + documentData.namespaces = namespaces; + documentData.structTreeAF = rootDict.get("AF") || null; + documentData.structTreePronunciationLexicon = rootDict.get("PronunciationLexicon") || null; + } } async #postCollectPageData(pageData) { const { page: { xref, annotations + }, + documentData: { + pagesMap, + destinations, + usedNamedDestinations } } = pageData; if (!annotations) { @@ -58489,6 +58777,18 @@ class PDFEditor { promises.push(xref.fetchIfRefAsync(annotationRef).then(async annotationDict => { if (!isName(annotationDict.get("Subtype"), "Link")) { newAnnotations[newAnnotationIndex] = annotationRef; + return; + } + const action = annotationDict.get("A"); + const dest = action instanceof Dict ? action.get("D") : annotationDict.get("Dest"); + if (!dest || Array.isArray(dest) && (!(dest[0] instanceof Ref) || pagesMap.has(dest[0]))) { + newAnnotations[newAnnotationIndex] = annotationRef; + } else if (typeof dest === "string") { + const destString = stringToPDFString(dest, true); + if (destinations.has(destString)) { + newAnnotations[newAnnotationIndex] = annotationRef; + usedNamedDestinations.add(destString); + } } })); } @@ -58496,6 +58796,358 @@ class PDFEditor { newAnnotations = newAnnotations.filter(annot => !!annot); pageData.annotations = newAnnotations.length > 0 ? newAnnotations : null; } + #setPostponedRefCopies(allDocumentData) { + for (const { + postponedRefCopies, + pagesMap + } of allDocumentData) { + for (const oldPageRef of pagesMap.keys()) { + postponedRefCopies.put(oldPageRef, []); + } + } + } + #fixPostponedRefCopies(allDocumentData) { + for (const { + postponedRefCopies, + oldRefMapping + } of allDocumentData) { + for (const [oldRef, actions] of postponedRefCopies.items()) { + const newRef = oldRefMapping.get(oldRef); + for (const action of actions) { + action(newRef); + } + } + postponedRefCopies.clear(); + } + } + #visitObject(obj, callback, visited = new RefSet()) { + if (obj instanceof Ref) { + if (!visited.has(obj)) { + visited.put(obj); + this.#visitObject(this.xref[obj.num], callback, visited); + } + return; + } + if (Array.isArray(obj)) { + for (const item of obj) { + this.#visitObject(item, callback, visited); + } + return; + } + let dict; + if (obj instanceof BaseStream) { + ({ + dict + } = obj); + } else if (obj instanceof Dict) { + dict = obj; + } + if (dict) { + callback(dict); + for (const value of dict.getRawValues()) { + this.#visitObject(value, callback, visited); + } + } + } + async #mergeStructTrees(allDocumentData) { + let newStructParentId = 0; + const { + parentTree: newParentTree + } = this; + for (let i = 0, ii = this.newPages.length; i < ii; i++) { + const { + documentData: { + parentTree, + oldRefMapping, + oldStructParentMapping, + usedStructParents, + document: { + xref + } + } + } = this.oldPages[i]; + if (!parentTree) { + continue; + } + const pageRef = this.newPages[i]; + const pageDict = this.xref[pageRef.num]; + this.#visitObject(pageDict, dict => { + const structParent = dict.get("StructParent") ?? dict.get("StructParents"); + if (typeof structParent !== "number") { + return; + } + usedStructParents.add(structParent); + let parent = parentTree.get(structParent); + const parentRef = parent instanceof Ref ? parent : null; + if (parentRef) { + const array = xref.fetch(parentRef); + if (Array.isArray(array)) { + parent = array; + } + } + if (Array.isArray(parent) && parent.every(ref => ref === null)) { + parent = null; + } + if (!parent) { + if (dict.has("StructParent")) { + dict.delete("StructParent"); + } else { + dict.delete("StructParents"); + } + return; + } + let newStructParent = oldStructParentMapping.get(structParent); + if (newStructParent === undefined) { + newStructParent = newStructParentId++; + oldStructParentMapping.set(structParent, newStructParent); + newParentTree.set(newStructParent, [oldRefMapping, parent]); + } + if (dict.has("StructParent")) { + dict.set("StructParent", newStructParent); + } else { + dict.set("StructParents", newStructParent); + } + }); + } + const { + structTreeKids, + idTree: newIdTree, + classMap: newClassMap, + roleMap: newRoleMap, + namespaces: newNamespaces, + structTreeAF: newStructTreeAF, + structTreePronunciationLexicon: newStructTreePronunciationLexicon + } = this; + for (const documentData of allDocumentData) { + const { + document: { + xref + }, + oldRefMapping, + parentTree, + usedStructParents, + structTreeRoot, + idTree, + classMap, + roleMap, + namespaces, + structTreeAF, + structTreePronunciationLexicon + } = documentData; + if (!structTreeRoot) { + continue; + } + this.currentDocument = documentData; + const removedStructElements = new RefSet(); + for (const [key, value] of parentTree || []) { + if (!usedStructParents.has(key) && value instanceof Ref) { + removedStructElements.put(value); + } + } + const dedupIDs = new Map(); + for (const [id, nodeRef] of idTree || []) { + let _id = id; + if (newIdTree.has(id)) { + for (let i = 1;; i++) { + const newId = `${id}_${i}`; + if (!newIdTree.has(newId)) { + dedupIDs.set(id, newId); + _id = newId; + break; + } + } + } + newIdTree.set(_id, nodeRef); + } + const dedupClasses = new Map(); + if (classMap?.size > 0) { + for (let [className, classDict] of classMap) { + classDict = await this.#collectDependencies(classDict, true, xref); + if (newClassMap.has(className)) { + for (let i = 1;; i++) { + const newClassName = `${className}_${i}`; + if (!newClassMap.has(newClassName)) { + dedupClasses.set(className, newClassName); + className = newClassName; + break; + } + } + } + newClassMap.set(className, classDict); + } + } + const dedupRoles = new Map(); + if (roleMap?.size > 0) { + for (const [roleName, mappedName] of roleMap) { + const newMappedName = newRoleMap.get(roleName); + if (!newMappedName) { + newRoleMap.set(roleName, mappedName); + continue; + } + if (newMappedName === mappedName) { + continue; + } + for (let i = 1;; i++) { + const newRoleName = `${roleName}_${i}`; + if (!newRoleMap.has(newRoleName)) { + dedupRoles.set(roleName, newRoleName); + newRoleMap.set(newRoleName, mappedName); + break; + } + } + } + } + if (namespaces?.length > 0) { + for (const namespaceRef of namespaces) { + const namespace = await xref.fetchIfRefAsync(namespaceRef); + let ns = namespace.get("NS"); + if (!ns || newNamespaces.has(ns)) { + continue; + } + ns = stringToPDFString(ns, false); + const newNamespace = await this.#collectDependencies(namespace, true, xref); + newNamespaces.set(ns, newNamespace); + } + } + if (structTreeAF) { + for (const afRef of structTreeAF) { + newStructTreeAF.push(await this.#collectDependencies(afRef, true, xref)); + } + } + if (structTreePronunciationLexicon) { + for (const lexiconRef of structTreePronunciationLexicon) { + newStructTreePronunciationLexicon.push(await this.#collectDependencies(lexiconRef, true, xref)); + } + } + let kids = structTreeRoot.dict.get("K"); + if (!kids) { + continue; + } + kids = Array.isArray(kids) ? kids : [kids]; + for (let kid of kids) { + const kidRef = kid instanceof Ref ? kid : null; + if (kidRef && removedStructElements.has(kidRef)) { + continue; + } + kid = await xref.fetchIfRefAsync(kid); + const newKidRef = await this.#cloneStructTreeNode(kidRef, kid, xref, removedStructElements, dedupIDs, dedupClasses, dedupRoles); + if (newKidRef) { + structTreeKids.push(newKidRef); + } + } + for (const [id, nodeRef] of idTree || []) { + const newNodeRef = oldRefMapping.get(nodeRef); + const newId = dedupIDs.get(id) || id; + if (newNodeRef) { + newIdTree.set(newId, newNodeRef); + } else { + newIdTree.delete(newId); + } + } + } + for (const [key, [oldRefMapping, parent]] of newParentTree) { + if (!parent) { + newParentTree.delete(key); + continue; + } + if (!Array.isArray(parent)) { + const newParent = oldRefMapping.get(parent); + if (newParent === undefined) { + newParentTree.delete(key); + } else { + newParentTree.set(key, newParent); + } + continue; + } + const newParents = parent.map(ref => ref instanceof Ref && oldRefMapping.get(ref) || null); + if (newParents.length === 0 || newParents.every(ref => ref === null)) { + newParentTree.delete(key); + continue; + } + newParentTree.set(key, newParents); + } + this.currentDocument = null; + } + #collectValidDestinations(allDocumentData) { + for (const documentData of allDocumentData) { + if (!documentData.destinations) { + continue; + } + const { + destinations, + pagesMap + } = documentData; + const newDestinations = documentData.destinations = new Map(); + for (const [key, dest] of Object.entries(destinations)) { + const pageRef = dest[0]; + const pageData = pagesMap.get(pageRef); + if (!pageData) { + continue; + } + (pageData.pointingNamedDestinations ||= new Set()).add(key); + newDestinations.set(key, dest); + } + } + } + #findDuplicateNamedDestinations() { + const { + namedDestinations + } = this; + for (let i = 0, ii = this.oldPages.length; i < ii; i++) { + const page = this.oldPages[i]; + const { + documentData: { + destinations, + dedupNamedDestinations, + usedNamedDestinations + } + } = page; + let { + pointingNamedDestinations + } = page; + if (!pointingNamedDestinations) { + continue; + } + page.pointingNamedDestinations = pointingNamedDestinations = pointingNamedDestinations.intersection(usedNamedDestinations); + for (const pointingDest of pointingNamedDestinations) { + if (!usedNamedDestinations.has(pointingDest)) { + continue; + } + const dest = destinations.get(pointingDest).slice(); + if (!namedDestinations.has(pointingDest)) { + namedDestinations.set(pointingDest, dest); + continue; + } + const newName = `${pointingDest}_p${i + 1}`; + dedupNamedDestinations.set(pointingDest, newName); + namedDestinations.set(newName, dest); + } + } + } + #fixNamedDestinations(annotations, dedupNamedDestinations) { + if (dedupNamedDestinations.size === 0) { + return; + } + const fixDestination = (dict, key, dest) => { + if (typeof dest === "string") { + dict.set(key, dedupNamedDestinations.get(stringToPDFString(dest, true)) || dest); + } + }; + for (const annotRef of annotations) { + const annotDict = this.xref[annotRef.num]; + if (!isName(annotDict.get("Subtype"), "Link")) { + continue; + } + const action = annotDict.get("A"); + if (action instanceof Dict && action.has("D")) { + const dest = action.get("D"); + fixDestination(action, "D", dest); + continue; + } + const dest = annotDict.get("Dest"); + fixDestination(annotDict, "Dest", dest); + } + } async #collectPageLabels() { if (!this.hasSingleFile) { return; @@ -58529,7 +59181,7 @@ class PDFEditor { } if (stFirstIndex !== -1) { const st = currentLabel.get("St"); - currentLabel = currentLabel.clone(); + currentLabel = this.cloneDict(currentLabel); currentLabel.set("St", st + (i - stFirstIndex)); stFirstIndex = -1; } @@ -58552,10 +59204,12 @@ class PDFEditor { const { page, documentData, - annotations + annotations, + pointingNamedDestinations } = this.oldPages[pageIndex]; this.currentDocument = documentData; const { + dedupNamedDestinations, oldRefMapping } = documentData; const { @@ -58566,8 +59220,15 @@ class PDFEditor { ref: oldPageRef } = page; const pageRef = this.newRef; - const pageDict = this.xref[pageRef.num] = page.pageDict.clone(); + const pageDict = this.xref[pageRef.num] = this.cloneDict(page.pageDict); oldRefMapping.put(oldPageRef, pageRef); + if (pointingNamedDestinations) { + for (const pointingDest of pointingNamedDestinations) { + const name = dedupNamedDestinations.get(pointingDest) || pointingDest; + const dest = this.namedDestinations.get(name); + dest[0] = pageRef; + } + } for (const key of ["Rotate", "MediaBox", "CropBox", "BleedBox", "TrimBox", "ArtBox", "Resources", "Annots", "Parent", "UserUnit"]) { pageDict.delete(key); } @@ -58586,7 +59247,11 @@ class PDFEditor { pageDict.set("UserUnit", userUnit); } pageDict.setIfDict("Resources", await this.#collectDependencies(resources, true, xref)); - pageDict.setIfArray("Annots", await this.#collectDependencies(annotations, true, xref)); + if (annotations) { + const newAnnotations = await this.#collectDependencies(annotations, true, xref); + this.#fixNamedDestinations(newAnnotations, dedupNamedDestinations); + pageDict.setIfArray("Annots", newAnnotations); + } if (this.useObjectStreams) { const newLastRef = this.newRefCount; const pageObjectRefs = []; @@ -58707,14 +59372,90 @@ class PDFEditor { const pageLabelsRef = this.#makeNameNumTree(this.pageLabels, false); rootDict.set("PageLabels", pageLabelsRef); } + #makeDestinationsTree() { + const { + namedDestinations + } = this; + if (namedDestinations.size === 0) { + return; + } + if (!this.namesDict) { + [this.namesRef, this.namesDict] = this.newDict; + this.rootDict.set("Names", this.namesRef); + } + this.namesDict.set("Dests", this.#makeNameNumTree(Array.from(namedDestinations.entries()), true)); + } + #makeStructTree() { + const { + structTreeKids + } = this; + if (!structTreeKids || structTreeKids.length === 0) { + return; + } + const { + rootDict + } = this; + const structTreeRef = this.newRef; + const structTree = this.xref[structTreeRef.num] = new Dict(); + structTree.setIfName("Type", "StructTreeRoot"); + structTree.setIfArray("K", structTreeKids); + for (const kidRef of structTreeKids) { + const kid = this.xref[kidRef.num]; + const type = kid.get("Type"); + if (!type || isName(type, "StructElem")) { + kid.set("P", structTreeRef); + } + } + if (this.parentTree.size > 0) { + const parentTreeRef = this.#makeNameNumTree(Array.from(this.parentTree.entries()), false); + const parentTree = this.xref[parentTreeRef.num]; + parentTree.setIfName("Type", "ParentTree"); + structTree.set("ParentTree", parentTreeRef); + structTree.set("ParentTreeNextKey", this.parentTree.size); + } + if (this.idTree.size > 0) { + const idTreeRef = this.#makeNameNumTree(Array.from(this.idTree.entries()), true); + const idTree = this.xref[idTreeRef.num]; + idTree.setIfName("Type", "IDTree"); + structTree.set("IDTree", idTreeRef); + } + if (this.classMap.size > 0) { + const classMapRef = this.newRef; + this.xref[classMapRef.num] = this.classMap; + structTree.set("ClassMap", classMapRef); + } + if (this.roleMap.size > 0) { + const roleMapRef = this.newRef; + this.xref[roleMapRef.num] = this.roleMap; + structTree.set("RoleMap", roleMapRef); + } + if (this.namespaces.size > 0) { + const namespacesRef = this.newRef; + this.xref[namespacesRef.num] = Array.from(this.namespaces.values()); + structTree.set("Namespaces", namespacesRef); + } + if (this.structTreeAF.length > 0) { + const structTreeAFRef = this.newRef; + this.xref[structTreeAFRef.num] = this.structTreeAF; + structTree.set("AF", structTreeAFRef); + } + if (this.structTreePronunciationLexicon.length > 0) { + const structTreePronunciationLexiconRef = this.newRef; + this.xref[structTreePronunciationLexiconRef.num] = this.structTreePronunciationLexicon; + structTree.set("PronunciationLexicon", structTreePronunciationLexiconRef); + } + rootDict.set("StructTreeRoot", structTreeRef); + } async #makeRoot() { const { rootDict } = this; rootDict.setIfName("Type", "Catalog"); - rootDict.set("Version", this.version); + rootDict.setIfName("Version", this.version); this.#makePageTree(); this.#makePageLabelsTree(); + this.#makeDestinationsTree(); + this.#makeStructTree(); } #makeInfo() { const infoMap = new Map(); @@ -59016,7 +59757,7 @@ class WorkerMessageHandler { docId, apiVersion } = docParams; - const workerVersion = "5.4.402"; + const workerVersion = "5.4.445"; if (apiVersion !== workerVersion) { throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`); } @@ -59302,7 +60043,7 @@ class WorkerMessageHandler { return pdfManager.ensureCatalog("permissions"); }); handler.on("GetMetadata", function (data) { - return Promise.all([pdfManager.ensureDoc("documentInfo"), pdfManager.ensureCatalog("metadata")]); + return Promise.all([pdfManager.ensureDoc("documentInfo"), pdfManager.ensureCatalog("metadata"), pdfManager.ensureCatalog("hasStructTree")]); }); handler.on("GetMarkInfo", function (data) { return pdfManager.ensureCatalog("markInfo"); diff --git a/toolkit/components/pdfjs/content/web/viewer-geckoview.css b/toolkit/components/pdfjs/content/web/viewer-geckoview.css @@ -1407,6 +1407,7 @@ rgb(251 251 254 / 0.1) ); --sidebar-box-shadow:0 0.25px 0.75px light-dark(rgb(0 0 0 / 0.05), rgb(0 0 0 / 0.2)), 0 2px 6px 0 light-dark(rgb(0 0 0 / 0.1), rgb(0 0 0 / 0.4)); + --sidebar-backdrop-filter:none; --sidebar-border-radius:8px; --sidebar-padding:5px; --sidebar-min-width:180px; @@ -1431,6 +1432,7 @@ width:var(--sidebar-width); min-width:var(--sidebar-min-width); max-width:var(--sidebar-max-width); + backdrop-filter:var(--sidebar-backdrop-filter); .sidebarResizer{ width:var(--resizer-width); @@ -1449,6 +1451,10 @@ &:hover{ background-color:var(--resizer-hover-bg-color); } + &:focus-visible{ + background-color:var(--resizer-hover-bg-color); + outline:none; + } } &.resizing{ diff --git a/toolkit/components/pdfjs/content/web/viewer-geckoview.html b/toolkit/components/pdfjs/content/web/viewer-geckoview.html @@ -1,4 +1,4 @@ -<!DOCTYPE html> +<!doctype html> <!-- Copyright 2012 Mozilla Foundation @@ -22,8 +22,8 @@ See https://github.com/adobe-type-tools/cmap-resources --> <html dir="ltr" mozdisallowselectionprint> <head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <title>PDF.js viewer</title> <!-- This snippet is used in the Firefox extension (included from viewer.html) --> @@ -33,14 +33,11 @@ See https://github.com/adobe-type-tools/cmap-resources <link rel="localization" href="toolkit/pdfviewer/viewer.ftl"/> <script src="resource://pdf.js/web/viewer.mjs" type="module"></script> - </head> <body tabindex="0"> <div id="outerContainer"> - <div id="mainContainer"> - <div id="floatingToolbar"> <button id="download" class="toolbarButton" type="button" tabindex="0" data-l10n-id="pdfjs-download-button"> <span data-l10n-id="pdfjs-download-button-label"></span> @@ -50,7 +47,8 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="viewerContainer" tabindex="0"> <div id="viewer" class="pdfViewer"></div> </div> - </div> <!-- mainContainer --> + </div> + <!-- mainContainer --> <div id="dialogContainer"> <dialog id="passwordDialog"> @@ -58,16 +56,21 @@ See https://github.com/adobe-type-tools/cmap-resources <label for="password" id="passwordText" data-l10n-id="pdfjs-password-label"></label> </div> <div class="row"> - <input type="password" id="password" class="toolbarField"> + <input type="password" id="password" class="toolbarField" /> </div> <div class="buttonRow"> - <button id="passwordCancel" class="dialogButton" type="button"><span data-l10n-id="pdfjs-password-cancel-button"></span></button> - <button id="passwordSubmit" class="dialogButton" type="button"><span data-l10n-id="pdfjs-password-ok-button"></span></button> + <button id="passwordCancel" class="dialogButton" type="button"> + <span data-l10n-id="pdfjs-password-cancel-button"></span> + </button> + <button id="passwordSubmit" class="dialogButton" type="button"> + <span data-l10n-id="pdfjs-password-ok-button"></span> + </button> </div> </dialog> - </div> <!-- dialogContainer --> - - </div> <!-- outerContainer --> + </div> + <!-- dialogContainer --> + </div> + <!-- outerContainer --> <div id="printContainer"></div> </body> </html> 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.402 - * pdfjsBuild = 57334bd20 + * pdfjsVersion = 5.4.445 + * pdfjsBuild = ec5330f78 */ /******/ // The require scope /******/ var __webpack_require__ = {}; @@ -2454,9 +2454,129 @@ class CaretBrowsingMode { } } +;// ./web/sidebar.js + +class Sidebar { + #minWidth = 0; + #maxWidth = 0; + #initialWidth = 0; + #width = 0; + #coefficient; + #visible = false; + constructor({ + sidebar, + resizer, + toggleButton + }, ltr, isResizerOnTheLeft) { + this._sidebar = sidebar; + this.#coefficient = ltr === isResizerOnTheLeft ? -1 : 1; + 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); + toggleButton.addEventListener("click", this.toggle.bind(this)); + sidebar.hidden = true; + } + #makeSidebarResizable(resizer, isResizerOnTheLeft) { + resizer.ariaValueMin = this.#minWidth; + resizer.ariaValueMax = this.#maxWidth; + resizer.ariaValueNow = this.#width; + let pointerMoveAC; + const cancelResize = () => { + this.#width = MathClamp(this.#width, this.#minWidth, this.#maxWidth); + this._sidebar.classList.remove("resizing"); + pointerMoveAC?.abort(); + pointerMoveAC = null; + }; + resizer.addEventListener("pointerdown", e => { + if (pointerMoveAC) { + cancelResize(); + return; + } + const { + clientX + } = e; + stopEvent(e); + let prevX = clientX; + pointerMoveAC = new AbortController(); + const { + signal + } = pointerMoveAC; + const sidebar = this._sidebar; + const sidebarStyle = sidebar.style; + sidebar.classList.add("resizing"); + const parentStyle = sidebar.parentElement.style; + parentStyle.minWidth = 0; + window.addEventListener("contextmenu", noContextMenu, { + signal + }); + window.addEventListener("pointermove", ev => { + if (!pointerMoveAC) { + return; + } + stopEvent(ev); + const { + clientX: x + } = ev; + this.#setNewWidth(x - prevX, parentStyle, resizer, sidebarStyle, isResizerOnTheLeft, false); + prevX = x; + }, { + signal, + capture: true + }); + window.addEventListener("blur", cancelResize, { + signal + }); + window.addEventListener("pointerup", ev => { + if (pointerMoveAC) { + cancelResize(); + stopEvent(ev); + } + }, { + signal + }); + }); + resizer.addEventListener("keydown", e => { + const { + key + } = e; + const isArrowLeft = key === "ArrowLeft"; + if (isArrowLeft || key === "ArrowRight") { + 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); + 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; + } + resizer.ariaValueNow = Math.round(newWidth); + sidebarStyle.width = `${newWidth.toFixed(3)}px`; + if (isResizerOnTheLeft) { + parentStyle.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`; + } + } + toggle() { + this._sidebar.hidden = !(this.#visible = !this.#visible); + } +} + ;// ./web/comment_manager.js + class CommentManager { #dialog; #popup; @@ -2521,12 +2641,11 @@ class CommentManager { this.#popup.destroy(); } } -class CommentSidebar { +class CommentSidebar extends Sidebar { #annotations = null; #eventBus; #boundCommentClick = this.#commentClick.bind(this); #boundCommentKeydown = this.#commentKeydown.bind(this); - #sidebar; #closeButton; #commentsList; #commentCount; @@ -2538,11 +2657,6 @@ class CommentSidebar { #elementsToAnnotations = null; #idsToElements = null; #uiManager = null; - #minWidth = 0; - #maxWidth = 0; - #initialWidth = 0; - #width = 0; - #ltr; constructor({ learnMoreUrl, sidebar, @@ -2553,7 +2667,11 @@ class CommentSidebar { closeButton, commentToolbarButton }, eventBus, linkService, popup, dateFormat, ltr) { - this.#sidebar = sidebar; + super({ + sidebar, + resizer: sidebarResizer, + toggleButton: commentToolbarButton + }, ltr, true); this.#sidebarTitle = sidebarTitle; this.#commentsList = commentsList; this.#commentCount = commentCount; @@ -2562,13 +2680,7 @@ class CommentSidebar { this.#closeButton = closeButton; this.#popup = popup; this.#dateFormat = dateFormat; - this.#ltr = ltr; this.#eventBus = eventBus; - 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(sidebarResizer); closeButton.addEventListener("click", () => { eventBus.dispatch("switchannotationeditormode", { source: this, @@ -2586,70 +2698,6 @@ class CommentSidebar { }; commentToolbarButton.addEventListener("keydown", keyDownCallback); sidebar.addEventListener("keydown", keyDownCallback); - this.#sidebar.hidden = true; - } - #makeSidebarResizable(resizer) { - let pointerMoveAC; - const cancelResize = () => { - this.#width = MathClamp(this.#width, this.#minWidth, this.#maxWidth); - this.#sidebar.classList.remove("resizing"); - pointerMoveAC?.abort(); - pointerMoveAC = null; - }; - resizer.addEventListener("pointerdown", e => { - if (pointerMoveAC) { - cancelResize(); - return; - } - const { - clientX - } = e; - stopEvent(e); - let prevX = clientX; - pointerMoveAC = new AbortController(); - const { - signal - } = pointerMoveAC; - const sign = this.#ltr ? -1 : 1; - const sidebar = this.#sidebar; - const sidebarStyle = sidebar.style; - sidebar.classList.add("resizing"); - const parentStyle = sidebar.parentElement.style; - parentStyle.minWidth = 0; - window.addEventListener("contextmenu", noContextMenu, { - signal - }); - window.addEventListener("pointermove", ev => { - if (!pointerMoveAC) { - return; - } - stopEvent(ev); - const { - clientX: x - } = ev; - const newWidth = this.#width += sign * (x - prevX); - prevX = x; - if (newWidth > this.#maxWidth || newWidth < this.#minWidth) { - return; - } - sidebarStyle.width = `${newWidth.toFixed(3)}px`; - parentStyle.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`; - }, { - signal, - capture: true - }); - window.addEventListener("blur", cancelResize, { - signal - }); - window.addEventListener("pointerup", ev => { - if (pointerMoveAC) { - cancelResize(); - stopEvent(ev); - } - }, { - signal - }); - }); } setUIManager(uiManager) { this.#uiManager = uiManager; @@ -2669,7 +2717,7 @@ class CommentSidebar { } else { this.#setCommentsCount(); } - this.#sidebar.hidden = false; + this._sidebar.hidden = false; this.#eventBus.dispatch("reporttelemetry", { source: this, details: { @@ -2681,7 +2729,7 @@ class CommentSidebar { }); } hide() { - this.#sidebar.hidden = true; + this._sidebar.hidden = true; this.#commentsList.replaceChildren(); this.#elementsToAnnotations = null; this.#idsToElements = null; @@ -2704,7 +2752,7 @@ class CommentSidebar { if (!element) { return; } - this.#sidebar.scrollTop = element.offsetTop - this.#sidebar.offsetTop; + this._sidebar.scrollTop = element.offsetTop - this._sidebar.offsetTop; for (const el of this.#commentsList.children) { el.classList.toggle("selected", el === element); } @@ -2733,8 +2781,8 @@ class CommentSidebar { if (index >= this.#annotations.length) { return; } - this.#setDate(element.firstChild, modificationDate || creationDate); - this.#setText(element.lastChild, richText, contentsObj); + this.#setDate(element.firstElementChild, modificationDate || creationDate); + this.#setText(element.lastElementChild, richText, contentsObj); this.#annotations.splice(index, 1); index = binarySearchFirstItem(this.#annotations, a => this.#sortComments(a, annotation) >= 0); this.#annotations.splice(index, 0, annotation); @@ -3007,6 +3055,11 @@ class CommentDialog { textInput.addEventListener("input", () => { saveButton.disabled = textInput.value === this.#previousText; }); + textInput.addEventListener("keydown", e => { + if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && !saveButton.disabled) { + this.#save(); + } + }); let pointerMoveAC; const cancelDrag = () => { dialog.classList.remove("dragging"); @@ -3704,6 +3757,7 @@ class PasswordPrompt { } ;// ./web/pdf_find_utils.js + const CharacterType = { SPACE: 0, ALPHA_LETTER: 1, @@ -3773,7 +3827,7 @@ function getCharacterType(charCode) { } let NormalizeWithNFKC; function getNormalizeWithNFKC() { - NormalizeWithNFKC ||= ` ¨ª¯²-µ¸-º¼-¾IJ-ijĿ-ŀʼnſDŽ-njDZ-dzʰ-ʸ˘-˝ˠ-ˤʹͺ;΄-΅·ϐ-ϖϰ-ϲϴ-ϵϹևٵ-ٸक़-य़ড়-ঢ়য়ਲ਼ਸ਼ਖ਼-ਜ਼ਫ਼ଡ଼-ଢ଼ำຳໜ-ໝ༌གྷཌྷདྷབྷཛྷཀྵჼᴬ-ᴮᴰ-ᴺᴼ-ᵍᵏ-ᵪᵸᶛ-ᶿẚ-ẛάέήίόύώΆ᾽-῁ΈΉ῍-῏ΐΊ῝-῟ΰΎ῭-`ΌΏ´-῾ - ‑‗․-… ″-‴‶-‷‼‾⁇-⁉⁗ ⁰-ⁱ⁴-₎ₐ-ₜ₨℀-℃℅-ℇ℉-ℓℕ-№ℙ-ℝ℠-™ℤΩℨK-ℭℯ-ℱℳ-ℹ℻-⅀ⅅ-ⅉ⅐-ⅿ↉∬-∭∯-∰〈-〉①-⓪⨌⩴-⩶⫝̸ⱼ-ⱽⵯ⺟⻳⼀-⿕ 〶〸-〺゛-゜ゟヿㄱ-ㆎ㆒-㆟㈀-㈞㈠-㉇㉐-㉾㊀-㏿ꚜ-ꚝꝰꟲ-ꟴꟸ-ꟹꭜ-ꭟꭩ豈-嗀塚晴凞-羽蘒諸逸-都飯-舘並-龎ff-stﬓ-ﬗיִײַ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-﷼︐-︙︰-﹄﹇-﹒﹔-﹦﹨-﹫ﹰ-ﹲﹴﹶ-ﻼ!-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ¢-₩`; + NormalizeWithNFKC ||= `\xA0¨ª¯²-µ¸-º¼-¾IJ-ijĿ-ŀʼnſDŽ-njDZ-dzʰ-ʸ˘-˝ˠ-ˤʹͺ;΄-΅·ϐ-ϖϰ-ϲϴ-ϵϹևٵ-ٸक़-य़ড়-ঢ়য়ਲ਼ਸ਼ਖ਼-ਜ਼ਫ਼ଡ଼-ଢ଼ำຳໜ-ໝ༌གྷཌྷདྷབྷཛྷཀྵჼᴬ-ᴮᴰ-ᴺᴼ-ᵍᵏ-ᵪᵸᶛ-ᶿẚ-ẛάέήίόύώΆ᾽-῁ΈΉ῍-῏ΐΊ῝-῟ΰΎ῭-`ΌΏ´-῾ - ‑‗․-… ″-‴‶-‷‼‾⁇-⁉⁗ ⁰-ⁱ⁴-₎ₐ-ₜ₨℀-℃℅-ℇ℉-ℓℕ-№ℙ-ℝ℠-™ℤΩℨK-ℭℯ-ℱℳ-ℹ℻-⅀ⅅ-ⅉ⅐-ⅿ↉∬-∭∯-∰〈-〉①-⓪⨌⩴-⩶⫝̸ⱼ-ⱽⵯ⺟⻳⼀-⿕ 〶〸-〺゛-゜ゟヿㄱ-ㆎ㆒-㆟㈀-㈞㈠-㉇㉐-㉾㊀-㏿ꚜ-ꚝꝰ꟱-ꟴꟸ-ꟹꭜ-ꭟꭩ豈-嗀塚晴凞-羽蘒諸逸-都飯-舘並-龎ff-stﬓ-ﬗיִײַ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-﷼︐-︙︰-﹄﹇-﹒﹔-﹦﹨-﹫ﹰ-ﹲﹴﹶ-ﻼ!-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ¢-₩`; return NormalizeWithNFKC; } @@ -3805,7 +3859,7 @@ const CHARACTERS_TO_NORMALIZE = { const DIACRITICS_EXCEPTION = new Set([0x3099, 0x309a, 0x094d, 0x09cd, 0x0a4d, 0x0acd, 0x0b4d, 0x0bcd, 0x0c4d, 0x0ccd, 0x0d3b, 0x0d3c, 0x0d4d, 0x0dca, 0x0e3a, 0x0eba, 0x0f84, 0x1039, 0x103a, 0x1714, 0x1734, 0x17d2, 0x1a60, 0x1b44, 0x1baa, 0x1bab, 0x1bf2, 0x1bf3, 0x2d7f, 0xa806, 0xa82c, 0xa8c4, 0xa953, 0xa9c0, 0xaaf6, 0xabed, 0x0c56, 0x0f71, 0x0f72, 0x0f7a, 0x0f7b, 0x0f7c, 0x0f7d, 0x0f80, 0x0f74]); let DIACRITICS_EXCEPTION_STR; const DIACRITICS_REG_EXP = /\p{M}+/gu; -const SPECIAL_CHARS_REG_EXP = /([.*+?^${}()|[\]\\])|(\p{P})|(\s+)|(\p{M})|(\p{L})/gu; +const SPECIAL_CHARS_REG_EXP = /([*+^${}()|[\]\\])|(\p{P}+)|(\s+)|(\p{M})|(\p{L})/gu; const NOT_DIACRITIC_FROM_END_REG_EXP = /([^\p{M}])\p{M}*$/u; const NOT_DIACRITIC_FROM_START_REG_EXP = /^\p{M}*([^\p{M}])/u; const SYLLABLES_REG_EXP = /[\uAC00-\uD7AF\uFA6C\uFACF-\uFAD1\uFAD5-\uFAD7]+/g; @@ -4201,12 +4255,24 @@ class PDFFindController { matchDiacritics } = this.#state; let isUnicode = false; + const addExtraWhitespaces = (original, fixed) => { + if (original === query) { + return fixed; + } + if (query.startsWith(original)) { + return `${fixed}[ ]*`; + } + if (query.endsWith(original)) { + return `[ ]*${fixed}`; + } + return `[ ]*${fixed}[ ]*`; + }; query = query.replaceAll(SPECIAL_CHARS_REG_EXP, (match, p1, p2, p3, p4, p5) => { if (p1) { - return `[ ]*\\${p1}[ ]*`; + return addExtraWhitespaces(p1, `\\${p1}`); } if (p2) { - return `[ ]*${p2}[ ]*`; + return addExtraWhitespaces(p2, p2.replaceAll(/[.?]/g, "\\$&")); } if (p3) { return "[ ]+"; @@ -8300,7 +8366,7 @@ class PDFViewer { #textLayerMode = TextLayerMode.ENABLE; #viewerAlert = null; constructor(options) { - const viewerVersion = "5.4.402"; + const viewerVersion = "5.4.445"; if (version !== viewerVersion) { throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`); } @@ -11005,11 +11071,16 @@ const PDFViewerApplication = { info, metadata, contentDispositionFilename, - contentLength + contentLength, + hasStructTree } = await pdfDocument.getMetadata(); if (pdfDocument !== this.pdfDocument) { return; } + this.externalServices.reportTelemetry({ + type: "taggedPDF", + data: hasStructTree + }); this.documentInfo = info; this.metadata = metadata; this._contentDispositionFilename ??= contentDispositionFilename; diff --git a/toolkit/components/pdfjs/content/web/viewer.css b/toolkit/components/pdfjs/content/web/viewer.css @@ -4544,6 +4544,7 @@ rgb(251 251 254 / 0.1) ); --sidebar-box-shadow:0 0.25px 0.75px light-dark(rgb(0 0 0 / 0.05), rgb(0 0 0 / 0.2)), 0 2px 6px 0 light-dark(rgb(0 0 0 / 0.1), rgb(0 0 0 / 0.4)); + --sidebar-backdrop-filter:none; --sidebar-border-radius:8px; --sidebar-padding:5px; --sidebar-min-width:180px; @@ -4568,6 +4569,7 @@ width:var(--sidebar-width); min-width:var(--sidebar-min-width); max-width:var(--sidebar-max-width); + backdrop-filter:var(--sidebar-backdrop-filter); .sidebarResizer{ width:var(--resizer-width); @@ -4586,6 +4588,10 @@ &:hover{ background-color:var(--resizer-hover-bg-color); } + &:focus-visible{ + background-color:var(--resizer-hover-bg-color); + outline:none; + } } &.resizing{ diff --git a/toolkit/components/pdfjs/content/web/viewer.html b/toolkit/components/pdfjs/content/web/viewer.html @@ -1,4 +1,4 @@ -<!DOCTYPE html> +<!doctype html> <!-- Copyright 2012 Mozilla Foundation @@ -22,8 +22,8 @@ See https://github.com/adobe-type-tools/cmap-resources --> <html dir="ltr" mozdisallowselectionprint> <head> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> <title>PDF.js viewer</title> <!-- This snippet is used in the Firefox extension (included from viewer.html) --> @@ -43,17 +43,53 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="toolbarSidebar" class="toolbarHorizontalGroup"> <div id="toolbarSidebarLeft"> <div id="sidebarViewButtons" class="toolbarHorizontalGroup toggled" role="radiogroup"> - <button id="viewThumbnail" class="toolbarButton toggled" type="button" tabindex="0" data-l10n-id="pdfjs-thumbs-button" role="radio" aria-checked="true" aria-controls="thumbnailView"> - <span data-l10n-id="pdfjs-thumbs-button-label"></span> + <button + id="viewThumbnail" + class="toolbarButton toggled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-thumbs-button" + role="radio" + aria-checked="true" + aria-controls="thumbnailView" + > + <span data-l10n-id="pdfjs-thumbs-button-label"></span> </button> - <button id="viewOutline" class="toolbarButton" type="button" tabindex="0" data-l10n-id="pdfjs-document-outline-button" role="radio" aria-checked="false" aria-controls="outlineView"> - <span data-l10n-id="pdfjs-document-outline-button-label"></span> + <button + id="viewOutline" + class="toolbarButton" + type="button" + tabindex="0" + data-l10n-id="pdfjs-document-outline-button" + role="radio" + aria-checked="false" + aria-controls="outlineView" + > + <span data-l10n-id="pdfjs-document-outline-button-label"></span> </button> - <button id="viewAttachments" class="toolbarButton" type="button" tabindex="0" data-l10n-id="pdfjs-attachments-button" role="radio" aria-checked="false" aria-controls="attachmentsView"> - <span data-l10n-id="pdfjs-attachments-button-label"></span> + <button + id="viewAttachments" + class="toolbarButton" + type="button" + tabindex="0" + data-l10n-id="pdfjs-attachments-button" + role="radio" + aria-checked="false" + aria-controls="attachmentsView" + > + <span data-l10n-id="pdfjs-attachments-button-label"></span> </button> - <button id="viewLayers" class="toolbarButton" type="button" tabindex="0" data-l10n-id="pdfjs-layers-button" role="radio" aria-checked="false" aria-controls="layersView"> - <span data-l10n-id="pdfjs-layers-button-label"></span> + <button + id="viewLayers" + class="toolbarButton" + type="button" + tabindex="0" + data-l10n-id="pdfjs-layers-button" + role="radio" + aria-checked="false" + aria-controls="layersView" + > + <span data-l10n-id="pdfjs-layers-button-label"></span> </button> </div> </div> @@ -62,42 +98,63 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="outlineOptionsContainer" class="toolbarHorizontalGroup"> <div class="verticalToolbarSeparator"></div> - <button id="currentOutlineItem" class="toolbarButton" type="button" disabled="disabled" tabindex="0" data-l10n-id="pdfjs-current-outline-item-button"> + <button + id="currentOutlineItem" + class="toolbarButton" + type="button" + disabled="disabled" + tabindex="0" + data-l10n-id="pdfjs-current-outline-item-button" + > <span data-l10n-id="pdfjs-current-outline-item-button-label"></span> </button> </div> </div> </div> <div id="sidebarContent"> - <div id="thumbnailView"> - </div> - <div id="outlineView" class="hidden"> - </div> - <div id="attachmentsView" class="hidden"> - </div> - <div id="layersView" class="hidden"> - </div> + <div id="thumbnailView"></div> + <div id="outlineView" class="hidden"></div> + <div id="attachmentsView" class="hidden"></div> + <div id="layersView" class="hidden"></div> </div> <div id="sidebarResizer"></div> - </div> <!-- sidebarContainer --> + </div> + <!-- sidebarContainer --> <div id="mainContainer"> <div class="toolbar"> <div id="toolbarContainer"> <div id="toolbarViewer" class="toolbarHorizontalGroup"> <div id="toolbarViewerLeft" class="toolbarHorizontalGroup"> - <button id="sidebarToggleButton" class="toolbarButton" type="button" tabindex="0" data-l10n-id="pdfjs-toggle-sidebar-button" aria-expanded="false" aria-haspopup="true" aria-controls="sidebarContainer"> + <button + id="sidebarToggleButton" + class="toolbarButton" + type="button" + tabindex="0" + data-l10n-id="pdfjs-toggle-sidebar-button" + aria-expanded="false" + aria-haspopup="true" + aria-controls="sidebarContainer" + > <span data-l10n-id="pdfjs-toggle-sidebar-button-label"></span> </button> <div class="toolbarButtonSpacer"></div> <div class="toolbarButtonWithContainer"> - <button id="viewFindButton" class="toolbarButton" type="button" tabindex="0" data-l10n-id="pdfjs-findbar-button" aria-expanded="false" aria-controls="findbar"> + <button + id="viewFindButton" + class="toolbarButton" + type="button" + tabindex="0" + data-l10n-id="pdfjs-findbar-button" + aria-expanded="false" + aria-controls="findbar" + > <span data-l10n-id="pdfjs-findbar-button-label"></span> </button> <div class="hidden doorHanger toolbarHorizontalGroup" id="findbar"> <div id="findInputContainer" class="toolbarHorizontalGroup"> <span class="loadingInput end toolbarHorizontalGroup"> - <input id="findInput" class="toolbarField" tabindex="0" data-l10n-id="pdfjs-find-input" aria-invalid="false"> + <input id="findInput" class="toolbarField" tabindex="0" data-l10n-id="pdfjs-find-input" aria-invalid="false" /> </span> <div class="toolbarHorizontalGroup"> <button id="findPreviousButton" class="toolbarButton" type="button" tabindex="0" data-l10n-id="pdfjs-find-previous-button"> @@ -135,7 +192,8 @@ See https://github.com/adobe-type-tools/cmap-resources <span id="findResultsCount" class="toolbarLabel"></span> <span id="findMsg" class="toolbarLabel"></span> </div> - </div> <!-- findbar --> + </div> + <!-- findbar --> </div> <div class="toolbarHorizontalGroup hiddenSmallView"> <button class="toolbarButton" type="button" id="previous" tabindex="0" data-l10n-id="pdfjs-previous-button"> @@ -148,7 +206,16 @@ See https://github.com/adobe-type-tools/cmap-resources </div> <div class="toolbarHorizontalGroup"> <span class="loadingInput start toolbarHorizontalGroup"> - <input type="number" id="pageNumber" class="toolbarField" value="1" min="1" tabindex="0" data-l10n-id="pdfjs-page-input" autocomplete="off"> + <input + type="number" + id="pageNumber" + class="toolbarField" + value="1" + min="1" + tabindex="0" + data-l10n-id="pdfjs-page-input" + autocomplete="off" + /> </span> <span id="numPages" class="toolbarLabel"></span> </div> @@ -169,7 +236,14 @@ See https://github.com/adobe-type-tools/cmap-resources <option id="pageActualOption" value="page-actual" data-l10n-id="pdfjs-page-scale-actual"></option> <option id="pageFitOption" value="page-fit" data-l10n-id="pdfjs-page-scale-fit"></option> <option id="pageWidthOption" value="page-width" data-l10n-id="pdfjs-page-scale-width"></option> - <option id="customScaleOption" value="custom" disabled="disabled" hidden="true" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 0 }'></option> + <option + id="customScaleOption" + value="custom" + disabled="disabled" + hidden="true" + data-l10n-id="pdfjs-page-scale-percent" + data-l10n-args='{ "scale": 0 }' + ></option> <option value="0.5" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 50 }'></option> <option value="0.75" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 75 }'></option> <option value="1" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 100 }'></option> @@ -184,12 +258,22 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="toolbarViewerRight" class="toolbarHorizontalGroup"> <div id="editorModeButtons" class="toolbarHorizontalGroup"> <div id="editorComment" class="toolbarButtonWithContainer" hidden="true"> - <button id="editorCommentButton" class="toolbarButton" type="button" tabindex="0" disabled="disabled" aria-expanded="false" aria-haspopup="true" aria-controls="editorCommentParamsToolbar" data-l10n-id="pdfjs-editor-comment-button"> + <button + id="editorCommentButton" + class="toolbarButton" + type="button" + tabindex="0" + disabled="disabled" + aria-expanded="false" + aria-haspopup="true" + aria-controls="editorCommentParamsToolbar" + data-l10n-id="pdfjs-editor-comment-button" + > <span data-l10n-id="pdfjs-editor-comment-button-label"></span> </button> <div class="editorParamsToolbar hidden menu" id="editorCommentParamsToolbar"> <div id="editorCommentsSidebar" class="menuContainer comment sidebar" role="landmark" aria-labelledby="editorCommentsSidebarHeader"> - <div id="editorCommentsSidebarResizer" class="sidebarResizer"></div> + <div id="editorCommentsSidebarResizer" class="sidebarResizer" role="separator" aria-controls="editorCommentsSidebar" tabindex="0"></div> <div id="editorCommentsSidebarHeader" role="heading" aria-level="2"> <span class="commentCount"> <span id="editorCommentsSidebarTitle" data-l10n-id="pdfjs-editor-comments-sidebar-title" data-l10n-args='{ "count": 0 }'></span> @@ -206,19 +290,45 @@ See https://github.com/adobe-type-tools/cmap-resources </div> </div> <div id="editorSignature" class="toolbarButtonWithContainer" hidden="true"> - <button id="editorSignatureButton" class="toolbarButton" type="button" tabindex="0" disabled="disabled" aria-expanded="false" aria-haspopup="true" aria-controls="editorSignatureParamsToolbar" data-l10n-id="pdfjs-editor-signature-button"> + <button + id="editorSignatureButton" + class="toolbarButton" + type="button" + tabindex="0" + disabled="disabled" + aria-expanded="false" + aria-haspopup="true" + aria-controls="editorSignatureParamsToolbar" + data-l10n-id="pdfjs-editor-signature-button" + > <span data-l10n-id="pdfjs-editor-signature-button-label"></span> </button> <div class="editorParamsToolbar hidden doorHangerRight menu" id="editorSignatureParamsToolbar"> <div id="addSignatureDoorHanger" class="menuContainer" role="region" data-l10n-id="pdfjs-editor-add-signature-container"> - <button id="editorSignatureAddSignature" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-editor-signature-add-signature-button"> + <button + id="editorSignatureAddSignature" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-editor-signature-add-signature-button" + > <span data-l10n-id="pdfjs-editor-signature-add-signature-button-label" class="editorParamsLabel"></span> </button> </div> </div> </div> <div id="editorHighlight" class="toolbarButtonWithContainer"> - <button id="editorHighlightButton" class="toolbarButton" type="button" disabled="disabled" aria-expanded="false" aria-haspopup="true" aria-controls="editorHighlightParamsToolbar" tabindex="0" data-l10n-id="pdfjs-editor-highlight-button"> + <button + id="editorHighlightButton" + class="toolbarButton" + type="button" + disabled="disabled" + aria-expanded="false" + aria-haspopup="true" + aria-controls="editorHighlightParamsToolbar" + tabindex="0" + data-l10n-id="pdfjs-editor-highlight-button" + > <span data-l10n-id="pdfjs-editor-highlight-button-label"></span> </button> <div class="editorParamsToolbar hidden doorHangerRight" id="editorHighlightParamsToolbar"> @@ -227,66 +337,123 @@ See https://github.com/adobe-type-tools/cmap-resources <span id="highlightColorPickerLabel" class="editorParamsLabel" data-l10n-id="pdfjs-editor-highlight-colorpicker-label"></span> </div> <div id="editorHighlightThickness"> - <label for="editorFreeHighlightThickness" class="editorParamsLabel" data-l10n-id="pdfjs-editor-free-highlight-thickness-input"></label> + <label + for="editorFreeHighlightThickness" + class="editorParamsLabel" + data-l10n-id="pdfjs-editor-free-highlight-thickness-input" + ></label> <div class="thicknessPicker"> - <input type="range" id="editorFreeHighlightThickness" class="editorParamsSlider" data-l10n-id="pdfjs-editor-free-highlight-thickness-title" value="12" min="8" max="24" step="1" tabindex="0"> + <input + type="range" + id="editorFreeHighlightThickness" + class="editorParamsSlider" + data-l10n-id="pdfjs-editor-free-highlight-thickness-title" + value="12" + min="8" + max="24" + step="1" + tabindex="0" + /> </div> </div> <div id="editorHighlightVisibility"> <div class="divider"></div> <div class="toggler"> <label for="editorHighlightShowAll" class="editorParamsLabel" data-l10n-id="pdfjs-editor-highlight-show-all-button-label"></label> - <button id="editorHighlightShowAll" class="toggle-button" type="button" data-l10n-id="pdfjs-editor-highlight-show-all-button" aria-pressed="true" tabindex="0"></button> + <button + id="editorHighlightShowAll" + class="toggle-button" + type="button" + data-l10n-id="pdfjs-editor-highlight-show-all-button" + aria-pressed="true" + tabindex="0" + ></button> </div> </div> </div> </div> </div> <div id="editorFreeText" class="toolbarButtonWithContainer"> - <button id="editorFreeTextButton" class="toolbarButton" type="button" disabled="disabled" aria-expanded="false" aria-haspopup="true" aria-controls="editorFreeTextParamsToolbar" tabindex="0" data-l10n-id="pdfjs-editor-free-text-button"> + <button + id="editorFreeTextButton" + class="toolbarButton" + type="button" + disabled="disabled" + aria-expanded="false" + aria-haspopup="true" + aria-controls="editorFreeTextParamsToolbar" + tabindex="0" + data-l10n-id="pdfjs-editor-free-text-button" + > <span data-l10n-id="pdfjs-editor-free-text-button-label"></span> </button> <div class="editorParamsToolbar hidden doorHangerRight" id="editorFreeTextParamsToolbar"> <div class="editorParamsToolbarContainer"> <div class="editorParamsSetter"> <label for="editorFreeTextColor" class="editorParamsLabel" data-l10n-id="pdfjs-editor-free-text-color-input"></label> - <input type="color" id="editorFreeTextColor" class="editorParamsColor" tabindex="0"> + <input type="color" id="editorFreeTextColor" class="editorParamsColor" tabindex="0" /> </div> <div class="editorParamsSetter"> <label for="editorFreeTextFontSize" class="editorParamsLabel" data-l10n-id="pdfjs-editor-free-text-size-input"></label> - <input type="range" id="editorFreeTextFontSize" class="editorParamsSlider" value="10" min="5" max="100" step="1" tabindex="0"> + <input type="range" id="editorFreeTextFontSize" class="editorParamsSlider" value="10" min="5" max="100" step="1" tabindex="0" /> </div> </div> </div> </div> <div id="editorInk" class="toolbarButtonWithContainer"> - <button id="editorInkButton" class="toolbarButton" type="button" disabled="disabled" aria-expanded="false" aria-haspopup="true" aria-controls="editorInkParamsToolbar" tabindex="0" data-l10n-id="pdfjs-editor-ink-button"> + <button + id="editorInkButton" + class="toolbarButton" + type="button" + disabled="disabled" + aria-expanded="false" + aria-haspopup="true" + aria-controls="editorInkParamsToolbar" + tabindex="0" + data-l10n-id="pdfjs-editor-ink-button" + > <span data-l10n-id="pdfjs-editor-ink-button-label"></span> </button> <div class="editorParamsToolbar hidden doorHangerRight" id="editorInkParamsToolbar"> <div class="editorParamsToolbarContainer"> <div class="editorParamsSetter"> <label for="editorInkColor" class="editorParamsLabel" data-l10n-id="pdfjs-editor-ink-color-input"></label> - <input type="color" id="editorInkColor" class="editorParamsColor" tabindex="0"> + <input type="color" id="editorInkColor" class="editorParamsColor" tabindex="0" /> </div> <div class="editorParamsSetter"> <label for="editorInkThickness" class="editorParamsLabel" data-l10n-id="pdfjs-editor-ink-thickness-input"></label> - <input type="range" id="editorInkThickness" class="editorParamsSlider" value="1" min="1" max="20" step="1" tabindex="0"> + <input type="range" id="editorInkThickness" class="editorParamsSlider" value="1" min="1" max="20" step="1" tabindex="0" /> </div> <div class="editorParamsSetter"> <label for="editorInkOpacity" class="editorParamsLabel" data-l10n-id="pdfjs-editor-ink-opacity-input"></label> - <input type="range" id="editorInkOpacity" class="editorParamsSlider" value="1" min="0.05" max="1" step="0.05" tabindex="0"> + <input type="range" id="editorInkOpacity" class="editorParamsSlider" value="1" min="0.05" max="1" step="0.05" tabindex="0" /> </div> </div> </div> </div> <div id="editorStamp" class="toolbarButtonWithContainer"> - <button id="editorStampButton" class="toolbarButton" type="button" disabled="disabled" aria-expanded="false" aria-haspopup="true" aria-controls="editorStampParamsToolbar" tabindex="0" data-l10n-id="pdfjs-editor-stamp-button"> + <button + id="editorStampButton" + class="toolbarButton" + type="button" + disabled="disabled" + aria-expanded="false" + aria-haspopup="true" + aria-controls="editorStampParamsToolbar" + tabindex="0" + data-l10n-id="pdfjs-editor-stamp-button" + > <span data-l10n-id="pdfjs-editor-stamp-button-label"></span> </button> <div class="editorParamsToolbar hidden doorHangerRight menu" id="editorStampParamsToolbar"> <div class="menuContainer"> - <button id="editorStampAddImage" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-editor-stamp-add-image-button"> + <button + id="editorStampAddImage" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-editor-stamp-add-image-button" + > <span class="editorParamsLabel" data-l10n-id="pdfjs-editor-stamp-add-image-button-label"></span> </button> </div> @@ -309,7 +476,16 @@ See https://github.com/adobe-type-tools/cmap-resources <div class="verticalToolbarSeparator hiddenMediumView"></div> <div id="secondaryToolbarToggle" class="toolbarButtonWithContainer"> - <button id="secondaryToolbarToggleButton" class="toolbarButton" type="button" tabindex="0" data-l10n-id="pdfjs-tools-button" aria-expanded="false" aria-haspopup="true" aria-controls="secondaryToolbar"> + <button + id="secondaryToolbarToggleButton" + class="toolbarButton" + type="button" + tabindex="0" + data-l10n-id="pdfjs-tools-button" + aria-expanded="false" + aria-haspopup="true" + aria-controls="secondaryToolbar" + > <span data-l10n-id="pdfjs-tools-button-label"></span> </button> <div id="secondaryToolbar" class="hidden doorHangerRight menu"> @@ -356,10 +532,26 @@ See https://github.com/adobe-type-tools/cmap-resources <div class="horizontalToolbarSeparator"></div> <div id="cursorToolButtons" role="radiogroup"> - <button id="cursorSelectTool" class="toolbarButton labeled toggled" type="button" tabindex="0" data-l10n-id="pdfjs-cursor-text-select-tool-button" role="radio" aria-checked="true"> + <button + id="cursorSelectTool" + class="toolbarButton labeled toggled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-cursor-text-select-tool-button" + role="radio" + aria-checked="true" + > <span data-l10n-id="pdfjs-cursor-text-select-tool-button-label"></span> </button> - <button id="cursorHandTool" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-cursor-hand-tool-button" role="radio" aria-checked="false"> + <button + id="cursorHandTool" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-cursor-hand-tool-button" + role="radio" + aria-checked="false" + > <span data-l10n-id="pdfjs-cursor-hand-tool-button-label"></span> </button> </div> @@ -367,16 +559,48 @@ See https://github.com/adobe-type-tools/cmap-resources <div class="horizontalToolbarSeparator"></div> <div id="scrollModeButtons" role="radiogroup"> - <button id="scrollPage" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-scroll-page-button" role="radio" aria-checked="false"> + <button + id="scrollPage" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-scroll-page-button" + role="radio" + aria-checked="false" + > <span data-l10n-id="pdfjs-scroll-page-button-label"></span> </button> - <button id="scrollVertical" class="toolbarButton labeled toggled" type="button" tabindex="0" data-l10n-id="pdfjs-scroll-vertical-button" role="radio" aria-checked="true"> + <button + id="scrollVertical" + class="toolbarButton labeled toggled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-scroll-vertical-button" + role="radio" + aria-checked="true" + > <span data-l10n-id="pdfjs-scroll-vertical-button-label"></span> </button> - <button id="scrollHorizontal" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-scroll-horizontal-button" role="radio" aria-checked="false"> + <button + id="scrollHorizontal" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-scroll-horizontal-button" + role="radio" + aria-checked="false" + > <span data-l10n-id="pdfjs-scroll-horizontal-button-label"></span> </button> - <button id="scrollWrapped" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-scroll-wrapped-button" role="radio" aria-checked="false"> + <button + id="scrollWrapped" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-scroll-wrapped-button" + role="radio" + aria-checked="false" + > <span data-l10n-id="pdfjs-scroll-wrapped-button-label"></span> </button> </div> @@ -384,36 +608,74 @@ See https://github.com/adobe-type-tools/cmap-resources <div class="horizontalToolbarSeparator"></div> <div id="spreadModeButtons" role="radiogroup"> - <button id="spreadNone" class="toolbarButton labeled toggled" type="button" tabindex="0" data-l10n-id="pdfjs-spread-none-button" role="radio" aria-checked="true"> + <button + id="spreadNone" + class="toolbarButton labeled toggled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-spread-none-button" + role="radio" + aria-checked="true" + > <span data-l10n-id="pdfjs-spread-none-button-label"></span> </button> - <button id="spreadOdd" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-spread-odd-button" role="radio" aria-checked="false"> + <button + id="spreadOdd" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-spread-odd-button" + role="radio" + aria-checked="false" + > <span data-l10n-id="pdfjs-spread-odd-button-label"></span> </button> - <button id="spreadEven" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-spread-even-button" role="radio" aria-checked="false"> + <button + id="spreadEven" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-spread-even-button" + role="radio" + aria-checked="false" + > <span data-l10n-id="pdfjs-spread-even-button-label"></span> </button> </div> <div id="imageAltTextSettingsSeparator" class="horizontalToolbarSeparator hidden"></div> - <button id="imageAltTextSettings" type="button" class="toolbarButton labeled hidden" tabindex="0" data-l10n-id="pdfjs-image-alt-text-settings-button" aria-controls="altTextSettingsDialog"> + <button + id="imageAltTextSettings" + type="button" + class="toolbarButton labeled hidden" + tabindex="0" + data-l10n-id="pdfjs-image-alt-text-settings-button" + aria-controls="altTextSettingsDialog" + > <span data-l10n-id="pdfjs-image-alt-text-settings-button-label"></span> </button> <div class="horizontalToolbarSeparator"></div> - <button id="documentProperties" class="toolbarButton labeled" type="button" tabindex="0" data-l10n-id="pdfjs-document-properties-button" aria-controls="documentPropertiesDialog"> + <button + id="documentProperties" + class="toolbarButton labeled" + type="button" + tabindex="0" + data-l10n-id="pdfjs-document-properties-button" + aria-controls="documentPropertiesDialog" + > <span data-l10n-id="pdfjs-document-properties-button-label"></span> </button> </div> - </div> <!-- secondaryToolbar --> + </div> + <!-- secondaryToolbar --> </div> </div> </div> <div id="loadingBar"> <div class="progress"> - <div class="glimmer"> - </div> + <div class="glimmer"></div> </div> </div> </div> @@ -422,7 +684,8 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="viewerContainer" tabindex="0"> <div id="viewer" class="pdfViewer"></div> </div> - </div> <!-- mainContainer --> + </div> + <!-- mainContainer --> <div id="dialogContainer"> <dialog id="passwordDialog"> @@ -430,7 +693,7 @@ See https://github.com/adobe-type-tools/cmap-resources <label for="password" id="passwordText" data-l10n-id="pdfjs-password-label"></label> </div> <div class="row"> - <input type="password" id="password" class="toolbarField"> + <input type="password" id="password" class="toolbarField" /> </div> <div class="buttonRow"> <button id="passwordCancel" class="dialogButton" type="button"><span data-l10n-id="pdfjs-password-cancel-button"></span></button> @@ -510,7 +773,7 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="addDescription"> <div class="radio"> <div class="radioButton"> - <input type="radio" id="descriptionButton" name="altTextOption" tabindex="0" aria-describedby="descriptionAreaLabel" checked> + <input type="radio" id="descriptionButton" name="altTextOption" tabindex="0" aria-describedby="descriptionAreaLabel" checked /> <label for="descriptionButton" data-l10n-id="pdfjs-editor-alt-text-add-description-label"></label> </div> <div class="radioLabel"> @@ -524,7 +787,7 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="markAsDecorative"> <div class="radio"> <div class="radioButton"> - <input type="radio" id="decorativeButton" name="altTextOption" aria-describedby="decorativeLabel"> + <input type="radio" id="decorativeButton" name="altTextOption" aria-describedby="decorativeLabel" /> <label for="decorativeButton" data-l10n-id="pdfjs-editor-alt-text-mark-decorative-label"></label> </div> <div class="radioLabel"> @@ -533,7 +796,9 @@ See https://github.com/adobe-type-tools/cmap-resources </div> </div> <div id="buttons"> - <button id="altTextCancel" class="secondaryButton" type="button" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-cancel-button"></span></button> + <button id="altTextCancel" class="secondaryButton" type="button" tabindex="0"> + <span data-l10n-id="pdfjs-editor-alt-text-cancel-button"></span> + </button> <button id="altTextSave" class="primaryButton" type="button" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-save-button"></span></button> </div> </div> @@ -548,17 +813,43 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="descriptionInstruction"> <div id="newAltTextDescriptionContainer"> <div class="altTextSpinner" role="status" aria-live="polite"></div> - <textarea id="newAltTextDescriptionTextarea" aria-labelledby="descriptionAreaLabel" data-l10n-id="pdfjs-editor-new-alt-text-textarea" tabindex="0"></textarea> + <textarea + id="newAltTextDescriptionTextarea" + aria-labelledby="descriptionAreaLabel" + data-l10n-id="pdfjs-editor-new-alt-text-textarea" + tabindex="0" + ></textarea> </div> <span id="newAltTextDescription" role="note" data-l10n-id="pdfjs-editor-new-alt-text-description"></span> - <div id="newAltTextDisclaimer" role="note"><div><span data-l10n-id="pdfjs-editor-new-alt-text-disclaimer1"></span> <a href="https://support.mozilla.org/en-US/kb/pdf-alt-text" target="_blank" rel="noopener noreferrer" id="newAltTextLearnMore" data-l10n-id="pdfjs-editor-new-alt-text-disclaimer-learn-more-url" tabindex="0"></a></div></div> + <div id="newAltTextDisclaimer" role="note"> + <div> + <span data-l10n-id="pdfjs-editor-new-alt-text-disclaimer1"></span> + <a + href="https://support.mozilla.org/en-US/kb/pdf-alt-text" + target="_blank" + rel="noopener noreferrer" + id="newAltTextLearnMore" + data-l10n-id="pdfjs-editor-new-alt-text-disclaimer-learn-more-url" + tabindex="0" + ></a> + </div> + </div> </div> <div id="newAltTextCreateAutomatically" class="toggler"> <button id="newAltTextCreateAutomaticallyButton" class="toggle-button" type="button" aria-pressed="true" tabindex="0"></button> - <label for="newAltTextCreateAutomaticallyButton" class="togglerLabel" data-l10n-id="pdfjs-editor-new-alt-text-create-automatically-button-label"></label> + <label + for="newAltTextCreateAutomaticallyButton" + class="togglerLabel" + data-l10n-id="pdfjs-editor-new-alt-text-create-automatically-button-label" + ></label> </div> <div id="newAltTextDownloadModel" class="hidden"> - <span id="newAltTextDownloadModelDescription" data-l10n-id="pdfjs-editor-new-alt-text-ai-model-downloading-progress" aria-valuemin="0" data-l10n-args='{ "totalSize": 0, "downloadedSize": 0 }'></span> + <span + id="newAltTextDownloadModelDescription" + data-l10n-id="pdfjs-editor-new-alt-text-ai-model-downloading-progress" + aria-valuemin="0" + data-l10n-args='{ "totalSize": 0, "downloadedSize": 0 }' + ></span> </div> </div> <div id="newAltTextImagePreview"></div> @@ -567,15 +858,23 @@ See https://github.com/adobe-type-tools/cmap-resources <div> <div> <span class="title" data-l10n-id="pdfjs-editor-new-alt-text-error-title"></span> - <span class="description" data-l10n-id="pdfjs-editor-new-alt-text-error-description"></span> + <span class="description" data-l10n-id="pdfjs-editor-new-alt-text-error-description"></span> </div> - <button id="newAltTextCloseButton" class="closeButton" type="button" tabindex="0"><span data-l10n-id="pdfjs-editor-new-alt-text-error-close-button"></span></button> + <button id="newAltTextCloseButton" class="closeButton" type="button" tabindex="0"> + <span data-l10n-id="pdfjs-editor-new-alt-text-error-close-button"></span> + </button> </div> </div> <div id="newAltTextButtons" class="dialogButtonsGroup"> - <button id="newAltTextCancel" type="button" class="secondaryButton hidden" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-cancel-button"></span></button> - <button id="newAltTextNotNow" type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-new-alt-text-not-now-button"></span></button> - <button id="newAltTextSave" type="button" class="primaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-save-button"></span></button> + <button id="newAltTextCancel" type="button" class="secondaryButton hidden" tabindex="0"> + <span data-l10n-id="pdfjs-editor-alt-text-cancel-button"></span> + </button> + <button id="newAltTextNotNow" type="button" class="secondaryButton" tabindex="0"> + <span data-l10n-id="pdfjs-editor-new-alt-text-not-now-button"></span> + </button> + <button id="newAltTextSave" type="button" class="primaryButton" tabindex="0"> + <span data-l10n-id="pdfjs-editor-alt-text-save-button"></span> + </button> </div> </div> </dialog> @@ -594,7 +893,15 @@ See https://github.com/adobe-type-tools/cmap-resources <label for="createModelButton" class="togglerLabel" data-l10n-id="pdfjs-editor-alt-text-settings-create-model-button-label"></label> </div> <div id="createModelDescription" class="description"> - <span data-l10n-id="pdfjs-editor-alt-text-settings-create-model-description"></span> <a href="https://support.mozilla.org/en-US/kb/pdf-alt-text" target="_blank" rel="noopener noreferrer" id="altTextSettingsLearnMore" data-l10n-id="pdfjs-editor-new-alt-text-disclaimer-learn-more-url" tabindex="0"></a> + <span data-l10n-id="pdfjs-editor-alt-text-settings-create-model-description"></span> + <a + href="https://support.mozilla.org/en-US/kb/pdf-alt-text" + target="_blank" + rel="noopener noreferrer" + id="altTextSettingsLearnMore" + data-l10n-id="pdfjs-editor-new-alt-text-disclaimer-learn-more-url" + tabindex="0" + ></a> </div> </div> <div id="aiModelSettings"> @@ -604,8 +911,12 @@ See https://github.com/adobe-type-tools/cmap-resources <span data-l10n-id="pdfjs-editor-alt-text-settings-ai-model-description"></span> </div> </div> - <button id="deleteModelButton" type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-settings-delete-model-button"></span></button> - <button id="downloadModelButton" type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-settings-download-model-button"></span></button> + <button id="deleteModelButton" type="button" class="secondaryButton" tabindex="0"> + <span data-l10n-id="pdfjs-editor-alt-text-settings-delete-model-button"></span> + </button> + <button id="downloadModelButton" type="button" class="secondaryButton" tabindex="0"> + <span data-l10n-id="pdfjs-editor-alt-text-settings-download-model-button"></span> + </button> </div> </div> </div> @@ -623,7 +934,9 @@ See https://github.com/adobe-type-tools/cmap-resources </div> </div> <div id="buttons" class="dialogButtonsGroup"> - <button id="altTextSettingsCloseButton" type="button" class="primaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-alt-text-settings-close-button"></span></button> + <button id="altTextSettingsCloseButton" type="button" class="primaryButton" tabindex="0"> + <span data-l10n-id="pdfjs-editor-alt-text-settings-close-button"></span> + </button> </div> </div> </dialog> @@ -635,13 +948,37 @@ See https://github.com/adobe-type-tools/cmap-resources <span role="sectionhead" data-l10n-id="pdfjs-editor-add-signature-dialog-title" tabindex="0"></span> </div> <div role="tablist" id="addSignatureOptions"> - <button id="addSignatureTypeButton" type="button" role="tab" aria-selected="true" aria-controls="addSignatureTypeContainer" data-l10n-id="pdfjs-editor-add-signature-type-button" tabindex="0"></button> - <button id="addSignatureDrawButton" type="button" role="tab" aria-selected="false" aria-controls="addSignatureDrawContainer" data-l10n-id="pdfjs-editor-add-signature-draw-button" tabindex="0"></button> - <button id="addSignatureImageButton" type="button" role="tab" aria-selected="false" aria-controls="addSignatureImageContainer" data-l10n-id="pdfjs-editor-add-signature-image-button" tabindex="-1"></button> + <button + id="addSignatureTypeButton" + type="button" + role="tab" + aria-selected="true" + aria-controls="addSignatureTypeContainer" + data-l10n-id="pdfjs-editor-add-signature-type-button" + tabindex="0" + ></button> + <button + id="addSignatureDrawButton" + type="button" + role="tab" + aria-selected="false" + aria-controls="addSignatureDrawContainer" + data-l10n-id="pdfjs-editor-add-signature-draw-button" + tabindex="0" + ></button> + <button + id="addSignatureImageButton" + type="button" + role="tab" + aria-selected="false" + aria-controls="addSignatureImageContainer" + data-l10n-id="pdfjs-editor-add-signature-image-button" + tabindex="-1" + ></button> </div> <div id="addSignatureActionContainer" data-selected="type"> <div id="addSignatureTypeContainer" role="tabpanel" aria-labelledby="addSignatureTypeContainer"> - <input id="addSignatureTypeInput" type="text" data-l10n-id="pdfjs-editor-add-signature-type-input" tabindex="0"></input> + <input id="addSignatureTypeInput" type="text" data-l10n-id="pdfjs-editor-add-signature-type-input" tabindex="0" /> </div> <div id="addSignatureDrawContainer" role="tabpanel" aria-labelledby="addSignatureDrawButton" tabindex="-1"> <svg id="addSignatureDraw" xmlns="http://www.w3.org/2000/svg" aria-labelledby="addSignatureDrawPlaceholder"></svg> @@ -649,7 +986,17 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="thickness"> <div> <label for="addSignatureDrawThickness" data-l10n-id="pdfjs-editor-add-signature-draw-thickness-range-label"></label> - <input type="range" id="addSignatureDrawThickness" min="1" max="5" step="1" value="1" data-l10n-id="pdfjs-editor-add-signature-draw-thickness-range" data-l10n-args='{ "thickness": 1 }' tabindex="0"> + <input + type="range" + id="addSignatureDrawThickness" + min="1" + max="5" + step="1" + value="1" + data-l10n-id="pdfjs-editor-add-signature-draw-thickness-range" + data-l10n-args='{ "thickness": 1 }' + tabindex="0" + /> </div> </div> </div> @@ -660,7 +1007,7 @@ See https://github.com/adobe-type-tools/cmap-resources <label id="addSignatureImageBrowse" for="addSignatureFilePicker" tabindex="0"> <a data-l10n-id="pdfjs-editor-add-signature-image-browse-link"></a> </label> - <input id="addSignatureFilePicker" type="file"></input> + <input id="addSignatureFilePicker" type="file" /> </div> </div> <div id="addSignatureControls"> @@ -668,14 +1015,16 @@ See https://github.com/adobe-type-tools/cmap-resources <div id="addSignatureDescriptionContainer"> <label for="addSignatureDescInput" data-l10n-id="pdfjs-editor-add-signature-description-label"></label> <span id="addSignatureDescription" class="inputWithClearButton"> - <input id="addSignatureDescInput" type="text" data-l10n-id="pdfjs-editor-add-signature-description-input" tabindex="0"></input> + <input id="addSignatureDescInput" type="text" data-l10n-id="pdfjs-editor-add-signature-description-input" tabindex="0" /> <button class="clearInputButton" type="button" tabindex="0" aria-hidden="true"></button> </span> </div> - <button id="clearSignatureButton" type="button" data-l10n-id="pdfjs-editor-add-signature-clear-button" tabindex="0"><span data-l10n-id="pdfjs-editor-add-signature-clear-button-label"></span></button> + <button id="clearSignatureButton" type="button" data-l10n-id="pdfjs-editor-add-signature-clear-button" tabindex="0"> + <span data-l10n-id="pdfjs-editor-add-signature-clear-button-label"></span> + </button> </div> <div id="addSignatureSaveContainer"> - <input type="checkbox" id="addSignatureSaveCheckbox"></input> + <input type="checkbox" id="addSignatureSaveCheckbox" /> <label for="addSignatureSaveCheckbox" data-l10n-id="pdfjs-editor-add-signature-save-checkbox"></label> <span></span> <span id="addSignatureSaveWarning" data-l10n-id="pdfjs-editor-add-signature-save-warning-message"></span> @@ -687,57 +1036,68 @@ See https://github.com/adobe-type-tools/cmap-resources <span id="addSignatureErrorTitle" class="title" data-l10n-id="pdfjs-editor-add-signature-image-upload-error-title"></span> <span id="addSignatureErrorDescription" class="description" data-l10n-id="pdfjs-editor-add-signature-image-upload-error-description"></span> </div> - <button id="addSignatureErrorCloseButton" class="closeButton" type="button" tabindex="0"><span data-l10n-id="pdfjs-editor-add-signature-error-close-button"></span></button> + <button id="addSignatureErrorCloseButton" class="closeButton" type="button" tabindex="0"> + <span data-l10n-id="pdfjs-editor-add-signature-error-close-button"></span> + </button> </div> </div> <div class="dialogButtonsGroup"> - <button id="addSignatureCancelButton" type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-add-signature-cancel-button"></span></button> - <button id="addSignatureAddButton" type="button" class="primaryButton" disabled tabindex="0"><span data-l10n-id="pdfjs-editor-add-signature-add-button"></span></button> + <button id="addSignatureCancelButton" type="button" class="secondaryButton" tabindex="0"> + <span data-l10n-id="pdfjs-editor-add-signature-cancel-button"></span> + </button> + <button id="addSignatureAddButton" type="button" class="primaryButton" disabled tabindex="0"> + <span data-l10n-id="pdfjs-editor-add-signature-add-button"></span> + </button> </div> </div> </div> - </dialog> + </dialog> - <dialog class="dialog signatureDialog" id="editSignatureDescriptionDialog" aria-labelledby="editSignatureDescriptionTitle"> - <div id="editSignatureDescriptionContainer" class="mainContainer"> - <div class="title"> - <span id="editSignatureDescriptionTitle" role="sectionhead" data-l10n-id="pdfjs-editor-edit-signature-dialog-title" tabindex="0"></span> - </div> - <div id="editSignatureDescriptionAndView"> - <div id="editSignatureDescriptionContainer"> - <label for="editSignatureDescInput" data-l10n-id="pdfjs-editor-add-signature-description-label"></label> - <span id="editSignatureDescription" class="inputWithClearButton"> - <input id="editSignatureDescInput" type="text" data-l10n-id="pdfjs-editor-add-signature-description-input" tabindex="0"></input> - <button class="clearInputButton" type="button" tabindex="0" aria-hidden="true"></button> - </span> + <dialog class="dialog signatureDialog" id="editSignatureDescriptionDialog" aria-labelledby="editSignatureDescriptionTitle"> + <div id="editSignatureDescriptionContainer" class="mainContainer"> + <div class="title"> + <span id="editSignatureDescriptionTitle" role="sectionhead" data-l10n-id="pdfjs-editor-edit-signature-dialog-title" tabindex="0"></span> + </div> + <div id="editSignatureDescriptionAndView"> + <div id="editSignatureDescriptionContainer"> + <label for="editSignatureDescInput" data-l10n-id="pdfjs-editor-add-signature-description-label"></label> + <span id="editSignatureDescription" class="inputWithClearButton"> + <input id="editSignatureDescInput" type="text" data-l10n-id="pdfjs-editor-add-signature-description-input" tabindex="0" /> + <button class="clearInputButton" type="button" tabindex="0" aria-hidden="true"></button> + </span> + </div> + <svg id="editSignatureView" xmlns="http://www.w3.org/2000/svg"></svg> + </div> + <div class="dialogButtonsGroup"> + <button id="editSignatureCancelButton" type="button" class="secondaryButton" tabindex="0"> + <span data-l10n-id="pdfjs-editor-add-signature-cancel-button"></span> + </button> + <button id="editSignatureUpdateButton" type="button" class="primaryButton" disabled tabindex="0"> + <span data-l10n-id="pdfjs-editor-edit-signature-update-button"></span> + </button> </div> - <svg id="editSignatureView" xmlns="http://www.w3.org/2000/svg"></svg> - </div> - <div class="dialogButtonsGroup"> - <button id="editSignatureCancelButton" type="button" class="secondaryButton" tabindex="0"><span data-l10n-id="pdfjs-editor-add-signature-cancel-button"></span></button> - <button id="editSignatureUpdateButton" type="button" class="primaryButton" disabled tabindex="0"><span data-l10n-id="pdfjs-editor-edit-signature-update-button"></span></button> </div> - </div> - </dialog> + </dialog> - <dialog class="dialog commentManager" id="commentManagerDialog" aria-labelledby="commentManagerTitle"> - <div class="mainContainer"> - <div class="title" id="commentManagerToolbar"> - <span id="commentManagerTitle" role="sectionhead" data-l10n-id="pdfjs-editor-edit-comment-dialog-title-when-adding"></span> - </div> - <textarea id="commentManagerTextInput" data-l10n-id="pdfjs-editor-edit-comment-dialog-text-input" tabindex="0"></textarea> - <div class="dialogButtonsGroup"> - <button id="commentManagerCancelButton" type="button" class="secondaryButton" tabindex="0"> - <span data-l10n-id="pdfjs-editor-edit-comment-dialog-cancel-button"></span> - </button> - <button id="commentManagerSaveButton" type="button" class="primaryButton" disabled tabindex="0"> - <span data-l10n-id="pdfjs-editor-edit-comment-dialog-save-button-when-adding"></span> - </button> + <dialog class="dialog commentManager" id="commentManagerDialog" aria-labelledby="commentManagerTitle"> + <div class="mainContainer"> + <div class="title" id="commentManagerToolbar"> + <span id="commentManagerTitle" role="sectionhead" data-l10n-id="pdfjs-editor-edit-comment-dialog-title-when-adding"></span> + </div> + <textarea id="commentManagerTextInput" data-l10n-id="pdfjs-editor-edit-comment-dialog-text-input" tabindex="0"></textarea> + <div class="dialogButtonsGroup"> + <button id="commentManagerCancelButton" type="button" class="secondaryButton" tabindex="0"> + <span data-l10n-id="pdfjs-editor-edit-comment-dialog-cancel-button"></span> + </button> + <button id="commentManagerSaveButton" type="button" class="primaryButton" disabled tabindex="0"> + <span data-l10n-id="pdfjs-editor-edit-comment-dialog-save-button-when-adding"></span> + </button> + </div> </div> - </div> - </dialog> + </dialog> - </div> <!-- dialogContainer --> + </div> + <!-- dialogContainer --> <div id="editorUndoBar" class="messageBar" role="status" aria-labelledby="editorUndoBarMessage" tabindex="-1" hidden> <div> @@ -751,9 +1111,10 @@ See https://github.com/adobe-type-tools/cmap-resources <span data-l10n-id="pdfjs-editor-undo-bar-close-button-label"></span> </button> </div> - </div> <!-- editorUndoBar --> - - </div> <!-- outerContainer --> + </div> + <!-- editorUndoBar --> + </div> + <!-- outerContainer --> <div id="printContainer"></div> </body> </html> 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.402 - * pdfjsBuild = 57334bd20 + * pdfjsVersion = 5.4.445 + * pdfjsBuild = ec5330f78 */ /******/ // The require scope /******/ var __webpack_require__ = {}; @@ -2294,6 +2294,11 @@ class NewAltTextManager { textarea.addEventListener("input", () => { this.#toggleTitleAndDisclaimer(); }); + textarea.addEventListener("keydown", e => { + if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && !saveButton.disabled) { + this.#save(); + } + }); eventBus._on("enableguessalttext", ({ value }) => { @@ -2536,7 +2541,7 @@ class NewAltTextManager { this.#overlayManager.closeIfActive(this.#dialog); } #close() { - const canvas = this.#imagePreview.firstChild; + const canvas = this.#imagePreview.firstElementChild; canvas.remove(); canvas.width = canvas.height = 0; this.#imageData = null; @@ -2680,7 +2685,7 @@ class ImageAltTextSettings { async #download(isFromUI = false) { if (isFromUI) { this.#downloadModelButton.disabled = true; - const span = this.#downloadModelButton.firstChild; + const span = this.#downloadModelButton.firstElementChild; span.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-settings-downloading-model-button"); await this.#mlManager.downloadModel("altText"); span.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-settings-download-model-button"); @@ -2789,6 +2794,11 @@ class AltTextManager { saveButton.addEventListener("click", this.#save.bind(this)); optionDescription.addEventListener("change", onUpdateUIState); optionDecorative.addEventListener("change", onUpdateUIState); + textarea.addEventListener("keydown", e => { + if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && !saveButton.disabled) { + this.#save(); + } + }); this.#overlayManager.register(dialog); } #createSVGElement() { @@ -3301,9 +3311,129 @@ class CaretBrowsingMode { } } +;// ./web/sidebar.js + +class Sidebar { + #minWidth = 0; + #maxWidth = 0; + #initialWidth = 0; + #width = 0; + #coefficient; + #visible = false; + constructor({ + sidebar, + resizer, + toggleButton + }, ltr, isResizerOnTheLeft) { + this._sidebar = sidebar; + this.#coefficient = ltr === isResizerOnTheLeft ? -1 : 1; + 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); + toggleButton.addEventListener("click", this.toggle.bind(this)); + sidebar.hidden = true; + } + #makeSidebarResizable(resizer, isResizerOnTheLeft) { + resizer.ariaValueMin = this.#minWidth; + resizer.ariaValueMax = this.#maxWidth; + resizer.ariaValueNow = this.#width; + let pointerMoveAC; + const cancelResize = () => { + this.#width = MathClamp(this.#width, this.#minWidth, this.#maxWidth); + this._sidebar.classList.remove("resizing"); + pointerMoveAC?.abort(); + pointerMoveAC = null; + }; + resizer.addEventListener("pointerdown", e => { + if (pointerMoveAC) { + cancelResize(); + return; + } + const { + clientX + } = e; + stopEvent(e); + let prevX = clientX; + pointerMoveAC = new AbortController(); + const { + signal + } = pointerMoveAC; + const sidebar = this._sidebar; + const sidebarStyle = sidebar.style; + sidebar.classList.add("resizing"); + const parentStyle = sidebar.parentElement.style; + parentStyle.minWidth = 0; + window.addEventListener("contextmenu", noContextMenu, { + signal + }); + window.addEventListener("pointermove", ev => { + if (!pointerMoveAC) { + return; + } + stopEvent(ev); + const { + clientX: x + } = ev; + this.#setNewWidth(x - prevX, parentStyle, resizer, sidebarStyle, isResizerOnTheLeft, false); + prevX = x; + }, { + signal, + capture: true + }); + window.addEventListener("blur", cancelResize, { + signal + }); + window.addEventListener("pointerup", ev => { + if (pointerMoveAC) { + cancelResize(); + stopEvent(ev); + } + }, { + signal + }); + }); + resizer.addEventListener("keydown", e => { + const { + key + } = e; + const isArrowLeft = key === "ArrowLeft"; + if (isArrowLeft || key === "ArrowRight") { + 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); + 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; + } + resizer.ariaValueNow = Math.round(newWidth); + sidebarStyle.width = `${newWidth.toFixed(3)}px`; + if (isResizerOnTheLeft) { + parentStyle.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`; + } + } + toggle() { + this._sidebar.hidden = !(this.#visible = !this.#visible); + } +} + ;// ./web/comment_manager.js + class CommentManager { #dialog; #popup; @@ -3368,12 +3498,11 @@ class CommentManager { this.#popup.destroy(); } } -class CommentSidebar { +class CommentSidebar extends Sidebar { #annotations = null; #eventBus; #boundCommentClick = this.#commentClick.bind(this); #boundCommentKeydown = this.#commentKeydown.bind(this); - #sidebar; #closeButton; #commentsList; #commentCount; @@ -3385,11 +3514,6 @@ class CommentSidebar { #elementsToAnnotations = null; #idsToElements = null; #uiManager = null; - #minWidth = 0; - #maxWidth = 0; - #initialWidth = 0; - #width = 0; - #ltr; constructor({ learnMoreUrl, sidebar, @@ -3400,7 +3524,11 @@ class CommentSidebar { closeButton, commentToolbarButton }, eventBus, linkService, popup, dateFormat, ltr) { - this.#sidebar = sidebar; + super({ + sidebar, + resizer: sidebarResizer, + toggleButton: commentToolbarButton + }, ltr, true); this.#sidebarTitle = sidebarTitle; this.#commentsList = commentsList; this.#commentCount = commentCount; @@ -3409,13 +3537,7 @@ class CommentSidebar { this.#closeButton = closeButton; this.#popup = popup; this.#dateFormat = dateFormat; - this.#ltr = ltr; this.#eventBus = eventBus; - 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(sidebarResizer); closeButton.addEventListener("click", () => { eventBus.dispatch("switchannotationeditormode", { source: this, @@ -3433,70 +3555,6 @@ class CommentSidebar { }; commentToolbarButton.addEventListener("keydown", keyDownCallback); sidebar.addEventListener("keydown", keyDownCallback); - this.#sidebar.hidden = true; - } - #makeSidebarResizable(resizer) { - let pointerMoveAC; - const cancelResize = () => { - this.#width = MathClamp(this.#width, this.#minWidth, this.#maxWidth); - this.#sidebar.classList.remove("resizing"); - pointerMoveAC?.abort(); - pointerMoveAC = null; - }; - resizer.addEventListener("pointerdown", e => { - if (pointerMoveAC) { - cancelResize(); - return; - } - const { - clientX - } = e; - stopEvent(e); - let prevX = clientX; - pointerMoveAC = new AbortController(); - const { - signal - } = pointerMoveAC; - const sign = this.#ltr ? -1 : 1; - const sidebar = this.#sidebar; - const sidebarStyle = sidebar.style; - sidebar.classList.add("resizing"); - const parentStyle = sidebar.parentElement.style; - parentStyle.minWidth = 0; - window.addEventListener("contextmenu", noContextMenu, { - signal - }); - window.addEventListener("pointermove", ev => { - if (!pointerMoveAC) { - return; - } - stopEvent(ev); - const { - clientX: x - } = ev; - const newWidth = this.#width += sign * (x - prevX); - prevX = x; - if (newWidth > this.#maxWidth || newWidth < this.#minWidth) { - return; - } - sidebarStyle.width = `${newWidth.toFixed(3)}px`; - parentStyle.insetInlineStart = `${(this.#initialWidth - newWidth).toFixed(3)}px`; - }, { - signal, - capture: true - }); - window.addEventListener("blur", cancelResize, { - signal - }); - window.addEventListener("pointerup", ev => { - if (pointerMoveAC) { - cancelResize(); - stopEvent(ev); - } - }, { - signal - }); - }); } setUIManager(uiManager) { this.#uiManager = uiManager; @@ -3516,7 +3574,7 @@ class CommentSidebar { } else { this.#setCommentsCount(); } - this.#sidebar.hidden = false; + this._sidebar.hidden = false; this.#eventBus.dispatch("reporttelemetry", { source: this, details: { @@ -3528,7 +3586,7 @@ class CommentSidebar { }); } hide() { - this.#sidebar.hidden = true; + this._sidebar.hidden = true; this.#commentsList.replaceChildren(); this.#elementsToAnnotations = null; this.#idsToElements = null; @@ -3551,7 +3609,7 @@ class CommentSidebar { if (!element) { return; } - this.#sidebar.scrollTop = element.offsetTop - this.#sidebar.offsetTop; + this._sidebar.scrollTop = element.offsetTop - this._sidebar.offsetTop; for (const el of this.#commentsList.children) { el.classList.toggle("selected", el === element); } @@ -3580,8 +3638,8 @@ class CommentSidebar { if (index >= this.#annotations.length) { return; } - this.#setDate(element.firstChild, modificationDate || creationDate); - this.#setText(element.lastChild, richText, contentsObj); + this.#setDate(element.firstElementChild, modificationDate || creationDate); + this.#setText(element.lastElementChild, richText, contentsObj); this.#annotations.splice(index, 1); index = binarySearchFirstItem(this.#annotations, a => this.#sortComments(a, annotation) >= 0); this.#annotations.splice(index, 0, annotation); @@ -3854,6 +3912,11 @@ class CommentDialog { textInput.addEventListener("input", () => { saveButton.disabled = textInput.value === this.#previousText; }); + textInput.addEventListener("keydown", e => { + if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && !saveButton.disabled) { + this.#save(); + } + }); let pointerMoveAC; const cancelDrag = () => { dialog.classList.remove("dragging"); @@ -5154,6 +5217,7 @@ class PDFDocumentProperties { } ;// ./web/pdf_find_utils.js + const CharacterType = { SPACE: 0, ALPHA_LETTER: 1, @@ -5223,7 +5287,7 @@ function getCharacterType(charCode) { } let NormalizeWithNFKC; function getNormalizeWithNFKC() { - NormalizeWithNFKC ||= ` ¨ª¯²-µ¸-º¼-¾IJ-ijĿ-ŀʼnſDŽ-njDZ-dzʰ-ʸ˘-˝ˠ-ˤʹͺ;΄-΅·ϐ-ϖϰ-ϲϴ-ϵϹևٵ-ٸक़-य़ড়-ঢ়য়ਲ਼ਸ਼ਖ਼-ਜ਼ਫ਼ଡ଼-ଢ଼ำຳໜ-ໝ༌གྷཌྷདྷབྷཛྷཀྵჼᴬ-ᴮᴰ-ᴺᴼ-ᵍᵏ-ᵪᵸᶛ-ᶿẚ-ẛάέήίόύώΆ᾽-῁ΈΉ῍-῏ΐΊ῝-῟ΰΎ῭-`ΌΏ´-῾ - ‑‗․-… ″-‴‶-‷‼‾⁇-⁉⁗ ⁰-ⁱ⁴-₎ₐ-ₜ₨℀-℃℅-ℇ℉-ℓℕ-№ℙ-ℝ℠-™ℤΩℨK-ℭℯ-ℱℳ-ℹ℻-⅀ⅅ-ⅉ⅐-ⅿ↉∬-∭∯-∰〈-〉①-⓪⨌⩴-⩶⫝̸ⱼ-ⱽⵯ⺟⻳⼀-⿕ 〶〸-〺゛-゜ゟヿㄱ-ㆎ㆒-㆟㈀-㈞㈠-㉇㉐-㉾㊀-㏿ꚜ-ꚝꝰꟲ-ꟴꟸ-ꟹꭜ-ꭟꭩ豈-嗀塚晴凞-羽蘒諸逸-都飯-舘並-龎ff-stﬓ-ﬗיִײַ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-﷼︐-︙︰-﹄﹇-﹒﹔-﹦﹨-﹫ﹰ-ﹲﹴﹶ-ﻼ!-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ¢-₩`; + NormalizeWithNFKC ||= `\xA0¨ª¯²-µ¸-º¼-¾IJ-ijĿ-ŀʼnſDŽ-njDZ-dzʰ-ʸ˘-˝ˠ-ˤʹͺ;΄-΅·ϐ-ϖϰ-ϲϴ-ϵϹևٵ-ٸक़-य़ড়-ঢ়য়ਲ਼ਸ਼ਖ਼-ਜ਼ਫ਼ଡ଼-ଢ଼ำຳໜ-ໝ༌གྷཌྷདྷབྷཛྷཀྵჼᴬ-ᴮᴰ-ᴺᴼ-ᵍᵏ-ᵪᵸᶛ-ᶿẚ-ẛάέήίόύώΆ᾽-῁ΈΉ῍-῏ΐΊ῝-῟ΰΎ῭-`ΌΏ´-῾ - ‑‗․-… ″-‴‶-‷‼‾⁇-⁉⁗ ⁰-ⁱ⁴-₎ₐ-ₜ₨℀-℃℅-ℇ℉-ℓℕ-№ℙ-ℝ℠-™ℤΩℨK-ℭℯ-ℱℳ-ℹ℻-⅀ⅅ-ⅉ⅐-ⅿ↉∬-∭∯-∰〈-〉①-⓪⨌⩴-⩶⫝̸ⱼ-ⱽⵯ⺟⻳⼀-⿕ 〶〸-〺゛-゜ゟヿㄱ-ㆎ㆒-㆟㈀-㈞㈠-㉇㉐-㉾㊀-㏿ꚜ-ꚝꝰ꟱-ꟴꟸ-ꟹꭜ-ꭟꭩ豈-嗀塚晴凞-羽蘒諸逸-都飯-舘並-龎ff-stﬓ-ﬗיִײַ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-﷼︐-︙︰-﹄﹇-﹒﹔-﹦﹨-﹫ﹰ-ﹲﹴﹶ-ﻼ!-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ¢-₩`; return NormalizeWithNFKC; } @@ -5255,7 +5319,7 @@ const CHARACTERS_TO_NORMALIZE = { const DIACRITICS_EXCEPTION = new Set([0x3099, 0x309a, 0x094d, 0x09cd, 0x0a4d, 0x0acd, 0x0b4d, 0x0bcd, 0x0c4d, 0x0ccd, 0x0d3b, 0x0d3c, 0x0d4d, 0x0dca, 0x0e3a, 0x0eba, 0x0f84, 0x1039, 0x103a, 0x1714, 0x1734, 0x17d2, 0x1a60, 0x1b44, 0x1baa, 0x1bab, 0x1bf2, 0x1bf3, 0x2d7f, 0xa806, 0xa82c, 0xa8c4, 0xa953, 0xa9c0, 0xaaf6, 0xabed, 0x0c56, 0x0f71, 0x0f72, 0x0f7a, 0x0f7b, 0x0f7c, 0x0f7d, 0x0f80, 0x0f74]); let DIACRITICS_EXCEPTION_STR; const DIACRITICS_REG_EXP = /\p{M}+/gu; -const SPECIAL_CHARS_REG_EXP = /([.*+?^${}()|[\]\\])|(\p{P})|(\s+)|(\p{M})|(\p{L})/gu; +const SPECIAL_CHARS_REG_EXP = /([*+^${}()|[\]\\])|(\p{P}+)|(\s+)|(\p{M})|(\p{L})/gu; const NOT_DIACRITIC_FROM_END_REG_EXP = /([^\p{M}])\p{M}*$/u; const NOT_DIACRITIC_FROM_START_REG_EXP = /^\p{M}*([^\p{M}])/u; const SYLLABLES_REG_EXP = /[\uAC00-\uD7AF\uFA6C\uFACF-\uFAD1\uFAD5-\uFAD7]+/g; @@ -5651,12 +5715,24 @@ class PDFFindController { matchDiacritics } = this.#state; let isUnicode = false; + const addExtraWhitespaces = (original, fixed) => { + if (original === query) { + return fixed; + } + if (query.startsWith(original)) { + return `${fixed}[ ]*`; + } + if (query.endsWith(original)) { + return `[ ]*${fixed}`; + } + return `[ ]*${fixed}[ ]*`; + }; query = query.replaceAll(SPECIAL_CHARS_REG_EXP, (match, p1, p2, p3, p4, p5) => { if (p1) { - return `[ ]*\\${p1}[ ]*`; + return addExtraWhitespaces(p1, `\\${p1}`); } if (p2) { - return `[ ]*${p2}[ ]*`; + return addExtraWhitespaces(p2, p2.replaceAll(/[.?]/g, "\\$&")); } if (p3) { return "[ ]+"; @@ -11485,7 +11561,7 @@ class PDFViewer { #textLayerMode = TextLayerMode.ENABLE; #viewerAlert = null; constructor(options) { - const viewerVersion = "5.4.402"; + const viewerVersion = "5.4.445"; if (version !== viewerVersion) { throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`); } @@ -15641,11 +15717,16 @@ const PDFViewerApplication = { info, metadata, contentDispositionFilename, - contentLength + contentLength, + hasStructTree } = await pdfDocument.getMetadata(); if (pdfDocument !== this.pdfDocument) { return; } + this.externalServices.reportTelemetry({ + type: "taggedPDF", + data: hasStructTree + }); this.documentInfo = info; this.metadata = metadata; this._contentDispositionFilename ??= contentDispositionFilename; 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: 57334bd20544a713c400476e7bf966491dd1c823 (2025-11-07T16:21:38Z). - revision: 57334bd20544a713c400476e7bf966491dd1c823 + release: ec5330f78c1feb384156bdf6c69b18bedf36b4a3 (2025-11-28T15:59:23Z). + revision: ec5330f78c1feb384156bdf6c69b18bedf36b4a3 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/