When a viewer page was copied, each copy got its own annotationStorage
entry, but newAnnotationsByPage was keyed only by source page. As a
result, every output copy received all entries and reused their memoized
references.
Tag each entry with its rank among output copies of the same source page.
The display and worker compute this rank independently; inserted documents
preserve the order of copies. Keep unextracted entries at rank -1 so shared
stamp bitmaps remain available without being written. Without ranks, new
annotations are applied only to the first copy.
This fixes a bug when using the <kbd>Home</kbd> and <kbd>End</kbd> keyboard shortcuts to navigate through a `Menu` instance. These two buttons didn't update the `#lastIndex` field, which means that e.g. a following <kbd>ArrowDown</kbd> or <kbd>ArrowUp</kbd> press could make focus "jump" to an unexpected menu-item.
Also, the helper method reduces a little bit of code duplication in the event handlers.
The local part and the domain labels were unbounded, making the
search quadratic in the length of a run of characters preceding
an "@": scanning the text of a single page could take seconds.
Compared to regular Objects there's a number of advantages to using Maps:
- They support proper iteration.
- They have a simple way to check for the existence of data.
- They have a simple/efficient way to check the number of elements.
If this functionality was added today, I cannot imagine that we'd choose an Object for this data.
In the Firefox PDF Viewer sending Maps to the scripting-implementation should be fine, since it uses the browser `Cu.cloneInto` functionality; see https://searchfox.org/firefox-main/source/toolkit/components/pdfjs/content/PdfSandbox.sys.mjs
However with QuickJS, used by the GENERIC viewer, all data needs to be stringified and Maps are converted into regular Objects (see also PR 21664). Hence the `objects` property, in the scripting-implementation, is converted back into a Map using the (renamed) `createMap` helper function.
`TouchManager` computes the pinch distances with the screen coordinates, but it
was passing the pinch center in that space too, while the viewer expects a
client coordinate, like the origin coming from a wheel event.
Since a two-finger gesture can be synthesized with WebDriver BiDi, the
pinch-zoom test doesn't have to rely on CDP anymore and can run in Chrome, where
it fails without the above fix.
In a couple of cases, where a boolean is returned, it's also possible to use an OR operator rather a ternary.
*Note:* This patch reduces the size of the `gulp mozcentral` bundle by `960` bytes.
The chrome is now authored against Firefox design-token names with the shipped
value as a fallback `var(--fx-token, <literal>)` so colors, borders and radii
follow Firefox instead of having to be re-synced by hand.
In MOZCENTRAL viewer.css imports chrome://global/skin/design-system/tokens-brand.css,
gated on the new pdfjs.enableNova pref (default false, read straight from CSS via -moz-pref()).
With the pref off the sheet isn't applied, every fallback resolves, and the
viewer renders as before, as it also does in the GENERIC and components builds,
which never see the chrome sheet.
Consuming a token means never declaring its name in an unlayered rule, which
would shadow Firefox's for the whole subtree; --focus-outline, --border-color and
--text-color/--button-border-color are therefore renamed to
--editor-selection-outline, --signature-border-color and --views-*. In forced
colors each state reads a matching background/foreground/border triple so it
can't collapse to a single system color.
The dialog button styling also moves out of `.dialog .mainContainer button` into
a shared .primaryButton/.secondaryButton primitive (buttons.css, imported from
pdf_viewer.css so the components build gets it too). The password,
document-properties, print and undo-bar buttons use it in place of .dialogButton,
and the custom radio appearance gives way to native controls tinted with
accent-color.
The draw layer keeps a reference on the text layer div in order to render the
selection, but it was destroyed only along with the annotation editor layer.
Hence, when the editor is disabled, like in Firefox for Android, scrolling far
enough to destroy a page view left the draw layer with a reference on a removed
text layer: the new one was never registered, consequently no selection was
rendered anymore on that page.
Compared to regular Objects there's a number of advantages to using Maps, e.g. they support proper iteration.
Additionally the conditionally defined options are moved into the main list, to maintain alphabetical order for each individual `OptionKind`.
Finally, the `enableSelectionRendering` option is moved to its "correct" position and the `enableFakeMLManager` option is limited to TESTING builds (since that's consistent with its usage in `web/app.js`).
*Note:* Despite making the source-code larger, the size of the `gulp mozcentral` bundle is *reduced* by `686` bytes.
The bar itself (`<pdf-features-notification>`) ships from mozilla-central, so
the viewer only hosts it.
Finally the maximum number of preferences is raised to 60, to match the change
made in mozilla-central in bug 2056102, since this adds a 51st one.
Compared to regular Objects there's a number of advantages to using Maps:
- They support proper iteration.
- They have a simple way to check for the existence of data.
- They have a simple/efficient way to check the number of elements.
If this functionality was added today, I cannot imagine that we'd choose an Object for this data.
When saving or downloading a document, an annotation that was still
being edited (e.g. an in-progress ink drawing) wasn't committed first,
so its content was missing from the saved file.
`getCharacterType` was a port of the old `WordBreaker::GetClass`,
which has been removed and replaced with ICU4X word segmentation.
Use `Intl.Segmenter` instead, testing each match boundary on the two
adjacent grapheme clusters in isolation like Firefox's find.
It fixes#21520.
Despite the `enableSignatureVerification` option/preference being enabled in development mode and TESTING builds, the lack of any `SignatureVerifier` implementation still renders the UI disabled.
This seems unfortunate, since it makes it more difficult to check that the digital signature UI works correctly:
- That the button is visible when it's supposed to be, and that the panel can be opened/closed.
- That the UI looks correct, w.r.t. the HTML elements and their CSS rules.
- Currently the viewer JS code is effectively uncovered, with overall test-coverage dropping from `90` to `89` percent when PR 21247 landed.
To improve the current situation this patch adds a `FakeSignatureVerifier` class, limited to only development mode and TESTING builds, that treats every digital signature as invalid.
This allows easier manual testing, and also the addition of a few *very rudimentary* integration-tests.
*Note:* It probably wouldn't be all that difficult to add additional integration-tests for *valid* certificates, by having the test-cases instruct (during setup) the `FakeSignatureVerifier` how to respond to simulate verified certificates.
Note that the `prettier` update introduces a handful of formatting
changes (see https://prettier.io/blog/2026/06/27/3.9.0).
Moreover, the `tsc-alias` update requires explicitly defining the root
URL in the compiler options now, otherwise it errors with `tsc-alias
error: compilerOptions.rootDir is required with implicit baseUrl` (see
https://github.com/justkey007/tsc-alias/pull/259/changes).
This has a couple of advantages:
- It allows accessing and iterating `this.#results` directly, without having to (needlessly) create a temporary array.
- By moving the "worst status" computation into its own getter, it can be re-used from the `#updateButtonState` method as well which reduces code duplication.
The warn and error state badges shipped as byte-identical files (an
X-in-circle glyph). Drop the redundant
toolbarButton-signaturePropertiesWarn.svg and point the warn state at
the error icon instead; the amber-vs-red distinction is preserved via
background-color (--sig-icon-warn vs --sig-icon-error), not the glyph.
Currently the GeckoView development viewer, i.e. http://localhost:8888/web/viewer-geckoview.html, is completely broken with the following error:
```
Uncaught TypeError: The specifier “web-digital_signature_properties_manager” was a bare specifier, but was not remapped to anything. Relative module specifiers must start with “./”, “../” or “/”. app.js:97:44
```
- Replace the `loadFromDocument` and `reset` methods with a single `setDocument` method, since that's consistent with many other viewer components.
- Replace the internal `#loadToken` field with simple `pdfDocument` checks, when checking if the document is still current, which again is consistent with (all) other viewer components.
- Remove a couple of comments, which didn't add a lot of value and sounded a whole lot like "AI speak".
Chromium 148+ improved their selection behavior when it comes to absolutely
positioned elements, thus making text selectino in PDF.js much better.
Unfortunately this does not only mean that the workaround we currently have
for Chromium is unnecessary, but it actually become harmful. It conflicts
with Chromium's new behavior, making text selection *worse* on mobile.
As the change has been released in Chrome only a month ago, this patch keeps
the workaround for older Chromium versions. There is no easy way to
feture-detect is, so unfortunately we need to do user agent version detection.