[api-major] Update the minimum supported browsers, and remove no longer needed polyfills

By removing support for older browsers it's possible to simplify both the code and the build-scripts, in addition to removing manually implemented polyfills.
Using the PDF.js library/viewer will now require native support for the following features:
 - The `AbortSignal.any()` static method, see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static#browser_compatibility
 - The `:dir()` CSS pseudo-class, see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:dir#browser_compatibility
 - The `light-dark()` CSS function, see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/light-dark#browser_compatibility
 - The CSS `&` nesting selector, see https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/Nesting_selector#browser_compatibility

This patch updates the minimum supported browsers as follows:
 - Google Chrome 125, which was released on 2024-05-15; see https://chromereleases.googleblog.com/2024/05/stable-channel-update-for-desktop_15.html
 - Safari 18, which was released on 2024-09-16; see https://developer.apple.com/documentation/safari-release-notes/safari-18-release-notes
   *Note:* This version is the first with experimental support for the `CanvasRenderingContext2D.filter` property, which is a long-standing missing feature in Safari, however it must be *manually enabled*; see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter#browser_compatibility

Note that nowadays we usually try, where feasible and possible, to support browsers that are about two years old. By limiting support to only "recent" browsers we reduce the risk of holding back improvements of the *built-in* Firefox PDF Viewer, and also (significantly) reduce the maintenance/support burden for the PDF.js contributors.

*Please note:* As always, the minimum supported browser version assumes that a `legacy`-build of the PDF.js library is being used; see https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support
This commit is contained in:
Jonas Jenwald 2026-04-24 12:03:54 +02:00
parent 7ebf3a4d7c
commit 270b68feb9
6 changed files with 20 additions and 256 deletions

View File

@ -40,10 +40,7 @@ import Metalsmith from "metalsmith";
import ordered from "ordered-read-streams";
import path from "path";
import postcss from "gulp-postcss";
import postcssDirPseudoClass from "postcss-dir-pseudo-class";
import postcssDiscardComments from "postcss-discard-comments";
import postcssLightDarkFunction from "@csstools/postcss-light-dark-function";
import postcssNesting from "postcss-nesting";
import { preprocess } from "./external/builder/builder.mjs";
import relative from "metalsmith-html-relative";
import rename from "gulp-rename";
@ -91,9 +88,9 @@ const config = JSON.parse(fs.readFileSync(CONFIG_FILE).toString());
const ENV_TARGETS = [
"last 2 versions",
"Chrome >= 118",
"Chrome >= 125",
"Firefox ESR",
"Safari >= 16.4",
"Safari >= 18",
"Node >= 22",
"> 1%",
"not IE > 0",
@ -1246,15 +1243,7 @@ function buildGeneric(defines, dir) {
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
preprocessCSS("web/viewer.css", defines)
.pipe(
postcss([
postcssDirPseudoClass(),
discardCommentsCSS(),
postcssNesting(),
postcssLightDarkFunction({ preserve: true }),
autoprefixer(AUTOPREFIXER_CONFIG),
])
)
.pipe(postcss([discardCommentsCSS(), autoprefixer(AUTOPREFIXER_CONFIG)]))
.pipe(gulp.dest(dir + "web")),
gulp
@ -1314,15 +1303,7 @@ function buildComponents(defines, dir) {
.src(COMPONENTS_IMAGES, { encoding: false })
.pipe(gulp.dest(dir + "images")),
preprocessCSS("web/pdf_viewer.css", defines)
.pipe(
postcss([
postcssDirPseudoClass(),
discardCommentsCSS(),
postcssNesting(),
postcssLightDarkFunction({ preserve: true }),
autoprefixer(AUTOPREFIXER_CONFIG),
])
)
.pipe(postcss([discardCommentsCSS(), autoprefixer(AUTOPREFIXER_CONFIG)]))
.pipe(gulp.dest(dir)),
]);
}
@ -1651,13 +1632,7 @@ gulp.task(
),
preprocessCSS("web/viewer.css", defines)
.pipe(
postcss([
postcssDirPseudoClass(),
discardCommentsCSS(),
postcssNesting(),
postcssLightDarkFunction({ preserve: true }),
autoprefixer(AUTOPREFIXER_CONFIG),
])
postcss([discardCommentsCSS(), autoprefixer(AUTOPREFIXER_CONFIG)])
)
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
@ -2584,15 +2559,7 @@ function buildInternalViewer(defines, dir) {
gulp.dest(dir + "web")
),
preprocessCSS("web/internal/debugger.css", defines)
.pipe(
postcss([
postcssDirPseudoClass(),
discardCommentsCSS(),
postcssNesting(),
postcssLightDarkFunction({ preserve: true }),
autoprefixer(AUTOPREFIXER_CONFIG),
])
)
.pipe(postcss([discardCommentsCSS(), autoprefixer(AUTOPREFIXER_CONFIG)]))
.pipe(gulp.dest(dir + "web")),
createCMapBundle().pipe(gulp.dest(dir + "web/cmaps")),
createICCBundle().pipe(gulp.dest(dir + "web/iccs")),

