fix: intersect all clip rects in stack for nested overflow:hidden

Previously Output.get() only used the last clip rect (clips.at(-1)),
which meant an inner overflow:hidden box wider than its outer ancestor
could effectively loosen the outer clip. Now all active clips are
intersected via reduce, and writes are skipped entirely when the
intersection is empty.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yunfei He
2026-05-25 22:53:41 +08:00
parent da7c843d44
commit 04707e82d9
+17 -3
View File
@@ -93,12 +93,26 @@ class Output {
return l;
});
// Apply active clip rect
const clip = clips.at(-1);
if (clip) {
// Apply active clip rect — intersect ALL clips in the stack so nested
// overflow:hidden boxes correctly constrain children.
if (clips.length > 0) {
const clip = clips.reduce<ClipRect>(
(acc, c) => ({
x1: acc.x1 != null && c.x1 != null ? Math.max(acc.x1, c.x1) : (acc.x1 ?? c.x1),
x2: acc.x2 != null && c.x2 != null ? Math.min(acc.x2, c.x2) : (acc.x2 ?? c.x2),
y1: acc.y1 != null && c.y1 != null ? Math.max(acc.y1, c.y1) : (acc.y1 ?? c.y1),
y2: acc.y2 != null && c.y2 != null ? Math.min(acc.y2, c.y2) : (acc.y2 ?? c.y2),
}),
{ x1: undefined, x2: undefined, y1: undefined, y2: undefined },
);
const clipH = typeof clip.x1 === "number" && typeof clip.x2 === "number";
const clipV = typeof clip.y1 === "number" && typeof clip.y2 === "number";
// If the intersection is empty, skip the write entirely
if (clipH && clip.x1! >= clip.x2!) continue;
if (clipV && clip.y1! >= clip.y2!) continue;
// Skip entirely out-of-bounds writes
if (clipV) {
const height = lines.length;