It flags the regexes whose search is quadratic in the input length, like
the autolinker and XFA-path ones fixed recently.
The three existing offenders: `\s*` matched the CSS indentation but also
the line terminators that make `^` match with the `m` flag (the
preprocessed CSS is unchanged), `(\d+)` made every digit of a number a
candidate start position, and `/T.*$/` could fail on the `$` and
backtrack since `.` doesn't match a line terminator.
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.
When an AcroForm has no Fields entry, #fixFields rebuilds it by walking
each widget's Parent chain up to its root field. A chain looping back on
itself made that walk spin forever and hung the worker.
Matching the name with a leading `.+` is quadratic in the length of
a component which doesn't end with a position, and every AcroForm
field name goes through this.
`toFixed(10)` switches to the exponential notation from 1e21 on, which isn't
valid PDF syntax, and removing the trailing zeros then dropped a digit of the
exponent: 1e30 was written "1e+3" and 1e100 "1e+1". Such a number, necessarily
an integer, is now written with all its digits.
The trailing zeros are removed with a backward scan, since `toFixed(10)` always
produces exactly 10 decimals. Below the 1e21 limit its output is at most 33
characters long, so the previous `$`-anchored regex wasn't a performance issue.
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.
In PR 12569 the `_actions` class-field was changed from an Object into a Map, with *most* of the code updated to reflect that.
However, in the `setAction` method it's still treated as an Object which means that any added script will simply be ignored. Most likely that part of the scripting-implementation isn't being used, since this code has been "wrong" for close to six years now.
Scanning to the end of the string for every "&" made the entity
resolution quadratic. Stopping at the next "&" also fixes a bare
ampersand swallowing the reference which follows it.
Searching for a name followed by ".pdf" is quadratic on a hash which
doesn't contain one, so locate the last ".pdf" first and then extend
it to the left.
Removing the trailing whitespace with a `$`-anchored regex is
quadratic in the length of the run, which a server controls.
The helper lives in network_utils.js, to be unit testable.
`String.fromCodePoint` throws on anything which isn't a code point,
so e.g. "&#xZZ;" or "�" aborted the whole parsing. Such a
reference is now kept as-is, like an unknown named entity.
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.
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.
Note also how in the scripting-implementation the `actions` were already converted into a Map, via the `createActionsMap` helper.
In the Firefox PDF Viewer sending `Map`s 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 unfortunately `JSON.stringify()` doesn't support Maps. Hence we convert Maps to Objects, via a [`replacer` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#replacer), since the existing `createActionsMap` usage will convert the actions-Objects back to Maps.
The `PDFDocument.prototype.signatures` getter returns a (shadowed) Promise, however the way that it invokes various helper-methods can lead to *intermittent* failures to parse the signature data.
These helper-methods will lookup a fair amount of Dictionary data, however any one of those cases could throw `MissingDataException` during document loading.
To avoid having to re-factor those methods a lot, and adding a bunch more `pdfManager.ensureDoc()` calls, they are instead made asynchronous and the Dictionary lookups changed to use `Dict.prototype.getAsync` (similar to the existing `fieldObjects` handling).
Technically this additional asynchronicity may be ever so slightly slower, however I don't think it matters in practice since: most PDFs don't have any signatures, the signature-UI is initialized lazily in the viewer, and finally fetching/parsing of signatures do not block rendering.
Also, note how multiple values are being fetched in parallel in order to attempt to reduce overall asynchronicity.
*Note:* The unit-test changes are essentially fixing pre-existing bugs, that this patch exposed, since the test-only `Dict` instances weren't able to fetch indirect objects.
The axial/radial ramps and the function-based lattice converted one color per
call, i.e. one Wasm round-trip each for `IccColorSpace` (hence for
`/DeviceCMYK`). Add `ColorSpace.getRgbItems`, overridden by `IccColorSpace` and
`AlternateCS`, to convert a whole batch at once.
Currently we mostly use `toThrow()`, which seems intended for things that throw non-Errors (something that we "forbid" with ESLint).
Hence `toThrowError()` seems more appropriate, and it also simplifies things slightly; see https://jasmine.github.io/api/edge/matchers.html#toThrowError
Part of this is very old code, hence modernizing it a little bit more really shouldn't hurt.
This patch also shortens some `XRef` code by using nullish coalescing assignment respectively ternary operators more.
Finally, adds the recently introduced `countUpdatesAfter` method to the `XRefMock`/`XRefWrapper` classes to avoid having to check for its existence.
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.