135
package-lock.json generated
View File

@ -10,7 +10,6 @@
"@babel/core": "^7.29.0",
"@babel/preset-env": "^7.29.2",
"@babel/runtime": "^7.29.2",
"@csstools/postcss-light-dark-function": "^3.0.0",
"@eslint/json": "^1.2.0",
"@fluent/bundle": "^0.19.1",
"@fluent/dom": "^0.10.2",
@ -53,9 +52,7 @@
"ordered-read-streams": "^2.0.0",
"pngjs": "^7.0.0",
"postcss": "^8.5.10",
"postcss-dir-pseudo-class": "^10.0.0",
"postcss-discard-comments": "^7.0.7",
"postcss-nesting": "^14.0.0",
"postcss-values-parser": "^7.0.0",
"prettier": "^3.8.3",
"puppeteer": "^24.42.0",
@ -1775,61 +1772,6 @@
"@csstools/css-tokenizer": "^4.0.0"
}
},
"node_modules/@csstools/postcss-light-dark-function": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-3.0.0.tgz",
"integrity": "sha512-s++V5/hYazeRUCYIn2lsBVzUsxdeC46gtwpgW6lu5U/GlPOS5UTDT14kkEyPgXmFbCvaWLREqV7YTMJq1K3G6w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"dependencies": {
"@csstools/css-parser-algorithms": "^4.0.0",
"@csstools/css-tokenizer": "^4.0.0",
"@csstools/postcss-progressive-custom-properties": "^5.0.0",
"@csstools/utilities": "^3.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"postcss": "^8.4"
}
},
"node_modules/@csstools/postcss-progressive-custom-properties": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-5.0.0.tgz",
"integrity": "sha512-NsJoZ89rxmDrUsITf8QIk5w+lQZQ8Xw5K6cLFG+cfiffsLYHb3zcbOOrHLetGl1WIhjWWQ4Cr8MMrg46Q+oACg==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"dependencies": {
"postcss-value-parser": "^4.2.0"
},
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"postcss": "^8.4"
}
},
"node_modules/@csstools/selector-resolve-nested": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.0.tgz",
@ -1876,29 +1818,6 @@
"postcss-selector-parser": "^7.1.1"
}
},
"node_modules/@csstools/utilities": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@csstools/utilities/-/utilities-3.0.0.tgz",
"integrity": "sha512-etDqA/4jYvOGBM6yfKCOsEXfH96BKztZdgGmGqKi2xHnDe0ILIBraRspwgYatJH9JsCZ5HCGoCst8w18EKOAdg==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"postcss": "^8.4"
}
},
"node_modules/@dot/log": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/@dot/log/-/log-0.2.1.tgz",
@ -9280,32 +9199,6 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-dir-pseudo-class": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-10.0.0.tgz",
"integrity": "sha512-DmtIzULpyC8XaH4b5AaUgt4Jic4QmrECqidNCdR7u7naQFdnxX80YI06u238a+ZVRXwURDxVzy0s/UQnWmpVeg==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"dependencies": {
"postcss-selector-parser": "^7.1.1"
},
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"postcss": "^8.4"
}
},
"node_modules/postcss-discard-comments": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.7.tgz",
@ -9362,34 +9255,6 @@
}
}
},
"node_modules/postcss-nesting": {
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-14.0.0.tgz",
"integrity": "sha512-YGFOfVrjxYfeGTS5XctP1WCI5hu8Lr9SmntjfRC+iX5hCihEO+QZl9Ra+pkjqkgoVdDKvb2JccpElcowhZtzpw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/csstools"
},
{
"type": "opencollective",
"url": "https://opencollective.com/csstools"
}
],
"license": "MIT-0",
"dependencies": {
"@csstools/selector-resolve-nested": "^4.0.0",
"@csstools/selector-specificity": "^6.0.0",
"postcss-selector-parser": "^7.1.1"
},
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"postcss": "^8.4"
}
},
"node_modules/postcss-safe-parser": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz",

View File

