5179 Commits

Author SHA1 Message Date
Jonas Jenwald
664526ee65 Shorten the ViewHistory class a little bit
- Replace the manual loop, used to find an existing entry, with the native [`findIndex` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex).

 - Remove the `fingerprint`/`cacheSize` class fields, since they are completely unused.
2026-08-01 12:19:20 +02:00
Tim van der Meij
bb1a948397
Merge pull request #21672 from Snuffleupagus/more-ternary
Replace simple return `if` statements with ternary operators
2026-07-31 19:59:46 +02:00
Tim van der Meij
4b4c784a85
Merge pull request #21673 from Snuffleupagus/EventBus-Map-Set
Re-factor the `EventBus` to use Map/Set internally
2026-07-31 19:50:16 +02:00
calixteman
af38789499
Merge pull request #21659 from calixteman/fix/pinch-zoom-origin
Use the client coordinates for the pinch-zoom origin
2026-07-31 18:00:52 +02:00
Jonas Jenwald
0d5df9d96e Re-factor the EventBus to use Map/Set internally
Especially the Array to Set conversion should be helpful, since adding/removing elements from a Set is more efficient.
2026-07-31 17:37:05 +02:00
Calixte Denizet
8960eddfc1 Use the client coordinates for the pinch-zoom origin
`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.
2026-07-31 16:44:28 +02:00
Jonas Jenwald
ee59db5018 Replace simple return if statements with ternary operators
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.
2026-07-31 12:24:08 +02:00
Calixte Denizet
b4d028132b Sync the viewer chrome with the Firefox design system
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.
2026-07-30 23:13:43 +02:00
Calixte Denizet
9e02ceed09 Fix the selection rendering when a page has been destroyed and rendered again (bug 2054348)
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.
2026-07-30 18:37:03 +02:00
Tim van der Meij
93958e4ff6
Merge pull request #21640 from Snuffleupagus/defaultOptions-Map
Change `defaultOptions`, in the `web/app_options.js` file, to a Map
2026-07-28 20:18:03 +02:00
calixteman
1609bd87c5
Merge commit from fork
Harden the XFA layer and restrict scripting updates to known fields
2026-07-27 19:38:24 +02:00
Jonas Jenwald
5406d3159b Change defaultOptions, in the web/app_options.js file, to a Map
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.
2026-07-27 12:53:13 +02:00
Calixte Denizet
f195bec9b2
Add the Firefox features notification bar to the viewer (bug 2057608)
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.
2026-07-25 08:43:01 +02:00
Tim van der Meij
47969a5953
Merge pull request #21620 from calixteman/improve_modif_detections
Improve the detection of the changes made after a document has been signed
2026-07-23 20:57:59 +02:00
Jonas Jenwald
c474a97932 [api-minor] Convert getOpenAction to return data in a Map
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.
2026-07-23 13:52:02 +02:00
Calixte Denizet
ed81227557 Improve the detection of the changes made after a document has been signed 2026-07-23 12:44:20 +02:00
Calixte Denizet
4ea07c2431
Only handle scripting updates that target a known field
Collect the ids of the document field annotations and ignore any id
coming from the scripting sandbox that isn't among them.
2026-07-22 16:11:57 +02:00
Tim van der Meij
be89d6475d
Merge pull request #21585 from Snuffleupagus/Iterator-join
Start using `Iterator.prototype.join` in the code-base
2026-07-19 13:48:06 +02:00
Jonas Jenwald
47f2b22842 Use Iterator methods to avoid some unnecessary Array creation
Currently there are some spots in the code-base where intermediate Arrays are unnecessarily created from Iterators, before `filter` and `map` is used to create a final Array.
Thanks to newer Iterators methods, see e.g. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/filter, that can be now be avoided.
2026-07-17 21:23:08 +02:00
Jonas Jenwald
9a7335d26b Start using Iterator.prototype.join in the code-base
This is an upcoming JavaScript feature, which helps avoid creating intermediate Arrays in some cases; see https://github.com/tc39/proposal-iterator-join

Firefox implemented this in [bug 2004803](https://bugzilla.mozilla.org/show_bug.cgi?id=2004803), and it was enabled by default in [bug 2047995](https://bugzilla.mozilla.org/show_bug.cgi?id=2047995).

This patch changes two Objects to Maps, in the XFA-parsing respectively the find-implementation, to make use of the new feature.
2026-07-16 20:45:01 +02:00
Jonas Jenwald
6d7bca521b Use Map.prototype.getOrInsertComputed more in the web/ folder 2026-07-16 19:37:13 +02:00
Jonas Jenwald
a3a7247366 Add a makeSet helper, to reduce function creation
This replaces inline `() => new Set()` statements, and also makes some `getOrInsertComputed` calls slightly shorter.
2026-07-15 15:24:25 +02:00
Calixte Denizet
125f692589 Commit the current editing session before saving (bug 2047516)
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.
2026-07-13 19:08:42 +02:00
Jonas Jenwald
c46720d893 Use for...of even more in the code-base
This replaces a couple of "standard" `for` loops with the shorter `for...of` format instead.
2026-07-11 13:15:36 +02:00
Jonas Jenwald
6689097a0a
Merge pull request #21543 from Snuffleupagus/FakeSignatureVerifier
Add a `FakeSignatureVerifier` for development mode and TESTING builds (PR 21247 follow-up)
2026-07-05 23:20:58 +02:00
Calixte Denizet
0c97aa7555
Use Intl.Segmenter for the entire-word search matching
`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.
2026-07-05 14:37:21 +02:00
Jonas Jenwald
c188012280 Add a FakeSignatureVerifier for development mode and TESTING builds (PR 21247 follow-up)
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.
2026-07-05 13:43:25 +02:00
Tim van der Meij
4fd232a124
Update dependencies to the most recent versions
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).
2026-07-04 14:49:36 +02:00
Jonas Jenwald
046e68d140 Extend the unicorn/logical-assignment-operators rule to if-statements
Please see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/logical-assignment-operators.md and https://eslint.org/docs/latest/rules/logical-assignment-operators#enforceforifstatements
2026-07-04 12:25:57 +02:00
Tim van der Meij
32f3906960
Merge pull request #21533 from Snuffleupagus/SignaturePropertiesManager-#bannerState
Move the `bannerStateForResults` helper into the `SignaturePropertiesManager` class (PR 21247 follow-up)
2026-07-03 20:13:58 +02:00
Tim van der Meij
e414f74375
Merge pull request #21534 from Snuffleupagus/addPasteButton-#getPageL10nArgs
Use the `#getPageL10nArgs` helper in the `PDFThumbnailView.prototype.addPasteButton` method
2026-07-03 20:11:49 +02:00
Tim van der Meij
70c52132af
Merge pull request #21514 from nicolo-ribaudo/remove-selection-conflict-chrome
Avoid text selection workaround in modern Chromium
2026-07-03 20:10:28 +02:00
Jonas Jenwald
0c076f5f0b Use the #getPageL10nArgs helper in the PDFThumbnailView.prototype.addPasteButton method
Rather than effectively duplicating code, we can re-use the existing helper here as well.
2026-07-03 16:18:25 +02:00
Jonas Jenwald
14f4cce29d Move the bannerStateForResults helper into the SignaturePropertiesManager class (PR 21247 follow-up)
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.
2026-07-03 12:41:15 +02:00
calixteman
ab90210aa4
Merge pull request #21531 from Snuffleupagus/PDFThumbnailViewer-#updateStatus-isCopy
Reduce duplication when updating undoButton/undoCloseButton in `PDFThumbnailViewer.prototype.#updateStatus`
2026-07-03 08:14:23 +02:00
calixteman
44ae5b0e9a
Merge pull request #21529 from Snuffleupagus/SignaturePropertiesManager-geckoview
[GeckoView] Add `web-digital_signature_properties_manager` import map (PR 21247 follow-up)
2026-07-03 08:13:08 +02:00
Jonas Jenwald
f373fbfa5b Reduce duplication when updating undoButton/undoCloseButton in PDFThumbnailViewer.prototype.#updateStatus 2026-07-03 00:03:17 +02:00
Benjamin Beurdouche
34629bd2cf Deduplicate the signature-properties warn/error toolbar icon
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.
2026-07-02 23:00:08 +02:00
Jonas Jenwald
12691fdf50 Remove unnecessary class-field resetting in SignaturePropertiesManager.prototype.setDocument (PR 21247 follow-up)
This corresponds to the initial values of these fields, and they were *already* reset when a (previous) PDF document was closed.
2026-07-02 19:44:35 +02:00
Jonas Jenwald
a1f1a56080 [GeckoView] Add web-digital_signature_properties_manager import map (PR 21247 follow-up)
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
```
2026-07-02 19:44:28 +02:00
Jonas Jenwald
fa207b4ce8 A couple of SignaturePropertiesManager improvements (PR 21247 follow-up)
- 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".
2026-07-02 14:07:04 +02:00
Nicolò Ribaudo
ea43bb43fb
Avoid text selection workaround in modern Chromium
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.
2026-07-01 13:14:34 +02:00
Benjamin Beurdouche
07b1c625e1 Add Digital signature properties verification panel
Adds a new "Digital signature properties" doorhanger to the pdf.js
toolbar that lists every digital signature found in the opened PDF,
verifies each one (via NSS in the Firefox build through a new chrome
bridge), and shows per-signature status + certificate state.

The viewer side parses /Sig dicts in the worker
(`PDFDocument.signatures`), strict-validates the /ByteRange offsets
before slicing, and ships only signature metadata across the worker
boundary. The PKCS#7 blob and signed-data byte spans live in a
worker-side map and are fetched lazily one signature at a time via
a new `getSignatureData(id)` RPC, immediately before verification
runs, so the bytes never sit in main-thread memory for the
document's lifetime.

The panel is feature-gated by `pdfjs.enableSignatureVerification`
(true in MOZCENTRAL/TESTING, off by default in the GENERIC build).
External services expose a `createSignatureVerifier()` factory that
the Firefox build wires up to `nsIX509CertDB.asyncVerifyPKCS7Object`;
GENERIC builds return null and the toolbar button stays hidden.

UI summary:
- Toolbar button states: loading dots while in flight, then green
  check, orange `!`, or red `✕` based on the worst aggregate
  signature status.
- Doorhanger contains a banner summarising the document state, then
  one card per signature with status row + certificate row (sub-
  signatures nested under their outer revision via /ByteRange
  containment).
- Icons are mono SVGs themed via `mask-image` + `background-color`
  so they pick up light/dark/HCM via `--sig-icon-*` vars; flipped
  under RTL via `scaleX(var(--dir-factor))`. The HCM mapping reuses
  the alt-text vocabulary (ButtonFace / ButtonText / ButtonBorder /
  GrayText / AccentColor / LinkText) so this panel reads the same
  as the rest of the editor toolbar in high-contrast mode.
- All visible strings are localized via Fluent
  (`pdfjs-digital-signature-properties-*`); status row, banner, and
  certificate row use explicit lookup tables instead of generated
  ids so a grep finds them.
- Esc + outside-click close the panel through the viewer's existing
  handlers; the manager exposes `isOpen`, `close()`, and
  `shouldCloseOnClick(target)` for that.

This commit also adds a `test/pdfs/sig_corpus/` directory holding a
Python generator that produces a corpus of signed PDFs covering
every visible state of the doorhanger (verified / untrusted /
expired / invalid / unknown / multi-signature variants). The corpus
is intentionally NOT part of the automated test suite — it is a
manual-test tool. Generated `.pdf` files are gitignored; only the
generator, README, and a `user.js.example` snippet are tracked.
The generator shells out to mozilla-central's
`security/manager/tools/pycms.py` (resolved via `--mozilla-central
<path>` or the `MOZILLA_CENTRAL_SRC` env var) and the embedded test
trust anchors (`pdf-sign-ca` / `pdf-sign-ca-expired`), gated by
`security.pdf_signature_verification.enable_test_trust_anchors` so
the test certificates never validate in shipping Firefox.
2026-06-30 13:25:09 +02:00
Calixte Denizet
d142fd2451 Remove the BOM from html files (bug 2051221)
Add add a linter in order to avoid future regressions.
2026-06-29 11:20:34 +02:00
Jonas Jenwald
b7b3a4c454 Use more optional chaining in the src/ and web/ folders
There's a few spots where we check if something is either undefined or if its length is zero, which can be simplified by instead using optional chaining.
2026-06-27 12:20:36 +02:00
Tim van der Meij
195226e3a6
Merge pull request #21500 from calixteman/bug2050191
Disable selection rendering when backdrop-filter is unsupported (bug 2050191)
2026-06-26 20:12:48 +02:00
Nicolò Ribaudo
5d81fe5098
Move SVG text selection styles to pdf_viewer.css
draw_layer_builder.css, which originally included these styles, is not
loaded in GECKOVIEW. This is because it also includes all the styles
related to highlights and drawing, which are only supported in the main
viewer.

The new SVG-based highlights are also used in GECKOVIEW, so even though
the JS logic for them lives in the DrawLayer builder, we need to move the CSS
somewhere where we know it's going to be loaded.
2026-06-25 15:55:26 +02:00
Calixte Denizet
7f9c54a259 Disable selection rendering when backdrop-filter is unsupported (bug 2050191)
Selection rendering relies on the CSS backdrop-filter property, so it must
be gated on browser support for it.
2026-06-24 20:16:44 +02:00
Calixte Denizet
d8ea2afe47 Add support for Screen annotations playing embedded media
Screen annotations whose rendition action resolves to an embedded audio/video
file now play through the same play-button overlay as RichMedia.
Factor the shared resolution logic into a MediaAnnotation base (used by both RichMedia and Screen).

It fixes #6078 and #2787.
2026-06-23 20:48:35 +02:00
Calixte Denizet
d537f5ba4b
Add support for RichMedia annotations
Render `/Subtype /RichMedia` annotations so embedded video and audio can
be played in the viewer.

The core layer parses the `RichMediaContent` dictionary to locate the
primary playable asset and its MIME type. The display layer overlays a
play button on the annotation's poster; clicking it swaps in a
`<video>`/`<audio>` element backed by a `blob:` URL. Presentation mode
lets events reach the media controls instead of advancing the page.

It fixes #2787.
2026-06-22 21:27:52 +02:00