commit 09f5e8014568124215e737b186a3198462ae297e
parent 11f456595ac43091170bff03ee639757f0428667
Author: David Shin <dshin@mozilla.com>
Date: Wed, 22 Oct 2025 13:40:05 +0000
Bug 1993650: Fix up align/justify overflow handling. r=layout-reviewers,emilio
Follows the spec [1] closely now.
[1]: https://drafts.csswg.org/css-align-3/#auto-safety-position
Differential Revision: https://phabricator.services.mozilla.com/D268240
Diffstat:
1 file changed, 33 insertions(+), 19 deletions(-)
diff --git a/layout/generic/AbsoluteContainingBlock.cpp b/layout/generic/AbsoluteContainingBlock.cpp
@@ -657,27 +657,41 @@ static nscoord OffsetToAlignedStaticPos(
// alignment values: https://drafts.csswg.org/css-position-3/#abspos-layout
// Skip if the raw self alignment for this element is `auto` to preserve
// legacy behaviour.
- // We've already aligned as if unsafe. Now get the union of inset-reduced
- // containing block and the containing block.
- const auto unionedStartOffset =
- std::min(0, aNonAutoAlignParams->mCurrentStartInset);
+ // Follows https://drafts.csswg.org/css-align-3/#auto-safety-position
const auto cbSize = aAbsPosCBSize.Size(aAbsPosCBAxis, aAbsPosCBWM);
- const auto unionedEndOffset =
- std::max(cbSize, cbSize - aNonAutoAlignParams->mCurrentEndInset);
- const auto kidSizeInAxis =
- aKidSizeInAbsPosCBWM.Size(aAbsPosCBAxis, aAbsPosCBWM);
- if (unionedEndOffset - unionedStartOffset < kidSizeInAxis) {
- // Kid is bigger than the union - start align it.
- offset = -aNonAutoAlignParams->mCurrentStartInset + unionedStartOffset;
- } else {
- const auto start = aNonAutoAlignParams->mCurrentStartInset;
- const auto end = start + kidSizeInAxis;
- // Nudge into the union
- if (start < unionedStartOffset) {
- offset = unionedStartOffset - start;
- } else if (end > unionedEndOffset) {
- offset = unionedEndOffset - end;
+ // IMCB stands for "Inset-Modified Containing Block."
+ const auto imcbStart = aNonAutoAlignParams->mCurrentStartInset;
+ const auto imcbEnd = cbSize - aNonAutoAlignParams->mCurrentEndInset;
+ const auto kidSize = aKidSizeInAbsPosCBWM.Size(aAbsPosCBAxis, aAbsPosCBWM);
+ const auto kidStart = aNonAutoAlignParams->mCurrentStartInset + offset;
+ const auto kidEnd = kidStart + kidSize;
+ // "[...] the overflow limit rect is the bounding rectangle of the alignment
+ // subject’s inset-modified containing block and its original containing
+ // block."
+ const auto overflowLimitRectStart = std::min(0, imcbStart);
+ const auto overflowLimitRectEnd = std::max(cbSize, imcbEnd);
+
+ if (kidStart >= imcbStart && kidEnd <= imcbEnd) {
+ // 1. We fit inside the IMCB, no action needed.
+ } else if (kidSize <= overflowLimitRectEnd - overflowLimitRectStart) {
+ // 2. We overflowed IMCB, try to cover IMCB completely, if it's not.
+ if (kidEnd < imcbEnd) {
+ offset += imcbEnd - kidEnd;
+ } else if (kidStart > imcbStart) {
+ offset -= kidStart - imcbStart;
+ } else {
+ // IMCB already covered, ensure that we aren't escaping the limit rect.
+ if (kidStart < overflowLimitRectStart) {
+ offset += overflowLimitRectStart - kidStart;
+ } else if (kidEnd > overflowLimitRectEnd) {
+ offset -= kidEnd - overflowLimitRectEnd;
+ }
}
+ } else {
+ // 3. We'll overflow the limit rect. Start align the subject int overflow
+ // limit rect.
+ offset =
+ -aNonAutoAlignParams->mCurrentStartInset + overflowLimitRectStart;
}
}