1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* state and methods used while laying out a single line of a block frame */
9 #include "nsLineLayout.h"
11 #include "mozilla/ComputedStyle.h"
12 #include "mozilla/SVGTextFrame.h"
14 #include "LayoutLogging.h"
15 #include "nsBlockFrame.h"
16 #include "nsFontMetrics.h"
17 #include "nsStyleConsts.h"
18 #include "nsContainerFrame.h"
19 #include "nsFloatManager.h"
20 #include "nsPresContext.h"
21 #include "nsGkAtoms.h"
22 #include "nsIContent.h"
23 #include "nsLayoutUtils.h"
24 #include "nsTextFrame.h"
25 #include "nsStyleStructInlines.h"
26 #include "nsBidiPresUtils.h"
27 #include "nsRubyFrame.h"
28 #include "nsRubyTextFrame.h"
29 #include "RubyUtils.h"
33 # undef NOISY_INLINEDIR_ALIGN
34 # undef NOISY_BLOCKDIR_ALIGN
36 # undef REALLY_NOISY_REFLOW
38 # undef REALLY_NOISY_PUSHING
39 # undef NOISY_CAN_PLACE_FRAME
41 # undef REALLY_NOISY_TRIM
44 using namespace mozilla
;
46 //----------------------------------------------------------------------
48 nsLineLayout::nsLineLayout(nsPresContext
* aPresContext
,
49 nsFloatManager
* aFloatManager
,
50 const ReflowInput
& aLineContainerRI
,
51 const nsLineList::iterator
* aLine
,
52 nsLineLayout
* aBaseLineLayout
)
53 : mPresContext(aPresContext
),
54 mFloatManager(aFloatManager
),
55 mLineContainerRI(aLineContainerRI
),
56 mBaseLineLayout(aBaseLineLayout
),
57 mLastOptionalBreakFrame(nullptr),
58 mForceBreakFrame(nullptr),
59 mLastOptionalBreakPriority(gfxBreakPriority::eNoBreak
),
60 mLastOptionalBreakFrameOffset(-1),
61 mForceBreakFrameOffset(-1),
67 mFirstLetterStyleOK(false),
69 mImpactedByFloats(false),
70 mLastFloatWasLetterFrame(false),
76 mInFirstLetter(false),
78 mDirtyNextLine(false),
81 mSuppressLineWrap(LineContainerFrame()->IsInSVGTextSubtree()),
82 mUsedOverflowWrap(false)
91 NS_ASSERTION(aFloatManager
|| LineContainerFrame()->IsLetterFrame(),
92 "float manager should be present");
94 !!mBaseLineLayout
== LineContainerFrame()->IsRubyTextContainerFrame(),
95 "Only ruby text container frames have a different base line layout");
96 MOZ_COUNT_CTOR(nsLineLayout
);
98 // Stash away some style data that we need
99 nsBlockFrame
* blockFrame
= do_QueryFrame(LineContainerFrame());
100 mStyleText
= blockFrame
? blockFrame
->StyleTextForLineLayout()
101 : LineContainerFrame()->StyleText();
104 mTotalPlacedFrames
= 0;
108 mInflationMinFontSize
=
109 nsLayoutUtils::InflationMinFontSizeFor(LineContainerFrame());
111 // Instead of always pre-initializing the free-lists for frames and
112 // spans, we do it on demand so that situations that only use a few
113 // frames and spans won't waste a lot of time in unneeded
115 mFrameFreeList
= nullptr;
116 mSpanFreeList
= nullptr;
118 mCurrentSpan
= mRootSpan
= nullptr;
127 nsLineLayout::~nsLineLayout() {
128 MOZ_COUNT_DTOR(nsLineLayout
);
130 NS_ASSERTION(nullptr == mRootSpan
, "bad line-layout user");
133 // Find out if the frame has a non-null prev-in-flow, i.e., whether it
134 // is a continuation.
135 inline bool HasPrevInFlow(nsIFrame
* aFrame
) {
136 nsIFrame
* prevInFlow
= aFrame
->GetPrevInFlow();
137 return prevInFlow
!= nullptr;
140 void nsLineLayout::BeginLineReflow(nscoord aICoord
, nscoord aBCoord
,
141 nscoord aISize
, nscoord aBSize
,
142 bool aImpactedByFloats
, bool aIsTopOfPage
,
143 WritingMode aWritingMode
,
144 const nsSize
& aContainerSize
,
146 NS_ASSERTION(nullptr == mRootSpan
, "bad linelayout user");
147 LAYOUT_WARN_IF_FALSE(aISize
!= NS_UNCONSTRAINEDSIZE
,
148 "have unconstrained width; this should only result from "
149 "very large sizes, not attempts at intrinsic width "
152 if ((aISize
!= NS_UNCONSTRAINEDSIZE
) && ABSURD_SIZE(aISize
) &&
153 !LineContainerFrame()->GetParent()->IsAbsurdSizeAssertSuppressed()) {
154 LineContainerFrame()->ListTag(stdout
);
155 printf(": Init: bad caller: width WAS %d(0x%x)\n", aISize
, aISize
);
157 if ((aBSize
!= NS_UNCONSTRAINEDSIZE
) && ABSURD_SIZE(aBSize
) &&
158 !LineContainerFrame()->GetParent()->IsAbsurdSizeAssertSuppressed()) {
159 LineContainerFrame()->ListTag(stdout
);
160 printf(": Init: bad caller: height WAS %d(0x%x)\n", aBSize
, aBSize
);
164 LineContainerFrame()->ListTag(stdout
);
165 printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n", aICoord
, aBCoord
,
166 aISize
, aBSize
, aImpactedByFloats
? "true" : "false",
167 aIsTopOfPage
? "top-of-page" : "");
170 mSpansAllocated
= mSpansFreed
= mFramesAllocated
= mFramesFreed
= 0;
173 mFirstLetterStyleOK
= false;
174 mIsTopOfPage
= aIsTopOfPage
;
175 mImpactedByFloats
= aImpactedByFloats
;
176 mTotalPlacedFrames
= 0;
177 if (!mBaseLineLayout
) {
181 mLineIsEmpty
= false;
182 mLineAtStart
= false;
184 mLineEndsInBR
= false;
186 mMaxStartBoxBSize
= mMaxEndBoxBSize
= 0;
189 mLineBox
->ClearHasMarker();
192 PerSpanData
* psd
= NewPerSpanData();
193 mCurrentSpan
= mRootSpan
= psd
;
194 psd
->mReflowInput
= &mLineContainerRI
;
195 psd
->mIStart
= aICoord
;
196 psd
->mICoord
= aICoord
;
197 psd
->mIEnd
= aICoord
+ aISize
;
198 // Set up inset to be used for text-wrap:balance implementation, but only if
199 // the available size is greater than inset.
200 psd
->mInset
= aISize
> aInset
? aInset
: 0;
201 mContainerSize
= aContainerSize
;
203 mBStartEdge
= aBCoord
;
205 psd
->mNoWrap
= !mStyleText
->WhiteSpaceCanWrapStyle() || mSuppressLineWrap
;
206 psd
->mWritingMode
= aWritingMode
;
208 // Determine if this is the first line of the block (or first after a hard
209 // line-break, if `each-line` is in effect).
210 nsIFrame
* containerFrame
= LineContainerFrame();
211 if (!containerFrame
->IsRubyTextContainerFrame()) {
212 bool isFirstLineOrAfterHardBreak
= [&] {
213 if (mLineNumber
> 0) {
214 return mStyleText
->mTextIndent
.each_line
&& GetLine() &&
215 !GetLine()->prev()->IsLineWrapped();
217 if (nsBlockFrame
* prevBlock
=
218 do_QueryFrame(containerFrame
->GetPrevInFlow())) {
219 return mStyleText
->mTextIndent
.each_line
&&
220 (prevBlock
->Lines().empty() ||
221 !prevBlock
->LinesEnd().prev()->IsLineWrapped());
226 // Resolve and apply the text-indent value if this line requires it.
227 // The `hanging` option inverts which lines are to be indented.
228 if (isFirstLineOrAfterHardBreak
!= mStyleText
->mTextIndent
.hanging
) {
229 nscoord pctBasis
= mLineContainerRI
.ComputedISize();
230 mTextIndent
= mStyleText
->mTextIndent
.length
.Resolve(pctBasis
);
231 psd
->mICoord
+= mTextIndent
;
235 PerFrameData
* pfd
= NewPerFrameData(containerFrame
);
239 if (containerFrame
->IsRubyTextContainerFrame()) {
240 // Ruby text container won't be reflowed via ReflowFrame, hence the
241 // relative positioning information should be recorded here.
242 MOZ_ASSERT(mBaseLineLayout
!= this);
243 pfd
->mIsRelativelyOrStickyPos
=
244 mLineContainerRI
.mStyleDisplay
->IsRelativelyOrStickyPositionedStyle();
245 if (pfd
->mIsRelativelyOrStickyPos
) {
246 MOZ_ASSERT(mLineContainerRI
.GetWritingMode() == pfd
->mWritingMode
,
247 "mLineContainerRI.frame == frame, "
248 "hence they should have identical writing mode");
250 mLineContainerRI
.ComputedLogicalOffsets(pfd
->mWritingMode
);
255 bool nsLineLayout::EndLineReflow() {
257 LineContainerFrame()->ListTag(stdout
);
258 printf(": EndLineReflow: width=%d\n",
259 mRootSpan
->mICoord
- mRootSpan
->mIStart
);
262 NS_ASSERTION(!mBaseLineLayout
||
263 (!mSpansAllocated
&& !mSpansFreed
&& !mSpanFreeList
&&
264 !mFramesAllocated
&& !mFramesFreed
&& !mFrameFreeList
),
265 "Allocated frames or spans on non-base line layout?");
266 MOZ_ASSERT(mRootSpan
== mCurrentSpan
);
268 UnlinkFrame(mRootSpan
->mFrame
);
269 mCurrentSpan
= mRootSpan
= nullptr;
271 NS_ASSERTION(mSpansAllocated
== mSpansFreed
, "leak");
272 NS_ASSERTION(mFramesAllocated
== mFramesFreed
, "leak");
275 static int32_t maxSpansAllocated
= NS_LINELAYOUT_NUM_SPANS
;
276 static int32_t maxFramesAllocated
= NS_LINELAYOUT_NUM_FRAMES
;
277 if (mSpansAllocated
> maxSpansAllocated
) {
278 printf("XXX: saw a line with %d spans\n", mSpansAllocated
);
279 maxSpansAllocated
= mSpansAllocated
;
281 if (mFramesAllocated
> maxFramesAllocated
) {
282 printf("XXX: saw a line with %d frames\n", mFramesAllocated
);
283 maxFramesAllocated
= mFramesAllocated
;
287 return mUsedOverflowWrap
;
290 // XXX swtich to a single mAvailLineWidth that we adjust as each frame
291 // on the line is placed. Each span can still have a per-span mICoord that
292 // tracks where a child frame is going in its span; they don't need a
295 void nsLineLayout::UpdateBand(WritingMode aWM
,
296 const LogicalRect
& aNewAvailSpace
,
297 nsIFrame
* aFloatFrame
) {
298 WritingMode lineWM
= mRootSpan
->mWritingMode
;
299 // need to convert to our writing mode, because we might have a different
300 // mode from the caller due to dir: auto
301 LogicalRect availSpace
=
302 aNewAvailSpace
.ConvertTo(lineWM
, aWM
, ContainerSize());
303 #ifdef REALLY_NOISY_REFLOW
305 "nsLL::UpdateBand %d, %d, %d, %d, (converted to %d, %d, %d, %d); "
306 "frame=%p\n will set mImpacted to true\n",
307 aNewAvailSpace
.IStart(aWM
), aNewAvailSpace
.BStart(aWM
),
308 aNewAvailSpace
.ISize(aWM
), aNewAvailSpace
.BSize(aWM
),
309 availSpace
.IStart(lineWM
), availSpace
.BStart(lineWM
),
310 availSpace
.ISize(lineWM
), availSpace
.BSize(lineWM
), aFloatFrame
);
313 if ((availSpace
.ISize(lineWM
) != NS_UNCONSTRAINEDSIZE
) &&
314 ABSURD_SIZE(availSpace
.ISize(lineWM
)) &&
315 !LineContainerFrame()->GetParent()->IsAbsurdSizeAssertSuppressed()) {
316 LineContainerFrame()->ListTag(stdout
);
317 printf(": UpdateBand: bad caller: ISize WAS %d(0x%x)\n",
318 availSpace
.ISize(lineWM
), availSpace
.ISize(lineWM
));
320 if ((availSpace
.BSize(lineWM
) != NS_UNCONSTRAINEDSIZE
) &&
321 ABSURD_SIZE(availSpace
.BSize(lineWM
)) &&
322 !LineContainerFrame()->GetParent()->IsAbsurdSizeAssertSuppressed()) {
323 LineContainerFrame()->ListTag(stdout
);
324 printf(": UpdateBand: bad caller: BSize WAS %d(0x%x)\n",
325 availSpace
.BSize(lineWM
), availSpace
.BSize(lineWM
));
329 // Compute the difference between last times width and the new width
330 NS_WARNING_ASSERTION(
331 mRootSpan
->mIEnd
!= NS_UNCONSTRAINEDSIZE
&&
332 availSpace
.ISize(lineWM
) != NS_UNCONSTRAINEDSIZE
,
333 "have unconstrained inline size; this should only result from very large "
334 "sizes, not attempts at intrinsic width calculation");
335 // The root span's mIStart moves to aICoord
336 nscoord deltaICoord
= availSpace
.IStart(lineWM
) - mRootSpan
->mIStart
;
337 // The inline size of all spans changes by this much (the root span's
338 // mIEnd moves to aICoord + aISize, its new inline size is aISize)
340 availSpace
.ISize(lineWM
) - (mRootSpan
->mIEnd
- mRootSpan
->mIStart
);
342 LineContainerFrame()->ListTag(stdout
);
343 printf(": UpdateBand: %d,%d,%d,%d deltaISize=%d deltaICoord=%d\n",
344 availSpace
.IStart(lineWM
), availSpace
.BStart(lineWM
),
345 availSpace
.ISize(lineWM
), availSpace
.BSize(lineWM
), deltaISize
,
349 // Update the root span position
350 mRootSpan
->mIStart
+= deltaICoord
;
351 mRootSpan
->mIEnd
+= deltaICoord
;
352 mRootSpan
->mICoord
+= deltaICoord
;
354 // Now update the right edges of the open spans to account for any
355 // change in available space width
356 for (PerSpanData
* psd
= mCurrentSpan
; psd
; psd
= psd
->mParent
) {
357 psd
->mIEnd
+= deltaISize
;
358 psd
->mContainsFloat
= true;
360 printf(" span %p: oldIEnd=%d newIEnd=%d\n", psd
, psd
->mIEnd
- deltaISize
,
364 NS_ASSERTION(mRootSpan
->mContainsFloat
&&
365 mRootSpan
->mIStart
== availSpace
.IStart(lineWM
) &&
366 mRootSpan
->mIEnd
== availSpace
.IEnd(lineWM
),
367 "root span was updated incorrectly?");
369 // Update frame bounds
370 // Note: Only adjust the outermost frames (the ones that are direct
371 // children of the block), not the ones in the child spans. The reason
372 // is simple: the frames in the spans have coordinates local to their
373 // parent therefore they are moved when their parent span is moved.
374 if (deltaICoord
!= 0) {
375 for (PerFrameData
* pfd
= mRootSpan
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
376 pfd
->mBounds
.IStart(lineWM
) += deltaICoord
;
380 mBStartEdge
= availSpace
.BStart(lineWM
);
381 mImpactedByFloats
= true;
383 mLastFloatWasLetterFrame
= aFloatFrame
->IsLetterFrame();
386 nsLineLayout::PerSpanData
* nsLineLayout::NewPerSpanData() {
387 nsLineLayout
* outerLineLayout
= GetOutermostLineLayout();
388 PerSpanData
* psd
= outerLineLayout
->mSpanFreeList
;
390 void* mem
= outerLineLayout
->mArena
.Allocate(sizeof(PerSpanData
));
391 psd
= reinterpret_cast<PerSpanData
*>(mem
);
393 outerLineLayout
->mSpanFreeList
= psd
->mNextFreeSpan
;
395 psd
->mParent
= nullptr;
396 psd
->mFrame
= nullptr;
397 psd
->mFirstFrame
= nullptr;
398 psd
->mLastFrame
= nullptr;
399 psd
->mContainsFloat
= false;
400 psd
->mHasNonemptyContent
= false;
403 outerLineLayout
->mSpansAllocated
++;
408 void nsLineLayout::BeginSpan(nsIFrame
* aFrame
,
409 const ReflowInput
* aSpanReflowInput
,
410 nscoord aIStart
, nscoord aIEnd
,
411 nscoord
* aBaseline
) {
412 NS_ASSERTION(aIEnd
!= NS_UNCONSTRAINEDSIZE
,
413 "should no longer be using unconstrained sizes");
415 nsIFrame::IndentBy(stdout
, mSpanDepth
+ 1);
416 aFrame
->ListTag(stdout
);
417 printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aIStart
, aIEnd
);
420 PerSpanData
* psd
= NewPerSpanData();
421 // Link up span frame's pfd to point to its child span data
422 PerFrameData
* pfd
= mCurrentSpan
->mLastFrame
;
423 NS_ASSERTION(pfd
->mFrame
== aFrame
, "huh?");
428 psd
->mParent
= mCurrentSpan
;
429 psd
->mReflowInput
= aSpanReflowInput
;
430 psd
->mIStart
= aIStart
;
431 psd
->mICoord
= aIStart
;
433 psd
->mInset
= 0; // inset applies only to the root span
434 psd
->mBaseline
= aBaseline
;
436 nsIFrame
* frame
= aSpanReflowInput
->mFrame
;
437 psd
->mNoWrap
= !frame
->StyleText()->WhiteSpaceCanWrap(frame
) ||
438 mSuppressLineWrap
|| frame
->Style()->ShouldSuppressLineBreak();
439 psd
->mWritingMode
= aSpanReflowInput
->GetWritingMode();
441 // Switch to new span
446 nscoord
nsLineLayout::EndSpan(nsIFrame
* aFrame
) {
447 NS_ASSERTION(mSpanDepth
> 0, "end-span without begin-span");
449 nsIFrame::IndentBy(stdout
, mSpanDepth
);
450 aFrame
->ListTag(stdout
);
451 printf(": EndSpan width=%d\n", mCurrentSpan
->mICoord
- mCurrentSpan
->mIStart
);
453 PerSpanData
* psd
= mCurrentSpan
;
454 MOZ_ASSERT(psd
->mParent
, "We never call this on the root");
456 if (psd
->mNoWrap
&& !psd
->mParent
->mNoWrap
) {
460 nscoord iSizeResult
= psd
->mLastFrame
? (psd
->mICoord
- psd
->mIStart
) : 0;
463 mCurrentSpan
->mReflowInput
= nullptr; // no longer valid so null it out!
464 mCurrentSpan
= mCurrentSpan
->mParent
;
468 void nsLineLayout::AttachFrameToBaseLineLayout(PerFrameData
* aFrame
) {
469 MOZ_ASSERT(mBaseLineLayout
,
470 "This method must not be called in a base line layout.");
472 PerFrameData
* baseFrame
= mBaseLineLayout
->LastFrame();
473 MOZ_ASSERT(aFrame
&& baseFrame
);
474 MOZ_ASSERT(!aFrame
->mIsLinkedToBase
,
475 "The frame must not have been linked with the base");
477 LayoutFrameType baseType
= baseFrame
->mFrame
->Type();
478 LayoutFrameType annotationType
= aFrame
->mFrame
->Type();
479 MOZ_ASSERT((baseType
== LayoutFrameType::RubyBaseContainer
&&
480 annotationType
== LayoutFrameType::RubyTextContainer
) ||
481 (baseType
== LayoutFrameType::RubyBase
&&
482 annotationType
== LayoutFrameType::RubyText
));
485 aFrame
->mNextAnnotation
= baseFrame
->mNextAnnotation
;
486 baseFrame
->mNextAnnotation
= aFrame
;
487 aFrame
->mIsLinkedToBase
= true;
490 int32_t nsLineLayout::GetCurrentSpanCount() const {
491 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
493 PerFrameData
* pfd
= mRootSpan
->mFirstFrame
;
494 while (nullptr != pfd
) {
501 void nsLineLayout::SplitLineTo(int32_t aNewCount
) {
502 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
504 #ifdef REALLY_NOISY_PUSHING
505 printf("SplitLineTo %d (current count=%d); before:\n", aNewCount
,
506 GetCurrentSpanCount());
507 DumpPerSpanData(mRootSpan
, 1);
509 PerSpanData
* psd
= mRootSpan
;
510 PerFrameData
* pfd
= psd
->mFirstFrame
;
511 while (nullptr != pfd
) {
512 if (--aNewCount
== 0) {
513 // Truncate list at pfd (we keep pfd, but anything following is freed)
514 PerFrameData
* next
= pfd
->mNext
;
515 pfd
->mNext
= nullptr;
516 psd
->mLastFrame
= pfd
;
518 // Now unlink all of the frames following pfd
525 printf("SplitLineTo %d (current count=%d); after:\n", aNewCount
,
526 GetCurrentSpanCount());
527 DumpPerSpanData(mRootSpan
, 1);
531 void nsLineLayout::PushFrame(nsIFrame
* aFrame
) {
532 PerSpanData
* psd
= mCurrentSpan
;
533 NS_ASSERTION(psd
->mLastFrame
->mFrame
== aFrame
, "pushing non-last frame");
535 #ifdef REALLY_NOISY_PUSHING
536 nsIFrame::IndentBy(stdout
, mSpanDepth
);
537 printf("PushFrame %p, before:\n", psd
);
538 DumpPerSpanData(psd
, 1);
541 // Take the last frame off of the span's frame list
542 PerFrameData
* pfd
= psd
->mLastFrame
;
543 if (pfd
== psd
->mFirstFrame
) {
544 // We are pushing away the only frame...empty the list
545 psd
->mFirstFrame
= nullptr;
546 psd
->mLastFrame
= nullptr;
548 PerFrameData
* prevFrame
= pfd
->mPrev
;
549 prevFrame
->mNext
= nullptr;
550 psd
->mLastFrame
= prevFrame
;
553 // Now unlink the frame
554 MOZ_ASSERT(!pfd
->mNext
);
557 nsIFrame::IndentBy(stdout
, mSpanDepth
);
558 printf("PushFrame: %p after:\n", psd
);
559 DumpPerSpanData(psd
, 1);
563 void nsLineLayout::UnlinkFrame(PerFrameData
* pfd
) {
564 while (nullptr != pfd
) {
565 PerFrameData
* next
= pfd
->mNext
;
566 if (pfd
->mIsLinkedToBase
) {
567 // This frame is linked to a ruby base, and should not be freed
568 // now. Just unlink it from the span. It will be freed when its
569 // base frame gets unlinked.
570 pfd
->mNext
= pfd
->mPrev
= nullptr;
575 // It is a ruby base frame. If there are any annotations
576 // linked to this frame, free them first.
577 PerFrameData
* annotationPFD
= pfd
->mNextAnnotation
;
578 while (annotationPFD
) {
579 PerFrameData
* nextAnnotation
= annotationPFD
->mNextAnnotation
;
581 annotationPFD
->mNext
== nullptr && annotationPFD
->mPrev
== nullptr,
582 "PFD in annotations should have been unlinked.");
583 FreeFrame(annotationPFD
);
584 annotationPFD
= nextAnnotation
;
592 void nsLineLayout::FreeFrame(PerFrameData
* pfd
) {
593 if (nullptr != pfd
->mSpan
) {
594 FreeSpan(pfd
->mSpan
);
596 nsLineLayout
* outerLineLayout
= GetOutermostLineLayout();
597 pfd
->mNext
= outerLineLayout
->mFrameFreeList
;
598 outerLineLayout
->mFrameFreeList
= pfd
;
600 outerLineLayout
->mFramesFreed
++;
604 void nsLineLayout::FreeSpan(PerSpanData
* psd
) {
606 UnlinkFrame(psd
->mFirstFrame
);
608 nsLineLayout
* outerLineLayout
= GetOutermostLineLayout();
609 // Now put the span on the free list since it's free too
610 psd
->mNextFreeSpan
= outerLineLayout
->mSpanFreeList
;
611 outerLineLayout
->mSpanFreeList
= psd
;
613 outerLineLayout
->mSpansFreed
++;
617 bool nsLineLayout::IsZeroBSize() {
618 PerSpanData
* psd
= mCurrentSpan
;
619 PerFrameData
* pfd
= psd
->mFirstFrame
;
620 while (nullptr != pfd
) {
621 if (0 != pfd
->mBounds
.BSize(psd
->mWritingMode
)) {
629 nsLineLayout::PerFrameData
* nsLineLayout::NewPerFrameData(nsIFrame
* aFrame
) {
630 nsLineLayout
* outerLineLayout
= GetOutermostLineLayout();
631 PerFrameData
* pfd
= outerLineLayout
->mFrameFreeList
;
633 void* mem
= outerLineLayout
->mArena
.Allocate(sizeof(PerFrameData
));
634 pfd
= reinterpret_cast<PerFrameData
*>(mem
);
636 outerLineLayout
->mFrameFreeList
= pfd
->mNext
;
638 pfd
->mSpan
= nullptr;
639 pfd
->mNext
= nullptr;
640 pfd
->mPrev
= nullptr;
641 pfd
->mNextAnnotation
= nullptr;
642 pfd
->mFrame
= aFrame
;
644 // all flags default to false
645 pfd
->mIsRelativelyOrStickyPos
= false;
646 pfd
->mIsTextFrame
= false;
647 pfd
->mIsNonEmptyTextFrame
= false;
648 pfd
->mIsNonWhitespaceTextFrame
= false;
649 pfd
->mIsLetterFrame
= false;
650 pfd
->mRecomputeOverflow
= false;
651 pfd
->mIsMarker
= false;
652 pfd
->mSkipWhenTrimmingWhitespace
= false;
653 pfd
->mIsEmpty
= false;
654 pfd
->mIsPlaceholder
= false;
655 pfd
->mIsLinkedToBase
= false;
657 pfd
->mWritingMode
= aFrame
->GetWritingMode();
658 WritingMode lineWM
= mRootSpan
->mWritingMode
;
659 pfd
->mBounds
= LogicalRect(lineWM
);
660 pfd
->mOverflowAreas
.Clear();
661 pfd
->mMargin
= LogicalMargin(lineWM
);
662 pfd
->mBorderPadding
= LogicalMargin(lineWM
);
663 pfd
->mOffsets
= LogicalMargin(pfd
->mWritingMode
);
665 pfd
->mJustificationInfo
= JustificationInfo();
666 pfd
->mJustificationAssignment
= JustificationAssignment();
669 pfd
->mBlockDirAlign
= 0xFF;
670 outerLineLayout
->mFramesAllocated
++;
675 bool nsLineLayout::LineIsBreakable() const {
676 // XXX mTotalPlacedFrames should go away and we should just use
677 // mLineIsEmpty here instead
678 if ((0 != mTotalPlacedFrames
) || mImpactedByFloats
) {
684 // Checks all four sides for percentage units. This means it should
685 // only be used for things (margin, padding) where percentages on top
686 // and bottom depend on the *width* just like percentages on left and
688 template <typename T
>
689 static bool HasPercentageUnitSide(const StyleRect
<T
>& aSides
) {
690 return aSides
.Any([](const auto& aLength
) { return aLength
.HasPercent(); });
693 static bool IsPercentageAware(const nsIFrame
* aFrame
, WritingMode aWM
) {
694 NS_ASSERTION(aFrame
, "null frame is not allowed");
696 LayoutFrameType fType
= aFrame
->Type();
697 if (fType
== LayoutFrameType::Text
) {
698 // None of these things can ever be true for text frames.
702 // Some of these things don't apply to non-replaced inline frames
703 // (that is, fType == LayoutFrameType::Inline), but we won't bother making
704 // things unnecessarily complicated, since they'll probably be set
707 const nsStyleMargin
* margin
= aFrame
->StyleMargin();
708 if (HasPercentageUnitSide(margin
->mMargin
)) {
712 const nsStylePadding
* padding
= aFrame
->StylePadding();
713 if (HasPercentageUnitSide(padding
->mPadding
)) {
717 // Note that borders can't be aware of percentages
719 const nsStylePosition
* pos
= aFrame
->StylePosition();
721 if ((pos
->ISizeDependsOnContainer(aWM
) && !pos
->ISize(aWM
).IsAuto()) ||
722 pos
->MaxISizeDependsOnContainer(aWM
) ||
723 pos
->MinISizeDependsOnContainer(aWM
) ||
724 pos
->mOffset
.GetIStart(aWM
).HasPercent() ||
725 pos
->mOffset
.GetIEnd(aWM
).HasPercent()) {
729 if (pos
->ISize(aWM
).IsAuto()) {
730 // We need to check for frames that shrink-wrap when they're auto
732 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
733 if ((disp
->DisplayOutside() == StyleDisplayOutside::Inline
&&
734 (disp
->DisplayInside() == StyleDisplayInside::FlowRoot
||
735 disp
->DisplayInside() == StyleDisplayInside::Table
)) ||
736 fType
== LayoutFrameType::HTMLButtonControl
||
737 fType
== LayoutFrameType::GfxButtonControl
||
738 fType
== LayoutFrameType::FieldSet
) {
742 // Per CSS 2.1, section 10.3.2:
743 // If 'height' and 'width' both have computed values of 'auto' and
744 // the element has an intrinsic ratio but no intrinsic height or
745 // width and the containing block's width does not itself depend
746 // on the replaced element's width, then the used value of 'width'
747 // is calculated from the constraint equation used for
748 // block-level, non-replaced elements in normal flow.
749 nsIFrame
* f
= const_cast<nsIFrame
*>(aFrame
);
750 if (f
->GetAspectRatio() &&
751 // Some percents are treated like 'auto', so check != coord
752 !pos
->BSize(aWM
).ConvertsToLength()) {
753 const IntrinsicSize
& intrinsicSize
= f
->GetIntrinsicSize();
754 if (!intrinsicSize
.width
&& !intrinsicSize
.height
) {
763 void nsLineLayout::ReflowFrame(nsIFrame
* aFrame
, nsReflowStatus
& aReflowStatus
,
764 ReflowOutput
* aMetrics
, bool& aPushedFrame
) {
765 // Initialize OUT parameter
766 aPushedFrame
= false;
768 PerFrameData
* pfd
= NewPerFrameData(aFrame
);
769 PerSpanData
* psd
= mCurrentSpan
;
770 psd
->AppendFrame(pfd
);
772 #ifdef REALLY_NOISY_REFLOW
773 nsIFrame::IndentBy(stdout
, mSpanDepth
);
774 printf("%p: Begin ReflowFrame pfd=%p ", psd
, pfd
);
775 aFrame
->ListTag(stdout
);
779 if (mCurrentSpan
== mRootSpan
) {
780 pfd
->mFrame
->RemoveProperty(nsIFrame::LineBaselineOffset());
784 pfd
->mFrame
->GetProperty(nsIFrame::LineBaselineOffset(), &hasLineOffset
);
785 NS_ASSERTION(!hasLineOffset
,
786 "LineBaselineOffset was set but was not expected");
790 mJustificationInfo
= JustificationInfo();
792 // Stash copies of some of the computed state away for later
793 // (block-direction alignment, for example)
794 WritingMode frameWM
= pfd
->mWritingMode
;
795 WritingMode lineWM
= mRootSpan
->mWritingMode
;
797 // NOTE: While the inline direction coordinate remains relative to the
798 // parent span, the block direction coordinate is fixed at the top
799 // edge for the line. During VerticalAlignFrames we will repair this
800 // so that the block direction coordinate is properly set and relative
801 // to the appropriate span.
802 pfd
->mBounds
.IStart(lineWM
) = psd
->mICoord
;
803 pfd
->mBounds
.BStart(lineWM
) = mBStartEdge
;
805 // We want to guarantee that we always make progress when
806 // formatting. Therefore, if the object being placed on the line is
807 // too big for the line, but it is the only thing on the line and is not
808 // impacted by a float, then we go ahead and place it anyway. (If the line
809 // is impacted by one or more floats, then it is safe to break because
810 // we can move the line down below float(s).)
812 // Capture this state *before* we reflow the frame in case it clears
813 // the state out. We need to know how to treat the current frame
815 bool notSafeToBreak
= LineIsEmpty() && !mImpactedByFloats
;
817 // Figure out whether we're talking about a textframe here
818 LayoutFrameType frameType
= aFrame
->Type();
819 const bool isText
= frameType
== LayoutFrameType::Text
;
821 // Inline-ish and text-ish things don't compute their width;
822 // everything else does. We need to give them an available width that
823 // reflects the space left on the line.
824 LAYOUT_WARN_IF_FALSE(psd
->mIEnd
!= NS_UNCONSTRAINEDSIZE
,
825 "have unconstrained width; this should only result from "
826 "very large sizes, not attempts at intrinsic width "
828 nscoord availableSpaceOnLine
= psd
->mIEnd
- psd
->mICoord
- psd
->mInset
;
830 // Setup reflow input for reflowing the frame
831 Maybe
<ReflowInput
> reflowInputHolder
;
833 // Compute the available size for the frame. This available width
834 // includes room for the side margins.
835 // For now, set the available block-size to unconstrained always.
836 LogicalSize availSize
= mLineContainerRI
.ComputedSize(frameWM
);
837 availSize
.BSize(frameWM
) = NS_UNCONSTRAINEDSIZE
;
838 reflowInputHolder
.emplace(mPresContext
, *psd
->mReflowInput
, aFrame
,
840 ReflowInput
& reflowInput
= *reflowInputHolder
;
841 reflowInput
.mLineLayout
= this;
842 reflowInput
.mFlags
.mIsTopOfPage
= mIsTopOfPage
;
843 if (reflowInput
.ComputedISize() == NS_UNCONSTRAINEDSIZE
) {
844 reflowInput
.SetAvailableISize(availableSpaceOnLine
);
846 pfd
->mMargin
= reflowInput
.ComputedLogicalMargin(lineWM
);
847 pfd
->mBorderPadding
= reflowInput
.ComputedLogicalBorderPadding(lineWM
);
848 pfd
->mIsRelativelyOrStickyPos
=
849 reflowInput
.mStyleDisplay
->IsRelativelyOrStickyPositionedStyle();
850 if (pfd
->mIsRelativelyOrStickyPos
) {
851 pfd
->mOffsets
= reflowInput
.ComputedLogicalOffsets(frameWM
);
854 // Calculate whether the the frame should have a start margin and
855 // subtract the margin from the available width if necessary.
856 // The margin will be applied to the starting inline coordinates of
857 // the frame in CanPlaceFrame() after reflowing the frame.
858 AllowForStartMargin(pfd
, reflowInput
);
860 // if isText(), no need to propagate NS_FRAME_IS_DIRTY from the parent,
861 // because reflow doesn't look at the dirty bits on the frame being reflowed.
863 // See if this frame depends on the inline-size of its containing block.
864 // If so, disable resize reflow optimizations for the line. (Note that,
865 // to be conservative, we do this if we *try* to fit a frame on a
866 // line, even if we don't succeed.) (Note also that we can only make
867 // this IsPercentageAware check *after* we've constructed our
868 // ReflowInput, because that construction may be what forces aFrame
869 // to lazily initialize its (possibly-percent-valued) intrinsic size.)
870 if (mGotLineBox
&& IsPercentageAware(aFrame
, lineWM
)) {
871 mLineBox
->DisableResizeReflowOptimization();
874 // Note that we don't bother positioning the frame yet, because we're probably
875 // going to end up moving it when we do the block-direction alignment.
877 // Adjust float manager coordinate system for the frame.
878 ReflowOutput
reflowOutput(lineWM
);
880 reflowOutput
.ISize(lineWM
) = nscoord(0xdeadbeef);
881 reflowOutput
.BSize(lineWM
) = nscoord(0xdeadbeef);
883 nscoord tI
= pfd
->mBounds
.LineLeft(lineWM
, ContainerSize());
884 nscoord tB
= pfd
->mBounds
.BStart(lineWM
);
885 mFloatManager
->Translate(tI
, tB
);
887 int32_t savedOptionalBreakOffset
;
888 gfxBreakPriority savedOptionalBreakPriority
;
889 nsIFrame
* savedOptionalBreakFrame
= GetLastOptionalBreakPosition(
890 &savedOptionalBreakOffset
, &savedOptionalBreakPriority
);
893 aFrame
->Reflow(mPresContext
, reflowOutput
, *reflowInputHolder
,
896 static_cast<nsTextFrame
*>(aFrame
)->ReflowText(
897 *this, availableSpaceOnLine
,
898 psd
->mReflowInput
->mRenderingContext
->GetDrawTarget(), reflowOutput
,
902 pfd
->mJustificationInfo
= mJustificationInfo
;
903 mJustificationInfo
= JustificationInfo();
905 // See if the frame is a placeholderFrame and if it is process
906 // the float. At the same time, check if the frame has any non-collapsed-away
908 bool placedFloat
= false;
910 if (frameType
== LayoutFrameType::None
) {
911 isEmpty
= pfd
->mFrame
->IsEmpty();
912 } else if (LayoutFrameType::Placeholder
== frameType
) {
914 pfd
->mIsPlaceholder
= true;
915 pfd
->mSkipWhenTrimmingWhitespace
= true;
916 nsIFrame
* outOfFlowFrame
= nsLayoutUtils::GetFloatFromPlaceholder(aFrame
);
917 if (outOfFlowFrame
) {
919 // We can always place floats in an empty line.
921 // We always place floating letter frames. This kinda sucks. They'd
922 // usually fall into the LineIsEmpty() check anyway, except when
923 // there's something like a ::marker before or what not. We actually
924 // need to place them now, because they're pretty nasty and they
925 // create continuations that are in flow and not a kid of the
926 // previous continuation's parent. We don't want the deferred reflow
927 // of the letter frame to kill a continuation after we've stored it
928 // in the line layout data structures. See bug 1490281 to fix the
929 // underlying issue. When that's fixed this check should be removed.
930 !outOfFlowFrame
->IsLetterFrame() &&
931 !GetOutermostLineLayout()->mBlockRS
->mFlags
.mCanHaveOverflowMarkers
) {
932 // We'll do this at the next break opportunity.
933 RecordNoWrapFloat(outOfFlowFrame
);
935 placedFloat
= TryToPlaceFloat(outOfFlowFrame
);
939 // Note non-empty text-frames for inline frame compatibility hackery
940 pfd
->mIsTextFrame
= true;
941 auto* textFrame
= static_cast<nsTextFrame
*>(pfd
->mFrame
);
942 isEmpty
= !textFrame
->HasNoncollapsedCharacters();
944 pfd
->mIsNonEmptyTextFrame
= true;
945 pfd
->mIsNonWhitespaceTextFrame
=
946 !textFrame
->GetContent()->TextIsOnlyWhitespace();
948 } else if (LayoutFrameType::Br
== frameType
) {
949 pfd
->mSkipWhenTrimmingWhitespace
= true;
952 if (LayoutFrameType::Letter
== frameType
) {
953 pfd
->mIsLetterFrame
= true;
956 isEmpty
= !pfd
->mSpan
->mHasNonemptyContent
&& pfd
->mFrame
->IsSelfEmpty();
958 isEmpty
= pfd
->mFrame
->IsEmpty();
961 pfd
->mIsEmpty
= isEmpty
;
963 mFloatManager
->Translate(-tI
, -tB
);
965 NS_ASSERTION(reflowOutput
.ISize(lineWM
) >= 0, "bad inline size");
966 NS_ASSERTION(reflowOutput
.BSize(lineWM
) >= 0, "bad block size");
967 if (reflowOutput
.ISize(lineWM
) < 0) {
968 reflowOutput
.ISize(lineWM
) = 0;
970 if (reflowOutput
.BSize(lineWM
) < 0) {
971 reflowOutput
.BSize(lineWM
) = 0;
975 // Note: break-before means ignore the reflow metrics since the
976 // frame will be reflowed another time.
977 if (!aReflowStatus
.IsInlineBreakBefore()) {
978 if ((ABSURD_SIZE(reflowOutput
.ISize(lineWM
)) ||
979 ABSURD_SIZE(reflowOutput
.BSize(lineWM
))) &&
980 !LineContainerFrame()->GetParent()->IsAbsurdSizeAssertSuppressed()) {
981 printf("nsLineLayout: ");
982 aFrame
->ListTag(stdout
);
983 printf(" metrics=%d,%d!\n", reflowOutput
.Width(), reflowOutput
.Height());
985 if ((reflowOutput
.Width() == nscoord(0xdeadbeef)) ||
986 (reflowOutput
.Height() == nscoord(0xdeadbeef))) {
987 printf("nsLineLayout: ");
988 aFrame
->ListTag(stdout
);
989 printf(" didn't set w/h %d,%d!\n", reflowOutput
.Width(),
990 reflowOutput
.Height());
995 // Unlike with non-inline reflow, the overflow area here does *not*
996 // include the accumulation of the frame's bounds and its inline
997 // descendants' bounds. Nor does it include the outline area; it's
998 // just the union of the bounds of any absolute children. That is
999 // added in later by nsLineLayout::ReflowInlineFrames.
1000 pfd
->mOverflowAreas
= reflowOutput
.mOverflowAreas
;
1002 pfd
->mBounds
.ISize(lineWM
) = reflowOutput
.ISize(lineWM
);
1003 pfd
->mBounds
.BSize(lineWM
) = reflowOutput
.BSize(lineWM
);
1005 // Size the frame, but |RelativePositionFrames| will size the view.
1006 aFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerSizeForSpan(psd
));
1008 // Tell the frame that we're done reflowing it
1009 aFrame
->DidReflow(mPresContext
, isText
? nullptr : reflowInputHolder
.ptr());
1012 *aMetrics
= reflowOutput
;
1015 if (!aReflowStatus
.IsInlineBreakBefore()) {
1016 // If frame is complete and has a next-in-flow, we need to delete
1017 // them now. Do not do this when a break-before is signaled because
1018 // the frame is going to get reflowed again (and may end up wanting
1019 // a next-in-flow where it ends up).
1020 if (aReflowStatus
.IsComplete()) {
1021 if (nsIFrame
* kidNextInFlow
= aFrame
->GetNextInFlow()) {
1022 // Remove all of the childs next-in-flows. Make sure that we ask
1023 // the right parent to do the removal (it's possible that the
1024 // parent is not this because we are executing pullup code)
1025 FrameDestroyContext
context(aFrame
->PresShell());
1026 kidNextInFlow
->GetParent()->DeleteNextInFlowChild(context
,
1027 kidNextInFlow
, true);
1031 // Check whether this frame breaks up text runs. All frames break up text
1032 // runs (hence return false here) except for text frames and inline
1034 bool continuingTextRun
= aFrame
->CanContinueTextRun();
1036 // Clear any residual mTrimmableISize if this isn't a text frame
1037 if (!continuingTextRun
&& !pfd
->mSkipWhenTrimmingWhitespace
) {
1038 mTrimmableISize
= 0;
1041 // See if we can place the frame. If we can't fit it, then we
1043 bool optionalBreakAfterFits
;
1044 NS_ASSERTION(isText
|| !reflowInputHolder
->mStyleDisplay
->IsFloating(
1045 reflowInputHolder
->mFrame
),
1046 "How'd we get a floated inline frame? "
1047 "The frame ctor should've dealt with this.");
1048 if (CanPlaceFrame(pfd
, notSafeToBreak
, continuingTextRun
,
1049 savedOptionalBreakFrame
!= nullptr, reflowOutput
,
1050 aReflowStatus
, &optionalBreakAfterFits
)) {
1052 psd
->mHasNonemptyContent
= true;
1053 mLineIsEmpty
= false;
1055 // nonempty leaf content has been placed
1056 mLineAtStart
= false;
1058 if (LayoutFrameType::Ruby
== frameType
) {
1060 SyncAnnotationBounds(pfd
);
1064 // Place the frame, updating aBounds with the final size and
1065 // location. Then apply the bottom+right margins (as
1066 // appropriate) to the frame.
1067 PlaceFrame(pfd
, reflowOutput
);
1068 PerSpanData
* span
= pfd
->mSpan
;
1070 // The frame we just finished reflowing is an inline
1071 // container. It needs its child frames aligned in the block direction,
1072 // so do most of it now.
1073 VerticalAlignFrames(span
);
1076 if (!continuingTextRun
&& !psd
->mNoWrap
) {
1077 if (!LineIsEmpty() || placedFloat
) {
1078 // record soft break opportunity after this content that can't be
1079 // part of a text run. This is not a text frame so we know
1080 // that offset INT32_MAX means "after the content".
1081 if ((!aFrame
->IsPlaceholderFrame() || LineIsEmpty()) &&
1082 NotifyOptionalBreakPosition(aFrame
, INT32_MAX
,
1083 optionalBreakAfterFits
,
1084 gfxBreakPriority::eNormalBreak
)) {
1085 // If this returns true then we are being told to actually break
1087 aReflowStatus
.SetInlineLineBreakAfter();
1093 aPushedFrame
= true;
1094 // Undo any saved break positions that the frame might have told us about,
1095 // since we didn't end up placing it
1096 RestoreSavedBreakPosition(savedOptionalBreakFrame
,
1097 savedOptionalBreakOffset
,
1098 savedOptionalBreakPriority
);
1102 aPushedFrame
= true;
1105 #ifdef REALLY_NOISY_REFLOW
1106 nsIFrame::IndentBy(stdout
, mSpanDepth
);
1107 printf("End ReflowFrame ");
1108 aFrame
->ListTag(stdout
);
1109 printf(" status=%x\n", aReflowStatus
);
1113 void nsLineLayout::AllowForStartMargin(PerFrameData
* pfd
,
1114 ReflowInput
& aReflowInput
) {
1115 NS_ASSERTION(!aReflowInput
.mStyleDisplay
->IsFloating(aReflowInput
.mFrame
),
1116 "How'd we get a floated inline frame? "
1117 "The frame ctor should've dealt with this.");
1119 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1121 // Only apply start-margin on the first-in flow for inline frames,
1122 // and make sure to not apply it to any inline other than the first
1123 // in an ib split. Note that the ib sibling (block-in-inline
1124 // sibling) annotations only live on the first continuation, but we
1125 // don't want to apply the start margin for later continuations
1126 // anyway. For box-decoration-break:clone we apply the start-margin
1127 // on all continuations.
1128 if ((pfd
->mFrame
->GetPrevContinuation() ||
1129 pfd
->mFrame
->FrameIsNonFirstInIBSplit()) &&
1130 aReflowInput
.mStyleBorder
->mBoxDecorationBreak
==
1131 StyleBoxDecorationBreak::Slice
) {
1132 // Zero this out so that when we compute the max-element-width of
1133 // the frame we will properly avoid adding in the starting margin.
1134 pfd
->mMargin
.IStart(lineWM
) = 0;
1135 } else if (NS_UNCONSTRAINEDSIZE
== aReflowInput
.ComputedISize()) {
1136 NS_WARNING_ASSERTION(
1137 NS_UNCONSTRAINEDSIZE
!= aReflowInput
.AvailableISize(),
1138 "have unconstrained inline-size; this should only result from very "
1139 "large sizes, not attempts at intrinsic inline-size calculation");
1140 // For inline-ish and text-ish things (which don't compute widths
1141 // in the reflow input), adjust available inline-size to account
1142 // for the start margin. The end margin will be accounted for when
1143 // we finish flowing the frame.
1144 WritingMode wm
= aReflowInput
.GetWritingMode();
1145 aReflowInput
.SetAvailableISize(
1146 aReflowInput
.AvailableISize() -
1147 pfd
->mMargin
.ConvertTo(wm
, lineWM
).IStart(wm
));
1151 nscoord
nsLineLayout::GetCurrentFrameInlineDistanceFromBlock() {
1154 for (psd
= mCurrentSpan
; psd
; psd
= psd
->mParent
) {
1161 * This method syncs bounds of ruby annotations and ruby annotation
1162 * containers from their rect. It is necessary because:
1163 * Containers are not part of the line in their levels, which means
1164 * their bounds are not set properly before.
1165 * Ruby annotations' position may have been changed when reflowing
1168 void nsLineLayout::SyncAnnotationBounds(PerFrameData
* aRubyFrame
) {
1169 MOZ_ASSERT(aRubyFrame
->mFrame
->IsRubyFrame());
1170 MOZ_ASSERT(aRubyFrame
->mSpan
);
1172 PerSpanData
* span
= aRubyFrame
->mSpan
;
1173 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1174 for (PerFrameData
* pfd
= span
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1175 for (PerFrameData
* rtc
= pfd
->mNextAnnotation
; rtc
;
1176 rtc
= rtc
->mNextAnnotation
) {
1177 if (lineWM
.IsOrthogonalTo(rtc
->mFrame
->GetWritingMode())) {
1178 // Inter-character case: don't attempt to sync annotation bounds.
1181 // When the annotation container is reflowed, the width of the
1182 // ruby container is unknown so we use a dummy container size;
1183 // in the case of RTL block direction, the final position will be
1185 const nsSize dummyContainerSize
;
1186 LogicalRect
rtcBounds(lineWM
, rtc
->mFrame
->GetRect(), dummyContainerSize
);
1187 rtc
->mBounds
= rtcBounds
;
1188 nsSize rtcSize
= rtcBounds
.Size(lineWM
).GetPhysicalSize(lineWM
);
1189 for (PerFrameData
* rt
= rtc
->mSpan
->mFirstFrame
; rt
; rt
= rt
->mNext
) {
1190 LogicalRect rtBounds
= rt
->mFrame
->GetLogicalRect(lineWM
, rtcSize
);
1191 MOZ_ASSERT(rt
->mBounds
.Size(lineWM
) == rtBounds
.Size(lineWM
),
1192 "Size of the annotation should not have been changed");
1193 rt
->mBounds
.SetOrigin(lineWM
, rtBounds
.Origin(lineWM
));
1200 * See if the frame can be placed now that we know it's desired size.
1201 * We can always place the frame if the line is empty. Note that we
1202 * know that the reflow-status is not a break-before because if it was
1203 * ReflowFrame above would have returned false, preventing this method
1204 * from being called. The logic in this method assumes that.
1206 * Note that there is no check against the Y coordinate because we
1207 * assume that the caller will take care of that.
1209 bool nsLineLayout::CanPlaceFrame(PerFrameData
* pfd
, bool aNotSafeToBreak
,
1210 bool aFrameCanContinueTextRun
,
1211 bool aCanRollBackBeforeFrame
,
1212 ReflowOutput
& aMetrics
,
1213 nsReflowStatus
& aStatus
,
1214 bool* aOptionalBreakAfterFits
) {
1215 MOZ_ASSERT(pfd
&& pfd
->mFrame
, "bad args, null pointers for frame data");
1217 *aOptionalBreakAfterFits
= true;
1219 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1221 * We want to only apply the end margin if we're the last continuation and
1222 * either not in an {ib} split or the last inline in it. In all other
1223 * cases we want to zero it out. That means zeroing it out if any of these
1225 * 1) The frame is not complete (in this case it will get a next-in-flow)
1226 * 2) The frame is complete but has a non-fluid continuation on its
1227 * continuation chain. Note that if it has a fluid continuation, that
1228 * continuation will get destroyed later, so we don't want to drop the
1229 * end-margin in that case.
1230 * 3) The frame is in an {ib} split and is not the last part.
1232 * However, none of that applies if this is a letter frame (XXXbz why?)
1234 * For box-decoration-break:clone we apply the end margin on all
1235 * continuations (that are not letter frames).
1237 if ((aStatus
.IsIncomplete() ||
1238 pfd
->mFrame
->LastInFlow()->GetNextContinuation() ||
1239 pfd
->mFrame
->FrameIsNonLastInIBSplit()) &&
1240 !pfd
->mIsLetterFrame
&&
1241 pfd
->mFrame
->StyleBorder()->mBoxDecorationBreak
==
1242 StyleBoxDecorationBreak::Slice
) {
1243 pfd
->mMargin
.IEnd(lineWM
) = 0;
1246 // Apply the start margin to the frame bounds.
1247 nscoord startMargin
= pfd
->mMargin
.IStart(lineWM
);
1248 nscoord endMargin
= pfd
->mMargin
.IEnd(lineWM
);
1250 pfd
->mBounds
.IStart(lineWM
) += startMargin
;
1252 PerSpanData
* psd
= mCurrentSpan
;
1254 // When wrapping is off, everything fits.
1258 #ifdef NOISY_CAN_PLACE_FRAME
1260 psd
->mFrame
->mFrame
->ListTag(stdout
);
1262 printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak
? "true" : "false");
1263 pfd
->mFrame
->ListTag(stdout
);
1264 printf(" frameWidth=%d, margins=%d,%d\n",
1265 pfd
->mBounds
.IEnd(lineWM
) + endMargin
- psd
->mICoord
, startMargin
,
1269 // Set outside to true if the result of the reflow leads to the
1270 // frame sticking outside of our available area.
1272 pfd
->mBounds
.IEnd(lineWM
) - mTrimmableISize
+ endMargin
> psd
->mIEnd
;
1274 // If it fits, it fits
1275 #ifdef NOISY_CAN_PLACE_FRAME
1276 printf(" ==> inside\n");
1280 *aOptionalBreakAfterFits
= false;
1282 // When it doesn't fit, check for a few special conditions where we
1283 // allow it to fit anyway.
1284 if (0 == startMargin
+ pfd
->mBounds
.ISize(lineWM
) + endMargin
) {
1285 // Empty frames always fit right where they are
1286 #ifdef NOISY_CAN_PLACE_FRAME
1287 printf(" ==> empty frame fits\n");
1292 // another special case: always place a BR
1293 if (pfd
->mFrame
->IsBrFrame()) {
1294 #ifdef NOISY_CAN_PLACE_FRAME
1295 printf(" ==> BR frame fits\n");
1300 if (aNotSafeToBreak
) {
1301 // There are no frames on the line that take up width and the line is
1302 // not impacted by floats, so we must allow the current frame to be
1303 // placed on the line
1304 #ifdef NOISY_CAN_PLACE_FRAME
1305 printf(" ==> not-safe and not-impacted fits: ");
1306 while (nullptr != psd
) {
1307 printf("<psd=%p x=%d left=%d> ", psd
, psd
->mICoord
, psd
->mIStart
);
1315 // Special check for span frames
1316 if (pfd
->mSpan
&& pfd
->mSpan
->mContainsFloat
) {
1317 // If the span either directly or indirectly contains a float then
1318 // it fits. Why? It's kind of complicated, but here goes:
1320 // 1. CanPlaceFrame is used for all frame placements on a line,
1321 // and in a span. This includes recursively placement of frames
1322 // inside of spans, and the span itself. Because the logic always
1323 // checks for room before proceeding (the code above here), the
1324 // only things on a line will be those things that "fit".
1326 // 2. Before a float is placed on a line, the line has to be empty
1327 // (otherwise it's a "below current line" float and will be placed
1330 // Therefore, if the span directly or indirectly has a float
1331 // then it means that at the time of the placement of the float
1332 // the line was empty. Because of #1, only the frames that fit can
1333 // be added after that point, therefore we can assume that the
1334 // current span being placed has fit.
1336 // So how do we get here and have a span that should already fit
1337 // and yet doesn't: Simple: span's that have the no-wrap attribute
1338 // set on them and contain a float and are placed where they
1339 // don't naturally fit.
1343 if (aFrameCanContinueTextRun
) {
1344 // Let it fit, but we reserve the right to roll back.
1345 // Note that we usually won't get here because a text frame will break
1346 // itself to avoid exceeding the available width.
1347 // We'll only get here for text frames that couldn't break early enough.
1348 #ifdef NOISY_CAN_PLACE_FRAME
1349 printf(" ==> placing overflowing textrun, requesting backup\n");
1352 // We will want to try backup.
1357 #ifdef NOISY_CAN_PLACE_FRAME
1358 printf(" ==> didn't fit\n");
1360 aStatus
.SetInlineLineBreakBeforeAndReset();
1365 * Place the frame. Update running counters.
1367 void nsLineLayout::PlaceFrame(PerFrameData
* pfd
, ReflowOutput
& aMetrics
) {
1368 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1370 // If the frame's block direction does not match the line's, we can't use
1371 // its ascent; instead, treat it as a block with baseline at the block-end
1372 // edge (or block-begin in the case of an "inverted" line).
1373 if (pfd
->mWritingMode
.GetBlockDir() != lineWM
.GetBlockDir()) {
1374 pfd
->mAscent
= lineWM
.IsAlphabeticalBaseline()
1375 ? lineWM
.IsLineInverted() ? 0 : aMetrics
.BSize(lineWM
)
1376 : aMetrics
.BSize(lineWM
) / 2;
1378 // For inline reflow participants, baseline may get assigned as the frame is
1379 // vertically aligned, which happens after this.
1380 const auto baselineSource
= pfd
->mFrame
->StyleDisplay()->mBaselineSource
;
1381 if (baselineSource
== StyleBaselineSource::Auto
||
1382 pfd
->mFrame
->IsLineParticipant()) {
1383 if (aMetrics
.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE
) {
1384 pfd
->mAscent
= pfd
->mFrame
->GetLogicalBaseline(lineWM
);
1386 pfd
->mAscent
= aMetrics
.BlockStartAscent();
1389 const auto sourceGroup
= [baselineSource
]() {
1390 switch (baselineSource
) {
1391 case StyleBaselineSource::First
:
1392 return BaselineSharingGroup::First
;
1393 case StyleBaselineSource::Last
:
1394 return BaselineSharingGroup::Last
;
1395 case StyleBaselineSource::Auto
:
1398 MOZ_ASSERT_UNREACHABLE("Auto should be already handled?");
1399 return BaselineSharingGroup::First
;
1401 // We ignore line-layout specific layout quirks by setting
1402 // `BaselineExportContext::Other`.
1403 // Note(dshin): For a lot of frames, the export context does not make a
1404 // difference, and we may be wasting the value cached in
1405 // `BlockStartAscent`.
1406 pfd
->mAscent
= pfd
->mFrame
->GetLogicalBaseline(
1407 lineWM
, sourceGroup
, BaselineExportContext::Other
);
1411 // Advance to next inline coordinate
1412 mCurrentSpan
->mICoord
= pfd
->mBounds
.IEnd(lineWM
) + pfd
->mMargin
.IEnd(lineWM
);
1414 // Count the number of non-placeholder frames on the line...
1415 if (pfd
->mFrame
->IsPlaceholderFrame()) {
1417 pfd
->mBounds
.ISize(lineWM
) == 0 && pfd
->mBounds
.BSize(lineWM
) == 0,
1418 "placeholders should have 0 width/height (checking "
1419 "placeholders were never counted by the old code in "
1422 mTotalPlacedFrames
++;
1426 void nsLineLayout::AddMarkerFrame(nsIFrame
* aFrame
,
1427 const ReflowOutput
& aMetrics
) {
1428 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
1429 NS_ASSERTION(mGotLineBox
, "must have line box");
1431 nsBlockFrame
* blockFrame
= do_QueryFrame(LineContainerFrame());
1432 MOZ_ASSERT(blockFrame
, "must be for block");
1433 if (!blockFrame
->MarkerIsEmpty()) {
1435 mLineBox
->SetHasMarker();
1438 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1439 PerFrameData
* pfd
= NewPerFrameData(aFrame
);
1440 PerSpanData
* psd
= mRootSpan
;
1442 MOZ_ASSERT(psd
->mFirstFrame
, "adding marker to an empty line?");
1443 // Prepend the marker frame to the line.
1444 psd
->mFirstFrame
->mPrev
= pfd
;
1445 pfd
->mNext
= psd
->mFirstFrame
;
1446 psd
->mFirstFrame
= pfd
;
1448 pfd
->mIsMarker
= true;
1449 if (aMetrics
.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE
) {
1450 pfd
->mAscent
= aFrame
->GetLogicalBaseline(lineWM
);
1452 pfd
->mAscent
= aMetrics
.BlockStartAscent();
1455 // Note: block-coord value will be updated during block-direction alignment
1456 pfd
->mBounds
= LogicalRect(lineWM
, aFrame
->GetRect(), ContainerSize());
1457 pfd
->mOverflowAreas
= aMetrics
.mOverflowAreas
;
1460 void nsLineLayout::RemoveMarkerFrame(nsIFrame
* aFrame
) {
1461 PerSpanData
* psd
= mCurrentSpan
;
1462 MOZ_ASSERT(psd
== mRootSpan
, "::marker on non-root span?");
1463 MOZ_ASSERT(psd
->mFirstFrame
->mFrame
== aFrame
,
1464 "::marker is not the first frame?");
1465 PerFrameData
* pfd
= psd
->mFirstFrame
;
1466 MOZ_ASSERT(pfd
!= psd
->mLastFrame
, "::marker is the only frame?");
1467 pfd
->mNext
->mPrev
= nullptr;
1468 psd
->mFirstFrame
= pfd
->mNext
;
1472 void nsLineLayout::DumpPerSpanData(PerSpanData
* psd
, int32_t aIndent
) {
1473 nsIFrame::IndentBy(stdout
, aIndent
);
1474 printf("%p: left=%d x=%d right=%d\n", static_cast<void*>(psd
), psd
->mIStart
,
1475 psd
->mICoord
, psd
->mIEnd
);
1476 PerFrameData
* pfd
= psd
->mFirstFrame
;
1477 while (nullptr != pfd
) {
1478 nsIFrame::IndentBy(stdout
, aIndent
+ 1);
1479 pfd
->mFrame
->ListTag(stdout
);
1481 pfd
->mBounds
.GetPhysicalRect(psd
->mWritingMode
, ContainerSize());
1482 printf(" %d,%d,%d,%d\n", rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1484 DumpPerSpanData(pfd
->mSpan
, aIndent
+ 1);
1491 void nsLineLayout::RecordNoWrapFloat(nsIFrame
* aFloat
) {
1492 GetOutermostLineLayout()->mBlockRS
->mNoWrapFloats
.AppendElement(aFloat
);
1495 void nsLineLayout::FlushNoWrapFloats() {
1496 auto& noWrapFloats
= GetOutermostLineLayout()->mBlockRS
->mNoWrapFloats
;
1497 for (nsIFrame
* floatedFrame
: noWrapFloats
) {
1498 TryToPlaceFloat(floatedFrame
);
1500 noWrapFloats
.Clear();
1503 bool nsLineLayout::TryToPlaceFloat(nsIFrame
* aFloat
) {
1504 // Add mTrimmableISize to the available width since if the line ends here, the
1505 // width of the inline content will be reduced by mTrimmableISize.
1506 nscoord availableISize
=
1507 mCurrentSpan
->mIEnd
- (mCurrentSpan
->mICoord
- mTrimmableISize
);
1508 NS_ASSERTION(!(aFloat
->IsLetterFrame() && GetFirstLetterStyleOK()),
1509 "FirstLetterStyle set on line with floating first letter");
1510 return GetOutermostLineLayout()->AddFloat(aFloat
, availableISize
);
1513 bool nsLineLayout::NotifyOptionalBreakPosition(nsIFrame
* aFrame
,
1514 int32_t aOffset
, bool aFits
,
1515 gfxBreakPriority aPriority
) {
1516 NS_ASSERTION(!aFits
|| !mNeedBackup
,
1517 "Shouldn't be updating the break position with a break that fits"
1518 " after we've already flagged an overrun");
1519 MOZ_ASSERT(mCurrentSpan
, "Should be doing line layout");
1520 if (mCurrentSpan
->mNoWrap
) {
1521 FlushNoWrapFloats();
1524 // Remember the last break position that fits; if there was no break that fit,
1525 // just remember the first break
1526 if ((aFits
&& aPriority
>= mLastOptionalBreakPriority
) ||
1527 !mLastOptionalBreakFrame
) {
1528 mLastOptionalBreakFrame
= aFrame
;
1529 mLastOptionalBreakFrameOffset
= aOffset
;
1530 mLastOptionalBreakPriority
= aPriority
;
1532 return aFrame
&& mForceBreakFrame
== aFrame
&&
1533 mForceBreakFrameOffset
== aOffset
;
1536 #define VALIGN_OTHER 0
1537 #define VALIGN_TOP 1
1538 #define VALIGN_BOTTOM 2
1540 void nsLineLayout::VerticalAlignLine() {
1541 // Partially place the children of the block frame. The baseline for
1542 // this operation is set to zero so that the y coordinates for all
1543 // of the placed children will be relative to there.
1544 PerSpanData
* psd
= mRootSpan
;
1545 VerticalAlignFrames(psd
);
1547 // *** Note that comments here still use the anachronistic term
1548 // "line-height" when we really mean "size of the line in the block
1549 // direction", "vertical-align" when we really mean "alignment in
1550 // the block direction", and "top" and "bottom" when we really mean
1551 // "block start" and "block end". This is partly for brevity and
1552 // partly to retain the association with the CSS line-height and
1553 // vertical-align properties.
1555 // Compute the line-height. The line-height will be the larger of:
1557 // [1] maxBCoord - minBCoord (the distance between the first child's
1558 // block-start edge and the last child's block-end edge)
1560 // [2] the maximum logical box block size (since not every frame may have
1561 // participated in #1; for example: "top" and "botttom" aligned frames)
1563 // [3] the minimum line height ("line-height" property set on the
1565 nscoord lineBSize
= psd
->mMaxBCoord
- psd
->mMinBCoord
;
1567 // Now that the line-height is computed, we need to know where the
1568 // baseline is in the line. Position baseline so that mMinBCoord is just
1569 // inside the start of the line box.
1570 nscoord baselineBCoord
;
1571 if (psd
->mMinBCoord
< 0) {
1572 baselineBCoord
= mBStartEdge
- psd
->mMinBCoord
;
1574 baselineBCoord
= mBStartEdge
;
1577 // It's also possible that the line block-size isn't tall enough because
1578 // of "top" and "bottom" aligned elements that were not accounted for in
1581 // The CSS2 spec doesn't really say what happens when to the
1582 // baseline in this situations. What we do is if the largest start
1583 // aligned box block size is greater than the line block-size then we leave
1584 // the baseline alone. If the largest end aligned box is greater
1585 // than the line block-size then we slide the baseline forward by the extra
1588 // Navigator 4 gives precedence to the first top/bottom aligned
1589 // object. We just let block end aligned objects win.
1590 if (lineBSize
< mMaxEndBoxBSize
) {
1591 // When the line is shorter than the maximum block start aligned box
1592 nscoord extra
= mMaxEndBoxBSize
- lineBSize
;
1593 baselineBCoord
+= extra
;
1594 lineBSize
= mMaxEndBoxBSize
;
1596 if (lineBSize
< mMaxStartBoxBSize
) {
1597 lineBSize
= mMaxStartBoxBSize
;
1599 #ifdef NOISY_BLOCKDIR_ALIGN
1600 printf(" [line]==> lineBSize=%d baselineBCoord=%d\n", lineBSize
,
1604 // Now position all of the frames in the root span. We will also
1605 // recurse over the child spans and place any frames we find with
1606 // vertical-align: top or bottom.
1607 // XXX PERFORMANCE: set a bit per-span to avoid the extra work
1608 // (propagate it upward too)
1609 WritingMode lineWM
= psd
->mWritingMode
;
1610 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1611 if (pfd
->mBlockDirAlign
== VALIGN_OTHER
) {
1612 pfd
->mBounds
.BStart(lineWM
) += baselineBCoord
;
1613 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerSize());
1616 PlaceTopBottomFrames(psd
, -mBStartEdge
, lineBSize
);
1618 mFinalLineBSize
= lineBSize
;
1620 // Fill in returned line-box and max-element-width data
1621 mLineBox
->SetBounds(lineWM
, psd
->mIStart
, mBStartEdge
,
1622 psd
->mICoord
- psd
->mIStart
, lineBSize
,
1625 mLineBox
->SetLogicalAscent(baselineBCoord
- mBStartEdge
);
1626 #ifdef NOISY_BLOCKDIR_ALIGN
1627 printf(" [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
1628 mLineBox
->GetBounds().IStart(lineWM
),
1629 mLineBox
->GetBounds().BStart(lineWM
),
1630 mLineBox
->GetBounds().ISize(lineWM
),
1631 mLineBox
->GetBounds().BSize(lineWM
), mFinalLineBSize
,
1632 mLineBox
->GetLogicalAscent());
1637 // Place frames with CSS property vertical-align: top or bottom.
1638 void nsLineLayout::PlaceTopBottomFrames(PerSpanData
* psd
,
1639 nscoord aDistanceFromStart
,
1640 nscoord aLineBSize
) {
1641 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1642 PerSpanData
* span
= pfd
->mSpan
;
1644 NS_ASSERTION(0xFF != pfd
->mBlockDirAlign
, "umr");
1646 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1647 nsSize containerSize
= ContainerSizeForSpan(psd
);
1648 switch (pfd
->mBlockDirAlign
) {
1651 pfd
->mBounds
.BStart(lineWM
) = -aDistanceFromStart
- span
->mMinBCoord
;
1653 pfd
->mBounds
.BStart(lineWM
) =
1654 -aDistanceFromStart
+ pfd
->mMargin
.BStart(lineWM
);
1656 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, containerSize
);
1657 #ifdef NOISY_BLOCKDIR_ALIGN
1659 pfd
->mFrame
->ListTag(stdout
);
1660 printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n",
1661 pfd
->mBounds
.BStart(lineWM
), aDistanceFromStart
,
1662 span
? pfd
->mBorderPadding
.BStart(lineWM
) : 0,
1663 span
? span
->mBStartLeading
: 0);
1668 // Compute bottom leading
1669 pfd
->mBounds
.BStart(lineWM
) =
1670 -aDistanceFromStart
+ aLineBSize
- span
->mMaxBCoord
;
1672 pfd
->mBounds
.BStart(lineWM
) = -aDistanceFromStart
+ aLineBSize
-
1673 pfd
->mMargin
.BEnd(lineWM
) -
1674 pfd
->mBounds
.BSize(lineWM
);
1676 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, containerSize
);
1677 #ifdef NOISY_BLOCKDIR_ALIGN
1679 pfd
->mFrame
->ListTag(stdout
);
1680 printf(": y=%d\n", pfd
->mBounds
.BStart(lineWM
));
1685 nscoord fromStart
= aDistanceFromStart
+ pfd
->mBounds
.BStart(lineWM
);
1686 PlaceTopBottomFrames(span
, fromStart
, aLineBSize
);
1691 static nscoord
GetBSizeOfEmphasisMarks(nsIFrame
* aSpanFrame
, float aInflation
) {
1692 RefPtr
<nsFontMetrics
> fm
= nsLayoutUtils::GetFontMetricsOfEmphasisMarks(
1693 aSpanFrame
->Style(), aSpanFrame
->PresContext(), aInflation
);
1694 return fm
->MaxHeight();
1697 void nsLineLayout::AdjustLeadings(nsIFrame
* spanFrame
, PerSpanData
* psd
,
1698 const nsStyleText
* aStyleText
,
1700 bool* aZeroEffectiveSpanBox
) {
1701 MOZ_ASSERT(spanFrame
== psd
->mFrame
->mFrame
);
1702 nscoord requiredStartLeading
= 0;
1703 nscoord requiredEndLeading
= 0;
1704 if (spanFrame
->IsRubyFrame()) {
1705 // We may need to extend leadings here for ruby annotations as
1706 // required by section Line Spacing in the CSS Ruby spec.
1707 // See http://dev.w3.org/csswg/css-ruby/#line-height
1708 auto rubyFrame
= static_cast<nsRubyFrame
*>(spanFrame
);
1709 RubyBlockLeadings leadings
= rubyFrame
->GetBlockLeadings();
1710 requiredStartLeading
+= leadings
.mStart
;
1711 requiredEndLeading
+= leadings
.mEnd
;
1713 if (aStyleText
->HasEffectiveTextEmphasis()) {
1714 nscoord bsize
= GetBSizeOfEmphasisMarks(spanFrame
, aInflation
);
1715 LogicalSide side
= aStyleText
->TextEmphasisSide(mRootSpan
->mWritingMode
);
1716 if (side
== eLogicalSideBStart
) {
1717 requiredStartLeading
+= bsize
;
1719 MOZ_ASSERT(side
== eLogicalSideBEnd
,
1720 "emphasis marks must be in block axis");
1721 requiredEndLeading
+= bsize
;
1725 nscoord requiredLeading
= requiredStartLeading
+ requiredEndLeading
;
1726 // If we do not require any additional leadings, don't touch anything
1727 // here even if it is greater than the original leading, because the
1728 // latter could be negative.
1729 if (requiredLeading
!= 0) {
1730 nscoord leading
= psd
->mBStartLeading
+ psd
->mBEndLeading
;
1731 nscoord deltaLeading
= requiredLeading
- leading
;
1732 if (deltaLeading
> 0) {
1733 // If the total leading is not wide enough for ruby annotations
1734 // and/or emphasis marks, extend the side which is not enough. If
1735 // both sides are not wide enough, replace the leadings with the
1736 // requested values.
1737 if (requiredStartLeading
< psd
->mBStartLeading
) {
1738 psd
->mBEndLeading
+= deltaLeading
;
1739 } else if (requiredEndLeading
< psd
->mBEndLeading
) {
1740 psd
->mBStartLeading
+= deltaLeading
;
1742 psd
->mBStartLeading
= requiredStartLeading
;
1743 psd
->mBEndLeading
= requiredEndLeading
;
1745 psd
->mLogicalBSize
+= deltaLeading
;
1746 // We have adjusted the leadings, it is no longer a zero
1747 // effective span box.
1748 *aZeroEffectiveSpanBox
= false;
1753 static float GetInflationForBlockDirAlignment(nsIFrame
* aFrame
,
1754 nscoord aInflationMinFontSize
) {
1755 if (aFrame
->IsInSVGTextSubtree()) {
1756 const nsIFrame
* container
=
1757 nsLayoutUtils::GetClosestFrameOfType(aFrame
, LayoutFrameType::SVGText
);
1758 NS_ASSERTION(container
, "expected to find an ancestor SVGTextFrame");
1759 return static_cast<const SVGTextFrame
*>(container
)
1760 ->GetFontSizeScaleFactor();
1762 return nsLayoutUtils::FontSizeInflationInner(aFrame
, aInflationMinFontSize
);
1765 bool nsLineLayout::ShouldApplyLineHeightInPreserveWhiteSpace(
1766 const PerSpanData
* psd
) {
1767 if (psd
->mFrame
->mFrame
->Style()->IsAnonBox()) {
1768 // e.g. An empty `input[type=button]` should still be line-height sized.
1772 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1773 if (!pfd
->mIsEmpty
) {
1780 #define BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM nscoord_MAX
1781 #define BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM nscoord_MIN
1783 // Place frames in the block direction within a given span (CSS property
1784 // vertical-align) Note: this doesn't place frames with vertical-align:
1785 // top or bottom as those have to wait until the entire line box block
1786 // size is known. This is called after the span frame has finished being
1787 // reflowed so that we know its block size.
1788 void nsLineLayout::VerticalAlignFrames(PerSpanData
* psd
) {
1789 // Get parent frame info
1790 PerFrameData
* spanFramePFD
= psd
->mFrame
;
1791 nsIFrame
* spanFrame
= spanFramePFD
->mFrame
;
1793 // Get the parent frame's font for all of the frames in this span
1795 GetInflationForBlockDirAlignment(spanFrame
, mInflationMinFontSize
);
1796 RefPtr
<nsFontMetrics
> fm
=
1797 nsLayoutUtils::GetFontMetricsForFrame(spanFrame
, inflation
);
1799 bool preMode
= mStyleText
->WhiteSpaceIsSignificant();
1801 // See if the span is an empty continuation. It's an empty continuation iff:
1802 // - it has a prev-in-flow
1803 // - it has no next in flow
1804 // - it's zero sized
1805 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1806 bool emptyContinuation
= psd
!= mRootSpan
&& spanFrame
->GetPrevInFlow() &&
1807 !spanFrame
->GetNextInFlow() &&
1808 spanFramePFD
->mBounds
.IsZeroSize();
1810 #ifdef NOISY_BLOCKDIR_ALIGN
1811 printf("[%sSpan]", (psd
== mRootSpan
) ? "Root" : "");
1812 spanFrame
->ListTag(stdout
);
1813 printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s",
1814 preMode
? "yes" : "no",
1815 mPresContext
->CompatibilityMode() != eCompatibility_NavQuirks
? "yes"
1817 spanFramePFD
->mBounds
.ISize(lineWM
),
1818 spanFramePFD
->mBounds
.BSize(lineWM
), emptyContinuation
? "yes" : "no");
1819 if (psd
!= mRootSpan
) {
1820 printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
1821 spanFramePFD
->mBorderPadding
.Top(lineWM
),
1822 spanFramePFD
->mBorderPadding
.Right(lineWM
),
1823 spanFramePFD
->mBorderPadding
.Bottom(lineWM
),
1824 spanFramePFD
->mBorderPadding
.Left(lineWM
),
1825 spanFramePFD
->mMargin
.Top(lineWM
),
1826 spanFramePFD
->mMargin
.Right(lineWM
),
1827 spanFramePFD
->mMargin
.Bottom(lineWM
),
1828 spanFramePFD
->mMargin
.Left(lineWM
));
1833 // Compute the span's zeroEffectiveSpanBox flag. What we are trying
1834 // to determine is how we should treat the span: should it act
1835 // "normally" according to css2 or should it effectively
1838 // In general, if the document being processed is in full standards
1839 // mode then it should act normally (with one exception). The
1840 // exception case is when a span is continued and yet the span is
1841 // empty (e.g. compressed whitespace). For this kind of span we treat
1842 // it as if it were not there so that it doesn't impact the
1845 // In almost standards mode or quirks mode, we should sometimes make
1846 // it disappear. The cases that matter are those where the span
1847 // contains no real text elements that would provide an ascent and
1848 // descent and height. However, if css style elements have been
1849 // applied to the span (border/padding/margin) so that it's clear the
1850 // document author is intending css2 behavior then we act as if strict
1853 // This code works correctly for preMode, because a blank line
1854 // in PRE mode is encoded as a text node with a LF in it, since
1855 // text nodes with only whitespace are considered in preMode.
1857 // Much of this logic is shared with the various implementations of
1858 // nsIFrame::IsEmpty since they need to duplicate the way it makes
1859 // some lines empty. However, nsIFrame::IsEmpty can't be reused here
1860 // since this code sets zeroEffectiveSpanBox even when there are
1861 // non-empty children.
1862 bool zeroEffectiveSpanBox
= false;
1863 // XXXldb If we really have empty continuations, then all these other
1864 // checks don't make sense for them.
1865 // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that
1866 // it agrees with this code. (If it doesn't agree, it probably should.)
1867 if ((emptyContinuation
||
1868 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
) &&
1869 ((psd
== mRootSpan
) || (spanFramePFD
->mBorderPadding
.IsAllZero() &&
1870 spanFramePFD
->mMargin
.IsAllZero()))) {
1871 // This code handles an issue with compatibility with non-css
1872 // conformant browsers. In particular, there are some cases
1873 // where the font-size and line-height for a span must be
1874 // ignored and instead the span must *act* as if it were zero
1875 // sized. In general, if the span contains any non-compressed
1876 // text then we don't use this logic.
1877 // However, this is not propagated outwards, since (in compatibility
1878 // mode) we don't want big line heights for things like
1879 // <p><font size="-1">Text</font></p>
1881 // We shouldn't include any whitespace that collapses, unless we're
1882 // preformatted (in which case it shouldn't, but the width=0 test is
1883 // perhaps incorrect). This includes whitespace at the beginning of
1884 // a line and whitespace preceded (?) by other whitespace.
1885 // See bug 134580 and bug 155333.
1886 zeroEffectiveSpanBox
= true;
1887 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1888 if (pfd
->mIsTextFrame
&&
1889 (pfd
->mIsNonWhitespaceTextFrame
|| preMode
||
1890 pfd
->mBounds
.ISize(mRootSpan
->mWritingMode
) != 0)) {
1891 zeroEffectiveSpanBox
= false;
1897 // Setup baselineBCoord, minBCoord, and maxBCoord
1898 nscoord baselineBCoord
, minBCoord
, maxBCoord
;
1899 if (psd
== mRootSpan
) {
1900 // Use a zero baselineBCoord since we don't yet know where the baseline
1901 // will be (until we know how tall the line is; then we will
1902 // know). In addition, use extreme values for the minBCoord and maxBCoord
1903 // values so that only the child frames will impact their values
1904 // (since these are children of the block, there is no span box to
1905 // provide initial values).
1907 minBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
1908 maxBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
1909 #ifdef NOISY_BLOCKDIR_ALIGN
1910 printf("[RootSpan]");
1911 spanFrame
->ListTag(stdout
);
1913 ": pass1 valign frames: topEdge=%d minLineBSize=%d "
1914 "zeroEffectiveSpanBox=%s\n",
1915 mBStartEdge
, mMinLineBSize
, zeroEffectiveSpanBox
? "yes" : "no");
1918 // Compute the logical block size for this span. The logical block size
1919 // is based on the "line-height" value, not the font-size. Also
1920 // compute the top leading.
1922 GetInflationForBlockDirAlignment(spanFrame
, mInflationMinFontSize
);
1923 nscoord logicalBSize
= ReflowInput::CalcLineHeight(
1924 *spanFrame
->Style(), spanFrame
->PresContext(), spanFrame
->GetContent(),
1925 mLineContainerRI
.ComputedHeight(), inflation
);
1926 nscoord contentBSize
= spanFramePFD
->mBounds
.BSize(lineWM
) -
1927 spanFramePFD
->mBorderPadding
.BStartEnd(lineWM
);
1929 // Special-case for a ::first-letter frame, set the line height to
1930 // the frame block size if the user has left line-height == normal
1931 if (spanFramePFD
->mIsLetterFrame
&& !spanFrame
->GetPrevInFlow() &&
1932 spanFrame
->StyleFont()->mLineHeight
.IsNormal()) {
1933 logicalBSize
= spanFramePFD
->mBounds
.BSize(lineWM
);
1936 nscoord leading
= logicalBSize
- contentBSize
;
1937 psd
->mBStartLeading
= leading
/ 2;
1938 psd
->mBEndLeading
= leading
- psd
->mBStartLeading
;
1939 psd
->mLogicalBSize
= logicalBSize
;
1940 AdjustLeadings(spanFrame
, psd
, spanFrame
->StyleText(), inflation
,
1941 &zeroEffectiveSpanBox
);
1943 if (zeroEffectiveSpanBox
) {
1944 // When the span-box is to be ignored, zero out the initial
1945 // values so that the span doesn't impact the final line
1946 // height. The contents of the span can impact the final line
1949 // Note that things are readjusted for this span after its children
1951 minBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
1952 maxBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
1954 // The initial values for the min and max block coord values are in the
1955 // span's coordinate space, and cover the logical block size of the span.
1956 // If there are child frames in this span that stick out of this area
1957 // then the minBCoord and maxBCoord are updated by the amount of logical
1958 // blockSize that is outside this range.
1960 spanFramePFD
->mBorderPadding
.BStart(lineWM
) - psd
->mBStartLeading
;
1961 maxBCoord
= minBCoord
+ psd
->mLogicalBSize
;
1964 // This is the distance from the top edge of the parents visual
1965 // box to the baseline. The span already computed this for us,
1967 *psd
->mBaseline
= baselineBCoord
= spanFramePFD
->mAscent
;
1969 #ifdef NOISY_BLOCKDIR_ALIGN
1970 printf("[%sSpan]", (psd
== mRootSpan
) ? "Root" : "");
1971 spanFrame
->ListTag(stdout
);
1973 ": baseLine=%d logicalBSize=%d topLeading=%d h=%d bp=%d,%d "
1974 "zeroEffectiveSpanBox=%s\n",
1975 baselineBCoord
, psd
->mLogicalBSize
, psd
->mBStartLeading
,
1976 spanFramePFD
->mBounds
.BSize(lineWM
),
1977 spanFramePFD
->mBorderPadding
.Top(lineWM
),
1978 spanFramePFD
->mBorderPadding
.Bottom(lineWM
),
1979 zeroEffectiveSpanBox
? "yes" : "no");
1983 nscoord maxStartBoxBSize
= 0;
1984 nscoord maxEndBoxBSize
= 0;
1985 PerFrameData
* pfd
= psd
->mFirstFrame
;
1986 while (nullptr != pfd
) {
1987 nsIFrame
* frame
= pfd
->mFrame
;
1989 // sanity check (see bug 105168, non-reproducible crashes from null frame)
1991 "null frame in PerFrameData - something is very very bad");
1996 // Compute the logical block size of the frame
1997 nscoord logicalBSize
;
1998 PerSpanData
* frameSpan
= pfd
->mSpan
;
2000 // For span frames the logical-block-size and start-leading were
2001 // pre-computed when the span was reflowed.
2002 logicalBSize
= frameSpan
->mLogicalBSize
;
2004 // For other elements the logical block size is the same as the
2005 // frame's block size plus its margins.
2007 pfd
->mBounds
.BSize(lineWM
) + pfd
->mMargin
.BStartEnd(lineWM
);
2008 if (logicalBSize
< 0 &&
2009 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
) {
2010 pfd
->mAscent
-= logicalBSize
;
2015 // Get vertical-align property ("vertical-align" is the CSS name for
2016 // block-direction align)
2017 const auto& verticalAlign
= frame
->StyleDisplay()->mVerticalAlign
;
2018 Maybe
<StyleVerticalAlignKeyword
> verticalAlignEnum
=
2019 frame
->VerticalAlignEnum();
2020 #ifdef NOISY_BLOCKDIR_ALIGN
2022 frame
->ListTag(stdout
);
2023 printf(": verticalAlignIsKw=%d (enum == %d", verticalAlign
.IsKeyword(),
2024 verticalAlign
.IsKeyword()
2025 ? static_cast<int>(verticalAlign
.AsKeyword())
2027 if (verticalAlignEnum
) {
2028 printf(", after SVG dominant-baseline conversion == %d",
2029 static_cast<int>(*verticalAlignEnum
));
2034 if (verticalAlignEnum
) {
2035 StyleVerticalAlignKeyword keyword
= *verticalAlignEnum
;
2036 if (lineWM
.IsVertical()) {
2037 if (keyword
== StyleVerticalAlignKeyword::Middle
) {
2038 // For vertical writing mode where the dominant baseline is centered
2039 // (i.e. text-orientation is not sideways-*), we remap 'middle' to
2040 // 'middle-with-baseline' so that images align sensibly with the
2041 // center-baseline-aligned text.
2042 if (!lineWM
.IsSideways()) {
2043 keyword
= StyleVerticalAlignKeyword::MozMiddleWithBaseline
;
2045 } else if (lineWM
.IsLineInverted()) {
2046 // Swap the meanings of top and bottom when line is inverted
2047 // relative to block direction.
2049 case StyleVerticalAlignKeyword::Top
:
2050 keyword
= StyleVerticalAlignKeyword::Bottom
;
2052 case StyleVerticalAlignKeyword::Bottom
:
2053 keyword
= StyleVerticalAlignKeyword::Top
;
2055 case StyleVerticalAlignKeyword::TextTop
:
2056 keyword
= StyleVerticalAlignKeyword::TextBottom
;
2058 case StyleVerticalAlignKeyword::TextBottom
:
2059 keyword
= StyleVerticalAlignKeyword::TextTop
;
2067 // baseline coord that may be adjusted for script offset
2068 nscoord revisedBaselineBCoord
= baselineBCoord
;
2070 // For superscript and subscript, raise or lower the baseline of the box
2071 // to the proper offset of the parent's box, then proceed as for BASELINE
2072 if (keyword
== StyleVerticalAlignKeyword::Sub
||
2073 keyword
== StyleVerticalAlignKeyword::Super
) {
2074 revisedBaselineBCoord
+= lineWM
.FlowRelativeToLineRelativeFactor() *
2075 (keyword
== StyleVerticalAlignKeyword::Sub
2076 ? fm
->SubscriptOffset()
2077 : -fm
->SuperscriptOffset());
2078 keyword
= StyleVerticalAlignKeyword::Baseline
;
2083 case StyleVerticalAlignKeyword::Baseline
:
2084 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
- pfd
->mAscent
;
2085 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2088 case StyleVerticalAlignKeyword::Top
: {
2089 pfd
->mBlockDirAlign
= VALIGN_TOP
;
2090 nscoord subtreeBSize
= logicalBSize
;
2092 subtreeBSize
= frameSpan
->mMaxBCoord
- frameSpan
->mMinBCoord
;
2093 NS_ASSERTION(subtreeBSize
>= logicalBSize
,
2094 "unexpected subtree block size");
2096 if (subtreeBSize
> maxStartBoxBSize
) {
2097 maxStartBoxBSize
= subtreeBSize
;
2102 case StyleVerticalAlignKeyword::Bottom
: {
2103 pfd
->mBlockDirAlign
= VALIGN_BOTTOM
;
2104 nscoord subtreeBSize
= logicalBSize
;
2106 subtreeBSize
= frameSpan
->mMaxBCoord
- frameSpan
->mMinBCoord
;
2107 NS_ASSERTION(subtreeBSize
>= logicalBSize
,
2108 "unexpected subtree block size");
2110 if (subtreeBSize
> maxEndBoxBSize
) {
2111 maxEndBoxBSize
= subtreeBSize
;
2116 case StyleVerticalAlignKeyword::Middle
: {
2117 // Align the midpoint of the frame with 1/2 the parents
2118 // x-height above the baseline.
2119 nscoord parentXHeight
=
2120 lineWM
.FlowRelativeToLineRelativeFactor() * fm
->XHeight();
2122 pfd
->mBounds
.BStart(lineWM
) =
2124 (parentXHeight
+ pfd
->mBounds
.BSize(lineWM
)) / 2;
2126 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
-
2127 (parentXHeight
+ logicalBSize
) / 2 +
2128 pfd
->mMargin
.BStart(lineWM
);
2130 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2134 case StyleVerticalAlignKeyword::TextTop
: {
2135 // The top of the logical box is aligned with the top of
2136 // the parent element's text.
2137 // XXX For vertical text we will need a new API to get the logical
2139 nscoord parentAscent
=
2140 lineWM
.IsLineInverted() ? fm
->MaxDescent() : fm
->MaxAscent();
2142 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
- parentAscent
-
2143 pfd
->mBorderPadding
.BStart(lineWM
) +
2144 frameSpan
->mBStartLeading
;
2146 pfd
->mBounds
.BStart(lineWM
) =
2147 baselineBCoord
- parentAscent
+ pfd
->mMargin
.BStart(lineWM
);
2149 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2153 case StyleVerticalAlignKeyword::TextBottom
: {
2154 // The bottom of the logical box is aligned with the
2155 // bottom of the parent elements text.
2156 nscoord parentDescent
=
2157 lineWM
.IsLineInverted() ? fm
->MaxAscent() : fm
->MaxDescent();
2159 pfd
->mBounds
.BStart(lineWM
) =
2160 baselineBCoord
+ parentDescent
- pfd
->mBounds
.BSize(lineWM
) +
2161 pfd
->mBorderPadding
.BEnd(lineWM
) - frameSpan
->mBEndLeading
;
2163 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
+ parentDescent
-
2164 pfd
->mBounds
.BSize(lineWM
) -
2165 pfd
->mMargin
.BEnd(lineWM
);
2167 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2171 case StyleVerticalAlignKeyword::MozMiddleWithBaseline
: {
2172 // Align the midpoint of the frame with the baseline of the parent.
2174 pfd
->mBounds
.BStart(lineWM
) =
2175 baselineBCoord
- pfd
->mBounds
.BSize(lineWM
) / 2;
2177 pfd
->mBounds
.BStart(lineWM
) =
2178 baselineBCoord
- logicalBSize
/ 2 + pfd
->mMargin
.BStart(lineWM
);
2180 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2185 // We have either a coord, a percent, or a calc().
2186 nscoord offset
= verticalAlign
.AsLength().Resolve([&] {
2187 // Percentages are like lengths, except treated as a percentage
2188 // of the elements line block size value.
2190 GetInflationForBlockDirAlignment(frame
, mInflationMinFontSize
);
2191 return ReflowInput::CalcLineHeight(
2192 *frame
->Style(), frame
->PresContext(), frame
->GetContent(),
2193 mLineContainerRI
.ComputedBSize(), inflation
);
2196 // According to the CSS2 spec (10.8.1), a positive value
2197 // "raises" the box by the given distance while a negative value
2198 // "lowers" the box by the given distance (with zero being the
2199 // baseline). Since Y coordinates increase towards the bottom of
2200 // the screen we reverse the sign, unless the line orientation is
2201 // inverted relative to block direction.
2202 nscoord revisedBaselineBCoord
=
2203 baselineBCoord
- offset
* lineWM
.FlowRelativeToLineRelativeFactor();
2204 if (lineWM
.IsCentralBaseline()) {
2205 // If we're using a dominant center baseline, we align with the center
2206 // of the frame being placed (bug 1133945).
2207 pfd
->mBounds
.BStart(lineWM
) =
2208 revisedBaselineBCoord
- pfd
->mBounds
.BSize(lineWM
) / 2;
2210 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
- pfd
->mAscent
;
2212 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2215 // Update minBCoord/maxBCoord for frames that we just placed. Do not factor
2216 // text into the equation.
2217 if (pfd
->mBlockDirAlign
== VALIGN_OTHER
) {
2218 // Text frames do not contribute to the min/max Y values for the
2219 // line (instead their parent frame's font-size contributes).
2220 // XXXrbs -- relax this restriction because it causes text frames
2221 // to jam together when 'font-size-adjust' is enabled
2222 // and layout is using dynamic font heights (bug 20394)
2223 // -- Note #1: With this code enabled and with the fact that we are
2224 // not using Em[Ascent|Descent] as nsDimensions for text
2225 // metrics in GFX mean that the discussion in bug 13072 cannot
2227 // -- Note #2: We still don't want empty-text frames to interfere.
2228 // For example in quirks mode, avoiding empty text frames
2229 // prevents "tall" lines around elements like <hr> since the
2230 // rules of <hr> in quirks.css have pseudo text contents with LF
2233 if (pfd
->mIsTextFrame
) {
2234 // Only consider text frames if they're not empty and
2235 // line-height=normal.
2236 canUpdate
= pfd
->mIsNonWhitespaceTextFrame
&&
2237 frame
->StyleFont()->mLineHeight
.IsNormal();
2239 canUpdate
= !pfd
->mIsPlaceholder
;
2243 nscoord blockStart
, blockEnd
;
2245 // For spans that were are now placing, use their position
2246 // plus their already computed min-Y and max-Y values for
2247 // computing blockStart and blockEnd.
2248 blockStart
= pfd
->mBounds
.BStart(lineWM
) + frameSpan
->mMinBCoord
;
2249 blockEnd
= pfd
->mBounds
.BStart(lineWM
) + frameSpan
->mMaxBCoord
;
2252 pfd
->mBounds
.BStart(lineWM
) - pfd
->mMargin
.BStart(lineWM
);
2253 blockEnd
= blockStart
+ logicalBSize
;
2256 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
&&
2258 // Check if it's a BR frame that is not alone on its line (it
2259 // is given a block size of zero to indicate this), and if so reset
2260 // blockStart and blockEnd so that BR frames don't influence the line.
2261 if (frame
->IsBrFrame()) {
2262 blockStart
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
2263 blockEnd
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
2266 if (blockStart
< minBCoord
) minBCoord
= blockStart
;
2267 if (blockEnd
> maxBCoord
) maxBCoord
= blockEnd
;
2268 #ifdef NOISY_BLOCKDIR_ALIGN
2270 " [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d "
2271 "minBCoord=%d maxBCoord=%d\n",
2272 pfd
->mAscent
, pfd
->mBounds
.BSize(lineWM
),
2273 pfd
->mBorderPadding
.Top(lineWM
), pfd
->mBorderPadding
.Bottom(lineWM
),
2274 logicalBSize
, frameSpan
? frameSpan
->mBStartLeading
: 0,
2275 pfd
->mBounds
.BStart(lineWM
), minBCoord
, maxBCoord
);
2278 if (psd
!= mRootSpan
) {
2279 frame
->SetRect(lineWM
, pfd
->mBounds
, ContainerSizeForSpan(psd
));
2285 // Factor in the minimum line block-size when handling the root-span for
2287 if (psd
== mRootSpan
) {
2288 // We should factor in the block element's minimum line-height (as
2289 // defined in section 10.8.1 of the css2 spec) assuming that
2290 // zeroEffectiveSpanBox is not set on the root span. This only happens
2291 // in some cases in quirks mode:
2292 // (1) if the root span contains non-whitespace text directly (this
2293 // is handled by zeroEffectiveSpanBox
2294 // (2) if this line has a ::marker
2295 // (3) if this is the last line of an LI, DT, or DD element
2296 // (The last line before a block also counts, but not before a
2297 // BR) (NN4/IE5 quirk)
2299 // (1) and (2) above
2300 bool applyMinLH
= !zeroEffectiveSpanBox
|| mHasMarker
;
2302 !mGotLineBox
|| (!mLineBox
->IsLineWrapped() && !mLineEndsInBR
);
2303 if (!applyMinLH
&& isLastLine
) {
2304 nsIContent
* blockContent
= mRootSpan
->mFrame
->mFrame
->GetContent();
2306 // (3) above, if the last line of LI, DT, or DD
2307 if (blockContent
->IsAnyOfHTMLElements(nsGkAtoms::li
, nsGkAtoms::dt
,
2314 if (psd
->mHasNonemptyContent
||
2315 (preMode
&& ShouldApplyLineHeightInPreserveWhiteSpace(psd
)) ||
2317 #ifdef NOISY_BLOCKDIR_ALIGN
2318 printf(" [span]==> adjusting min/maxBCoord: currentValues: %d,%d",
2319 minBCoord
, maxBCoord
);
2321 nscoord minimumLineBSize
= mMinLineBSize
;
2322 nscoord blockStart
= -nsLayoutUtils::GetCenteredFontBaseline(
2323 fm
, minimumLineBSize
, lineWM
.IsLineInverted());
2324 nscoord blockEnd
= blockStart
+ minimumLineBSize
;
2326 if (mStyleText
->HasEffectiveTextEmphasis()) {
2327 nscoord fontMaxHeight
= fm
->MaxHeight();
2328 nscoord emphasisHeight
=
2329 GetBSizeOfEmphasisMarks(spanFrame
, inflation
);
2330 nscoord delta
= fontMaxHeight
+ emphasisHeight
- minimumLineBSize
;
2332 if (minimumLineBSize
< fontMaxHeight
) {
2333 // If the leadings are negative, fill them first.
2334 nscoord ascent
= fm
->MaxAscent();
2335 nscoord descent
= fm
->MaxDescent();
2336 if (lineWM
.IsLineInverted()) {
2337 std::swap(ascent
, descent
);
2339 blockStart
= -ascent
;
2341 delta
= emphasisHeight
;
2343 LogicalSide side
= mStyleText
->TextEmphasisSide(lineWM
);
2344 if (side
== eLogicalSideBStart
) {
2345 blockStart
-= delta
;
2352 if (blockStart
< minBCoord
) minBCoord
= blockStart
;
2353 if (blockEnd
> maxBCoord
) maxBCoord
= blockEnd
;
2355 #ifdef NOISY_BLOCKDIR_ALIGN
2356 printf(" new values: %d,%d\n", minBCoord
, maxBCoord
);
2358 #ifdef NOISY_BLOCKDIR_ALIGN
2360 " Used mMinLineBSize: %d, blockStart: %d, blockEnd: "
2362 mMinLineBSize
, blockStart
, blockEnd
);
2366 // [1] BR's on empty lines stop working
2367 // [2] May not honor css2's notion of handling empty elements
2368 // [3] blank lines in a pre-section ("\n") (handled with preMode)
2370 // XXX Are there other problems with this?
2371 #ifdef NOISY_BLOCKDIR_ALIGN
2373 " [span]==> zapping min/maxBCoord: currentValues: %d,%d "
2375 minBCoord
, maxBCoord
);
2377 minBCoord
= maxBCoord
= 0;
2382 if ((minBCoord
== BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
) ||
2383 (maxBCoord
== BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
)) {
2384 minBCoord
= maxBCoord
= baselineBCoord
;
2387 if (psd
!= mRootSpan
&& zeroEffectiveSpanBox
) {
2388 #ifdef NOISY_BLOCKDIR_ALIGN
2389 printf(" [span]adjusting for zeroEffectiveSpanBox\n");
2391 " Original: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, "
2392 "logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
2393 minBCoord
, maxBCoord
, spanFramePFD
->mBounds
.BSize(lineWM
),
2394 spanFramePFD
->mAscent
, psd
->mLogicalBSize
, psd
->mBStartLeading
,
2397 nscoord goodMinBCoord
=
2398 spanFramePFD
->mBorderPadding
.BStart(lineWM
) - psd
->mBStartLeading
;
2399 nscoord goodMaxBCoord
= goodMinBCoord
+ psd
->mLogicalBSize
;
2401 // For cases like the one in bug 714519 (text-decoration placement
2402 // or making nsLineLayout::IsZeroBSize() handle
2403 // vertical-align:top/bottom on a descendant of the line that's not
2404 // a child of it), we want to treat elements that are
2405 // vertical-align: top or bottom somewhat like children for the
2406 // purposes of this quirk. To some extent, this is guessing, since
2407 // they might end up being aligned anywhere. However, we'll guess
2408 // that they'll be placed aligned with the top or bottom of this
2409 // frame (as though this frame is the only thing in the line).
2410 // (Guessing isn't unreasonable, since all we're doing is reducing the
2411 // scope of a quirk and making the behavior more standards-like.)
2412 if (maxStartBoxBSize
> maxBCoord
- minBCoord
) {
2413 // Distribute maxStartBoxBSize to ascent (baselineBCoord - minBCoord), and
2414 // then to descent (maxBCoord - baselineBCoord) by adjusting minBCoord or
2415 // maxBCoord, but not to exceed goodMinBCoord and goodMaxBCoord.
2416 nscoord distribute
= maxStartBoxBSize
- (maxBCoord
- minBCoord
);
2417 nscoord ascentSpace
= std::max(minBCoord
- goodMinBCoord
, 0);
2418 if (distribute
> ascentSpace
) {
2419 distribute
-= ascentSpace
;
2420 minBCoord
-= ascentSpace
;
2421 nscoord descentSpace
= std::max(goodMaxBCoord
- maxBCoord
, 0);
2422 if (distribute
> descentSpace
) {
2423 maxBCoord
+= descentSpace
;
2425 maxBCoord
+= distribute
;
2428 minBCoord
-= distribute
;
2431 if (maxEndBoxBSize
> maxBCoord
- minBCoord
) {
2432 // Likewise, but preferring descent to ascent.
2433 nscoord distribute
= maxEndBoxBSize
- (maxBCoord
- minBCoord
);
2434 nscoord descentSpace
= std::max(goodMaxBCoord
- maxBCoord
, 0);
2435 if (distribute
> descentSpace
) {
2436 distribute
-= descentSpace
;
2437 maxBCoord
+= descentSpace
;
2438 nscoord ascentSpace
= std::max(minBCoord
- goodMinBCoord
, 0);
2439 if (distribute
> ascentSpace
) {
2440 minBCoord
-= ascentSpace
;
2442 minBCoord
-= distribute
;
2445 maxBCoord
+= distribute
;
2449 if (minBCoord
> goodMinBCoord
) {
2450 nscoord adjust
= minBCoord
- goodMinBCoord
; // positive
2452 // shrink the logical extents
2453 psd
->mLogicalBSize
-= adjust
;
2454 psd
->mBStartLeading
-= adjust
;
2456 if (maxBCoord
< goodMaxBCoord
) {
2457 nscoord adjust
= goodMaxBCoord
- maxBCoord
;
2458 psd
->mLogicalBSize
-= adjust
;
2459 psd
->mBEndLeading
-= adjust
;
2461 if (minBCoord
> 0) {
2462 // shrink the content by moving its block start down. This is tricky,
2463 // since the block start is the 0 for many coordinates, so what we do is
2464 // move everything else up.
2465 spanFramePFD
->mAscent
-= minBCoord
; // move the baseline up
2466 spanFramePFD
->mBounds
.BSize(lineWM
) -=
2467 minBCoord
; // move the block end up
2468 psd
->mBStartLeading
+= minBCoord
;
2469 *psd
->mBaseline
-= minBCoord
;
2471 pfd
= psd
->mFirstFrame
;
2472 while (nullptr != pfd
) {
2473 pfd
->mBounds
.BStart(lineWM
) -= minBCoord
; // move all the children
2475 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerSizeForSpan(psd
));
2478 maxBCoord
-= minBCoord
; // since minBCoord is in the frame's own
2479 // coordinate system
2482 if (maxBCoord
< spanFramePFD
->mBounds
.BSize(lineWM
)) {
2483 nscoord adjust
= spanFramePFD
->mBounds
.BSize(lineWM
) - maxBCoord
;
2484 spanFramePFD
->mBounds
.BSize(lineWM
) -= adjust
; // move the bottom up
2485 psd
->mBEndLeading
+= adjust
;
2487 #ifdef NOISY_BLOCKDIR_ALIGN
2489 " New: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, "
2490 "logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
2491 minBCoord
, maxBCoord
, spanFramePFD
->mBounds
.BSize(lineWM
),
2492 spanFramePFD
->mAscent
, psd
->mLogicalBSize
, psd
->mBStartLeading
,
2497 psd
->mMinBCoord
= minBCoord
;
2498 psd
->mMaxBCoord
= maxBCoord
;
2499 #ifdef NOISY_BLOCKDIR_ALIGN
2501 " [span]==> minBCoord=%d maxBCoord=%d delta=%d maxStartBoxBSize=%d "
2502 "maxEndBoxBSize=%d\n",
2503 minBCoord
, maxBCoord
, maxBCoord
- minBCoord
, maxStartBoxBSize
,
2506 if (maxStartBoxBSize
> mMaxStartBoxBSize
) {
2507 mMaxStartBoxBSize
= maxStartBoxBSize
;
2509 if (maxEndBoxBSize
> mMaxEndBoxBSize
) {
2510 mMaxEndBoxBSize
= maxEndBoxBSize
;
2514 static void SlideSpanFrameRect(nsIFrame
* aFrame
, nscoord aDeltaWidth
) {
2515 // This should not use nsIFrame::MovePositionBy because it happens
2516 // prior to relative positioning. In particular, because
2517 // nsBlockFrame::PlaceLine calls aLineLayout.TrimTrailingWhiteSpace()
2518 // prior to calling aLineLayout.RelativePositionFrames().
2519 nsPoint p
= aFrame
->GetPosition();
2521 aFrame
->SetPosition(p
);
2524 bool nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData
* psd
,
2525 nscoord
* aDeltaISize
) {
2526 PerFrameData
* pfd
= psd
->mFirstFrame
;
2532 while (nullptr != pfd
) {
2533 #ifdef REALLY_NOISY_TRIM
2534 psd
->mFrame
->mFrame
->ListTag(stdout
);
2535 printf(": attempting trim of ");
2536 pfd
->mFrame
->ListTag(stdout
);
2539 PerSpanData
* childSpan
= pfd
->mSpan
;
2540 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2542 // Maybe the child span has the trailing white-space in it?
2543 if (TrimTrailingWhiteSpaceIn(childSpan
, aDeltaISize
)) {
2544 nscoord deltaISize
= *aDeltaISize
;
2546 // Adjust the child spans frame size
2547 pfd
->mBounds
.ISize(lineWM
) -= deltaISize
;
2548 if (psd
!= mRootSpan
) {
2549 // When the child span is not a direct child of the block
2550 // we need to update the child spans frame rectangle
2551 // because it most likely will not be done again. Spans
2552 // that are direct children of the block will be updated
2553 // later, however, because the VerticalAlignFrames method
2554 // will be run after this method.
2555 nsSize containerSize
= ContainerSizeForSpan(childSpan
);
2556 nsIFrame
* f
= pfd
->mFrame
;
2557 LogicalRect
r(lineWM
, f
->GetRect(), containerSize
);
2558 r
.ISize(lineWM
) -= deltaISize
;
2559 f
->SetRect(lineWM
, r
, containerSize
);
2562 // Adjust the inline end edge of the span that contains the child span
2563 psd
->mICoord
-= deltaISize
;
2565 // Slide any frames that follow the child span over by the
2566 // correct amount. The only thing that can follow the child
2567 // span is empty stuff, so we are just making things
2568 // sensible (keeping the combined area honest).
2569 while (pfd
->mNext
) {
2571 pfd
->mBounds
.IStart(lineWM
) -= deltaISize
;
2572 if (psd
!= mRootSpan
) {
2573 // When the child span is not a direct child of the block
2574 // we need to update the child span's frame rectangle
2575 // because it most likely will not be done again. Spans
2576 // that are direct children of the block will be updated
2577 // later, however, because the VerticalAlignFrames method
2578 // will be run after this method.
2579 SlideSpanFrameRect(pfd
->mFrame
, deltaISize
);
2585 } else if (!pfd
->mIsTextFrame
&& !pfd
->mSkipWhenTrimmingWhitespace
) {
2586 // If we hit a frame on the end that's not text and not a placeholder,
2587 // then there is no trailing whitespace to trim. Stop the search.
2590 } else if (pfd
->mIsTextFrame
) {
2591 // Call TrimTrailingWhiteSpace even on empty textframes because they
2592 // might have a soft hyphen which should now appear, changing the frame's
2594 nsTextFrame::TrimOutput trimOutput
=
2595 static_cast<nsTextFrame
*>(pfd
->mFrame
)
2596 ->TrimTrailingWhiteSpace(
2597 mLineContainerRI
.mRenderingContext
->GetDrawTarget());
2599 psd
->mFrame
->mFrame
->ListTag(stdout
);
2600 printf(": trim of ");
2601 pfd
->mFrame
->ListTag(stdout
);
2602 printf(" returned %d\n", trimOutput
.mDeltaWidth
);
2605 if (trimOutput
.mChanged
) {
2606 pfd
->mRecomputeOverflow
= true;
2609 // Delta width not being zero means that
2610 // there is trimmed space in the frame.
2611 if (trimOutput
.mDeltaWidth
) {
2612 pfd
->mBounds
.ISize(lineWM
) -= trimOutput
.mDeltaWidth
;
2614 // If any trailing space is trimmed, the justification opportunity
2615 // generated by the space should be removed as well.
2616 pfd
->mJustificationInfo
.CancelOpportunityForTrimmedSpace();
2618 // See if the text frame has already been placed in its parent
2619 if (psd
!= mRootSpan
) {
2620 // The frame was already placed during psd's
2621 // reflow. Update the frames rectangle now.
2622 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerSizeForSpan(psd
));
2625 // Adjust containing span's right edge
2626 psd
->mICoord
-= trimOutput
.mDeltaWidth
;
2628 // Slide any frames that follow the text frame over by the
2629 // right amount. The only thing that can follow the text
2630 // frame is empty stuff, so we are just making things
2631 // sensible (keeping the combined area honest).
2632 while (pfd
->mNext
) {
2634 pfd
->mBounds
.IStart(lineWM
) -= trimOutput
.mDeltaWidth
;
2635 if (psd
!= mRootSpan
) {
2636 // When the child span is not a direct child of the block
2637 // we need to update the child spans frame rectangle
2638 // because it most likely will not be done again. Spans
2639 // that are direct children of the block will be updated
2640 // later, however, because the VerticalAlignFrames method
2641 // will be run after this method.
2642 SlideSpanFrameRect(pfd
->mFrame
, trimOutput
.mDeltaWidth
);
2647 if (pfd
->mIsNonEmptyTextFrame
|| trimOutput
.mChanged
) {
2648 // Pass up to caller so they can shrink their span
2649 *aDeltaISize
= trimOutput
.mDeltaWidth
;
2660 bool nsLineLayout::TrimTrailingWhiteSpace() {
2661 PerSpanData
* psd
= mRootSpan
;
2663 TrimTrailingWhiteSpaceIn(psd
, &deltaISize
);
2664 return 0 != deltaISize
;
2667 bool nsLineLayout::PerFrameData::ParticipatesInJustification() const {
2668 if (mIsMarker
|| mIsEmpty
|| mSkipWhenTrimmingWhitespace
) {
2669 // Skip ::markers, empty frames, and placeholders
2672 if (mIsTextFrame
&& !mIsNonWhitespaceTextFrame
&&
2673 static_cast<nsTextFrame
*>(mFrame
)->IsAtEndOfLine()) {
2674 // Skip trimmed whitespaces
2680 struct nsLineLayout::JustificationComputationState
{
2681 PerFrameData
* mFirstParticipant
;
2682 PerFrameData
* mLastParticipant
;
2683 // When we are going across a boundary of ruby base, i.e. entering
2684 // one, leaving one, or both, the following fields will be set to
2685 // the corresponding ruby base frame for handling ruby-align.
2686 PerFrameData
* mLastExitedRubyBase
;
2687 PerFrameData
* mLastEnteredRubyBase
;
2689 JustificationComputationState()
2690 : mFirstParticipant(nullptr),
2691 mLastParticipant(nullptr),
2692 mLastExitedRubyBase(nullptr),
2693 mLastEnteredRubyBase(nullptr) {}
2696 static bool IsRubyAlignSpaceAround(nsIFrame
* aRubyBase
) {
2697 return aRubyBase
->StyleText()->mRubyAlign
== StyleRubyAlign::SpaceAround
;
2701 * Assign justification gaps for justification
2702 * opportunities across two frames.
2705 int nsLineLayout::AssignInterframeJustificationGaps(
2706 PerFrameData
* aFrame
, JustificationComputationState
& aState
) {
2707 PerFrameData
* prev
= aState
.mLastParticipant
;
2710 auto& assign
= aFrame
->mJustificationAssignment
;
2711 auto& prevAssign
= prev
->mJustificationAssignment
;
2713 if (aState
.mLastExitedRubyBase
|| aState
.mLastEnteredRubyBase
) {
2714 PerFrameData
* exitedRubyBase
= aState
.mLastExitedRubyBase
;
2715 if (!exitedRubyBase
|| IsRubyAlignSpaceAround(exitedRubyBase
->mFrame
)) {
2716 prevAssign
.mGapsAtEnd
= 1;
2718 exitedRubyBase
->mJustificationAssignment
.mGapsAtEnd
= 1;
2721 PerFrameData
* enteredRubyBase
= aState
.mLastEnteredRubyBase
;
2722 if (!enteredRubyBase
|| IsRubyAlignSpaceAround(enteredRubyBase
->mFrame
)) {
2723 assign
.mGapsAtStart
= 1;
2725 enteredRubyBase
->mJustificationAssignment
.mGapsAtStart
= 1;
2728 // We are no longer going across a ruby base boundary.
2729 aState
.mLastExitedRubyBase
= nullptr;
2730 aState
.mLastEnteredRubyBase
= nullptr;
2734 const auto& info
= aFrame
->mJustificationInfo
;
2735 const auto& prevInfo
= prev
->mJustificationInfo
;
2736 if (!info
.mIsStartJustifiable
&& !prevInfo
.mIsEndJustifiable
) {
2740 if (!info
.mIsStartJustifiable
) {
2741 prevAssign
.mGapsAtEnd
= 2;
2742 assign
.mGapsAtStart
= 0;
2743 } else if (!prevInfo
.mIsEndJustifiable
) {
2744 prevAssign
.mGapsAtEnd
= 0;
2745 assign
.mGapsAtStart
= 2;
2747 prevAssign
.mGapsAtEnd
= 1;
2748 assign
.mGapsAtStart
= 1;
2754 * Compute the justification info of the given span, and store the
2755 * number of inner opportunities into the frame's justification info.
2756 * It returns the number of non-inner opportunities it detects.
2758 int32_t nsLineLayout::ComputeFrameJustification(
2759 PerSpanData
* aPSD
, JustificationComputationState
& aState
) {
2760 NS_ASSERTION(aPSD
, "null arg");
2761 NS_ASSERTION(!aState
.mLastParticipant
|| !aState
.mLastParticipant
->mSpan
,
2762 "Last participant shall always be a leaf frame");
2763 bool firstChild
= true;
2764 int32_t& innerOpportunities
=
2765 aPSD
->mFrame
->mJustificationInfo
.mInnerOpportunities
;
2766 MOZ_ASSERT(innerOpportunities
== 0,
2767 "Justification info should not have been set yet.");
2768 int32_t outerOpportunities
= 0;
2770 for (PerFrameData
* pfd
= aPSD
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
2771 if (!pfd
->ParticipatesInJustification()) {
2775 bool isRubyBase
= pfd
->mFrame
->IsRubyBaseFrame();
2776 PerFrameData
* outerRubyBase
= aState
.mLastEnteredRubyBase
;
2778 aState
.mLastEnteredRubyBase
= pfd
;
2781 int extraOpportunities
= 0;
2783 PerSpanData
* span
= pfd
->mSpan
;
2784 extraOpportunities
= ComputeFrameJustification(span
, aState
);
2785 innerOpportunities
+= pfd
->mJustificationInfo
.mInnerOpportunities
;
2787 if (pfd
->mIsTextFrame
) {
2788 innerOpportunities
+= pfd
->mJustificationInfo
.mInnerOpportunities
;
2791 if (!aState
.mLastParticipant
) {
2792 aState
.mFirstParticipant
= pfd
;
2793 // It is not an empty ruby base, but we are not assigning gaps
2794 // to the content for now. Clear the last entered ruby base so
2795 // that we can correctly set the last exited ruby base.
2796 aState
.mLastEnteredRubyBase
= nullptr;
2798 extraOpportunities
= AssignInterframeJustificationGaps(pfd
, aState
);
2801 aState
.mLastParticipant
= pfd
;
2805 if (aState
.mLastEnteredRubyBase
== pfd
) {
2806 // There is no justification participant inside this ruby base.
2807 // Ignore this ruby base completely and restore the outer ruby
2809 aState
.mLastEnteredRubyBase
= outerRubyBase
;
2811 aState
.mLastExitedRubyBase
= pfd
;
2816 outerOpportunities
= extraOpportunities
;
2819 innerOpportunities
+= extraOpportunities
;
2823 return outerOpportunities
;
2826 void nsLineLayout::AdvanceAnnotationInlineBounds(PerFrameData
* aPFD
,
2827 const nsSize
& aContainerSize
,
2828 nscoord aDeltaICoord
,
2829 nscoord aDeltaISize
) {
2830 nsIFrame
* frame
= aPFD
->mFrame
;
2831 LayoutFrameType frameType
= frame
->Type();
2832 MOZ_ASSERT(frameType
== LayoutFrameType::RubyText
||
2833 frameType
== LayoutFrameType::RubyTextContainer
);
2834 MOZ_ASSERT(aPFD
->mSpan
, "rt and rtc should have span.");
2836 PerSpanData
* psd
= aPFD
->mSpan
;
2837 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2838 aPFD
->mBounds
.IStart(lineWM
) += aDeltaICoord
;
2840 // Check whether this expansion should be counted into the reserved
2841 // isize or not. When it is a ruby text container, and it has some
2842 // children linked to the base, it must not have reserved isize,
2843 // or its children won't align with their bases. Otherwise, this
2844 // expansion should be reserved. There are two cases a ruby text
2845 // container does not have children linked to the base:
2846 // 1. it is a container for span; 2. its children are collapsed.
2847 // See bug 1055674 for the second case.
2848 if (frameType
== LayoutFrameType::RubyText
||
2849 // This ruby text container is a span.
2850 (psd
->mFirstFrame
== psd
->mLastFrame
&& psd
->mFirstFrame
&&
2851 !psd
->mFirstFrame
->mIsLinkedToBase
)) {
2852 // For ruby text frames, only increase frames
2853 // which are not auto-hidden.
2854 if (frameType
!= LayoutFrameType::RubyText
||
2855 !static_cast<nsRubyTextFrame
*>(frame
)->IsCollapsed()) {
2856 nscoord reservedISize
= RubyUtils::GetReservedISize(frame
);
2857 RubyUtils::SetReservedISize(frame
, reservedISize
+ aDeltaISize
);
2860 // It is a normal ruby text container. Its children will expand
2861 // themselves properly. We only need to expand its own size here.
2862 aPFD
->mBounds
.ISize(lineWM
) += aDeltaISize
;
2864 aPFD
->mFrame
->SetRect(lineWM
, aPFD
->mBounds
, aContainerSize
);
2868 * This function applies the changes of icoord and isize caused by
2869 * justification to annotations of the given frame.
2871 void nsLineLayout::ApplyLineJustificationToAnnotations(PerFrameData
* aPFD
,
2872 nscoord aDeltaICoord
,
2873 nscoord aDeltaISize
) {
2874 PerFrameData
* pfd
= aPFD
->mNextAnnotation
;
2876 nsSize containerSize
= pfd
->mFrame
->GetParent()->GetSize();
2877 AdvanceAnnotationInlineBounds(pfd
, containerSize
, aDeltaICoord
,
2880 // There are two cases where an annotation frame has siblings which
2881 // do not attached to a ruby base-level frame:
2882 // 1. there's an intra-annotation whitespace which has no intra-base
2883 // white-space to pair with;
2884 // 2. there are not enough ruby bases to be paired with annotations.
2885 // In these cases, their size should not be affected, but we still
2886 // need to move them so that they won't overlap other frames.
2887 PerFrameData
* sibling
= pfd
->mNext
;
2888 while (sibling
&& !sibling
->mIsLinkedToBase
) {
2889 AdvanceAnnotationInlineBounds(sibling
, containerSize
,
2890 aDeltaICoord
+ aDeltaISize
, 0);
2891 sibling
= sibling
->mNext
;
2894 pfd
= pfd
->mNextAnnotation
;
2898 nscoord
nsLineLayout::ApplyFrameJustification(
2899 PerSpanData
* aPSD
, JustificationApplicationState
& aState
) {
2900 NS_ASSERTION(aPSD
, "null arg");
2902 nscoord deltaICoord
= 0;
2903 for (PerFrameData
* pfd
= aPSD
->mFirstFrame
; pfd
!= nullptr;
2906 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2907 const auto& assign
= pfd
->mJustificationAssignment
;
2909 pfd
->mIsTextFrame
&& !pfd
->mWritingMode
.IsOrthogonalTo(lineWM
);
2911 // Don't apply justification if the frame doesn't participate. Same
2912 // as the condition used in ComputeFrameJustification. Note that,
2913 // we still need to move the frame based on deltaICoord even if the
2914 // frame itself doesn't expand.
2915 if (pfd
->ParticipatesInJustification()) {
2917 if (aState
.IsJustifiable()) {
2918 // Set corresponding justification gaps here, so that the
2919 // text frame knows how it should add gaps at its sides.
2920 const auto& info
= pfd
->mJustificationInfo
;
2921 auto textFrame
= static_cast<nsTextFrame
*>(pfd
->mFrame
);
2922 textFrame
->AssignJustificationGaps(assign
);
2923 dw
= aState
.Consume(JustificationUtils::CountGaps(info
, assign
));
2927 pfd
->mRecomputeOverflow
= true;
2930 if (nullptr != pfd
->mSpan
) {
2931 dw
= ApplyFrameJustification(pfd
->mSpan
, aState
);
2935 MOZ_ASSERT(!assign
.TotalGaps(),
2936 "Non-participants shouldn't have assigned gaps");
2939 pfd
->mBounds
.ISize(lineWM
) += dw
;
2940 nscoord gapsAtEnd
= 0;
2941 if (!isInlineText
&& assign
.TotalGaps()) {
2942 // It is possible that we assign gaps to non-text frame or an
2943 // orthogonal text frame. Apply the gaps as margin for them.
2944 deltaICoord
+= aState
.Consume(assign
.mGapsAtStart
);
2945 gapsAtEnd
= aState
.Consume(assign
.mGapsAtEnd
);
2948 pfd
->mBounds
.IStart(lineWM
) += deltaICoord
;
2950 // The gaps added to the end of the frame should also be
2951 // excluded from the isize added to the annotation.
2952 ApplyLineJustificationToAnnotations(pfd
, deltaICoord
, dw
- gapsAtEnd
);
2954 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerSizeForSpan(aPSD
));
2959 static nsIFrame
* FindNearestRubyBaseAncestor(nsIFrame
* aFrame
) {
2960 MOZ_ASSERT(aFrame
->Style()->ShouldSuppressLineBreak());
2961 while (aFrame
&& !aFrame
->IsRubyBaseFrame()) {
2962 aFrame
= aFrame
->GetParent();
2964 // XXX It is possible that no ruby base ancestor is found because of
2965 // some edge cases like form control or canvas inside ruby text.
2966 // See bug 1138092 comment 4.
2967 NS_WARNING_ASSERTION(aFrame
, "no ruby base ancestor?");
2972 * This method expands the given frame by the given reserved isize.
2974 void nsLineLayout::ExpandRubyBox(PerFrameData
* aFrame
, nscoord aReservedISize
,
2975 const nsSize
& aContainerSize
) {
2976 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2977 auto rubyAlign
= aFrame
->mFrame
->StyleText()->mRubyAlign
;
2978 switch (rubyAlign
) {
2979 case StyleRubyAlign::Start
:
2980 // do nothing for start
2982 case StyleRubyAlign::SpaceBetween
:
2983 case StyleRubyAlign::SpaceAround
: {
2984 int32_t opportunities
= aFrame
->mJustificationInfo
.mInnerOpportunities
;
2985 int32_t gaps
= opportunities
* 2;
2986 if (rubyAlign
== StyleRubyAlign::SpaceAround
) {
2987 // Each expandable ruby box with ruby-align space-around has a
2988 // gap at each of its sides. For rb/rbc, see comment in
2989 // AssignInterframeJustificationGaps; for rt/rtc, see comment
2990 // in ExpandRubyBoxWithAnnotations.
2994 JustificationApplicationState
state(gaps
, aReservedISize
);
2995 ApplyFrameJustification(aFrame
->mSpan
, state
);
2998 // If there are no justification opportunities for space-between,
2999 // fall-through to center per spec.
3002 case StyleRubyAlign::Center
:
3003 // Indent all children by half of the reserved inline size.
3004 for (PerFrameData
* child
= aFrame
->mSpan
->mFirstFrame
; child
;
3005 child
= child
->mNext
) {
3006 child
->mBounds
.IStart(lineWM
) += aReservedISize
/ 2;
3007 child
->mFrame
->SetRect(lineWM
, child
->mBounds
, aContainerSize
);
3011 MOZ_ASSERT_UNREACHABLE("Unknown ruby-align value");
3014 aFrame
->mBounds
.ISize(lineWM
) += aReservedISize
;
3015 aFrame
->mFrame
->SetRect(lineWM
, aFrame
->mBounds
, aContainerSize
);
3019 * This method expands the given frame by the reserved inline size.
3020 * It also expands its annotations if they are expandable and have
3021 * reserved isize larger than zero.
3023 void nsLineLayout::ExpandRubyBoxWithAnnotations(PerFrameData
* aFrame
,
3024 const nsSize
& aContainerSize
) {
3025 nscoord reservedISize
= RubyUtils::GetReservedISize(aFrame
->mFrame
);
3026 if (reservedISize
) {
3027 ExpandRubyBox(aFrame
, reservedISize
, aContainerSize
);
3030 WritingMode lineWM
= mRootSpan
->mWritingMode
;
3031 bool isLevelContainer
= aFrame
->mFrame
->IsRubyBaseContainerFrame();
3032 for (PerFrameData
* annotation
= aFrame
->mNextAnnotation
; annotation
;
3033 annotation
= annotation
->mNextAnnotation
) {
3034 if (lineWM
.IsOrthogonalTo(annotation
->mFrame
->GetWritingMode())) {
3035 // Inter-character case: don't attempt to expand ruby annotations.
3038 if (isLevelContainer
) {
3039 nsIFrame
* rtcFrame
= annotation
->mFrame
;
3040 MOZ_ASSERT(rtcFrame
->IsRubyTextContainerFrame());
3041 // It is necessary to set the rect again because the container
3042 // width was unknown, and zero was used instead when we reflow
3043 // them. The corresponding base containers were repositioned in
3044 // VerticalAlignFrames and PlaceTopBottomFrames.
3045 MOZ_ASSERT(rtcFrame
->GetLogicalSize(lineWM
) ==
3046 annotation
->mBounds
.Size(lineWM
));
3047 rtcFrame
->SetPosition(lineWM
, annotation
->mBounds
.Origin(lineWM
),
3051 nscoord reservedISize
= RubyUtils::GetReservedISize(annotation
->mFrame
);
3052 if (!reservedISize
) {
3056 MOZ_ASSERT(annotation
->mSpan
);
3057 JustificationComputationState computeState
;
3058 ComputeFrameJustification(annotation
->mSpan
, computeState
);
3059 if (!computeState
.mFirstParticipant
) {
3062 if (IsRubyAlignSpaceAround(annotation
->mFrame
)) {
3063 // Add one gap at each side of this annotation.
3064 computeState
.mFirstParticipant
->mJustificationAssignment
.mGapsAtStart
= 1;
3065 computeState
.mLastParticipant
->mJustificationAssignment
.mGapsAtEnd
= 1;
3067 nsIFrame
* parentFrame
= annotation
->mFrame
->GetParent();
3068 nsSize containerSize
= parentFrame
->GetSize();
3069 MOZ_ASSERT(containerSize
== aContainerSize
||
3070 parentFrame
->IsRubyTextContainerFrame(),
3071 "Container width should only be different when the current "
3072 "annotation is a ruby text frame, whose parent is not same "
3073 "as its base frame.");
3074 ExpandRubyBox(annotation
, reservedISize
, containerSize
);
3075 ExpandInlineRubyBoxes(annotation
->mSpan
);
3080 * This method looks for all expandable ruby box in the given span, and
3081 * calls ExpandRubyBox to expand them in depth-first preorder.
3083 void nsLineLayout::ExpandInlineRubyBoxes(PerSpanData
* aSpan
) {
3084 nsSize containerSize
= ContainerSizeForSpan(aSpan
);
3085 for (PerFrameData
* pfd
= aSpan
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
3086 if (RubyUtils::IsExpandableRubyBox(pfd
->mFrame
)) {
3087 ExpandRubyBoxWithAnnotations(pfd
, containerSize
);
3090 ExpandInlineRubyBoxes(pfd
->mSpan
);
3095 nscoord
nsLineLayout::GetHangFrom(const PerSpanData
* aSpan
,
3096 bool aLineIsRTL
) const {
3097 const PerFrameData
* pfd
= aSpan
->mLastFrame
;
3100 if (const PerSpanData
* childSpan
= pfd
->mSpan
) {
3101 return GetHangFrom(childSpan
, aLineIsRTL
);
3103 if (pfd
->mIsTextFrame
) {
3104 auto* lastText
= static_cast<nsTextFrame
*>(pfd
->mFrame
);
3105 result
= lastText
->GetHangableISize();
3107 // If the hangable space will be at the start edge of the line, due to
3108 // its bidi direction being against the line direction, we flag this by
3109 // negating the advance.
3110 lastText
->EnsureTextRun(nsTextFrame::eInflated
);
3111 auto* textRun
= lastText
->GetTextRun(nsTextFrame::eInflated
);
3112 if (textRun
&& textRun
->IsRightToLeft() != aLineIsRTL
) {
3118 if (!pfd
->mSkipWhenTrimmingWhitespace
) {
3119 // If we hit a frame on the end that's not text and not a placeholder or
3120 // <br>, then there is no trailing whitespace to hang. Stop the search.
3123 // Scan back for a preceding frame whose whitespace we can hang.
3129 gfxTextRun::TrimmableWS
nsLineLayout::GetTrimFrom(const PerSpanData
* aSpan
,
3130 bool aLineIsRTL
) const {
3131 const PerFrameData
* pfd
= aSpan
->mLastFrame
;
3133 if (const PerSpanData
* childSpan
= pfd
->mSpan
) {
3134 return GetTrimFrom(childSpan
, aLineIsRTL
);
3136 if (pfd
->mIsTextFrame
) {
3137 auto* lastText
= static_cast<nsTextFrame
*>(pfd
->mFrame
);
3138 auto result
= lastText
->GetTrimmableWS();
3139 if (result
.mAdvance
) {
3140 lastText
->EnsureTextRun(nsTextFrame::eInflated
);
3141 auto* textRun
= lastText
->GetTextRun(nsTextFrame::eInflated
);
3142 if (textRun
&& textRun
->IsRightToLeft() != aLineIsRTL
) {
3143 result
.mAdvance
= -result
.mAdvance
;
3148 if (!pfd
->mSkipWhenTrimmingWhitespace
) {
3149 // If we hit a frame on the end that's not text and not a placeholder or
3150 // <br>, then there is no trailing whitespace to trim. Stop the search.
3151 return gfxTextRun::TrimmableWS
{};
3153 // Scan back for a preceding frame whose whitespace we can trim.
3156 return gfxTextRun::TrimmableWS
{};
3159 // Align inline frames within the line according to the CSS text-align
3161 void nsLineLayout::TextAlignLine(nsLineBox
* aLine
, bool aIsLastLine
) {
3163 * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller
3164 * only in cases where the last line needs special handling.
3166 PerSpanData
* psd
= mRootSpan
;
3167 WritingMode lineWM
= psd
->mWritingMode
;
3168 LAYOUT_WARN_IF_FALSE(psd
->mIEnd
!= NS_UNCONSTRAINEDSIZE
,
3169 "have unconstrained width; this should only result from "
3170 "very large sizes, not attempts at intrinsic width "
3172 nscoord availISize
= psd
->mIEnd
- psd
->mIStart
;
3173 nscoord remainingISize
= availISize
- aLine
->ISize();
3174 #ifdef NOISY_INLINEDIR_ALIGN
3175 LineContainerFrame()->ListTag(stdout
);
3176 printf(": availISize=%d lineBounds.IStart=%d lineISize=%d delta=%d\n",
3177 availISize
, aLine
->IStart(), aLine
->ISize(), remainingISize
);
3181 StyleTextAlign textAlign
=
3182 aIsLastLine
? mStyleText
->TextAlignForLastLine() : mStyleText
->mTextAlign
;
3184 // Check if there's trailing whitespace we need to "hang" at line-wrap.
3186 uint32_t trimCount
= 0;
3187 if (aLine
->IsLineWrapped()) {
3188 if (textAlign
== StyleTextAlign::Justify
) {
3189 auto trim
= GetTrimFrom(mRootSpan
, lineWM
.IsBidiRTL());
3190 hang
= NSToCoordRound(trim
.mAdvance
);
3191 trimCount
= trim
.mCount
;
3193 hang
= GetHangFrom(mRootSpan
, lineWM
.IsBidiRTL());
3197 bool isSVG
= LineContainerFrame()->IsInSVGTextSubtree();
3198 bool doTextAlign
= remainingISize
> 0 || hang
!= 0;
3200 int32_t additionalGaps
= 0;
3202 (mHasRuby
|| (doTextAlign
&& textAlign
== StyleTextAlign::Justify
))) {
3203 JustificationComputationState computeState
;
3204 ComputeFrameJustification(psd
, computeState
);
3205 if (mHasRuby
&& computeState
.mFirstParticipant
) {
3206 PerFrameData
* firstFrame
= computeState
.mFirstParticipant
;
3207 if (firstFrame
->mFrame
->Style()->ShouldSuppressLineBreak()) {
3208 MOZ_ASSERT(!firstFrame
->mJustificationAssignment
.mGapsAtStart
);
3209 nsIFrame
* rubyBase
= FindNearestRubyBaseAncestor(firstFrame
->mFrame
);
3210 if (rubyBase
&& IsRubyAlignSpaceAround(rubyBase
)) {
3211 firstFrame
->mJustificationAssignment
.mGapsAtStart
= 1;
3215 PerFrameData
* lastFrame
= computeState
.mLastParticipant
;
3216 if (lastFrame
->mFrame
->Style()->ShouldSuppressLineBreak()) {
3217 MOZ_ASSERT(!lastFrame
->mJustificationAssignment
.mGapsAtEnd
);
3218 nsIFrame
* rubyBase
= FindNearestRubyBaseAncestor(lastFrame
->mFrame
);
3219 if (rubyBase
&& IsRubyAlignSpaceAround(rubyBase
)) {
3220 lastFrame
->mJustificationAssignment
.mGapsAtEnd
= 1;
3227 if (!isSVG
&& doTextAlign
) {
3228 switch (textAlign
) {
3229 case StyleTextAlign::Justify
: {
3230 int32_t opportunities
=
3231 psd
->mFrame
->mJustificationInfo
.mInnerOpportunities
-
3232 (hang
? trimCount
: 0);
3233 if (opportunities
> 0) {
3234 int32_t gaps
= opportunities
* 2 + additionalGaps
;
3235 remainingISize
+= std::abs(hang
);
3236 JustificationApplicationState
applyState(gaps
, remainingISize
);
3238 // Apply the justification, and make sure to update our linebox
3239 // width to account for it.
3240 aLine
->ExpandBy(ApplyFrameJustification(psd
, applyState
),
3241 ContainerSizeForSpan(psd
));
3243 // If the trimmable trailing whitespace that we want to hang had
3244 // reverse-inline directionality, adjust line position to account for
3245 // it being at the inline-start side.
3246 // On top of the original "hang" amount, justification will have
3247 // modified its width, so we include that adjustment here.
3249 dx
= hang
- trimCount
* remainingISize
/ opportunities
;
3252 // Gaps that belong to trimmed whitespace were not included in the
3253 // applyState count, so we need to add them here for the assert.
3254 DebugOnly
<int32_t> trimmedGaps
= hang
? trimCount
* 2 : 0;
3255 MOZ_ASSERT(applyState
.mGaps
.mHandled
==
3256 applyState
.mGaps
.mCount
+ trimmedGaps
,
3257 "Unprocessed justification gaps");
3258 // Similarly, account for the adjustment applied to the trimmed
3259 // whitespace, which is in addition to the adjustment that applies
3260 // within the actual width of the line.
3261 DebugOnly
<int32_t> trimmedAdjustment
=
3262 trimCount
* remainingISize
/ opportunities
;
3263 NS_ASSERTION(applyState
.mWidth
.mConsumed
==
3264 applyState
.mWidth
.mAvailable
+ trimmedAdjustment
,
3265 "Unprocessed justification width");
3268 // Fall through to the default case if we could not justify to fill
3273 case StyleTextAlign::Start
:
3274 case StyleTextAlign::Char
:
3275 // Default alignment is to start edge so do nothing, except to apply
3276 // any "reverse-hang" amount resulting from reversed-direction trailing
3278 // Char is for tables so treat as start if we find it in block layout.
3284 case StyleTextAlign::Left
:
3285 case StyleTextAlign::MozLeft
:
3286 if (lineWM
.IsBidiRTL()) {
3287 dx
= remainingISize
+ (hang
> 0 ? hang
: 0);
3288 } else if (hang
< 0) {
3293 case StyleTextAlign::Right
:
3294 case StyleTextAlign::MozRight
:
3295 if (lineWM
.IsBidiLTR()) {
3296 dx
= remainingISize
+ (hang
> 0 ? hang
: 0);
3297 } else if (hang
< 0) {
3302 case StyleTextAlign::End
:
3303 dx
= remainingISize
+ (hang
> 0 ? hang
: 0);
3306 case StyleTextAlign::Center
:
3307 case StyleTextAlign::MozCenter
:
3308 dx
= (remainingISize
+ hang
) / 2;
3314 ExpandInlineRubyBoxes(mRootSpan
);
3317 PerFrameData
* startFrame
= psd
->mFirstFrame
;
3318 MOZ_ASSERT(startFrame
, "empty line?");
3319 if (startFrame
->mIsMarker
) {
3320 // ::marker shouldn't participate in bidi reordering nor text alignment.
3321 startFrame
= startFrame
->mNext
;
3322 MOZ_ASSERT(startFrame
, "no frame after ::marker?");
3323 MOZ_ASSERT(!startFrame
->mIsMarker
, "multiple ::markers?");
3326 const bool bidi
= mPresContext
->BidiEnabled() &&
3327 (!mPresContext
->IsVisualMode() || lineWM
.IsBidiRTL());
3329 nsBidiPresUtils::ReorderFrames(startFrame
->mFrame
, aLine
->GetChildCount(),
3330 lineWM
, mContainerSize
,
3331 psd
->mIStart
+ mTextIndent
+ dx
);
3335 // For the bidi case, if startFrame is a ::first-line frame, the mIStart and
3336 // mTextIndent offsets will already have been applied to its position, but
3337 // we still need to apply the text-align adjustment |dx| to its position.
3338 const bool needToAdjustFrames
= !bidi
|| startFrame
->mFrame
->IsLineFrame();
3339 MOZ_ASSERT_IF(startFrame
->mFrame
->IsLineFrame(), !startFrame
->mNext
);
3340 if (needToAdjustFrames
) {
3341 for (PerFrameData
* pfd
= startFrame
; pfd
; pfd
= pfd
->mNext
) {
3342 pfd
->mBounds
.IStart(lineWM
) += dx
;
3343 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerSizeForSpan(psd
));
3346 aLine
->IndentBy(dx
, ContainerSize());
3350 // This method applies any relative positioning to the given frame.
3351 void nsLineLayout::ApplyRelativePositioning(PerFrameData
* aPFD
) {
3352 if (!aPFD
->mIsRelativelyOrStickyPos
) {
3356 nsIFrame
* frame
= aPFD
->mFrame
;
3357 WritingMode frameWM
= aPFD
->mWritingMode
;
3358 LogicalPoint origin
= frame
->GetLogicalPosition(ContainerSize());
3359 // right and bottom are handled by
3360 // ReflowInput::ComputeRelativeOffsets
3361 ReflowInput::ApplyRelativePositioning(frame
, frameWM
, aPFD
->mOffsets
, &origin
,
3363 frame
->SetPosition(frameWM
, origin
, ContainerSize());
3366 // This method do relative positioning for ruby annotations.
3367 void nsLineLayout::RelativePositionAnnotations(PerSpanData
* aRubyPSD
,
3368 OverflowAreas
& aOverflowAreas
) {
3369 MOZ_ASSERT(aRubyPSD
->mFrame
->mFrame
->IsRubyFrame());
3370 for (PerFrameData
* pfd
= aRubyPSD
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
3371 MOZ_ASSERT(pfd
->mFrame
->IsRubyBaseContainerFrame());
3372 for (PerFrameData
* rtc
= pfd
->mNextAnnotation
; rtc
;
3373 rtc
= rtc
->mNextAnnotation
) {
3374 nsIFrame
* rtcFrame
= rtc
->mFrame
;
3375 MOZ_ASSERT(rtcFrame
->IsRubyTextContainerFrame());
3376 ApplyRelativePositioning(rtc
);
3377 OverflowAreas rtcOverflowAreas
;
3378 RelativePositionFrames(rtc
->mSpan
, rtcOverflowAreas
);
3379 aOverflowAreas
.UnionWith(rtcOverflowAreas
+ rtcFrame
->GetPosition());
3384 void nsLineLayout::RelativePositionFrames(PerSpanData
* psd
,
3385 OverflowAreas
& aOverflowAreas
) {
3386 OverflowAreas overflowAreas
;
3387 WritingMode wm
= psd
->mWritingMode
;
3388 if (psd
!= mRootSpan
) {
3389 // The span's overflow areas come in three parts:
3390 // -- this frame's width and height
3391 // -- pfd->mOverflowAreas, which is the area of a ::marker or the union
3392 // of a relatively positioned frame's absolute children
3393 // -- the bounds of all inline descendants
3394 // The former two parts are computed right here, we gather the descendants
3396 // At this point psd->mFrame->mBounds might be out of date since
3397 // bidi reordering can move and resize the frames. So use the frame's
3398 // rect instead of mBounds.
3399 nsRect
adjustedBounds(nsPoint(0, 0), psd
->mFrame
->mFrame
->GetSize());
3401 overflowAreas
.ScrollableOverflow().UnionRect(
3402 psd
->mFrame
->mOverflowAreas
.ScrollableOverflow(), adjustedBounds
);
3403 overflowAreas
.InkOverflow().UnionRect(
3404 psd
->mFrame
->mOverflowAreas
.InkOverflow(), adjustedBounds
);
3406 LogicalRect
rect(wm
, psd
->mIStart
, mBStartEdge
, psd
->mICoord
- psd
->mIStart
,
3408 // The minimum combined area for the frames that are direct
3409 // children of the block starts at the upper left corner of the
3410 // line and is sized to match the size of the line's bounding box
3411 // (the same size as the values returned from VerticalAlignFrames)
3412 overflowAreas
.InkOverflow() = rect
.GetPhysicalRect(wm
, ContainerSize());
3413 overflowAreas
.ScrollableOverflow() = overflowAreas
.InkOverflow();
3416 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
3417 nsIFrame
* frame
= pfd
->mFrame
;
3419 // Adjust the origin of the frame
3420 ApplyRelativePositioning(pfd
);
3422 // We must position the view correctly before positioning its
3423 // descendants so that widgets are positioned properly (since only
3424 // some views have widgets).
3425 if (frame
->HasView())
3426 nsContainerFrame::SyncFrameViewAfterReflow(
3427 mPresContext
, frame
, frame
->GetView(),
3428 pfd
->mOverflowAreas
.InkOverflow(),
3429 nsIFrame::ReflowChildFlags::NoSizeView
);
3431 // Note: the combined area of a child is in its coordinate
3432 // system. We adjust the childs combined area into our coordinate
3433 // system before computing the aggregated value by adding in
3434 // <b>x</b> and <b>y</b> which were computed above.
3437 // Compute a new combined area for the child span before
3438 // aggregating it into our combined area.
3439 RelativePositionFrames(pfd
->mSpan
, r
);
3441 r
= pfd
->mOverflowAreas
;
3442 if (pfd
->mIsTextFrame
) {
3443 // We need to recompute overflow areas in four cases:
3444 // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
3445 // (2) When there are text decorations, since we can't recompute the
3446 // overflow area until Reflow and VerticalAlignLine have finished
3447 // (3) When there are text emphasis marks, since the marks may be
3448 // put further away if the text is inside ruby.
3449 // (4) When there are text strokes
3450 if (pfd
->mRecomputeOverflow
||
3451 frame
->Style()->HasTextDecorationLines() ||
3452 frame
->StyleText()->HasEffectiveTextEmphasis() ||
3453 frame
->StyleText()->HasWebkitTextStroke()) {
3454 nsTextFrame
* f
= static_cast<nsTextFrame
*>(frame
);
3455 r
= f
->RecomputeOverflow(LineContainerFrame());
3457 frame
->FinishAndStoreOverflow(r
, frame
->GetSize());
3460 // If we have something that's not an inline but with a complex frame
3461 // hierarchy inside that contains views, they need to be
3463 // All descendant views must be repositioned even if this frame
3464 // does have a view in case this frame's view does not have a
3465 // widget and some of the descendant views do have widgets --
3466 // otherwise the widgets won't be repositioned.
3467 nsContainerFrame::PositionChildViews(frame
);
3470 // Do this here (rather than along with setting the overflow rect
3471 // below) so we get leaf frames as well. No need to worry
3472 // about the root span, since it doesn't have a frame.
3473 if (frame
->HasView())
3474 nsContainerFrame::SyncFrameViewAfterReflow(
3475 mPresContext
, frame
, frame
->GetView(), r
.InkOverflow(),
3476 nsIFrame::ReflowChildFlags::NoMoveView
);
3478 overflowAreas
.UnionWith(r
+ frame
->GetPosition());
3481 // Also compute relative position in the annotations.
3482 if (psd
->mFrame
->mFrame
->IsRubyFrame()) {
3483 RelativePositionAnnotations(psd
, overflowAreas
);
3486 // If we just computed a spans combined area, we need to update its
3488 if (psd
!= mRootSpan
) {
3489 PerFrameData
* spanPFD
= psd
->mFrame
;
3490 nsIFrame
* frame
= spanPFD
->mFrame
;
3491 frame
->FinishAndStoreOverflow(overflowAreas
, frame
->GetSize());
3493 aOverflowAreas
= overflowAreas
;