@ -5,7 +5,6 @@
"@babel/core": "^7.29.0",
"@babel/preset-env": "^7.29.2",
"@babel/runtime": "^7.29.2",
"@csstools/postcss-light-dark-function": "^3.0.0",
"@eslint/json": "^1.2.0",
"@fluent/bundle": "^0.19.1",
"@fluent/dom": "^0.10.2",
@ -48,9 +47,7 @@
"ordered-read-streams": "^2.0.0",
"pngjs": "^7.0.0",
"postcss": "^8.5.10",
"postcss-dir-pseudo-class": "^10.0.0",
"postcss-discard-comments": "^7.0.7",
"postcss-nesting": "^14.0.0",
"postcss-values-parser": "^7.0.0",
"prettier": "^3.8.3",
"puppeteer": "^24.42.0",

View File

@ -42,14 +42,6 @@ import {
CanvasDependencyTracker,
CanvasImagesTracker,
} from "./canvas_dependency_tracker.js";
import {
deprecated,
isDataScheme,
isValidFetchUrl,
PageViewport,
RenderingCancelledException,
StatTimer,
} from "./display_utils.js";
import { FontFaceObject, FontLoader } from "./font_loader.js";
import {
FontInfo,
@ -63,6 +55,13 @@ import {
isRefProxy,
LoopbackPort,
} from "./api_utils.js";
import {
isDataScheme,
isValidFetchUrl,
PageViewport,
RenderingCancelledException,
StatTimer,
} from "./display_utils.js";
import { MessageHandler, wrapReason } from "../shared/message_handler.js";
import {
NodeBinaryDataFactory,
@ -649,18 +648,6 @@ class PDFDataRangeTransport {
this.initialData = initialData;
this.progressiveDone = progressiveDone;
this.contentDispositionFilename = contentDispositionFilename;
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
Object.defineProperty(this, "onDataProgress", {
value: () => {
deprecated(
"`PDFDataRangeTransport.prototype.onDataProgress` - method was " +
"removed, since loading progress is now reported automatically " +
"through the `PDFDataTransportStream` class (and related code)."
);
},
});
}
}
/**

View File

@ -16,7 +16,6 @@
import {
BaseException,
DrawOPS,
FeatureTest,
shadow,
stripPath,
Util,
@ -677,16 +676,9 @@ function setLayerDimensions(
if (viewport instanceof PageViewport) {
const { pageWidth, pageHeight } = viewport.rawDims;
const { style } = div;
const useRound = FeatureTest.isCSSRoundSupported;
const w = `var(--total-scale-factor) * ${pageWidth}px`,
h = `var(--total-scale-factor) * ${pageHeight}px`;
const widthStr = useRound
? `round(down, ${w}, var(--scale-round-x))`
: `calc(${w})`,
heightStr = useRound
? `round(down, ${h}, var(--scale-round-y))`
: `calc(${h})`;
const widthStr = `round(down, var(--total-scale-factor) * ${pageWidth}px, var(--scale-round-x))`,
heightStr = `round(down, var(--total-scale-factor) * ${pageHeight}px, var(--scale-round-y))`;
if (!mustFlip || viewport.rotation % 180 === 0) {
style.width = widthStr;

View File

@ -601,17 +601,13 @@ function objectSize(obj) {
return Object.keys(obj).length;
}
// Checks the endianness of the platform.
function isLittleEndian() {
const buffer8 = new Uint8Array(4);
buffer8[0] = 1;
const view32 = new Uint32Array(buffer8.buffer, 0, 1);
return view32[0] === 1;
}
class FeatureTest {
static get isLittleEndian() {
return shadow(this, "isLittleEndian", isLittleEndian());
const buffer8 = new Uint8Array(4);
buffer8[0] = 1;
const view32 = new Uint32Array(buffer8.buffer, 0, 1);
return shadow(this, "isLittleEndian", view32[0] === 1);
}
static get isOffscreenCanvasSupported() {
@ -660,14 +656,6 @@ class FeatureTest {
});
}
static get isCSSRoundSupported() {
return shadow(
this,
"isCSSRoundSupported",
globalThis.CSS?.supports?.("width: round(1.5px, 1px)")
);
}
static get isAlphaColorInputSupported() {
return shadow(
this,
@ -1259,38 +1247,6 @@ if (
};
}
// TODO: Remove this once Safari 17.4 is the lowest supported version.
if (
typeof PDFJSDev !== "undefined" &&
!PDFJSDev.test("SKIP_BABEL") &&
typeof AbortSignal.any !== "function"
) {
AbortSignal.any = function (iterable) {
const ac = new AbortController();
const { signal } = ac;
// Return immediately if any of the signals are already aborted.
for (const s of iterable) {
if (s.aborted) {
ac.abort(s.reason);
return signal;
}
}
// Register "abort" listeners for all signals.
for (const s of iterable) {
s.addEventListener(
"abort",
() => {
ac.abort(s.reason);
},
{ signal } // Automatically remove the listener.
);
}
return signal;
};
}
export {
_isValidExplicitDest,
AbortException,