From 5e18cfd8f0294c210fd0f68c03a7cbdbf6599663 Mon Sep 17 00:00:00 2001 From: calixteman Date: Thu, 14 May 2026 22:59:21 +0200 Subject: [PATCH 1/2] Clear the full SMask scratch canvas in compose() PR #21101 narrowed `compose()`'s `clearRect` from full canvas to the caller-supplied dirty box. That leaves pixels outside the current dirty box on the SMask scratch canvas between `compose()` calls; subsequent draws into scratch are then source-over-blended on top of those leftovers, so the output depends on the cumulative draw history rather than just the current draw. --- src/display/canvas.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/display/canvas.js b/src/display/canvas.js index 2781c19294b64..e104c9fbab920 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -1850,17 +1850,13 @@ class CanvasGraphics { return; } - // Whatever was drawn has been moved to the suspended canvas, now clear it - // out of the current canvas. Only the dirty box region needs clearing; - // everything outside it is already transparent. + // Clear the full scratch canvas, not just the dirty box. Pixels left + // outside dirtyBox can leak into a later compose() whose destination-in + // pass doesn't overwrite them, producing stale output -- this is what + // breaks `firefox-issue17779-partial` (issue #21276). this.ctx.save(); this.ctx.setTransform(1, 0, 0, 1, 0, 0); - this.ctx.clearRect( - dirtyBox[0], - dirtyBox[1], - dirtyBox[2] - dirtyBox[0], - dirtyBox[3] - dirtyBox[1] - ); + this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); this.ctx.restore(); } From 367f994d944b0b968c2f93ea2f24edbb7ebcee65 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 15 May 2026 15:07:44 +0200 Subject: [PATCH 2/2] Remove the `Util.domMatrixToTransform` method This method is unused in the worker-thread and has only *a single* call-site in the main-thread, which can be trivially replaced with the `getCurrentTransform` helper function. --- src/display/canvas_dependency_tracker.js | 3 ++- src/shared/util.js | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/display/canvas_dependency_tracker.js b/src/display/canvas_dependency_tracker.js index ad26428b18bd7..11b6a564f6505 100644 --- a/src/display/canvas_dependency_tracker.js +++ b/src/display/canvas_dependency_tracker.js @@ -14,6 +14,7 @@ */ import { BBOX_INIT, FeatureTest, Util } from "../shared/util.js"; +import { getCurrentTransform } from "./display_utils.js"; import { MathClamp } from "../shared/math_clamp.js"; const FORCED_DEPENDENCY_LABEL = "__forcedDependency"; @@ -1121,7 +1122,7 @@ class CanvasImagesTracker { this.#coords = newCoords; } - const transform = Util.domMatrixToTransform(ctx.getTransform()); + const transform = getCurrentTransform(ctx); // We want top left, bottom left, top right. // (0, 0) is the bottom left corner. diff --git a/src/shared/util.js b/src/shared/util.js index b75301b24c7b0..f73f6fc816b96 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -709,10 +709,6 @@ class Util { return `#${this.hexNums[r]}${this.hexNums[g]}${this.hexNums[b]}`; } - static domMatrixToTransform(dm) { - return [dm.a, dm.b, dm.c, dm.d, dm.e, dm.f]; - } - // Apply a scaling matrix to some min/max values. // If a scaling factor is negative then min and max must be // swapped.