commit a8154dccaa8c98ec37ae6ec5c52279d0369e9533
parent b2ed6ac947d48e37479b5cc16a9af372e4f8b953
Author: Jonathan Kew <jkew@mozilla.com>
Date: Thu, 6 Nov 2025 13:28:57 +0000
Bug 1998521 - Don't assume we can always get a line iterator in ComputeDecorationInset. r=layout-reviewers,emilio
Differential Revision: https://phabricator.services.mozilla.com/D271552
Diffstat:
3 files changed, 46 insertions(+), 38 deletions(-)
diff --git a/layout/generic/crashtests/1998521.html b/layout/generic/crashtests/1998521.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<!-- COMMENT -->
+<del style="display: ruby-text; -webkit-flex-grow: 946015166.8948371" lang="zh-hk">
+<!-- COMMENT -->
+\ No newline at end of file
diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list
@@ -824,3 +824,4 @@ asserts(0-5) load 1965081.html # Bug 457400 - ASSERTION: can't mark frame dirty
load 1978475-1.html
load 1990258.html
load 1990319.html
+load 1998521.html
diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp
@@ -5853,54 +5853,56 @@ static bool ComputeDecorationInset(
decContainer = aDecFrame;
} else {
nsIFrame* const lineContainer = FindLineContainer(aFrame);
- nsILineIterator* const iter = lineContainer->GetLineIterator();
- MOZ_ASSERT(iter,
- "Line container of a text frame must be able to produce a "
- "line iterator");
MOZ_ASSERT(
lineContainer->GetWritingMode().IsVertical() == wm.IsVertical(),
"Decorating frame and line container must have writing modes in the "
"same axis");
- const int32_t lineNum = GetFrameLineNum(aFrame, iter);
- const nsILineIterator::LineInfo lineInfo = iter->GetLine(lineNum).unwrap();
-
- // Create the rects, relative to the line container.
- decRect = lineInfo.mLineBounds;
- decContainer = lineContainer;
-
- // Account for text-indent, which will push text frames into the line box.
- const StyleTextIndent& textIndent = aFrame->StyleText()->mTextIndent;
- if (!textIndent.length.IsDefinitelyZero()) {
- bool isFirstLineOrAfterHardBreak = true;
- if (lineNum > 0 && !textIndent.each_line) {
- isFirstLineOrAfterHardBreak = false;
- } else if (nsBlockFrame* prevBlock =
- do_QueryFrame(lineContainer->GetPrevInFlow())) {
- if (!(textIndent.each_line &&
- (prevBlock->Lines().empty() ||
- !prevBlock->LinesEnd().prev()->IsLineWrapped()))) {
+ if (nsILineIterator* const iter = lineContainer->GetLineIterator()) {
+ const int32_t lineNum = GetFrameLineNum(aFrame, iter);
+ const nsILineIterator::LineInfo lineInfo =
+ iter->GetLine(lineNum).unwrap();
+ decRect = lineInfo.mLineBounds;
+
+ // Account for text-indent, which will push text frames into the line box.
+ const StyleTextIndent& textIndent = aFrame->StyleText()->mTextIndent;
+ if (!textIndent.length.IsDefinitelyZero()) {
+ bool isFirstLineOrAfterHardBreak = true;
+ if (lineNum > 0 && !textIndent.each_line) {
isFirstLineOrAfterHardBreak = false;
+ } else if (nsBlockFrame* prevBlock =
+ do_QueryFrame(lineContainer->GetPrevInFlow())) {
+ if (!(textIndent.each_line &&
+ (prevBlock->Lines().empty() ||
+ !prevBlock->LinesEnd().prev()->IsLineWrapped()))) {
+ isFirstLineOrAfterHardBreak = false;
+ }
+ }
+ if (isFirstLineOrAfterHardBreak != textIndent.hanging) {
+ // Determine which side to shrink.
+ const Side side = wm.PhysicalSide(LogicalSide::IStart);
+ // Calculate the text indent, and shrink the line box by this amount
+ // to account for the indent size at the start of the line.
+ const nscoord basis = lineContainer->GetLogicalSize(wm).ISize(wm);
+ nsMargin indentMargin;
+ indentMargin.Side(side) = textIndent.length.Resolve(basis);
+ decRect.Deflate(indentMargin);
}
}
- if (isFirstLineOrAfterHardBreak != textIndent.hanging) {
- // Determine which side to shrink.
- const Side side = wm.PhysicalSide(LogicalSide::IStart);
- // Calculate the text indent, and shrink the line box by this amount to
- // acount for the indent size at the start of the line.
- const nscoord basis = lineContainer->GetLogicalSize(wm).ISize(wm);
- nsMargin indentMargin;
- indentMargin.Side(side) = textIndent.length.Resolve(basis);
- decRect.Deflate(indentMargin);
+
+ // We can't allow a block frame to retain a line iterator if we're
+ // currently in reflow, as it will become invalid as the line list is
+ // reflowed.
+ if (lineContainer->HasAnyStateBits(NS_FRAME_IN_REFLOW) &&
+ lineContainer->IsBlockFrameOrSubclass()) {
+ static_cast<nsBlockFrame*>(lineContainer)->ClearLineIterator();
}
+ } else {
+ // Not a block or similar container with multiple lines; just use the
+ // content rect directly.
+ decRect = lineContainer->GetContentRectRelativeToSelf();
}
- // We can't allow a block frame to retain a line iterator if we're
- // currently in reflow, as it will become invalid as the line list is
- // reflowed.
- if (lineContainer->HasAnyStateBits(NS_FRAME_IN_REFLOW) &&
- lineContainer->IsBlockFrameOrSubclass()) {
- static_cast<nsBlockFrame*>(lineContainer)->ClearLineIterator();
- }
+ decContainer = lineContainer;
}
// The rect of the current frame, mapped to the same coordinate space as