1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* state and methods used while laying out a single line of a block frame */
8 // This has to be defined before nsLineLayout.h is included, because
9 // nsLineLayout.h has a #include for plarena.h, which needs this defined:
10 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
11 #include "nsLineLayout.h"
13 #include "SVGTextFrame.h"
14 #include "nsBlockFrame.h"
15 #include "nsFontMetrics.h"
16 #include "nsStyleConsts.h"
17 #include "nsContainerFrame.h"
18 #include "nsFloatManager.h"
19 #include "nsStyleContext.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 "RubyUtils.h"
32 #undef NOISY_INLINEDIR_ALIGN
33 #undef NOISY_BLOCKDIR_ALIGN
34 #undef REALLY_NOISY_BLOCKDIR_ALIGN
36 #undef REALLY_NOISY_REFLOW
38 #undef REALLY_NOISY_PUSHING
40 #undef NOISY_MAX_ELEMENT_SIZE
41 #undef REALLY_NOISY_MAX_ELEMENT_SIZE
42 #undef NOISY_CAN_PLACE_FRAME
44 #undef REALLY_NOISY_TRIM
47 using namespace mozilla
;
49 //----------------------------------------------------------------------
53 nsLineLayout::nsLineLayout(nsPresContext
* aPresContext
,
54 nsFloatManager
* aFloatManager
,
55 const nsHTMLReflowState
* aOuterReflowState
,
56 const nsLineList::iterator
* aLine
,
57 nsLineLayout
* aBaseLineLayout
)
58 : mPresContext(aPresContext
),
59 mFloatManager(aFloatManager
),
60 mBlockReflowState(aOuterReflowState
),
61 mBaseLineLayout(aBaseLineLayout
? aBaseLineLayout
->mBaseLineLayout
: this),
62 mLastOptionalBreakFrame(nullptr),
63 mForceBreakFrame(nullptr),
64 mBlockRS(nullptr),/* XXX temporary */
65 mLastOptionalBreakPriority(gfxBreakPriority::eNoBreak
),
66 mLastOptionalBreakFrameOffset(-1),
67 mForceBreakFrameOffset(-1),
70 mRubyReflowState(nullptr),
71 mFirstLetterStyleOK(false),
73 mImpactedByFloats(false),
74 mLastFloatWasLetterFrame(false),
80 mInFirstLetter(false),
82 mDirtyNextLine(false),
86 MOZ_ASSERT(aOuterReflowState
, "aOuterReflowState must not be null");
87 NS_ASSERTION(aFloatManager
|| aOuterReflowState
->frame
->GetType() ==
88 nsGkAtoms::letterFrame
,
89 "float manager should be present");
90 MOZ_COUNT_CTOR(nsLineLayout
);
92 // Stash away some style data that we need
93 nsBlockFrame
* blockFrame
= do_QueryFrame(aOuterReflowState
->frame
);
95 mStyleText
= blockFrame
->StyleTextForLineLayout();
97 mStyleText
= aOuterReflowState
->frame
->StyleText();
100 mTotalPlacedFrames
= 0;
104 mFinalLineBSize
= nscoord_MIN
;
107 mInflationMinFontSize
=
108 nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowState
->frame
);
110 // Instead of always pre-initializing the free-lists for frames and
111 // spans, we do it on demand so that situations that only use a few
112 // frames and spans won't waste a lot of time in unneeded
114 PL_INIT_ARENA_POOL(&mArena
, "nsLineLayout", 1024);
115 mFrameFreeList
= nullptr;
116 mSpanFreeList
= nullptr;
118 mCurrentSpan
= mRootSpan
= nullptr;
127 nsLineLayout::~nsLineLayout()
129 MOZ_COUNT_DTOR(nsLineLayout
);
131 NS_ASSERTION(nullptr == mRootSpan
, "bad line-layout user");
133 PL_FinishArenaPool(&mArena
);
136 // Find out if the frame has a non-null prev-in-flow, i.e., whether it
137 // is a continuation.
139 HasPrevInFlow(nsIFrame
*aFrame
)
141 nsIFrame
*prevInFlow
= aFrame
->GetPrevInFlow();
142 return prevInFlow
!= nullptr;
146 nsLineLayout::BeginLineReflow(nscoord aICoord
, nscoord aBCoord
,
147 nscoord aISize
, nscoord aBSize
,
148 bool aImpactedByFloats
,
150 WritingMode aWritingMode
,
151 nscoord aContainerWidth
)
153 NS_ASSERTION(nullptr == mRootSpan
, "bad linelayout user");
154 NS_WARN_IF_FALSE(aISize
!= NS_UNCONSTRAINEDSIZE
,
155 "have unconstrained width; this should only result from "
156 "very large sizes, not attempts at intrinsic width "
159 if ((aISize
!= NS_UNCONSTRAINEDSIZE
) && CRAZY_SIZE(aISize
)) {
160 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
161 printf(": Init: bad caller: width WAS %d(0x%x)\n",
164 if ((aBSize
!= NS_UNCONSTRAINEDSIZE
) && CRAZY_SIZE(aBSize
)) {
165 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
166 printf(": Init: bad caller: height WAS %d(0x%x)\n",
171 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
172 printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n",
173 aICoord
, aBCoord
, aISize
, aBSize
,
174 aImpactedByFloats
?"true":"false",
175 aIsTopOfPage
? "top-of-page" : "");
178 mSpansAllocated
= mSpansFreed
= mFramesAllocated
= mFramesFreed
= 0;
181 mFirstLetterStyleOK
= false;
182 mIsTopOfPage
= aIsTopOfPage
;
183 mImpactedByFloats
= aImpactedByFloats
;
184 mTotalPlacedFrames
= 0;
185 if (mBaseLineLayout
== this) {
189 mLineIsEmpty
= false;
190 mLineAtStart
= false;
192 mLineEndsInBR
= false;
194 mMaxStartBoxBSize
= mMaxEndBoxBSize
= 0;
197 mLineBox
->ClearHasBullet();
200 PerSpanData
* psd
= NewPerSpanData();
201 mCurrentSpan
= mRootSpan
= psd
;
202 psd
->mReflowState
= mBlockReflowState
;
203 psd
->mIStart
= aICoord
;
204 psd
->mICoord
= aICoord
;
205 psd
->mIEnd
= aICoord
+ aISize
;
206 mContainerWidth
= aContainerWidth
;
208 // If we're in a constrained height frame, then we don't allow a
209 // max line box width to take effect.
210 if (!(LineContainerFrame()->GetStateBits() &
211 NS_FRAME_IN_CONSTRAINED_HEIGHT
)) {
213 // If the available size is greater than the maximum line box width (if
214 // specified), then we need to adjust the line box width to be at the max
216 nscoord maxLineBoxWidth
=
217 LineContainerFrame()->PresContext()->PresShell()->MaxLineBoxWidth();
219 if (maxLineBoxWidth
> 0 &&
220 psd
->mIEnd
- psd
->mIStart
> maxLineBoxWidth
) {
221 psd
->mIEnd
= psd
->mIStart
+ maxLineBoxWidth
;
225 mBStartEdge
= aBCoord
;
228 !mStyleText
->WhiteSpaceCanWrapStyle() || LineContainerFrame()->IsSVGText();
229 psd
->mWritingMode
= aWritingMode
;
231 // If this is the first line of a block then see if the text-indent
232 // property amounts to anything.
234 if (0 == mLineNumber
&& !HasPrevInFlow(mBlockReflowState
->frame
)) {
235 const nsStyleCoord
&textIndent
= mStyleText
->mTextIndent
;
236 nscoord pctBasis
= 0;
237 if (textIndent
.HasPercent()) {
239 nsHTMLReflowState::GetContainingBlockContentWidth(mBlockReflowState
);
242 mLineBox
->DisableResizeReflowOptimization();
245 nscoord indent
= nsRuleNode::ComputeCoordPercentCalc(textIndent
, pctBasis
);
247 mTextIndent
= indent
;
249 psd
->mICoord
+= indent
;
252 PerFrameData
* pfd
= NewPerFrameData(mBlockReflowState
->frame
);
259 nsLineLayout::EndLineReflow()
262 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
263 printf(": EndLineReflow: width=%d\n", mRootSpan
->mICoord
- mRootSpan
->mIStart
);
266 NS_ASSERTION(mBaseLineLayout
== this ||
267 (!mSpansAllocated
&& !mSpansFreed
&& !mSpanFreeList
&&
268 !mFramesAllocated
&& !mFramesFreed
&& !mFrameFreeList
),
269 "Allocated frames or spans on non-base line layout?");
271 UnlinkFrame(mRootSpan
->mFrame
);
272 mCurrentSpan
= mRootSpan
= nullptr;
274 NS_ASSERTION(mSpansAllocated
== mSpansFreed
, "leak");
275 NS_ASSERTION(mFramesAllocated
== mFramesFreed
, "leak");
278 static int32_t maxSpansAllocated
= NS_LINELAYOUT_NUM_SPANS
;
279 static int32_t maxFramesAllocated
= NS_LINELAYOUT_NUM_FRAMES
;
280 if (mSpansAllocated
> maxSpansAllocated
) {
281 printf("XXX: saw a line with %d spans\n", mSpansAllocated
);
282 maxSpansAllocated
= mSpansAllocated
;
284 if (mFramesAllocated
> maxFramesAllocated
) {
285 printf("XXX: saw a line with %d frames\n", mFramesAllocated
);
286 maxFramesAllocated
= mFramesAllocated
;
291 // XXX swtich to a single mAvailLineWidth that we adjust as each frame
292 // on the line is placed. Each span can still have a per-span mICoord that
293 // tracks where a child frame is going in its span; they don't need a
297 nsLineLayout::UpdateBand(const nsRect
& aNewAvailSpace
,
298 nsIFrame
* aFloatFrame
)
300 WritingMode lineWM
= mRootSpan
->mWritingMode
;
301 LogicalRect
availSpace(lineWM
, aNewAvailSpace
, mContainerWidth
);
302 #ifdef REALLY_NOISY_REFLOW
303 printf("nsLL::UpdateBand %d, %d, %d, %d, (logical %d, %d, %d, %d); frame=%p\n will set mImpacted to true\n",
304 aNewAvailSpace
.x
, aNewAvailSpace
.y
,
305 aNewAvailSpace
.width
, aNewAvailSpace
.height
,
306 availSpace
.IStart(lineWM
), availSpace
.BStart(lineWM
),
307 availSpace
.ISize(lineWM
), availSpace
.BSize(lineWM
),
311 if ((availSpace
.ISize(lineWM
) != NS_UNCONSTRAINEDSIZE
) &&
312 CRAZY_SIZE(availSpace
.ISize(lineWM
))) {
313 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
314 printf(": UpdateBand: bad caller: ISize WAS %d(0x%x)\n",
315 availSpace
.ISize(lineWM
), availSpace
.ISize(lineWM
));
317 if ((availSpace
.BSize(lineWM
) != NS_UNCONSTRAINEDSIZE
) &&
318 CRAZY_SIZE(availSpace
.BSize(lineWM
))) {
319 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
320 printf(": UpdateBand: bad caller: BSize WAS %d(0x%x)\n",
321 availSpace
.BSize(lineWM
), availSpace
.BSize(lineWM
));
325 // Compute the difference between last times width and the new width
326 NS_WARN_IF_FALSE(mRootSpan
->mIEnd
!= NS_UNCONSTRAINEDSIZE
&&
327 aNewAvailSpace
.width
!= NS_UNCONSTRAINEDSIZE
,
328 "have unconstrained width; this should only result from "
329 "very large sizes, not attempts at intrinsic width "
331 // The root span's mIStart moves to aICoord
332 nscoord deltaICoord
= availSpace
.IStart(lineWM
) - mRootSpan
->mIStart
;
333 // The width of all spans changes by this much (the root span's
334 // mIEnd moves to aICoord + aISize, its new width is aISize)
335 nscoord deltaISize
= availSpace
.ISize(lineWM
) -
336 (mRootSpan
->mIEnd
- mRootSpan
->mIStart
);
338 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
339 printf(": UpdateBand: %d,%d,%d,%d deltaISize=%d deltaICoord=%d\n",
340 aNewAvailSpace
.IStart(lineWM
), aNewAvailSpace
.BStart(lineWM
),
341 aNewAvailSpace
.ISize(lineWM
), aNewAvailSpace
.BSize(lineWM
), deltaISize
, deltaICoord
);
344 // Update the root span position
345 mRootSpan
->mIStart
+= deltaICoord
;
346 mRootSpan
->mIEnd
+= deltaICoord
;
347 mRootSpan
->mICoord
+= deltaICoord
;
349 // Now update the right edges of the open spans to account for any
350 // change in available space width
351 for (PerSpanData
* psd
= mCurrentSpan
; psd
; psd
= psd
->mParent
) {
352 psd
->mIEnd
+= deltaISize
;
353 psd
->mContainsFloat
= true;
355 printf(" span %p: oldIEnd=%d newIEnd=%d\n",
356 psd
, psd
->mIEnd
- deltaISize
, psd
->mIEnd
);
359 NS_ASSERTION(mRootSpan
->mContainsFloat
&&
360 mRootSpan
->mIStart
== availSpace
.IStart(lineWM
) &&
361 mRootSpan
->mIEnd
== availSpace
.IEnd(lineWM
),
362 "root span was updated incorrectly?");
364 // Update frame bounds
365 // Note: Only adjust the outermost frames (the ones that are direct
366 // children of the block), not the ones in the child spans. The reason
367 // is simple: the frames in the spans have coordinates local to their
368 // parent therefore they are moved when their parent span is moved.
369 if (deltaICoord
!= 0) {
370 for (PerFrameData
* pfd
= mRootSpan
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
371 pfd
->mBounds
.IStart(lineWM
) += deltaICoord
;
375 mBStartEdge
= availSpace
.BStart(lineWM
);
376 mImpactedByFloats
= true;
378 mLastFloatWasLetterFrame
= nsGkAtoms::letterFrame
== aFloatFrame
->GetType();
381 nsLineLayout::PerSpanData
*
382 nsLineLayout::NewPerSpanData()
384 PerSpanData
* psd
= mBaseLineLayout
->mSpanFreeList
;
387 size_t sz
= sizeof(PerSpanData
);
388 PL_ARENA_ALLOCATE(mem
, &mBaseLineLayout
->mArena
, sz
);
392 psd
= reinterpret_cast<PerSpanData
*>(mem
);
395 mBaseLineLayout
->mSpanFreeList
= psd
->mNextFreeSpan
;
397 psd
->mParent
= nullptr;
398 psd
->mFrame
= nullptr;
399 psd
->mFirstFrame
= nullptr;
400 psd
->mLastFrame
= nullptr;
401 psd
->mContainsFloat
= false;
402 psd
->mZeroEffectiveSpanBox
= false;
403 psd
->mHasNonemptyContent
= false;
406 mBaseLineLayout
->mSpansAllocated
++;
412 nsLineLayout::BeginSpan(nsIFrame
* aFrame
,
413 const nsHTMLReflowState
* aSpanReflowState
,
414 nscoord aIStart
, nscoord aIEnd
,
417 NS_ASSERTION(aIEnd
!= NS_UNCONSTRAINEDSIZE
,
418 "should no longer be using unconstrained sizes");
420 nsFrame::IndentBy(stdout
, mSpanDepth
+1);
421 nsFrame::ListTag(stdout
, aFrame
);
422 printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aIStart
, aIEnd
);
425 PerSpanData
* psd
= NewPerSpanData();
426 // Link up span frame's pfd to point to its child span data
427 PerFrameData
* pfd
= mCurrentSpan
->mLastFrame
;
428 NS_ASSERTION(pfd
->mFrame
== aFrame
, "huh?");
433 psd
->mParent
= mCurrentSpan
;
434 psd
->mReflowState
= aSpanReflowState
;
435 psd
->mIStart
= aIStart
;
436 psd
->mICoord
= aIStart
;
438 psd
->mBaseline
= aBaseline
;
440 nsIFrame
* frame
= aSpanReflowState
->frame
;
441 psd
->mNoWrap
= !frame
->StyleText()->WhiteSpaceCanWrap(frame
) ||
442 frame
->StyleContext()->IsInlineDescendantOfRuby();
443 psd
->mWritingMode
= aSpanReflowState
->GetWritingMode();
445 // Switch to new span
451 nsLineLayout::EndSpan(nsIFrame
* aFrame
)
453 NS_ASSERTION(mSpanDepth
> 0, "end-span without begin-span");
455 nsFrame::IndentBy(stdout
, mSpanDepth
);
456 nsFrame::ListTag(stdout
, aFrame
);
457 printf(": EndSpan width=%d\n", mCurrentSpan
->mICoord
- mCurrentSpan
->mIStart
);
459 PerSpanData
* psd
= mCurrentSpan
;
460 nscoord iSizeResult
= psd
->mLastFrame
? (psd
->mICoord
- psd
->mIStart
) : 0;
463 mCurrentSpan
->mReflowState
= nullptr; // no longer valid so null it out!
464 mCurrentSpan
= mCurrentSpan
->mParent
;
469 nsLineLayout::AttachFrameToBaseLineLayout(PerFrameData
* aFrame
)
471 NS_PRECONDITION(this != mBaseLineLayout
,
472 "This method must not be called in a base line layout.");
474 PerFrameData
* baseFrame
= mBaseLineLayout
->LastFrame();
475 MOZ_ASSERT(aFrame
&& baseFrame
);
476 MOZ_ASSERT(!aFrame
->mIsLinkedToBase
,
477 "The frame must not have been linked with the base");
479 aFrame
->mNextAnnotation
= baseFrame
->mNextAnnotation
;
480 baseFrame
->mNextAnnotation
= aFrame
;
481 aFrame
->mIsLinkedToBase
= true;
485 nsLineLayout::GetCurrentSpanCount() const
487 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
489 PerFrameData
* pfd
= mRootSpan
->mFirstFrame
;
490 while (nullptr != pfd
) {
498 nsLineLayout::SplitLineTo(int32_t aNewCount
)
500 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
502 #ifdef REALLY_NOISY_PUSHING
503 printf("SplitLineTo %d (current count=%d); before:\n", aNewCount
,
504 GetCurrentSpanCount());
505 DumpPerSpanData(mRootSpan
, 1);
507 PerSpanData
* psd
= mRootSpan
;
508 PerFrameData
* pfd
= psd
->mFirstFrame
;
509 while (nullptr != pfd
) {
510 if (--aNewCount
== 0) {
511 // Truncate list at pfd (we keep pfd, but anything following is freed)
512 PerFrameData
* next
= pfd
->mNext
;
513 pfd
->mNext
= nullptr;
514 psd
->mLastFrame
= pfd
;
516 // Now unlink all of the frames following pfd
523 printf("SplitLineTo %d (current count=%d); after:\n", aNewCount
,
524 GetCurrentSpanCount());
525 DumpPerSpanData(mRootSpan
, 1);
530 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 nsFrame::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;
549 PerFrameData
* prevFrame
= pfd
->mPrev
;
550 prevFrame
->mNext
= nullptr;
551 psd
->mLastFrame
= prevFrame
;
554 // Now unlink the frame
555 MOZ_ASSERT(!pfd
->mNext
);
558 nsFrame::IndentBy(stdout
, mSpanDepth
);
559 printf("PushFrame: %p after:\n", psd
);
560 DumpPerSpanData(psd
, 1);
565 nsLineLayout::UnlinkFrame(PerFrameData
* pfd
)
567 while (nullptr != pfd
) {
568 PerFrameData
* next
= pfd
->mNext
;
569 if (pfd
->mIsLinkedToBase
) {
570 // This frame is linked to a ruby base, and should not be freed
571 // now. Just unlink it from the span. It will be freed when its
572 // base frame gets unlinked.
573 pfd
->mNext
= pfd
->mPrev
= nullptr;
578 // It is a ruby base frame. If there are any annotations
579 // linked to this frame, free them first.
580 PerFrameData
* annotationPFD
= pfd
->mNextAnnotation
;
581 while (annotationPFD
) {
582 PerFrameData
* nextAnnotation
= annotationPFD
->mNextAnnotation
;
583 MOZ_ASSERT(annotationPFD
->mNext
== nullptr &&
584 annotationPFD
->mPrev
== nullptr,
585 "PFD in annotations should have been unlinked.");
586 FreeFrame(annotationPFD
);
587 annotationPFD
= nextAnnotation
;
596 nsLineLayout::FreeFrame(PerFrameData
* pfd
)
598 if (nullptr != pfd
->mSpan
) {
599 FreeSpan(pfd
->mSpan
);
601 pfd
->mNext
= mBaseLineLayout
->mFrameFreeList
;
602 mBaseLineLayout
->mFrameFreeList
= pfd
;
604 mBaseLineLayout
->mFramesFreed
++;
609 nsLineLayout::FreeSpan(PerSpanData
* psd
)
612 UnlinkFrame(psd
->mFirstFrame
);
614 // Now put the span on the free list since it's free too
615 psd
->mNextFreeSpan
= mBaseLineLayout
->mSpanFreeList
;
616 mBaseLineLayout
->mSpanFreeList
= psd
;
618 mBaseLineLayout
->mSpansFreed
++;
623 nsLineLayout::IsZeroBSize()
625 PerSpanData
* psd
= mCurrentSpan
;
626 PerFrameData
* pfd
= psd
->mFirstFrame
;
627 while (nullptr != pfd
) {
628 if (0 != pfd
->mBounds
.BSize(psd
->mWritingMode
)) {
636 nsLineLayout::PerFrameData
*
637 nsLineLayout::NewPerFrameData(nsIFrame
* aFrame
)
639 PerFrameData
* pfd
= mBaseLineLayout
->mFrameFreeList
;
642 size_t sz
= sizeof(PerFrameData
);
643 PL_ARENA_ALLOCATE(mem
, &mBaseLineLayout
->mArena
, sz
);
647 pfd
= reinterpret_cast<PerFrameData
*>(mem
);
650 mBaseLineLayout
->mFrameFreeList
= pfd
->mNext
;
652 pfd
->mSpan
= nullptr;
653 pfd
->mNext
= nullptr;
654 pfd
->mPrev
= nullptr;
655 pfd
->mNextAnnotation
= nullptr;
656 pfd
->mFrame
= aFrame
;
658 // all flags default to false
659 pfd
->mRelativePos
= false;
660 pfd
->mIsTextFrame
= false;
661 pfd
->mIsNonEmptyTextFrame
= false;
662 pfd
->mIsNonWhitespaceTextFrame
= false;
663 pfd
->mIsLetterFrame
= false;
664 pfd
->mRecomputeOverflow
= false;
665 pfd
->mIsBullet
= false;
666 pfd
->mSkipWhenTrimmingWhitespace
= false;
667 pfd
->mIsEmpty
= false;
668 pfd
->mIsLinkedToBase
= false;
670 WritingMode frameWM
= aFrame
->GetWritingMode();
671 WritingMode lineWM
= mRootSpan
->mWritingMode
;
672 pfd
->mBounds
= LogicalRect(lineWM
);
673 pfd
->mMargin
= LogicalMargin(lineWM
);
674 pfd
->mBorderPadding
= LogicalMargin(lineWM
);
675 pfd
->mOffsets
= LogicalMargin(frameWM
);
677 pfd
->mJustificationInfo
= JustificationInfo();
678 pfd
->mJustificationAssignment
= JustificationAssignment();
681 pfd
->mBlockDirAlign
= 0xFF;
682 mBaseLineLayout
->mFramesAllocated
++;
688 nsLineLayout::LineIsBreakable() const
690 // XXX mTotalPlacedFrames should go away and we should just use
691 // mLineIsEmpty here instead
692 if ((0 != mTotalPlacedFrames
) || mImpactedByFloats
) {
698 // Checks all four sides for percentage units. This means it should
699 // only be used for things (margin, padding) where percentages on top
700 // and bottom depend on the *width* just like percentages on left and
703 HasPercentageUnitSide(const nsStyleSides
& aSides
)
705 NS_FOR_CSS_SIDES(side
) {
706 if (aSides
.Get(side
).HasPercent())
713 IsPercentageAware(const nsIFrame
* aFrame
)
715 NS_ASSERTION(aFrame
, "null frame is not allowed");
717 nsIAtom
*fType
= aFrame
->GetType();
718 if (fType
== nsGkAtoms::textFrame
) {
719 // None of these things can ever be true for text frames.
723 // Some of these things don't apply to non-replaced inline frames
724 // (that is, fType == nsGkAtoms::inlineFrame), but we won't bother making
725 // things unnecessarily complicated, since they'll probably be set
728 const nsStyleMargin
* margin
= aFrame
->StyleMargin();
729 if (HasPercentageUnitSide(margin
->mMargin
)) {
733 const nsStylePadding
* padding
= aFrame
->StylePadding();
734 if (HasPercentageUnitSide(padding
->mPadding
)) {
738 // Note that borders can't be aware of percentages
740 const nsStylePosition
* pos
= aFrame
->StylePosition();
742 if ((pos
->WidthDependsOnContainer() &&
743 pos
->mWidth
.GetUnit() != eStyleUnit_Auto
) ||
744 pos
->MaxWidthDependsOnContainer() ||
745 pos
->MinWidthDependsOnContainer() ||
746 pos
->OffsetHasPercent(NS_SIDE_RIGHT
) ||
747 pos
->OffsetHasPercent(NS_SIDE_LEFT
)) {
751 if (eStyleUnit_Auto
== pos
->mWidth
.GetUnit()) {
752 // We need to check for frames that shrink-wrap when they're auto
754 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
755 if (disp
->mDisplay
== NS_STYLE_DISPLAY_INLINE_BLOCK
||
756 disp
->mDisplay
== NS_STYLE_DISPLAY_INLINE_TABLE
||
757 fType
== nsGkAtoms::HTMLButtonControlFrame
||
758 fType
== nsGkAtoms::gfxButtonControlFrame
||
759 fType
== nsGkAtoms::fieldSetFrame
||
760 fType
== nsGkAtoms::comboboxDisplayFrame
) {
764 // Per CSS 2.1, section 10.3.2:
765 // If 'height' and 'width' both have computed values of 'auto' and
766 // the element has an intrinsic ratio but no intrinsic height or
767 // width and the containing block's width does not itself depend
768 // on the replaced element's width, then the used value of 'width'
769 // is calculated from the constraint equation used for
770 // block-level, non-replaced elements in normal flow.
771 nsIFrame
*f
= const_cast<nsIFrame
*>(aFrame
);
772 if (f
->GetIntrinsicRatio() != nsSize(0, 0) &&
773 // Some percents are treated like 'auto', so check != coord
774 pos
->mHeight
.GetUnit() != eStyleUnit_Coord
) {
775 const IntrinsicSize
&intrinsicSize
= f
->GetIntrinsicSize();
776 if (intrinsicSize
.width
.GetUnit() == eStyleUnit_None
&&
777 intrinsicSize
.height
.GetUnit() == eStyleUnit_None
) {
787 nsLineLayout::ReflowFrame(nsIFrame
* aFrame
,
788 nsReflowStatus
& aReflowStatus
,
789 nsHTMLReflowMetrics
* aMetrics
,
792 // Initialize OUT parameter
793 aPushedFrame
= false;
795 PerFrameData
* pfd
= NewPerFrameData(aFrame
);
796 PerSpanData
* psd
= mCurrentSpan
;
797 psd
->AppendFrame(pfd
);
799 #ifdef REALLY_NOISY_REFLOW
800 nsFrame::IndentBy(stdout
, mSpanDepth
);
801 printf("%p: Begin ReflowFrame pfd=%p ", psd
, pfd
);
802 nsFrame::ListTag(stdout
, aFrame
);
806 if (mCurrentSpan
== mRootSpan
) {
807 pfd
->mFrame
->Properties().Remove(nsIFrame::LineBaselineOffset());
811 pfd
->mFrame
->Properties().Get(nsIFrame::LineBaselineOffset(), &hasLineOffset
);
812 NS_ASSERTION(!hasLineOffset
, "LineBaselineOffset was set but was not expected");
816 mJustificationInfo
= JustificationInfo();
818 // Stash copies of some of the computed state away for later
819 // (block-direction alignment, for example)
820 WritingMode frameWM
= aFrame
->GetWritingMode();
821 WritingMode lineWM
= mRootSpan
->mWritingMode
;
823 // NOTE: While the inline direction coordinate remains relative to the
824 // parent span, the block direction coordinate is fixed at the top
825 // edge for the line. During VerticalAlignFrames we will repair this
826 // so that the block direction coordinate is properly set and relative
827 // to the appropriate span.
828 pfd
->mBounds
.IStart(lineWM
) = psd
->mICoord
;
829 pfd
->mBounds
.BStart(lineWM
) = mBStartEdge
;
831 // We want to guarantee that we always make progress when
832 // formatting. Therefore, if the object being placed on the line is
833 // too big for the line, but it is the only thing on the line and is not
834 // impacted by a float, then we go ahead and place it anyway. (If the line
835 // is impacted by one or more floats, then it is safe to break because
836 // we can move the line down below float(s).)
838 // Capture this state *before* we reflow the frame in case it clears
839 // the state out. We need to know how to treat the current frame
841 bool notSafeToBreak
= LineIsEmpty() && !mImpactedByFloats
;
843 // Figure out whether we're talking about a textframe here
844 nsIAtom
* frameType
= aFrame
->GetType();
845 bool isText
= frameType
== nsGkAtoms::textFrame
;
847 // Inline-ish and text-ish things don't compute their width;
848 // everything else does. We need to give them an available width that
849 // reflects the space left on the line.
850 NS_WARN_IF_FALSE(psd
->mIEnd
!= NS_UNCONSTRAINEDSIZE
,
851 "have unconstrained width; this should only result from "
852 "very large sizes, not attempts at intrinsic width "
854 nscoord availableSpaceOnLine
= psd
->mIEnd
- psd
->mICoord
;
856 // Setup reflow state for reflowing the frame
857 Maybe
<nsHTMLReflowState
> reflowStateHolder
;
859 // Compute the available size for the frame. This available width
860 // includes room for the side margins.
861 // For now, set the available block-size to unconstrained always.
862 LogicalSize availSize
= mBlockReflowState
->ComputedSize(frameWM
);
863 availSize
.BSize(frameWM
) = NS_UNCONSTRAINEDSIZE
;
864 reflowStateHolder
.emplace(mPresContext
, *psd
->mReflowState
,
866 nsHTMLReflowState
& reflowState
= *reflowStateHolder
;
867 reflowState
.mLineLayout
= this;
868 if (mRubyReflowState
) {
869 reflowState
.mRubyReflowState
= mRubyReflowState
;
870 mRubyReflowState
= nullptr;
872 reflowState
.mFlags
.mIsTopOfPage
= mIsTopOfPage
;
873 if (reflowState
.ComputedISize() == NS_UNCONSTRAINEDSIZE
) {
874 reflowState
.AvailableISize() = availableSpaceOnLine
;
876 WritingMode stateWM
= reflowState
.GetWritingMode();
878 reflowState
.ComputedLogicalMargin().ConvertTo(lineWM
, stateWM
);
879 pfd
->mBorderPadding
=
880 reflowState
.ComputedLogicalBorderPadding().ConvertTo(lineWM
, stateWM
);
882 reflowState
.mStyleDisplay
->IsRelativelyPositionedStyle();
883 if (pfd
->mRelativePos
) {
885 reflowState
.ComputedLogicalOffsets().ConvertTo(frameWM
, stateWM
);
888 // Calculate whether the the frame should have a start margin and
889 // subtract the margin from the available width if necessary.
890 // The margin will be applied to the starting inline coordinates of
891 // the frame in CanPlaceFrame() after reflowing the frame.
892 AllowForStartMargin(pfd
, reflowState
);
894 // if isText(), no need to propagate NS_FRAME_IS_DIRTY from the parent,
895 // because reflow doesn't look at the dirty bits on the frame being reflowed.
897 // See if this frame depends on the width of its containing block. If
898 // so, disable resize reflow optimizations for the line. (Note that,
899 // to be conservative, we do this if we *try* to fit a frame on a
900 // line, even if we don't succeed.) (Note also that we can only make
901 // this IsPercentageAware check *after* we've constructed our
902 // nsHTMLReflowState, because that construction may be what forces aFrame
903 // to lazily initialize its (possibly-percent-valued) intrinsic size.)
904 if (mGotLineBox
&& IsPercentageAware(aFrame
)) {
905 mLineBox
->DisableResizeReflowOptimization();
908 // Let frame know that are reflowing it. Note that we don't bother
909 // positioning the frame yet, because we're probably going to end up
910 // moving it when we do the block-direction alignment
911 aFrame
->WillReflow(mPresContext
);
913 // Adjust spacemanager coordinate system for the frame.
914 nsHTMLReflowMetrics
metrics(lineWM
);
916 metrics
.ISize(lineWM
) = nscoord(0xdeadbeef);
917 metrics
.BSize(lineWM
) = nscoord(0xdeadbeef);
919 nsRect physicalBounds
= pfd
->mBounds
.GetPhysicalRect(lineWM
, mContainerWidth
);
920 nscoord tx
= physicalBounds
.x
;
921 nscoord ty
= physicalBounds
.y
;
922 mFloatManager
->Translate(tx
, ty
);
924 int32_t savedOptionalBreakOffset
;
925 gfxBreakPriority savedOptionalBreakPriority
;
926 nsIFrame
* savedOptionalBreakFrame
=
927 GetLastOptionalBreakPosition(&savedOptionalBreakOffset
,
928 &savedOptionalBreakPriority
);
931 aFrame
->Reflow(mPresContext
, metrics
, *reflowStateHolder
, aReflowStatus
);
933 static_cast<nsTextFrame
*>(aFrame
)->
934 ReflowText(*this, availableSpaceOnLine
, psd
->mReflowState
->rendContext
,
935 metrics
, aReflowStatus
);
938 pfd
->mJustificationInfo
= mJustificationInfo
;
939 mJustificationInfo
= JustificationInfo();
941 // See if the frame is a placeholderFrame and if it is process
942 // the float. At the same time, check if the frame has any non-collapsed-away
944 bool placedFloat
= false;
947 isEmpty
= pfd
->mFrame
->IsEmpty();
949 if (nsGkAtoms::placeholderFrame
== frameType
) {
951 pfd
->mSkipWhenTrimmingWhitespace
= true;
952 nsIFrame
* outOfFlowFrame
= nsLayoutUtils::GetFloatFromPlaceholder(aFrame
);
953 if (outOfFlowFrame
) {
954 // Add mTrimmableISize to the available width since if the line ends
955 // here, the width of the inline content will be reduced by
957 nscoord availableISize
= psd
->mIEnd
- (psd
->mICoord
- mTrimmableISize
);
959 // If we place floats after inline content where there's
960 // no break opportunity, we don't know how much additional
961 // width is required for the non-breaking content after the float,
962 // so we can't know whether the float plus that content will fit
963 // on the line. So for now, don't place floats after inline
964 // content where there's no break opportunity. This is incorrect
965 // but hopefully rare. Fixing it will require significant
966 // restructuring of line layout.
967 // We might as well allow zero-width floats to be placed, though.
970 placedFloat
= mBaseLineLayout
->AddFloat(outOfFlowFrame
, availableISize
);
971 NS_ASSERTION(!(outOfFlowFrame
->GetType() == nsGkAtoms::letterFrame
&&
972 GetFirstLetterStyleOK()),
973 "FirstLetterStyle set on line with floating first letter");
977 // Note non-empty text-frames for inline frame compatibility hackery
978 pfd
->mIsTextFrame
= true;
979 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(pfd
->mFrame
);
980 isEmpty
= !textFrame
->HasNoncollapsedCharacters();
982 pfd
->mIsNonEmptyTextFrame
= true;
983 nsIContent
* content
= textFrame
->GetContent();
985 const nsTextFragment
* frag
= content
->GetText();
987 pfd
->mIsNonWhitespaceTextFrame
= !content
->TextIsOnlyWhitespace();
991 else if (nsGkAtoms::brFrame
== frameType
) {
992 pfd
->mSkipWhenTrimmingWhitespace
= true;
995 if (nsGkAtoms::letterFrame
==frameType
) {
996 pfd
->mIsLetterFrame
= true;
997 } else if (nsGkAtoms::rubyFrame
== frameType
) {
998 SyncAnnotationContainersBounds(pfd
);
1001 isEmpty
= !pfd
->mSpan
->mHasNonemptyContent
&& pfd
->mFrame
->IsSelfEmpty();
1003 isEmpty
= pfd
->mFrame
->IsEmpty();
1007 pfd
->mIsEmpty
= isEmpty
;
1009 mFloatManager
->Translate(-tx
, -ty
);
1011 NS_ASSERTION(metrics
.ISize(lineWM
) >= 0, "bad inline size");
1012 NS_ASSERTION(metrics
.BSize(lineWM
) >= 0,"bad block size");
1013 if (metrics
.ISize(lineWM
) < 0) {
1014 metrics
.ISize(lineWM
) = 0;
1016 if (metrics
.BSize(lineWM
) < 0) {
1017 metrics
.BSize(lineWM
) = 0;
1021 // Note: break-before means ignore the reflow metrics since the
1022 // frame will be reflowed another time.
1023 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus
)) {
1024 if (CRAZY_SIZE(metrics
.ISize(lineWM
)) ||
1025 CRAZY_SIZE(metrics
.BSize(lineWM
))) {
1026 printf("nsLineLayout: ");
1027 nsFrame::ListTag(stdout
, aFrame
);
1028 printf(" metrics=%d,%d!\n", metrics
.Width(), metrics
.Height());
1030 if ((metrics
.Width() == nscoord(0xdeadbeef)) ||
1031 (metrics
.Height() == nscoord(0xdeadbeef))) {
1032 printf("nsLineLayout: ");
1033 nsFrame::ListTag(stdout
, aFrame
);
1034 printf(" didn't set w/h %d,%d!\n", metrics
.Width(), metrics
.Height());
1039 // Unlike with non-inline reflow, the overflow area here does *not*
1040 // include the accumulation of the frame's bounds and its inline
1041 // descendants' bounds. Nor does it include the outline area; it's
1042 // just the union of the bounds of any absolute children. That is
1043 // added in later by nsLineLayout::ReflowInlineFrames.
1044 pfd
->mOverflowAreas
= metrics
.mOverflowAreas
;
1046 pfd
->mBounds
.ISize(lineWM
) = metrics
.ISize(lineWM
);
1047 pfd
->mBounds
.BSize(lineWM
) = metrics
.BSize(lineWM
);
1049 // Size the frame, but |RelativePositionFrames| will size the view.
1050 aFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerWidthForSpan(psd
));
1052 // Tell the frame that we're done reflowing it
1053 aFrame
->DidReflow(mPresContext
,
1054 isText
? nullptr : reflowStateHolder
.ptr(),
1055 nsDidReflowStatus::FINISHED
);
1058 *aMetrics
= metrics
;
1061 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus
)) {
1062 // If frame is complete and has a next-in-flow, we need to delete
1063 // them now. Do not do this when a break-before is signaled because
1064 // the frame is going to get reflowed again (and may end up wanting
1065 // a next-in-flow where it ends up).
1066 if (NS_FRAME_IS_COMPLETE(aReflowStatus
)) {
1067 nsIFrame
* kidNextInFlow
= aFrame
->GetNextInFlow();
1068 if (nullptr != kidNextInFlow
) {
1069 // Remove all of the childs next-in-flows. Make sure that we ask
1070 // the right parent to do the removal (it's possible that the
1071 // parent is not this because we are executing pullup code)
1072 kidNextInFlow
->GetParent()->
1073 DeleteNextInFlowChild(kidNextInFlow
, true);
1077 // Check whether this frame breaks up text runs. All frames break up text
1078 // runs (hence return false here) except for text frames and inline containers.
1079 bool continuingTextRun
= aFrame
->CanContinueTextRun();
1081 // Clear any residual mTrimmableISize if this isn't a text frame
1082 if (!continuingTextRun
&& !pfd
->mSkipWhenTrimmingWhitespace
) {
1083 mTrimmableISize
= 0;
1086 // See if we can place the frame. If we can't fit it, then we
1088 bool optionalBreakAfterFits
;
1089 NS_ASSERTION(isText
||
1090 !reflowStateHolder
->IsFloating(),
1091 "How'd we get a floated inline frame? "
1092 "The frame ctor should've dealt with this.");
1093 if (CanPlaceFrame(pfd
, notSafeToBreak
, continuingTextRun
,
1094 savedOptionalBreakFrame
!= nullptr, metrics
,
1095 aReflowStatus
, &optionalBreakAfterFits
)) {
1097 psd
->mHasNonemptyContent
= true;
1098 mLineIsEmpty
= false;
1100 // nonempty leaf content has been placed
1101 mLineAtStart
= false;
1103 if (nsGkAtoms::rubyFrame
== frameType
) {
1108 // Place the frame, updating aBounds with the final size and
1109 // location. Then apply the bottom+right margins (as
1110 // appropriate) to the frame.
1111 PlaceFrame(pfd
, metrics
);
1112 PerSpanData
* span
= pfd
->mSpan
;
1114 // The frame we just finished reflowing is an inline
1115 // container. It needs its child frames aligned in the block direction,
1116 // so do most of it now.
1117 VerticalAlignFrames(span
);
1120 if (!continuingTextRun
) {
1121 if (!psd
->mNoWrap
&& (!LineIsEmpty() || placedFloat
)) {
1122 // record soft break opportunity after this content that can't be
1123 // part of a text run. This is not a text frame so we know
1124 // that offset INT32_MAX means "after the content".
1125 if (NotifyOptionalBreakPosition(aFrame
, INT32_MAX
,
1126 optionalBreakAfterFits
,
1127 gfxBreakPriority::eNormalBreak
)) {
1128 // If this returns true then we are being told to actually break here.
1129 aReflowStatus
= NS_INLINE_LINE_BREAK_AFTER(aReflowStatus
);
1136 aPushedFrame
= true;
1137 // Undo any saved break positions that the frame might have told us about,
1138 // since we didn't end up placing it
1139 RestoreSavedBreakPosition(savedOptionalBreakFrame
,
1140 savedOptionalBreakOffset
,
1141 savedOptionalBreakPriority
);
1148 #ifdef REALLY_NOISY_REFLOW
1149 nsFrame::IndentBy(stdout
, mSpanDepth
);
1150 printf("End ReflowFrame ");
1151 nsFrame::ListTag(stdout
, aFrame
);
1152 printf(" status=%x\n", aReflowStatus
);
1157 nsLineLayout::AllowForStartMargin(PerFrameData
* pfd
,
1158 nsHTMLReflowState
& aReflowState
)
1160 NS_ASSERTION(!aReflowState
.IsFloating(),
1161 "How'd we get a floated inline frame? "
1162 "The frame ctor should've dealt with this.");
1164 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1166 // Only apply start-margin on the first-in flow for inline frames,
1167 // and make sure to not apply it to any inline other than the first
1168 // in an ib split. Note that the ib sibling (block-in-inline
1169 // sibling) annotations only live on the first continuation, but we
1170 // don't want to apply the start margin for later continuations
1171 // anyway. For box-decoration-break:clone we apply the start-margin
1172 // on all continuations.
1173 if ((pfd
->mFrame
->GetPrevContinuation() ||
1174 pfd
->mFrame
->FrameIsNonFirstInIBSplit()) &&
1175 aReflowState
.mStyleBorder
->mBoxDecorationBreak
==
1176 NS_STYLE_BOX_DECORATION_BREAK_SLICE
) {
1177 // Zero this out so that when we compute the max-element-width of
1178 // the frame we will properly avoid adding in the starting margin.
1179 pfd
->mMargin
.IStart(lineWM
) = 0;
1181 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE
!= aReflowState
.AvailableISize(),
1182 "have unconstrained inline-size; this should only result "
1183 "from very large sizes, not attempts at intrinsic "
1184 "inline-size calculation");
1185 if (NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedISize()) {
1186 // For inline-ish and text-ish things (which don't compute widths
1187 // in the reflow state), adjust available inline-size to account for the
1188 // start margin. The end margin will be accounted for when we
1189 // finish flowing the frame.
1190 WritingMode wm
= aReflowState
.GetWritingMode();
1191 aReflowState
.AvailableISize() -=
1192 pfd
->mMargin
.ConvertTo(wm
, lineWM
).IStart(wm
);
1198 nsLineLayout::GetCurrentFrameInlineDistanceFromBlock()
1202 for (psd
= mCurrentSpan
; psd
; psd
= psd
->mParent
) {
1209 * This method syncs all ruby annotation containers' bounds in their
1210 * PerFrameData from their rect. It is necessary to do so because the
1211 * containers are not part of line in their levels, which means their
1212 * bounds are not set properly before.
1215 nsLineLayout::SyncAnnotationContainersBounds(PerFrameData
* aRubyFrame
)
1217 MOZ_ASSERT(aRubyFrame
->mFrame
->GetType() == nsGkAtoms::rubyFrame
);
1218 MOZ_ASSERT(aRubyFrame
->mSpan
);
1220 PerSpanData
* span
= aRubyFrame
->mSpan
;
1221 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1222 nscoord containerWidth
= ContainerWidthForSpan(span
);
1223 for (PerFrameData
* pfd
= span
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1224 for (PerFrameData
* annotation
= pfd
->mNextAnnotation
;
1225 annotation
; annotation
= annotation
->mNextAnnotation
) {
1226 LogicalRect
bounds(lineWM
, annotation
->mFrame
->GetRect(), containerWidth
);
1227 annotation
->mBounds
= bounds
;
1233 * See if the frame can be placed now that we know it's desired size.
1234 * We can always place the frame if the line is empty. Note that we
1235 * know that the reflow-status is not a break-before because if it was
1236 * ReflowFrame above would have returned false, preventing this method
1237 * from being called. The logic in this method assumes that.
1239 * Note that there is no check against the Y coordinate because we
1240 * assume that the caller will take care of that.
1243 nsLineLayout::CanPlaceFrame(PerFrameData
* pfd
,
1244 bool aNotSafeToBreak
,
1245 bool aFrameCanContinueTextRun
,
1246 bool aCanRollBackBeforeFrame
,
1247 nsHTMLReflowMetrics
& aMetrics
,
1248 nsReflowStatus
& aStatus
,
1249 bool* aOptionalBreakAfterFits
)
1251 NS_PRECONDITION(pfd
&& pfd
->mFrame
, "bad args, null pointers for frame data");
1253 *aOptionalBreakAfterFits
= true;
1255 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1257 * We want to only apply the end margin if we're the last continuation and
1258 * either not in an {ib} split or the last inline in it. In all other
1259 * cases we want to zero it out. That means zeroing it out if any of these
1261 * 1) The frame is not complete (in this case it will get a next-in-flow)
1262 * 2) The frame is complete but has a non-fluid continuation on its
1263 * continuation chain. Note that if it has a fluid continuation, that
1264 * continuation will get destroyed later, so we don't want to drop the
1265 * end-margin in that case.
1266 * 3) The frame is in an {ib} split and is not the last part.
1268 * However, none of that applies if this is a letter frame (XXXbz why?)
1270 * For box-decoration-break:clone we apply the end margin on all
1271 * continuations (that are not letter frames).
1273 if ((NS_FRAME_IS_NOT_COMPLETE(aStatus
) ||
1274 pfd
->mFrame
->LastInFlow()->GetNextContinuation() ||
1275 pfd
->mFrame
->FrameIsNonLastInIBSplit()) &&
1276 !pfd
->mIsLetterFrame
&&
1277 pfd
->mFrame
->StyleBorder()->mBoxDecorationBreak
==
1278 NS_STYLE_BOX_DECORATION_BREAK_SLICE
) {
1279 pfd
->mMargin
.IEnd(lineWM
) = 0;
1282 // Apply the start margin to the frame bounds.
1283 nscoord startMargin
= pfd
->mMargin
.IStart(lineWM
);
1284 nscoord endMargin
= pfd
->mMargin
.IEnd(lineWM
);
1286 pfd
->mBounds
.IStart(lineWM
) += startMargin
;
1288 PerSpanData
* psd
= mCurrentSpan
;
1290 // When wrapping is off, everything fits.
1294 #ifdef NOISY_CAN_PLACE_FRAME
1295 if (nullptr != psd
->mFrame
) {
1296 nsFrame::ListTag(stdout
, psd
->mFrame
->mFrame
);
1298 printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak
? "true" : "false");
1299 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1300 printf(" frameWidth=%d, margins=%d,%d\n",
1301 pfd
->mBounds
.IEnd(lineWM
) + endMargin
- psd
->mICoord
,
1302 startMargin
, endMargin
);
1305 // Set outside to true if the result of the reflow leads to the
1306 // frame sticking outside of our available area.
1307 bool outside
= pfd
->mBounds
.IEnd(lineWM
) - mTrimmableISize
+ endMargin
>
1310 // If it fits, it fits
1311 #ifdef NOISY_CAN_PLACE_FRAME
1312 printf(" ==> inside\n");
1316 *aOptionalBreakAfterFits
= false;
1318 // When it doesn't fit, check for a few special conditions where we
1319 // allow it to fit anyway.
1320 if (0 == startMargin
+ pfd
->mBounds
.ISize(lineWM
) + endMargin
) {
1321 // Empty frames always fit right where they are
1322 #ifdef NOISY_CAN_PLACE_FRAME
1323 printf(" ==> empty frame fits\n");
1328 #ifdef FIX_BUG_50257
1329 // another special case: always place a BR
1330 if (nsGkAtoms::brFrame
== pfd
->mFrame
->GetType()) {
1331 #ifdef NOISY_CAN_PLACE_FRAME
1332 printf(" ==> BR frame fits\n");
1338 if (aNotSafeToBreak
) {
1339 // There are no frames on the line that take up width and the line is
1340 // not impacted by floats, so we must allow the current frame to be
1341 // placed on the line
1342 #ifdef NOISY_CAN_PLACE_FRAME
1343 printf(" ==> not-safe and not-impacted fits: ");
1344 while (nullptr != psd
) {
1345 printf("<psd=%p x=%d left=%d> ", psd
, psd
->mICoord
, psd
->mIStart
);
1353 // Special check for span frames
1354 if (pfd
->mSpan
&& pfd
->mSpan
->mContainsFloat
) {
1355 // If the span either directly or indirectly contains a float then
1356 // it fits. Why? It's kind of complicated, but here goes:
1358 // 1. CanPlaceFrame is used for all frame placements on a line,
1359 // and in a span. This includes recursively placement of frames
1360 // inside of spans, and the span itself. Because the logic always
1361 // checks for room before proceeding (the code above here), the
1362 // only things on a line will be those things that "fit".
1364 // 2. Before a float is placed on a line, the line has to be empty
1365 // (otherwise it's a "below current line" float and will be placed
1368 // Therefore, if the span directly or indirectly has a float
1369 // then it means that at the time of the placement of the float
1370 // the line was empty. Because of #1, only the frames that fit can
1371 // be added after that point, therefore we can assume that the
1372 // current span being placed has fit.
1374 // So how do we get here and have a span that should already fit
1375 // and yet doesn't: Simple: span's that have the no-wrap attribute
1376 // set on them and contain a float and are placed where they
1377 // don't naturally fit.
1381 if (aFrameCanContinueTextRun
) {
1382 // Let it fit, but we reserve the right to roll back.
1383 // Note that we usually won't get here because a text frame will break
1384 // itself to avoid exceeding the available width.
1385 // We'll only get here for text frames that couldn't break early enough.
1386 #ifdef NOISY_CAN_PLACE_FRAME
1387 printf(" ==> placing overflowing textrun, requesting backup\n");
1390 // We will want to try backup.
1395 #ifdef NOISY_CAN_PLACE_FRAME
1396 printf(" ==> didn't fit\n");
1398 aStatus
= NS_INLINE_LINE_BREAK_BEFORE();
1403 * Place the frame. Update running counters.
1406 nsLineLayout::PlaceFrame(PerFrameData
* pfd
, nsHTMLReflowMetrics
& aMetrics
)
1408 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1410 // Record ascent and update max-ascent and max-descent values
1411 if (aMetrics
.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE
) {
1412 pfd
->mAscent
= pfd
->mFrame
->GetLogicalBaseline(lineWM
);
1414 pfd
->mAscent
= aMetrics
.BlockStartAscent();
1417 // Advance to next inline coordinate
1418 mCurrentSpan
->mICoord
= pfd
->mBounds
.IEnd(lineWM
) +
1419 pfd
->mMargin
.IEnd(lineWM
);
1421 // Count the number of non-placeholder frames on the line...
1422 if (pfd
->mFrame
->GetType() == nsGkAtoms::placeholderFrame
) {
1423 NS_ASSERTION(pfd
->mBounds
.ISize(lineWM
) == 0 &&
1424 pfd
->mBounds
.BSize(lineWM
) == 0,
1425 "placeholders should have 0 width/height (checking "
1426 "placeholders were never counted by the old code in "
1429 mTotalPlacedFrames
++;
1434 nsLineLayout::AddBulletFrame(nsIFrame
* aFrame
,
1435 const nsHTMLReflowMetrics
& aMetrics
)
1437 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
1438 NS_ASSERTION(mGotLineBox
, "must have line box");
1440 nsIFrame
*blockFrame
= mBlockReflowState
->frame
;
1441 NS_ASSERTION(blockFrame
->IsFrameOfType(nsIFrame::eBlockFrame
),
1442 "must be for block");
1443 if (!static_cast<nsBlockFrame
*>(blockFrame
)->BulletIsEmpty()) {
1445 mLineBox
->SetHasBullet();
1448 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1449 PerFrameData
* pfd
= NewPerFrameData(aFrame
);
1450 mRootSpan
->AppendFrame(pfd
);
1451 pfd
->mIsBullet
= true;
1452 if (aMetrics
.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE
) {
1453 pfd
->mAscent
= aFrame
->GetLogicalBaseline(lineWM
);
1455 pfd
->mAscent
= aMetrics
.BlockStartAscent();
1458 // Note: block-coord value will be updated during block-direction alignment
1459 pfd
->mBounds
= LogicalRect(lineWM
, aFrame
->GetRect(), mContainerWidth
);
1460 pfd
->mOverflowAreas
= aMetrics
.mOverflowAreas
;
1465 nsLineLayout::DumpPerSpanData(PerSpanData
* psd
, int32_t aIndent
)
1467 nsFrame::IndentBy(stdout
, aIndent
);
1468 printf("%p: left=%d x=%d right=%d\n", static_cast<void*>(psd
),
1469 psd
->mIStart
, psd
->mICoord
, psd
->mIEnd
);
1470 PerFrameData
* pfd
= psd
->mFirstFrame
;
1471 while (nullptr != pfd
) {
1472 nsFrame::IndentBy(stdout
, aIndent
+1);
1473 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1474 nsRect rect
= pfd
->mBounds
.GetPhysicalRect(psd
->mWritingMode
,
1476 printf(" %d,%d,%d,%d\n", rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1478 DumpPerSpanData(pfd
->mSpan
, aIndent
+ 1);
1485 #define VALIGN_OTHER 0
1486 #define VALIGN_TOP 1
1487 #define VALIGN_BOTTOM 2
1490 nsLineLayout::VerticalAlignLine()
1492 // Partially place the children of the block frame. The baseline for
1493 // this operation is set to zero so that the y coordinates for all
1494 // of the placed children will be relative to there.
1495 PerSpanData
* psd
= mRootSpan
;
1496 VerticalAlignFrames(psd
);
1498 // *** Note that comments here still use the anachronistic term
1499 // "line-height" when we really mean "size of the line in the block
1500 // direction", "vertical-align" when we really mean "alignment in
1501 // the block direction", and "top" and "bottom" when we really mean
1502 // "block start" and "block end". This is partly for brevity and
1503 // partly to retain the association with the CSS line-height and
1504 // vertical-align properties.
1506 // Compute the line-height. The line-height will be the larger of:
1508 // [1] maxBCoord - minBCoord (the distance between the first child's
1509 // block-start edge and the last child's block-end edge)
1511 // [2] the maximum logical box block size (since not every frame may have
1512 // participated in #1; for example: "top" and "botttom" aligned frames)
1514 // [3] the minimum line height ("line-height" property set on the
1516 nscoord lineBSize
= psd
->mMaxBCoord
- psd
->mMinBCoord
;
1518 // Now that the line-height is computed, we need to know where the
1519 // baseline is in the line. Position baseline so that mMinBCoord is just
1520 // inside the start of the line box.
1521 nscoord baselineBCoord
;
1522 if (psd
->mMinBCoord
< 0) {
1523 baselineBCoord
= mBStartEdge
- psd
->mMinBCoord
;
1526 baselineBCoord
= mBStartEdge
;
1529 // It's also possible that the line block-size isn't tall enough because
1530 // of "top" and "bottom" aligned elements that were not accounted for in
1533 // The CSS2 spec doesn't really say what happens when to the
1534 // baseline in this situations. What we do is if the largest start
1535 // aligned box block size is greater than the line block-size then we leave
1536 // the baseline alone. If the largest end aligned box is greater
1537 // than the line block-size then we slide the baseline forward by the extra
1540 // Navigator 4 gives precedence to the first top/bottom aligned
1541 // object. We just let block end aligned objects win.
1542 if (lineBSize
< mMaxEndBoxBSize
) {
1543 // When the line is shorter than the maximum block start aligned box
1544 nscoord extra
= mMaxEndBoxBSize
- lineBSize
;
1545 baselineBCoord
+= extra
;
1546 lineBSize
= mMaxEndBoxBSize
;
1548 if (lineBSize
< mMaxStartBoxBSize
) {
1549 lineBSize
= mMaxStartBoxBSize
;
1551 #ifdef NOISY_BLOCKDIR_ALIGN
1552 printf(" [line]==> lineBSize=%d baselineBCoord=%d\n", lineBSize
, baselineBCoord
);
1555 // Now position all of the frames in the root span. We will also
1556 // recurse over the child spans and place any frames we find with
1557 // vertical-align: top or bottom.
1558 // XXX PERFORMANCE: set a bit per-span to avoid the extra work
1559 // (propagate it upward too)
1560 WritingMode lineWM
= psd
->mWritingMode
;
1561 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1562 if (pfd
->mBlockDirAlign
== VALIGN_OTHER
) {
1563 pfd
->mBounds
.BStart(lineWM
) += baselineBCoord
;
1564 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
1567 PlaceTopBottomFrames(psd
, -mBStartEdge
, lineBSize
);
1569 mFinalLineBSize
= lineBSize
;
1571 // Fill in returned line-box and max-element-width data
1572 mLineBox
->SetBounds(lineWM
,
1573 psd
->mIStart
, mBStartEdge
,
1574 psd
->mICoord
- psd
->mIStart
, lineBSize
,
1577 mLineBox
->SetLogicalAscent(baselineBCoord
- mBStartEdge
);
1578 #ifdef NOISY_BLOCKDIR_ALIGN
1580 " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
1581 mLineBox
->GetBounds().IStart(lineWM
), mLineBox
->GetBounds().BStart(lineWM
),
1582 mLineBox
->GetBounds().ISize(lineWM
), mLineBox
->GetBounds().BSize(lineWM
),
1583 mFinalLineBSize
, mLineBox
->GetLogicalAscent());
1588 // Place frames with CSS property vertical-align: top or bottom.
1590 nsLineLayout::PlaceTopBottomFrames(PerSpanData
* psd
,
1591 nscoord aDistanceFromStart
,
1594 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1595 PerSpanData
* span
= pfd
->mSpan
;
1597 NS_ASSERTION(0xFF != pfd
->mBlockDirAlign
, "umr");
1599 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1600 nscoord containerWidth
= ContainerWidthForSpan(psd
);
1601 switch (pfd
->mBlockDirAlign
) {
1604 pfd
->mBounds
.BStart(lineWM
) = -aDistanceFromStart
- span
->mMinBCoord
;
1607 pfd
->mBounds
.BStart(lineWM
) =
1608 -aDistanceFromStart
+ pfd
->mMargin
.BStart(lineWM
);
1610 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, containerWidth
);
1611 #ifdef NOISY_BLOCKDIR_ALIGN
1613 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1614 printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n",
1615 pfd
->mBounds
.BStart(lineWM
), aDistanceFromStart
,
1616 span
? pfd
->mBorderPadding
.BStart(lineWM
) : 0,
1617 span
? span
->mBStartLeading
: 0);
1622 // Compute bottom leading
1623 pfd
->mBounds
.BStart(lineWM
) =
1624 -aDistanceFromStart
+ aLineBSize
- span
->mMaxBCoord
;
1627 pfd
->mBounds
.BStart(lineWM
) = -aDistanceFromStart
+ aLineBSize
-
1628 pfd
->mMargin
.BEnd(lineWM
) - pfd
->mBounds
.BSize(lineWM
);
1630 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, containerWidth
);
1631 #ifdef NOISY_BLOCKDIR_ALIGN
1633 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1634 printf(": y=%d\n", pfd
->mBounds
.BStart(lineWM
));
1639 nscoord fromStart
= aDistanceFromStart
+ pfd
->mBounds
.BStart(lineWM
);
1640 PlaceTopBottomFrames(span
, fromStart
, aLineBSize
);
1646 GetInflationForBlockDirAlignment(nsIFrame
* aFrame
,
1647 nscoord aInflationMinFontSize
)
1649 if (aFrame
->IsSVGText()) {
1650 const nsIFrame
* container
=
1651 nsLayoutUtils::GetClosestFrameOfType(aFrame
, nsGkAtoms::svgTextFrame
);
1652 NS_ASSERTION(container
, "expected to find an ancestor SVGTextFrame");
1654 static_cast<const SVGTextFrame
*>(container
)->GetFontSizeScaleFactor();
1656 return nsLayoutUtils::FontSizeInflationInner(aFrame
, aInflationMinFontSize
);
1659 #define BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM nscoord_MAX
1660 #define BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM nscoord_MIN
1662 // Place frames in the block direction within a given span (CSS property
1663 // vertical-align) Note: this doesn't place frames with vertical-align:
1664 // top or bottom as those have to wait until the entire line box block
1665 // size is known. This is called after the span frame has finished being
1666 // reflowed so that we know its block size.
1668 nsLineLayout::VerticalAlignFrames(PerSpanData
* psd
)
1670 // Get parent frame info
1671 PerFrameData
* spanFramePFD
= psd
->mFrame
;
1672 nsIFrame
* spanFrame
= spanFramePFD
->mFrame
;
1674 // Get the parent frame's font for all of the frames in this span
1675 nsRefPtr
<nsFontMetrics
> fm
;
1677 GetInflationForBlockDirAlignment(spanFrame
, mInflationMinFontSize
);
1678 nsLayoutUtils::GetFontMetricsForFrame(spanFrame
, getter_AddRefs(fm
),
1681 bool preMode
= mStyleText
->WhiteSpaceIsSignificant();
1683 // See if the span is an empty continuation. It's an empty continuation iff:
1684 // - it has a prev-in-flow
1685 // - it has no next in flow
1686 // - it's zero sized
1687 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1688 bool emptyContinuation
= psd
!= mRootSpan
&&
1689 spanFrame
->GetPrevInFlow() && !spanFrame
->GetNextInFlow() &&
1690 spanFramePFD
->mBounds
.IsZeroSize();
1692 #ifdef NOISY_BLOCKDIR_ALIGN
1693 printf("[%sSpan]", (psd
== mRootSpan
)?"Root":"");
1694 nsFrame::ListTag(stdout
, spanFrame
);
1695 printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s",
1696 preMode
? "yes" : "no",
1697 mPresContext
->CompatibilityMode() != eCompatibility_NavQuirks
? "yes" : "no",
1698 spanFramePFD
->mBounds
.ISize(lineWM
),
1699 spanFramePFD
->mBounds
.BSize(lineWM
),
1700 emptyContinuation
? "yes" : "no");
1701 if (psd
!= mRootSpan
) {
1702 WritingMode frameWM
= spanFramePFD
->mFrame
->GetWritingMode();
1703 printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
1704 spanFramePFD
->mBorderPadding
.Top(lineWM
),
1705 spanFramePFD
->mBorderPadding
.Right(lineWM
),
1706 spanFramePFD
->mBorderPadding
.Bottom(lineWM
),
1707 spanFramePFD
->mBorderPadding
.Left(lineWM
),
1708 spanFramePFD
->mMargin
.Top(lineWM
),
1709 spanFramePFD
->mMargin
.Right(lineWM
),
1710 spanFramePFD
->mMargin
.Bottom(lineWM
),
1711 spanFramePFD
->mMargin
.Left(lineWM
));
1716 // Compute the span's mZeroEffectiveSpanBox flag. What we are trying
1717 // to determine is how we should treat the span: should it act
1718 // "normally" according to css2 or should it effectively
1721 // In general, if the document being processed is in full standards
1722 // mode then it should act normally (with one exception). The
1723 // exception case is when a span is continued and yet the span is
1724 // empty (e.g. compressed whitespace). For this kind of span we treat
1725 // it as if it were not there so that it doesn't impact the
1728 // In almost standards mode or quirks mode, we should sometimes make
1729 // it disappear. The cases that matter are those where the span
1730 // contains no real text elements that would provide an ascent and
1731 // descent and height. However, if css style elements have been
1732 // applied to the span (border/padding/margin) so that it's clear the
1733 // document author is intending css2 behavior then we act as if strict
1736 // This code works correctly for preMode, because a blank line
1737 // in PRE mode is encoded as a text node with a LF in it, since
1738 // text nodes with only whitespace are considered in preMode.
1740 // Much of this logic is shared with the various implementations of
1741 // nsIFrame::IsEmpty since they need to duplicate the way it makes
1742 // some lines empty. However, nsIFrame::IsEmpty can't be reused here
1743 // since this code sets zeroEffectiveSpanBox even when there are
1744 // non-empty children.
1745 bool zeroEffectiveSpanBox
= false;
1746 // XXXldb If we really have empty continuations, then all these other
1747 // checks don't make sense for them.
1748 // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that
1749 // it agrees with this code. (If it doesn't agree, it probably should.)
1750 if ((emptyContinuation
||
1751 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
) &&
1752 ((psd
== mRootSpan
) ||
1753 (spanFramePFD
->mBorderPadding
.IsAllZero() &&
1754 spanFramePFD
->mMargin
.IsAllZero()))) {
1755 // This code handles an issue with compatibility with non-css
1756 // conformant browsers. In particular, there are some cases
1757 // where the font-size and line-height for a span must be
1758 // ignored and instead the span must *act* as if it were zero
1759 // sized. In general, if the span contains any non-compressed
1760 // text then we don't use this logic.
1761 // However, this is not propagated outwards, since (in compatibility
1762 // mode) we don't want big line heights for things like
1763 // <p><font size="-1">Text</font></p>
1765 // We shouldn't include any whitespace that collapses, unless we're
1766 // preformatted (in which case it shouldn't, but the width=0 test is
1767 // perhaps incorrect). This includes whitespace at the beginning of
1768 // a line and whitespace preceded (?) by other whitespace.
1769 // See bug 134580 and bug 155333.
1770 zeroEffectiveSpanBox
= true;
1771 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1772 if (pfd
->mIsTextFrame
&&
1773 (pfd
->mIsNonWhitespaceTextFrame
|| preMode
||
1774 pfd
->mBounds
.ISize(mRootSpan
->mWritingMode
) != 0)) {
1775 zeroEffectiveSpanBox
= false;
1780 psd
->mZeroEffectiveSpanBox
= zeroEffectiveSpanBox
;
1782 // Setup baselineBCoord, minBCoord, and maxBCoord
1783 nscoord baselineBCoord
, minBCoord
, maxBCoord
;
1784 if (psd
== mRootSpan
) {
1785 // Use a zero baselineBCoord since we don't yet know where the baseline
1786 // will be (until we know how tall the line is; then we will
1787 // know). In addition, use extreme values for the minBCoord and maxBCoord
1788 // values so that only the child frames will impact their values
1789 // (since these are children of the block, there is no span box to
1790 // provide initial values).
1792 minBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
1793 maxBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
1794 #ifdef NOISY_BLOCKDIR_ALIGN
1795 printf("[RootSpan]");
1796 nsFrame::ListTag(stdout
, spanFrame
);
1797 printf(": pass1 valign frames: topEdge=%d minLineBSize=%d zeroEffectiveSpanBox=%s\n",
1798 mBStartEdge
, mMinLineBSize
,
1799 zeroEffectiveSpanBox
? "yes" : "no");
1803 // Compute the logical block size for this span. The logical block size
1804 // is based on the "line-height" value, not the font-size. Also
1805 // compute the top leading.
1807 GetInflationForBlockDirAlignment(spanFrame
, mInflationMinFontSize
);
1808 nscoord logicalBSize
= nsHTMLReflowState::
1809 CalcLineHeight(spanFrame
->GetContent(), spanFrame
->StyleContext(),
1810 mBlockReflowState
->ComputedHeight(),
1812 nscoord contentBSize
= spanFramePFD
->mBounds
.BSize(lineWM
) -
1813 spanFramePFD
->mBorderPadding
.BStartEnd(lineWM
);
1815 // Special-case for a ::first-letter frame, set the line height to
1816 // the frame block size if the user has left line-height == normal
1817 if (spanFramePFD
->mIsLetterFrame
&&
1818 !spanFrame
->GetPrevInFlow() &&
1819 spanFrame
->StyleText()->mLineHeight
.GetUnit() == eStyleUnit_Normal
) {
1820 logicalBSize
= spanFramePFD
->mBounds
.BSize(lineWM
);
1823 nscoord leading
= logicalBSize
- contentBSize
;
1824 psd
->mBStartLeading
= leading
/ 2;
1825 psd
->mBEndLeading
= leading
- psd
->mBStartLeading
;
1826 psd
->mLogicalBSize
= logicalBSize
;
1827 if (spanFrame
->GetType() == nsGkAtoms::rubyFrame
) {
1828 // We may need to extend leadings here for ruby annotations as
1829 // required by section Line Spacing in the CSS Ruby spec.
1830 // See http://dev.w3.org/csswg/css-ruby/#line-height
1831 auto rubyFrame
= static_cast<nsRubyFrame
*>(spanFrame
);
1832 nscoord startLeading
, endLeading
;
1833 rubyFrame
->GetBlockLeadings(startLeading
, endLeading
);
1834 nscoord deltaLeading
= startLeading
+ endLeading
- leading
;
1835 if (deltaLeading
> 0) {
1836 // If the total leading is not wide enough for ruby annotations,
1837 // extend the side which is not enough. If both sides are not
1838 // wide enough, replace the leadings with the requested values.
1839 if (startLeading
< psd
->mBStartLeading
) {
1840 psd
->mBEndLeading
+= deltaLeading
;
1841 } else if (endLeading
< psd
->mBEndLeading
) {
1842 psd
->mBStartLeading
+= deltaLeading
;
1844 psd
->mBStartLeading
= startLeading
;
1845 psd
->mBEndLeading
= endLeading
;
1847 psd
->mLogicalBSize
+= deltaLeading
;
1851 if (zeroEffectiveSpanBox
) {
1852 // When the span-box is to be ignored, zero out the initial
1853 // values so that the span doesn't impact the final line
1854 // height. The contents of the span can impact the final line
1857 // Note that things are readjusted for this span after its children
1859 minBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
1860 maxBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
1864 // The initial values for the min and max block coord values are in the
1865 // span's coordinate space, and cover the logical block size of the span.
1866 // If there are child frames in this span that stick out of this area
1867 // then the minBCoord and maxBCoord are updated by the amount of logical
1868 // blockSize that is outside this range.
1869 minBCoord
= spanFramePFD
->mBorderPadding
.BStart(lineWM
) -
1870 psd
->mBStartLeading
;
1871 maxBCoord
= minBCoord
+ psd
->mLogicalBSize
;
1874 // This is the distance from the top edge of the parents visual
1875 // box to the baseline. The span already computed this for us,
1877 *psd
->mBaseline
= baselineBCoord
= spanFramePFD
->mAscent
;
1880 #ifdef NOISY_BLOCKDIR_ALIGN
1881 printf("[%sSpan]", (psd
== mRootSpan
)?"Root":"");
1882 nsFrame::ListTag(stdout
, spanFrame
);
1883 printf(": baseLine=%d logicalBSize=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n",
1884 baselineBCoord
, psd
->mLogicalBSize
, psd
->mBStartLeading
,
1885 spanFramePFD
->mBounds
.BSize(lineWM
),
1886 spanFramePFD
->mBorderPadding
.Top(lineWM
),
1887 spanFramePFD
->mBorderPadding
.Bottom(lineWM
),
1888 zeroEffectiveSpanBox
? "yes" : "no");
1892 nscoord maxStartBoxBSize
= 0;
1893 nscoord maxEndBoxBSize
= 0;
1894 PerFrameData
* pfd
= psd
->mFirstFrame
;
1895 while (nullptr != pfd
) {
1896 nsIFrame
* frame
= pfd
->mFrame
;
1898 // sanity check (see bug 105168, non-reproducible crashes from null frame)
1899 NS_ASSERTION(frame
, "null frame in PerFrameData - something is very very bad");
1904 // Compute the logical block size of the frame
1905 nscoord logicalBSize
;
1906 PerSpanData
* frameSpan
= pfd
->mSpan
;
1908 // For span frames the logical-block-size and start-leading were
1909 // pre-computed when the span was reflowed.
1910 logicalBSize
= frameSpan
->mLogicalBSize
;
1913 // For other elements the logical block size is the same as the
1914 // frame's block size plus its margins.
1915 logicalBSize
= pfd
->mBounds
.BSize(lineWM
) +
1916 pfd
->mMargin
.BStartEnd(lineWM
);
1917 if (logicalBSize
< 0 &&
1918 mPresContext
->CompatibilityMode() == eCompatibility_NavQuirks
) {
1919 pfd
->mAscent
-= logicalBSize
;
1924 // Get vertical-align property ("vertical-align" is the CSS name for
1925 // block-direction align)
1926 const nsStyleCoord
& verticalAlign
=
1927 frame
->StyleTextReset()->mVerticalAlign
;
1928 uint8_t verticalAlignEnum
= frame
->VerticalAlignEnum();
1929 #ifdef NOISY_BLOCKDIR_ALIGN
1931 nsFrame::ListTag(stdout
, frame
);
1932 printf(": verticalAlignUnit=%d (enum == %d",
1933 verticalAlign
.GetUnit(),
1934 ((eStyleUnit_Enumerated
== verticalAlign
.GetUnit())
1935 ? verticalAlign
.GetIntValue()
1937 if (verticalAlignEnum
!= nsIFrame::eInvalidVerticalAlign
) {
1938 printf(", after SVG dominant-baseline conversion == %d",
1944 if (verticalAlignEnum
!= nsIFrame::eInvalidVerticalAlign
) {
1945 if (lineWM
.IsVertical()) {
1946 if (verticalAlignEnum
== NS_STYLE_VERTICAL_ALIGN_MIDDLE
) {
1947 // For vertical writing mode where the dominant baseline is centered
1948 // (i.e. text-orientation is not sideways-*), we remap 'middle' to
1949 // 'middle-with-baseline' so that images align sensibly with the
1950 // center-baseline-aligned text.
1951 if (!lineWM
.IsSideways()) {
1952 verticalAlignEnum
= NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
;
1954 } else if (lineWM
.IsLineInverted()) {
1955 // Swap the meanings of top and bottom when line is inverted
1956 // relative to block direction.
1957 switch (verticalAlignEnum
) {
1958 case NS_STYLE_VERTICAL_ALIGN_TOP
:
1959 verticalAlignEnum
= NS_STYLE_VERTICAL_ALIGN_BOTTOM
;
1961 case NS_STYLE_VERTICAL_ALIGN_BOTTOM
:
1962 verticalAlignEnum
= NS_STYLE_VERTICAL_ALIGN_TOP
;
1964 case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
:
1965 verticalAlignEnum
= NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
;
1967 case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
:
1968 verticalAlignEnum
= NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
;
1974 // baseline coord that may be adjusted for script offset
1975 nscoord revisedBaselineBCoord
= baselineBCoord
;
1977 // For superscript and subscript, raise or lower the baseline of the box
1978 // to the proper offset of the parent's box, then proceed as for BASELINE
1979 if (verticalAlignEnum
== NS_STYLE_VERTICAL_ALIGN_SUB
||
1980 verticalAlignEnum
== NS_STYLE_VERTICAL_ALIGN_SUPER
) {
1981 revisedBaselineBCoord
+= lineWM
.FlowRelativeToLineRelativeFactor() *
1982 (verticalAlignEnum
== NS_STYLE_VERTICAL_ALIGN_SUB
1983 ? fm
->SubscriptOffset() : -fm
->SuperscriptOffset());
1984 verticalAlignEnum
= NS_STYLE_VERTICAL_ALIGN_BASELINE
;
1987 switch (verticalAlignEnum
) {
1989 case NS_STYLE_VERTICAL_ALIGN_BASELINE
:
1990 if (lineWM
.IsVertical() && !lineWM
.IsSideways()) {
1992 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
-
1993 pfd
->mBounds
.BSize(lineWM
)/2;
1995 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
-
1997 pfd
->mMargin
.BStart(lineWM
);
2000 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
- pfd
->mAscent
;
2002 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2005 case NS_STYLE_VERTICAL_ALIGN_TOP
:
2007 pfd
->mBlockDirAlign
= VALIGN_TOP
;
2008 nscoord subtreeBSize
= logicalBSize
;
2010 subtreeBSize
= frameSpan
->mMaxBCoord
- frameSpan
->mMinBCoord
;
2011 NS_ASSERTION(subtreeBSize
>= logicalBSize
,
2012 "unexpected subtree block size");
2014 if (subtreeBSize
> maxStartBoxBSize
) {
2015 maxStartBoxBSize
= subtreeBSize
;
2020 case NS_STYLE_VERTICAL_ALIGN_BOTTOM
:
2022 pfd
->mBlockDirAlign
= VALIGN_BOTTOM
;
2023 nscoord subtreeBSize
= logicalBSize
;
2025 subtreeBSize
= frameSpan
->mMaxBCoord
- frameSpan
->mMinBCoord
;
2026 NS_ASSERTION(subtreeBSize
>= logicalBSize
,
2027 "unexpected subtree block size");
2029 if (subtreeBSize
> maxEndBoxBSize
) {
2030 maxEndBoxBSize
= subtreeBSize
;
2035 case NS_STYLE_VERTICAL_ALIGN_MIDDLE
:
2037 // Align the midpoint of the frame with 1/2 the parents
2038 // x-height above the baseline.
2039 nscoord parentXHeight
=
2040 lineWM
.FlowRelativeToLineRelativeFactor() * fm
->XHeight();
2042 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
-
2043 (parentXHeight
+ pfd
->mBounds
.BSize(lineWM
))/2;
2046 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
-
2047 (parentXHeight
+ logicalBSize
)/2 +
2048 pfd
->mMargin
.BStart(lineWM
);
2050 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2054 case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
:
2056 // The top of the logical box is aligned with the top of
2057 // the parent element's text.
2058 // XXX For vertical text we will need a new API to get the logical
2060 nscoord parentAscent
=
2061 lineWM
.IsLineInverted() ? fm
->MaxDescent() : fm
->MaxAscent();
2063 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
- parentAscent
-
2064 pfd
->mBorderPadding
.BStart(lineWM
) + frameSpan
->mBStartLeading
;
2067 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
- parentAscent
+
2068 pfd
->mMargin
.BStart(lineWM
);
2070 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2074 case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
:
2076 // The bottom of the logical box is aligned with the
2077 // bottom of the parent elements text.
2078 nscoord parentDescent
=
2079 lineWM
.IsLineInverted() ? fm
->MaxAscent() : fm
->MaxDescent();
2081 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
+ parentDescent
-
2082 pfd
->mBounds
.BSize(lineWM
) +
2083 pfd
->mBorderPadding
.BEnd(lineWM
) -
2084 frameSpan
->mBEndLeading
;
2087 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
+ parentDescent
-
2088 pfd
->mBounds
.BSize(lineWM
) -
2089 pfd
->mMargin
.BEnd(lineWM
);
2091 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2095 case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
:
2097 // Align the midpoint of the frame with the baseline of the parent.
2099 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
-
2100 pfd
->mBounds
.BSize(lineWM
)/2;
2103 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
- logicalBSize
/2 +
2104 pfd
->mMargin
.BStart(lineWM
);
2106 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2111 // We have either a coord, a percent, or a calc().
2112 nscoord pctBasis
= 0;
2113 if (verticalAlign
.HasPercent()) {
2114 // Percentages are like lengths, except treated as a percentage
2115 // of the elements line block size value.
2117 GetInflationForBlockDirAlignment(frame
, mInflationMinFontSize
);
2118 pctBasis
= nsHTMLReflowState::CalcLineHeight(frame
->GetContent(),
2119 frame
->StyleContext(), mBlockReflowState
->ComputedBSize(),
2123 nsRuleNode::ComputeCoordPercentCalc(verticalAlign
, pctBasis
);
2124 // According to the CSS2 spec (10.8.1), a positive value
2125 // "raises" the box by the given distance while a negative value
2126 // "lowers" the box by the given distance (with zero being the
2127 // baseline). Since Y coordinates increase towards the bottom of
2128 // the screen we reverse the sign.
2129 nscoord revisedBaselineBCoord
= baselineBCoord
- offset
;
2130 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
- pfd
->mAscent
;
2131 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
2134 // Update minBCoord/maxBCoord for frames that we just placed. Do not factor
2135 // text into the equation.
2136 if (pfd
->mBlockDirAlign
== VALIGN_OTHER
) {
2137 // Text frames do not contribute to the min/max Y values for the
2138 // line (instead their parent frame's font-size contributes).
2139 // XXXrbs -- relax this restriction because it causes text frames
2140 // to jam together when 'font-size-adjust' is enabled
2141 // and layout is using dynamic font heights (bug 20394)
2142 // -- Note #1: With this code enabled and with the fact that we are not
2143 // using Em[Ascent|Descent] as nsDimensions for text metrics in
2144 // GFX mean that the discussion in bug 13072 cannot hold.
2145 // -- Note #2: We still don't want empty-text frames to interfere.
2146 // For example in quirks mode, avoiding empty text frames prevents
2147 // "tall" lines around elements like <hr> since the rules of <hr>
2148 // in quirks.css have pseudo text contents with LF in them.
2150 if (!pfd
->mIsTextFrame
) {
2152 // Only consider non empty text frames when line-height=normal
2153 bool canUpdate
= !pfd
->mIsTextFrame
;
2154 if (!canUpdate
&& pfd
->mIsNonWhitespaceTextFrame
) {
2156 frame
->StyleText()->mLineHeight
.GetUnit() == eStyleUnit_Normal
;
2160 nscoord blockStart
, blockEnd
;
2162 // For spans that were are now placing, use their position
2163 // plus their already computed min-Y and max-Y values for
2164 // computing blockStart and blockEnd.
2165 blockStart
= pfd
->mBounds
.BStart(lineWM
) + frameSpan
->mMinBCoord
;
2166 blockEnd
= pfd
->mBounds
.BStart(lineWM
) + frameSpan
->mMaxBCoord
;
2169 blockStart
= pfd
->mBounds
.BStart(lineWM
) -
2170 pfd
->mMargin
.BStart(lineWM
);
2171 blockEnd
= blockStart
+ logicalBSize
;
2174 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
&&
2176 // Check if it's a BR frame that is not alone on its line (it
2177 // is given a block size of zero to indicate this), and if so reset
2178 // blockStart and blockEnd so that BR frames don't influence the line.
2179 if (nsGkAtoms::brFrame
== frame
->GetType()) {
2180 blockStart
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
2181 blockEnd
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
2184 if (blockStart
< minBCoord
) minBCoord
= blockStart
;
2185 if (blockEnd
> maxBCoord
) maxBCoord
= blockEnd
;
2186 #ifdef NOISY_BLOCKDIR_ALIGN
2187 printf(" [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minBCoord=%d maxBCoord=%d\n",
2188 pfd
->mAscent
, pfd
->mBounds
.BSize(lineWM
),
2189 pfd
->mBorderPadding
.Top(lineWM
),
2190 pfd
->mBorderPadding
.Bottom(lineWM
),
2192 frameSpan
? frameSpan
->mBStartLeading
: 0,
2193 pfd
->mBounds
.BStart(lineWM
), minBCoord
, maxBCoord
);
2196 if (psd
!= mRootSpan
) {
2197 frame
->SetRect(lineWM
, pfd
->mBounds
, ContainerWidthForSpan(psd
));
2203 // Factor in the minimum line block-size when handling the root-span for
2205 if (psd
== mRootSpan
) {
2206 // We should factor in the block element's minimum line-height (as
2207 // defined in section 10.8.1 of the css2 spec) assuming that
2208 // mZeroEffectiveSpanBox is not set on the root span. This only happens
2209 // in some cases in quirks mode:
2210 // (1) if the root span contains non-whitespace text directly (this
2211 // is handled by mZeroEffectiveSpanBox
2212 // (2) if this line has a bullet
2213 // (3) if this is the last line of an LI, DT, or DD element
2214 // (The last line before a block also counts, but not before a
2215 // BR) (NN4/IE5 quirk)
2217 // (1) and (2) above
2218 bool applyMinLH
= !psd
->mZeroEffectiveSpanBox
|| mHasBullet
;
2219 bool isLastLine
= !mGotLineBox
||
2220 (!mLineBox
->IsLineWrapped() && !mLineEndsInBR
);
2221 if (!applyMinLH
&& isLastLine
) {
2222 nsIContent
* blockContent
= mRootSpan
->mFrame
->mFrame
->GetContent();
2224 nsIAtom
*blockTagAtom
= blockContent
->Tag();
2225 // (3) above, if the last line of LI, DT, or DD
2226 if (blockTagAtom
== nsGkAtoms::li
||
2227 blockTagAtom
== nsGkAtoms::dt
||
2228 blockTagAtom
== nsGkAtoms::dd
) {
2234 if (psd
->mHasNonemptyContent
|| preMode
|| mHasBullet
) {
2235 #ifdef NOISY_BLOCKDIR_ALIGN
2236 printf(" [span]==> adjusting min/maxBCoord: currentValues: %d,%d", minBCoord
, maxBCoord
);
2238 nscoord minimumLineBSize
= mMinLineBSize
;
2239 nscoord blockStart
=
2240 -nsLayoutUtils::GetCenteredFontBaseline(fm
, minimumLineBSize
,
2241 lineWM
.IsLineInverted());
2242 nscoord blockEnd
= blockStart
+ minimumLineBSize
;
2244 if (blockStart
< minBCoord
) minBCoord
= blockStart
;
2245 if (blockEnd
> maxBCoord
) maxBCoord
= blockEnd
;
2247 #ifdef NOISY_BLOCKDIR_ALIGN
2248 printf(" new values: %d,%d\n", minBCoord
, maxBCoord
);
2250 #ifdef NOISY_BLOCKDIR_ALIGN
2251 printf(" Used mMinLineBSize: %d, blockStart: %d, blockEnd: %d\n", mMinLineBSize
, blockStart
, blockEnd
);
2256 // [1] BR's on empty lines stop working
2257 // [2] May not honor css2's notion of handling empty elements
2258 // [3] blank lines in a pre-section ("\n") (handled with preMode)
2260 // XXX Are there other problems with this?
2261 #ifdef NOISY_BLOCKDIR_ALIGN
2262 printf(" [span]==> zapping min/maxBCoord: currentValues: %d,%d newValues: 0,0\n",
2263 minBCoord
, maxBCoord
);
2265 minBCoord
= maxBCoord
= 0;
2270 if ((minBCoord
== BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
) ||
2271 (maxBCoord
== BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
)) {
2272 minBCoord
= maxBCoord
= baselineBCoord
;
2275 if ((psd
!= mRootSpan
) && (psd
->mZeroEffectiveSpanBox
)) {
2276 #ifdef NOISY_BLOCKDIR_ALIGN
2277 printf(" [span]adjusting for zeroEffectiveSpanBox\n");
2278 printf(" Original: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
2279 minBCoord
, maxBCoord
, spanFramePFD
->mBounds
.BSize(frameWM
),
2280 spanFramePFD
->mAscent
,
2281 psd
->mLogicalBSize
, psd
->mBStartLeading
, psd
->mBEndLeading
);
2283 nscoord goodMinBCoord
=
2284 spanFramePFD
->mBorderPadding
.BStart(lineWM
) - psd
->mBStartLeading
;
2285 nscoord goodMaxBCoord
= goodMinBCoord
+ psd
->mLogicalBSize
;
2287 // For cases like the one in bug 714519 (text-decoration placement
2288 // or making nsLineLayout::IsZeroBSize() handle
2289 // vertical-align:top/bottom on a descendant of the line that's not
2290 // a child of it), we want to treat elements that are
2291 // vertical-align: top or bottom somewhat like children for the
2292 // purposes of this quirk. To some extent, this is guessing, since
2293 // they might end up being aligned anywhere. However, we'll guess
2294 // that they'll be placed aligned with the top or bottom of this
2295 // frame (as though this frame is the only thing in the line).
2296 // (Guessing isn't crazy, since all we're doing is reducing the
2297 // scope of a quirk and making the behavior more standards-like.)
2298 if (maxStartBoxBSize
> maxBCoord
- minBCoord
) {
2299 // Distribute maxStartBoxBSize to ascent (baselineBCoord - minBCoord), and
2300 // then to descent (maxBCoord - baselineBCoord) by adjusting minBCoord or
2301 // maxBCoord, but not to exceed goodMinBCoord and goodMaxBCoord.
2302 nscoord distribute
= maxStartBoxBSize
- (maxBCoord
- minBCoord
);
2303 nscoord ascentSpace
= std::max(minBCoord
- goodMinBCoord
, 0);
2304 if (distribute
> ascentSpace
) {
2305 distribute
-= ascentSpace
;
2306 minBCoord
-= ascentSpace
;
2307 nscoord descentSpace
= std::max(goodMaxBCoord
- maxBCoord
, 0);
2308 if (distribute
> descentSpace
) {
2309 maxBCoord
+= descentSpace
;
2311 maxBCoord
+= distribute
;
2314 minBCoord
-= distribute
;
2317 if (maxEndBoxBSize
> maxBCoord
- minBCoord
) {
2318 // Likewise, but preferring descent to ascent.
2319 nscoord distribute
= maxEndBoxBSize
- (maxBCoord
- minBCoord
);
2320 nscoord descentSpace
= std::max(goodMaxBCoord
- maxBCoord
, 0);
2321 if (distribute
> descentSpace
) {
2322 distribute
-= descentSpace
;
2323 maxBCoord
+= descentSpace
;
2324 nscoord ascentSpace
= std::max(minBCoord
- goodMinBCoord
, 0);
2325 if (distribute
> ascentSpace
) {
2326 minBCoord
-= ascentSpace
;
2328 minBCoord
-= distribute
;
2331 maxBCoord
+= distribute
;
2335 if (minBCoord
> goodMinBCoord
) {
2336 nscoord adjust
= minBCoord
- goodMinBCoord
; // positive
2338 // shrink the logical extents
2339 psd
->mLogicalBSize
-= adjust
;
2340 psd
->mBStartLeading
-= adjust
;
2342 if (maxBCoord
< goodMaxBCoord
) {
2343 nscoord adjust
= goodMaxBCoord
- maxBCoord
;
2344 psd
->mLogicalBSize
-= adjust
;
2345 psd
->mBEndLeading
-= adjust
;
2347 if (minBCoord
> 0) {
2349 // shrink the content by moving its block start down. This is tricky,
2350 // since the block start is the 0 for many coordinates, so what we do is
2351 // move everything else up.
2352 spanFramePFD
->mAscent
-= minBCoord
; // move the baseline up
2353 spanFramePFD
->mBounds
.BSize(lineWM
) -= minBCoord
; // move the block end up
2354 psd
->mBStartLeading
+= minBCoord
;
2355 *psd
->mBaseline
-= minBCoord
;
2357 pfd
= psd
->mFirstFrame
;
2358 while (nullptr != pfd
) {
2359 pfd
->mBounds
.BStart(lineWM
) -= minBCoord
; // move all the children
2361 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerWidthForSpan(psd
));
2364 maxBCoord
-= minBCoord
; // since minBCoord is in the frame's own
2365 // coordinate system
2368 if (maxBCoord
< spanFramePFD
->mBounds
.BSize(lineWM
)) {
2369 nscoord adjust
= spanFramePFD
->mBounds
.BSize(lineWM
) - maxBCoord
;
2370 spanFramePFD
->mBounds
.BSize(lineWM
) -= adjust
; // move the bottom up
2371 psd
->mBEndLeading
+= adjust
;
2373 #ifdef NOISY_BLOCKDIR_ALIGN
2374 printf(" New: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
2375 minBCoord
, maxBCoord
, spanFramePFD
->mBounds
.BSize(lineWM
),
2376 spanFramePFD
->mAscent
,
2377 psd
->mLogicalBSize
, psd
->mBStartLeading
, psd
->mBEndLeading
);
2381 psd
->mMinBCoord
= minBCoord
;
2382 psd
->mMaxBCoord
= maxBCoord
;
2383 #ifdef NOISY_BLOCKDIR_ALIGN
2384 printf(" [span]==> minBCoord=%d maxBCoord=%d delta=%d maxStartBoxBSize=%d maxEndBoxBSize=%d\n",
2385 minBCoord
, maxBCoord
, maxBCoord
- minBCoord
, maxStartBoxBSize
, maxEndBoxBSize
);
2387 if (maxStartBoxBSize
> mMaxStartBoxBSize
) {
2388 mMaxStartBoxBSize
= maxStartBoxBSize
;
2390 if (maxEndBoxBSize
> mMaxEndBoxBSize
) {
2391 mMaxEndBoxBSize
= maxEndBoxBSize
;
2395 static void SlideSpanFrameRect(nsIFrame
* aFrame
, nscoord aDeltaWidth
)
2397 // This should not use nsIFrame::MovePositionBy because it happens
2398 // prior to relative positioning. In particular, because
2399 // nsBlockFrame::PlaceLine calls aLineLayout.TrimTrailingWhiteSpace()
2400 // prior to calling aLineLayout.RelativePositionFrames().
2401 nsPoint p
= aFrame
->GetPosition();
2403 aFrame
->SetPosition(p
);
2407 nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData
* psd
,
2408 nscoord
* aDeltaISize
)
2410 PerFrameData
* pfd
= psd
->mFirstFrame
;
2416 while (nullptr != pfd
) {
2417 #ifdef REALLY_NOISY_TRIM
2418 nsFrame::ListTag(stdout
, psd
->mFrame
->mFrame
);
2419 printf(": attempting trim of ");
2420 nsFrame::ListTag(stdout
, pfd
->mFrame
);
2423 PerSpanData
* childSpan
= pfd
->mSpan
;
2424 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2426 // Maybe the child span has the trailing white-space in it?
2427 if (TrimTrailingWhiteSpaceIn(childSpan
, aDeltaISize
)) {
2428 nscoord deltaISize
= *aDeltaISize
;
2430 // Adjust the child spans frame size
2431 pfd
->mBounds
.ISize(lineWM
) -= deltaISize
;
2432 if (psd
!= mRootSpan
) {
2433 // When the child span is not a direct child of the block
2434 // we need to update the child spans frame rectangle
2435 // because it most likely will not be done again. Spans
2436 // that are direct children of the block will be updated
2437 // later, however, because the VerticalAlignFrames method
2438 // will be run after this method.
2439 nscoord containerWidth
= ContainerWidthForSpan(childSpan
);
2440 nsIFrame
* f
= pfd
->mFrame
;
2441 LogicalRect
r(lineWM
, f
->GetRect(), containerWidth
);
2442 r
.ISize(lineWM
) -= deltaISize
;
2443 f
->SetRect(lineWM
, r
, containerWidth
);
2446 // Adjust the inline end edge of the span that contains the child span
2447 psd
->mICoord
-= deltaISize
;
2449 // Slide any frames that follow the child span over by the
2450 // correct amount. The only thing that can follow the child
2451 // span is empty stuff, so we are just making things
2452 // sensible (keeping the combined area honest).
2453 while (pfd
->mNext
) {
2455 pfd
->mBounds
.IStart(lineWM
) -= deltaISize
;
2456 if (psd
!= mRootSpan
) {
2457 // When the child span is not a direct child of the block
2458 // we need to update the child span's frame rectangle
2459 // because it most likely will not be done again. Spans
2460 // that are direct children of the block will be updated
2461 // later, however, because the VerticalAlignFrames method
2462 // will be run after this method.
2463 SlideSpanFrameRect(pfd
->mFrame
, deltaISize
);
2470 else if (!pfd
->mIsTextFrame
&& !pfd
->mSkipWhenTrimmingWhitespace
) {
2471 // If we hit a frame on the end that's not text and not a placeholder,
2472 // then there is no trailing whitespace to trim. Stop the search.
2476 else if (pfd
->mIsTextFrame
) {
2477 // Call TrimTrailingWhiteSpace even on empty textframes because they
2478 // might have a soft hyphen which should now appear, changing the frame's
2480 nsTextFrame::TrimOutput trimOutput
= static_cast<nsTextFrame
*>(pfd
->mFrame
)->
2481 TrimTrailingWhiteSpace(mBlockReflowState
->rendContext
);
2483 nsFrame::ListTag(stdout
, psd
->mFrame
->mFrame
);
2484 printf(": trim of ");
2485 nsFrame::ListTag(stdout
, pfd
->mFrame
);
2486 printf(" returned %d\n", trimOutput
.mDeltaWidth
);
2489 if (trimOutput
.mChanged
) {
2490 pfd
->mRecomputeOverflow
= true;
2493 // Delta width not being zero means that
2494 // there is trimmed space in the frame.
2495 if (trimOutput
.mDeltaWidth
) {
2496 pfd
->mBounds
.ISize(lineWM
) -= trimOutput
.mDeltaWidth
;
2498 // If any trailing space is trimmed, the justification opportunity
2499 // generated by the space should be removed as well.
2500 pfd
->mJustificationInfo
.CancelOpportunityForTrimmedSpace();
2502 // See if the text frame has already been placed in its parent
2503 if (psd
!= mRootSpan
) {
2504 // The frame was already placed during psd's
2505 // reflow. Update the frames rectangle now.
2506 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
,
2507 ContainerWidthForSpan(psd
));
2510 // Adjust containing span's right edge
2511 psd
->mICoord
-= trimOutput
.mDeltaWidth
;
2513 // Slide any frames that follow the text frame over by the
2514 // right amount. The only thing that can follow the text
2515 // frame is empty stuff, so we are just making things
2516 // sensible (keeping the combined area honest).
2517 while (pfd
->mNext
) {
2519 pfd
->mBounds
.IStart(lineWM
) -= trimOutput
.mDeltaWidth
;
2520 if (psd
!= mRootSpan
) {
2521 // When the child span is not a direct child of the block
2522 // we need to update the child spans frame rectangle
2523 // because it most likely will not be done again. Spans
2524 // that are direct children of the block will be updated
2525 // later, however, because the VerticalAlignFrames method
2526 // will be run after this method.
2527 SlideSpanFrameRect(pfd
->mFrame
, trimOutput
.mDeltaWidth
);
2532 if (pfd
->mIsNonEmptyTextFrame
|| trimOutput
.mChanged
) {
2533 // Pass up to caller so they can shrink their span
2534 *aDeltaISize
= trimOutput
.mDeltaWidth
;
2546 nsLineLayout::TrimTrailingWhiteSpace()
2548 PerSpanData
* psd
= mRootSpan
;
2550 TrimTrailingWhiteSpaceIn(psd
, &deltaISize
);
2551 return 0 != deltaISize
;
2555 nsLineLayout::PerFrameData::ParticipatesInJustification() const
2557 if (mIsBullet
|| mIsEmpty
|| mSkipWhenTrimmingWhitespace
) {
2558 // Skip bullets, empty frames, and placeholders
2561 if (mIsTextFrame
&& !mIsNonWhitespaceTextFrame
&&
2562 static_cast<nsTextFrame
*>(mFrame
)->IsAtEndOfLine()) {
2563 // Skip trimmed whitespaces
2569 struct nsLineLayout::JustificationComputationState
2571 PerFrameData
* mFirstParticipant
;
2572 PerFrameData
* mLastParticipant
;
2573 // Whether we are going across a boundary of ruby base, i.e.
2574 // entering one, leaving one, or both.
2575 bool mCrossingRubyBaseBoundary
;
2577 JustificationComputationState()
2578 : mFirstParticipant(nullptr)
2579 , mLastParticipant(nullptr)
2580 , mCrossingRubyBaseBoundary(false) { }
2584 * Compute the justification info of the given span, and store the
2585 * number of inner opportunities into the frame's justification info.
2586 * It returns the number of non-inner opportunities it detects.
2589 nsLineLayout::ComputeFrameJustification(PerSpanData
* aPSD
,
2590 JustificationComputationState
& aState
)
2592 NS_ASSERTION(aPSD
, "null arg");
2593 NS_ASSERTION(!aState
.mLastParticipant
|| !aState
.mLastParticipant
->mSpan
,
2594 "Last participant shall always be a leaf frame");
2595 bool firstChild
= true;
2596 int32_t& innerOpportunities
=
2597 aPSD
->mFrame
->mJustificationInfo
.mInnerOpportunities
;
2598 MOZ_ASSERT(innerOpportunities
== 0,
2599 "Justification info should not have been set yet.");
2600 int32_t outerOpportunities
= 0;
2602 for (PerFrameData
* pfd
= aPSD
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
2603 if (!pfd
->ParticipatesInJustification()) {
2607 bool isRubyBase
= pfd
->mFrame
->GetType() == nsGkAtoms::rubyBaseFrame
;
2609 aState
.mCrossingRubyBaseBoundary
= true;
2612 int extraOpportunities
= 0;
2614 PerSpanData
* span
= pfd
->mSpan
;
2615 extraOpportunities
= ComputeFrameJustification(span
, aState
);
2616 innerOpportunities
+= pfd
->mJustificationInfo
.mInnerOpportunities
;
2618 const auto& info
= pfd
->mJustificationInfo
;
2619 if (pfd
->mIsTextFrame
) {
2620 innerOpportunities
+= info
.mInnerOpportunities
;
2623 PerFrameData
* prev
= aState
.mLastParticipant
;
2625 aState
.mFirstParticipant
= pfd
;
2627 auto& assign
= pfd
->mJustificationAssignment
;
2628 auto& prevAssign
= prev
->mJustificationAssignment
;
2629 const auto& prevInfo
= prev
->mJustificationInfo
;
2631 if (info
.mIsStartJustifiable
||
2632 prevInfo
.mIsEndJustifiable
||
2633 aState
.mCrossingRubyBaseBoundary
) {
2634 extraOpportunities
= 1;
2635 if (aState
.mCrossingRubyBaseBoundary
) {
2636 // For ruby alignment with value space-around, there is
2637 // always an expansion opportunity at the boundary of a ruby
2638 // base, and it always generates one gap at each side. If we
2639 // don't do it here, the interaction between text align and
2640 // and ruby align could be strange.
2641 prevAssign
.mGapsAtEnd
= 1;
2642 assign
.mGapsAtStart
= 1;
2643 aState
.mCrossingRubyBaseBoundary
= false;
2644 } else if (!info
.mIsStartJustifiable
) {
2645 prevAssign
.mGapsAtEnd
= 2;
2646 assign
.mGapsAtStart
= 0;
2647 } else if (!prevInfo
.mIsEndJustifiable
) {
2648 prevAssign
.mGapsAtEnd
= 0;
2649 assign
.mGapsAtStart
= 2;
2651 prevAssign
.mGapsAtEnd
= 1;
2652 assign
.mGapsAtStart
= 1;
2657 aState
.mLastParticipant
= pfd
;
2661 aState
.mCrossingRubyBaseBoundary
= true;
2665 outerOpportunities
= extraOpportunities
;
2668 innerOpportunities
+= extraOpportunities
;
2672 return outerOpportunities
;
2676 nsLineLayout::AdvanceAnnotationInlineBounds(PerFrameData
* aPFD
,
2677 nscoord aContainerWidth
,
2678 nscoord aDeltaICoord
,
2679 nscoord aDeltaISize
)
2681 nsIFrame
* frame
= aPFD
->mFrame
;
2682 nsIAtom
* frameType
= frame
->GetType();
2683 MOZ_ASSERT(frameType
== nsGkAtoms::rubyTextFrame
||
2684 frameType
== nsGkAtoms::rubyTextContainerFrame
);
2685 MOZ_ASSERT(aPFD
->mSpan
, "rt and rtc should have span.");
2687 PerSpanData
* psd
= aPFD
->mSpan
;
2688 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2689 aPFD
->mBounds
.IStart(lineWM
) += aDeltaICoord
;
2691 // Check whether this expansion should be counted into the reserved
2692 // isize or not. When it is a ruby text container, and it has some
2693 // children linked to the base, it must not have reserved isize,
2694 // or its children won't align with their bases. Otherwise, this
2695 // expansion should be reserved. There are two cases a ruby text
2696 // container does not have children linked to the base:
2697 // 1. it is a container for span; 2. its children are collapsed.
2698 // See bug 1055674 for the second case.
2699 if (frameType
== nsGkAtoms::rubyTextFrame
||
2700 // This ruby text container is a span.
2701 (psd
->mFirstFrame
== psd
->mLastFrame
&& psd
->mFirstFrame
&&
2702 !psd
->mFirstFrame
->mIsLinkedToBase
)) {
2703 nscoord reservedISize
= RubyUtils::GetReservedISize(frame
);
2704 RubyUtils::SetReservedISize(frame
, reservedISize
+ aDeltaISize
);
2706 // It is a normal ruby text container. Its children will expand
2707 // themselves properly. We only need to expand its own size here.
2708 aPFD
->mBounds
.ISize(lineWM
) += aDeltaISize
;
2710 aPFD
->mFrame
->SetRect(lineWM
, aPFD
->mBounds
, aContainerWidth
);
2714 * This function applies the changes of icoord and isize caused by
2715 * justification to annotations of the given frame.
2716 * aPFD must be one of the frames in aContainingSpan.
2719 nsLineLayout::ApplyLineJustificationToAnnotations(PerFrameData
* aPFD
,
2720 PerSpanData
* aContainingSpan
,
2721 nscoord aDeltaICoord
,
2722 nscoord aDeltaISize
)
2724 PerFrameData
* pfd
= aPFD
->mNextAnnotation
;
2725 nscoord containerWidth
= ContainerWidthForSpan(aContainingSpan
);
2727 AdvanceAnnotationInlineBounds(pfd
, containerWidth
,
2728 aDeltaICoord
, aDeltaISize
);
2730 // There are two cases where an annotation frame has siblings which
2731 // do not attached to a ruby base-level frame:
2732 // 1. there's an intra-annotation whitespace which has no intra-base
2733 // white-space to pair with;
2734 // 2. there are not enough ruby bases to be paired with annotations.
2735 // In these cases, their size should not be affected, but we still
2736 // need to move them so that they won't overlap other frames.
2737 PerFrameData
* sibling
= pfd
->mNext
;
2738 while (sibling
&& !sibling
->mIsLinkedToBase
) {
2739 AdvanceAnnotationInlineBounds(sibling
, containerWidth
,
2740 aDeltaICoord
+ aDeltaISize
, 0);
2741 sibling
= sibling
->mNext
;
2744 pfd
= pfd
->mNextAnnotation
;
2749 nsLineLayout::ApplyFrameJustification(PerSpanData
* aPSD
,
2750 JustificationApplicationState
& aState
)
2752 NS_ASSERTION(aPSD
, "null arg");
2754 nscoord deltaICoord
= 0;
2755 for (PerFrameData
* pfd
= aPSD
->mFirstFrame
; pfd
!= nullptr; pfd
= pfd
->mNext
) {
2756 // Don't reposition bullets (and other frames that occur out of X-order?)
2757 if (!pfd
->mIsBullet
) {
2759 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2760 const auto& assign
= pfd
->mJustificationAssignment
;
2762 if (true == pfd
->mIsTextFrame
) {
2763 if (aState
.IsJustifiable()) {
2764 // Set corresponding justification gaps here, so that the
2765 // text frame knows how it should add gaps at its sides.
2766 const auto& info
= pfd
->mJustificationInfo
;
2767 auto textFrame
= static_cast<nsTextFrame
*>(pfd
->mFrame
);
2768 textFrame
->AssignJustificationGaps(assign
);
2769 dw
= aState
.Consume(JustificationUtils::CountGaps(info
, assign
));
2773 pfd
->mRecomputeOverflow
= true;
2777 if (nullptr != pfd
->mSpan
) {
2778 dw
= ApplyFrameJustification(pfd
->mSpan
, aState
);
2782 pfd
->mBounds
.ISize(lineWM
) += dw
;
2783 if (!pfd
->mIsTextFrame
&& assign
.TotalGaps()) {
2784 // It is possible that we assign gaps to non-text frame.
2785 // Apply the gaps as margin around the frame.
2786 deltaICoord
+= aState
.Consume(assign
.mGapsAtStart
);
2787 dw
+= aState
.Consume(assign
.mGapsAtEnd
);
2789 pfd
->mBounds
.IStart(lineWM
) += deltaICoord
;
2791 ApplyLineJustificationToAnnotations(pfd
, aPSD
, deltaICoord
, dw
);
2793 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerWidthForSpan(aPSD
));
2800 * This method expands the given frame by the given reserved isize.
2803 nsLineLayout::ExpandRubyBox(PerFrameData
* aFrame
, nscoord aReservedISize
,
2804 nscoord aContainerWidth
)
2806 int32_t opportunities
= aFrame
->mJustificationInfo
.mInnerOpportunities
;
2807 // Each expandable ruby box has an gap at each of its sides. For
2808 // rb/rbc, see comment in ComputeFrameJustification; for rt/rtc,
2809 // see comment in this method below.
2810 int32_t gaps
= opportunities
* 2 + 2;
2811 JustificationApplicationState
state(gaps
, aReservedISize
);
2812 ApplyFrameJustification(aFrame
->mSpan
, state
);
2814 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2815 aFrame
->mBounds
.ISize(lineWM
) += aReservedISize
;
2816 aFrame
->mFrame
->SetRect(lineWM
, aFrame
->mBounds
, aContainerWidth
);
2820 * This method expands the given frame by the reserved inline size.
2821 * It also expands its annotations if they are expandable and have
2822 * reserved isize larger than zero.
2825 nsLineLayout::ExpandRubyBoxWithAnnotations(PerFrameData
* aFrame
,
2826 nscoord aContainerWidth
)
2828 nscoord reservedISize
= RubyUtils::GetReservedISize(aFrame
->mFrame
);
2829 if (reservedISize
) {
2830 ExpandRubyBox(aFrame
, reservedISize
, aContainerWidth
);
2833 for (PerFrameData
* annotation
= aFrame
->mNextAnnotation
;
2834 annotation
; annotation
= annotation
->mNextAnnotation
) {
2835 nscoord reservedISize
= RubyUtils::GetReservedISize(annotation
->mFrame
);
2836 if (!reservedISize
) {
2840 MOZ_ASSERT(annotation
->mSpan
);
2841 JustificationComputationState computeState
;
2842 ComputeFrameJustification(annotation
->mSpan
, computeState
);
2843 if (!computeState
.mFirstParticipant
) {
2846 // Add one gap at each side of this annotation.
2847 computeState
.mFirstParticipant
->mJustificationAssignment
.mGapsAtStart
= 1;
2848 computeState
.mLastParticipant
->mJustificationAssignment
.mGapsAtEnd
= 1;
2849 ExpandRubyBox(annotation
, reservedISize
, aContainerWidth
);
2850 ExpandInlineRubyBoxes(annotation
->mSpan
);
2855 * This method looks for all expandable ruby box in the given span, and
2856 * calls ExpandRubyBox to expand them in depth-first preorder.
2859 nsLineLayout::ExpandInlineRubyBoxes(PerSpanData
* aSpan
)
2861 nscoord containerWidth
= ContainerWidthForSpan(aSpan
);
2862 for (PerFrameData
* pfd
= aSpan
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
2863 if (RubyUtils::IsExpandableRubyBox(pfd
->mFrame
)) {
2864 ExpandRubyBoxWithAnnotations(pfd
, containerWidth
);
2867 ExpandInlineRubyBoxes(pfd
->mSpan
);
2872 // Align inline frames within the line according to the CSS text-align
2875 nsLineLayout::TextAlignLine(nsLineBox
* aLine
,
2879 * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller
2880 * only in cases where the last line needs special handling.
2882 PerSpanData
* psd
= mRootSpan
;
2883 WritingMode lineWM
= psd
->mWritingMode
;
2884 NS_WARN_IF_FALSE(psd
->mIEnd
!= NS_UNCONSTRAINEDSIZE
,
2885 "have unconstrained width; this should only result from "
2886 "very large sizes, not attempts at intrinsic width "
2888 nscoord availISize
= psd
->mIEnd
- psd
->mIStart
;
2889 nscoord remainingISize
= availISize
- aLine
->ISize();
2890 #ifdef NOISY_INLINEDIR_ALIGN
2891 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
2892 printf(": availISize=%d lineBounds.IStart=%d lineISize=%d delta=%d\n",
2893 availISize
, aLine
->IStart(), aLine
->ISize(), remainingISize
);
2896 // 'text-align-last: auto' is equivalent to the value of the 'text-align'
2897 // property except when 'text-align' is set to 'justify', in which case it
2898 // is 'justify' when 'text-justify' is 'distribute' and 'start' otherwise.
2900 // XXX: the code below will have to change when we implement text-justify
2903 uint8_t textAlign
= mStyleText
->mTextAlign
;
2904 bool textAlignTrue
= mStyleText
->mTextAlignTrue
;
2906 textAlignTrue
= mStyleText
->mTextAlignLastTrue
;
2907 if (mStyleText
->mTextAlignLast
== NS_STYLE_TEXT_ALIGN_AUTO
) {
2908 if (textAlign
== NS_STYLE_TEXT_ALIGN_JUSTIFY
) {
2909 textAlign
= NS_STYLE_TEXT_ALIGN_DEFAULT
;
2912 textAlign
= mStyleText
->mTextAlignLast
;
2916 bool isSVG
= mBlockReflowState
->frame
->IsSVGText();
2917 bool doTextAlign
= remainingISize
> 0 || textAlignTrue
;
2919 int32_t additionalGaps
= 0;
2920 if (!isSVG
&& (mHasRuby
|| (doTextAlign
&&
2921 textAlign
== NS_STYLE_TEXT_ALIGN_JUSTIFY
))) {
2922 JustificationComputationState computeState
;
2923 ComputeFrameJustification(psd
, computeState
);
2924 if (mHasRuby
&& computeState
.mFirstParticipant
) {
2925 PerFrameData
* firstFrame
= computeState
.mFirstParticipant
;
2926 if (firstFrame
->mFrame
->StyleContext()->IsInlineDescendantOfRuby()) {
2927 MOZ_ASSERT(!firstFrame
->mJustificationAssignment
.mGapsAtStart
);
2928 firstFrame
->mJustificationAssignment
.mGapsAtStart
= 1;
2931 PerFrameData
* lastFrame
= computeState
.mLastParticipant
;
2932 if (lastFrame
->mFrame
->StyleContext()->IsInlineDescendantOfRuby()) {
2933 MOZ_ASSERT(!lastFrame
->mJustificationAssignment
.mGapsAtEnd
);
2934 lastFrame
->mJustificationAssignment
.mGapsAtEnd
= 1;
2940 if (!isSVG
&& doTextAlign
) {
2941 switch (textAlign
) {
2942 case NS_STYLE_TEXT_ALIGN_JUSTIFY
: {
2943 int32_t opportunities
=
2944 psd
->mFrame
->mJustificationInfo
.mInnerOpportunities
;
2945 if (opportunities
> 0) {
2946 int32_t gaps
= opportunities
* 2 + additionalGaps
;
2947 JustificationApplicationState
applyState(gaps
, remainingISize
);
2949 // Apply the justification, and make sure to update our linebox
2950 // width to account for it.
2951 aLine
->ExpandBy(ApplyFrameJustification(psd
, applyState
),
2952 ContainerWidthForSpan(psd
));
2954 MOZ_ASSERT(applyState
.mGaps
.mHandled
== applyState
.mGaps
.mCount
,
2955 "Unprocessed justification gaps");
2956 MOZ_ASSERT(applyState
.mWidth
.mConsumed
== applyState
.mWidth
.mAvailable
,
2957 "Unprocessed justification width");
2960 // Fall through to the default case if we could not justify to fill
2964 case NS_STYLE_TEXT_ALIGN_DEFAULT
:
2965 // default alignment is to start edge so do nothing
2968 case NS_STYLE_TEXT_ALIGN_LEFT
:
2969 case NS_STYLE_TEXT_ALIGN_MOZ_LEFT
:
2970 if (!lineWM
.IsBidiLTR()) {
2971 dx
= remainingISize
;
2975 case NS_STYLE_TEXT_ALIGN_RIGHT
:
2976 case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
:
2977 if (lineWM
.IsBidiLTR()) {
2978 dx
= remainingISize
;
2982 case NS_STYLE_TEXT_ALIGN_END
:
2983 dx
= remainingISize
;
2986 case NS_STYLE_TEXT_ALIGN_CENTER
:
2987 case NS_STYLE_TEXT_ALIGN_MOZ_CENTER
:
2988 dx
= remainingISize
/ 2;
2994 ExpandInlineRubyBoxes(mRootSpan
);
2997 if (mPresContext
->BidiEnabled() &&
2998 (!mPresContext
->IsVisualMode() || !lineWM
.IsBidiLTR())) {
2999 nsBidiPresUtils::ReorderFrames(psd
->mFirstFrame
->mFrame
,
3000 aLine
->GetChildCount(),
3001 lineWM
, mContainerWidth
,
3002 psd
->mIStart
+ mTextIndent
+ dx
);
3004 aLine
->IndentBy(dx
, mContainerWidth
);
3007 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
3008 pfd
->mBounds
.IStart(lineWM
) += dx
;
3009 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, ContainerWidthForSpan(psd
));
3011 aLine
->IndentBy(dx
, mContainerWidth
);
3016 nsLineLayout::RelativePositionFrames(nsOverflowAreas
& aOverflowAreas
)
3018 RelativePositionFrames(mRootSpan
, aOverflowAreas
);
3022 nsLineLayout::RelativePositionFrames(PerSpanData
* psd
, nsOverflowAreas
& aOverflowAreas
)
3024 nsOverflowAreas overflowAreas
;
3025 WritingMode wm
= psd
->mWritingMode
;
3026 if (psd
!= mRootSpan
) {
3027 // The span's overflow areas come in three parts:
3028 // -- this frame's width and height
3029 // -- pfd->mOverflowAreas, which is the area of a bullet or the union
3030 // of a relatively positioned frame's absolute children
3031 // -- the bounds of all inline descendants
3032 // The former two parts are computed right here, we gather the descendants
3034 // At this point psd->mFrame->mBounds might be out of date since
3035 // bidi reordering can move and resize the frames. So use the frame's
3036 // rect instead of mBounds.
3037 nsRect
adjustedBounds(nsPoint(0, 0), psd
->mFrame
->mFrame
->GetSize());
3039 overflowAreas
.ScrollableOverflow().UnionRect(
3040 psd
->mFrame
->mOverflowAreas
.ScrollableOverflow(), adjustedBounds
);
3041 overflowAreas
.VisualOverflow().UnionRect(
3042 psd
->mFrame
->mOverflowAreas
.VisualOverflow(), adjustedBounds
);
3045 LogicalRect
rect(wm
, psd
->mIStart
, mBStartEdge
,
3046 psd
->mICoord
- psd
->mIStart
, mFinalLineBSize
);
3047 // The minimum combined area for the frames that are direct
3048 // children of the block starts at the upper left corner of the
3049 // line and is sized to match the size of the line's bounding box
3050 // (the same size as the values returned from VerticalAlignFrames)
3051 overflowAreas
.VisualOverflow() = rect
.GetPhysicalRect(wm
, mContainerWidth
);
3052 overflowAreas
.ScrollableOverflow() = overflowAreas
.VisualOverflow();
3055 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
3056 nsIFrame
* frame
= pfd
->mFrame
;
3057 nsPoint origin
= frame
->GetPosition();
3059 // Adjust the origin of the frame
3060 if (pfd
->mRelativePos
) {
3061 //XXX temporary until ApplyRelativePositioning can handle logical offsets
3062 nsMargin physicalOffsets
=
3063 pfd
->mOffsets
.GetPhysicalMargin(pfd
->mFrame
->GetWritingMode());
3064 // right and bottom are handled by
3065 // nsHTMLReflowState::ComputeRelativeOffsets
3066 nsHTMLReflowState::ApplyRelativePositioning(pfd
->mFrame
,
3069 frame
->SetPosition(origin
);
3072 // We must position the view correctly before positioning its
3073 // descendants so that widgets are positioned properly (since only
3074 // some views have widgets).
3075 if (frame
->HasView())
3076 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext
, frame
,
3077 frame
->GetView(), pfd
->mOverflowAreas
.VisualOverflow(),
3078 NS_FRAME_NO_SIZE_VIEW
);
3080 // Note: the combined area of a child is in its coordinate
3081 // system. We adjust the childs combined area into our coordinate
3082 // system before computing the aggregated value by adding in
3083 // <b>x</b> and <b>y</b> which were computed above.
3086 // Compute a new combined area for the child span before
3087 // aggregating it into our combined area.
3088 RelativePositionFrames(pfd
->mSpan
, r
);
3090 r
= pfd
->mOverflowAreas
;
3091 if (pfd
->mIsTextFrame
) {
3092 // We need to recompute overflow areas in two cases:
3093 // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
3094 // (2) When there are text decorations, since we can't recompute the
3095 // overflow area until Reflow and VerticalAlignLine have finished
3096 if (pfd
->mRecomputeOverflow
||
3097 frame
->StyleContext()->HasTextDecorationLines()) {
3098 nsTextFrame
* f
= static_cast<nsTextFrame
*>(frame
);
3099 r
= f
->RecomputeOverflow(*mBlockReflowState
);
3101 frame
->FinishAndStoreOverflow(r
, frame
->GetSize());
3104 // If we have something that's not an inline but with a complex frame
3105 // hierarchy inside that contains views, they need to be
3107 // All descendant views must be repositioned even if this frame
3108 // does have a view in case this frame's view does not have a
3109 // widget and some of the descendant views do have widgets --
3110 // otherwise the widgets won't be repositioned.
3111 nsContainerFrame::PositionChildViews(frame
);
3114 // Do this here (rather than along with setting the overflow rect
3115 // below) so we get leaf frames as well. No need to worry
3116 // about the root span, since it doesn't have a frame.
3117 if (frame
->HasView())
3118 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext
, frame
,
3121 NS_FRAME_NO_MOVE_VIEW
);
3123 overflowAreas
.UnionWith(r
+ origin
);
3126 // If we just computed a spans combined area, we need to update its
3128 if (psd
!= mRootSpan
) {
3129 PerFrameData
* spanPFD
= psd
->mFrame
;
3130 nsIFrame
* frame
= spanPFD
->mFrame
;
3131 frame
->FinishAndStoreOverflow(overflowAreas
, frame
->GetSize());
3133 aOverflowAreas
= overflowAreas
;
3137 nsLineLayout::AdvanceICoord(nscoord aAmount
)
3139 mCurrentSpan
->mICoord
+= aAmount
;
3143 nsLineLayout::GetWritingMode()
3145 return mRootSpan
->mWritingMode
;
3149 nsLineLayout::GetCurrentICoord()
3151 return mCurrentSpan
->mICoord
;