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 "nsStyleConsts.h"
16 #include "nsContainerFrame.h"
17 #include "nsFloatManager.h"
18 #include "nsStyleContext.h"
19 #include "nsPresContext.h"
20 #include "nsRenderingContext.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"
30 #undef NOISY_INLINEDIR_ALIGN
31 #undef NOISY_BLOCKDIR_ALIGN
32 #undef REALLY_NOISY_BLOCKDIR_ALIGN
34 #undef REALLY_NOISY_REFLOW
36 #undef REALLY_NOISY_PUSHING
38 #undef NOISY_MAX_ELEMENT_SIZE
39 #undef REALLY_NOISY_MAX_ELEMENT_SIZE
40 #undef NOISY_CAN_PLACE_FRAME
42 #undef REALLY_NOISY_TRIM
45 using namespace mozilla
;
47 //----------------------------------------------------------------------
51 nsLineLayout::nsLineLayout(nsPresContext
* aPresContext
,
52 nsFloatManager
* aFloatManager
,
53 const nsHTMLReflowState
* aOuterReflowState
,
54 const nsLineList::iterator
* aLine
)
55 : mPresContext(aPresContext
),
56 mFloatManager(aFloatManager
),
57 mBlockReflowState(aOuterReflowState
),
58 mLastOptionalBreakContent(nullptr),
59 mForceBreakContent(nullptr),
60 mBlockRS(nullptr),/* XXX temporary */
61 mLastOptionalBreakPriority(gfxBreakPriority::eNoBreak
),
62 mLastOptionalBreakContentOffset(-1),
63 mForceBreakContentOffset(-1),
66 mFirstLetterStyleOK(false),
68 mImpactedByFloats(false),
69 mLastFloatWasLetterFrame(false),
75 mInFirstLetter(false),
77 mDirtyNextLine(false),
80 MOZ_ASSERT(aOuterReflowState
, "aOuterReflowState must not be null");
81 NS_ASSERTION(aFloatManager
|| aOuterReflowState
->frame
->GetType() ==
82 nsGkAtoms::letterFrame
,
83 "float manager should be present");
84 MOZ_COUNT_CTOR(nsLineLayout
);
86 // Stash away some style data that we need
87 nsBlockFrame
* blockFrame
= do_QueryFrame(aOuterReflowState
->frame
);
89 mStyleText
= blockFrame
->StyleTextForLineLayout();
91 mStyleText
= aOuterReflowState
->frame
->StyleText();
94 mTotalPlacedFrames
= 0;
98 mInflationMinFontSize
=
99 nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowState
->frame
);
101 // Instead of always pre-initializing the free-lists for frames and
102 // spans, we do it on demand so that situations that only use a few
103 // frames and spans won't waste a lot of time in unneeded
105 PL_INIT_ARENA_POOL(&mArena
, "nsLineLayout", 1024);
106 mFrameFreeList
= nullptr;
107 mSpanFreeList
= nullptr;
109 mCurrentSpan
= mRootSpan
= nullptr;
118 nsLineLayout::~nsLineLayout()
120 MOZ_COUNT_DTOR(nsLineLayout
);
122 NS_ASSERTION(nullptr == mRootSpan
, "bad line-layout user");
124 PL_FinishArenaPool(&mArena
);
127 // Find out if the frame has a non-null prev-in-flow, i.e., whether it
128 // is a continuation.
130 HasPrevInFlow(nsIFrame
*aFrame
)
132 nsIFrame
*prevInFlow
= aFrame
->GetPrevInFlow();
133 return prevInFlow
!= nullptr;
137 nsLineLayout::BeginLineReflow(nscoord aICoord
, nscoord aBCoord
,
138 nscoord aISize
, nscoord aBSize
,
139 bool aImpactedByFloats
,
141 WritingMode aWritingMode
,
142 nscoord aContainerWidth
)
144 NS_ASSERTION(nullptr == mRootSpan
, "bad linelayout user");
145 NS_WARN_IF_FALSE(aISize
!= NS_UNCONSTRAINEDSIZE
,
146 "have unconstrained width; this should only result from "
147 "very large sizes, not attempts at intrinsic width "
150 if ((aISize
!= NS_UNCONSTRAINEDSIZE
) && CRAZY_SIZE(aISize
)) {
151 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
152 printf(": Init: bad caller: width WAS %d(0x%x)\n",
155 if ((aBSize
!= NS_UNCONSTRAINEDSIZE
) && CRAZY_SIZE(aBSize
)) {
156 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
157 printf(": Init: bad caller: height WAS %d(0x%x)\n",
162 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
163 printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n",
164 aICoord
, aBCoord
, aISize
, aBSize
,
165 aImpactedByFloats
?"true":"false",
166 aIsTopOfPage
? "top-of-page" : "");
169 mSpansAllocated
= mSpansFreed
= mFramesAllocated
= mFramesFreed
= 0;
172 mFirstLetterStyleOK
= false;
173 mIsTopOfPage
= aIsTopOfPage
;
174 mImpactedByFloats
= aImpactedByFloats
;
175 mTotalPlacedFrames
= 0;
178 mLineEndsInBR
= false;
180 mMaxStartBoxBSize
= mMaxEndBoxBSize
= 0;
183 mLineBox
->ClearHasBullet();
186 PerSpanData
* psd
= NewPerSpanData();
187 mCurrentSpan
= mRootSpan
= psd
;
188 psd
->mReflowState
= mBlockReflowState
;
189 psd
->mIStart
= aICoord
;
190 psd
->mICoord
= aICoord
;
191 psd
->mIEnd
= aICoord
+ aISize
;
192 mContainerWidth
= aContainerWidth
;
194 // If we're in a constrained height frame, then we don't allow a
195 // max line box width to take effect.
196 if (!(LineContainerFrame()->GetStateBits() &
197 NS_FRAME_IN_CONSTRAINED_HEIGHT
)) {
199 // If the available size is greater than the maximum line box width (if
200 // specified), then we need to adjust the line box width to be at the max
202 nscoord maxLineBoxWidth
=
203 LineContainerFrame()->PresContext()->PresShell()->MaxLineBoxWidth();
205 if (maxLineBoxWidth
> 0 &&
206 psd
->mIEnd
- psd
->mIStart
> maxLineBoxWidth
) {
207 psd
->mIEnd
= psd
->mIStart
+ maxLineBoxWidth
;
211 mBStartEdge
= aBCoord
;
214 !mStyleText
->WhiteSpaceCanWrapStyle() || LineContainerFrame()->IsSVGText();
215 psd
->mWritingMode
= aWritingMode
;
217 // If this is the first line of a block then see if the text-indent
218 // property amounts to anything.
220 if (0 == mLineNumber
&& !HasPrevInFlow(mBlockReflowState
->frame
)) {
221 const nsStyleCoord
&textIndent
= mStyleText
->mTextIndent
;
222 nscoord pctBasis
= 0;
223 if (textIndent
.HasPercent()) {
225 nsHTMLReflowState::GetContainingBlockContentWidth(mBlockReflowState
);
228 mLineBox
->DisableResizeReflowOptimization();
231 nscoord indent
= nsRuleNode::ComputeCoordPercentCalc(textIndent
, pctBasis
);
233 mTextIndent
= indent
;
235 psd
->mICoord
+= indent
;
240 nsLineLayout::EndLineReflow()
243 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
244 printf(": EndLineReflow: width=%d\n", mRootSpan
->mICoord
- mRootSpan
->mIStart
);
248 mCurrentSpan
= mRootSpan
= nullptr;
250 NS_ASSERTION(mSpansAllocated
== mSpansFreed
, "leak");
251 NS_ASSERTION(mFramesAllocated
== mFramesFreed
, "leak");
254 static int32_t maxSpansAllocated
= NS_LINELAYOUT_NUM_SPANS
;
255 static int32_t maxFramesAllocated
= NS_LINELAYOUT_NUM_FRAMES
;
256 if (mSpansAllocated
> maxSpansAllocated
) {
257 printf("XXX: saw a line with %d spans\n", mSpansAllocated
);
258 maxSpansAllocated
= mSpansAllocated
;
260 if (mFramesAllocated
> maxFramesAllocated
) {
261 printf("XXX: saw a line with %d frames\n", mFramesAllocated
);
262 maxFramesAllocated
= mFramesAllocated
;
267 // XXX swtich to a single mAvailLineWidth that we adjust as each frame
268 // on the line is placed. Each span can still have a per-span mICoord that
269 // tracks where a child frame is going in its span; they don't need a
273 nsLineLayout::UpdateBand(const nsRect
& aNewAvailSpace
,
274 nsIFrame
* aFloatFrame
)
276 WritingMode lineWM
= mRootSpan
->mWritingMode
;
277 LogicalRect
availSpace(lineWM
, aNewAvailSpace
, mContainerWidth
);
278 #ifdef REALLY_NOISY_REFLOW
279 printf("nsLL::UpdateBand %d, %d, %d, %d, (logical %d, %d, %d, %d); frame=%p\n will set mImpacted to true\n",
280 aNewAvailSpace
.x
, aNewAvailSpace
.y
,
281 aNewAvailSpace
.width
, aNewAvailSpace
.height
,
282 availSpace
.IStart(lineWM
), availSpace
.BStart(lineWM
),
283 availSpace
.ISize(lineWM
), availSpace
.BSize(lineWM
),
287 if ((availSpace
.ISize(lineWM
) != NS_UNCONSTRAINEDSIZE
) &&
288 CRAZY_SIZE(availSpace
.ISize(lineWM
))) {
289 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
290 printf(": UpdateBand: bad caller: ISize WAS %d(0x%x)\n",
291 availSpace
.ISize(lineWM
), availSpace
.ISize(lineWM
));
293 if ((availSpace
.BSize(lineWM
) != NS_UNCONSTRAINEDSIZE
) &&
294 CRAZY_SIZE(availSpace
.BSize(lineWM
))) {
295 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
296 printf(": UpdateBand: bad caller: BSize WAS %d(0x%x)\n",
297 availSpace
.BSize(lineWM
), availSpace
.BSize(lineWM
));
301 // Compute the difference between last times width and the new width
302 NS_WARN_IF_FALSE(mRootSpan
->mIEnd
!= NS_UNCONSTRAINEDSIZE
&&
303 aNewAvailSpace
.width
!= NS_UNCONSTRAINEDSIZE
,
304 "have unconstrained width; this should only result from "
305 "very large sizes, not attempts at intrinsic width "
307 // The root span's mIStart moves to aICoord
308 nscoord deltaICoord
= availSpace
.IStart(lineWM
) - mRootSpan
->mIStart
;
309 // The width of all spans changes by this much (the root span's
310 // mIEnd moves to aICoord + aISize, its new width is aISize)
311 nscoord deltaISize
= availSpace
.ISize(lineWM
) -
312 (mRootSpan
->mIEnd
- mRootSpan
->mIStart
);
314 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
315 printf(": UpdateBand: %d,%d,%d,%d deltaISize=%d deltaICoord=%d\n",
316 aNewAvailSpace
.x
, aNewAvailSpace
.y
,
317 aNewAvailSpace
.width
, aNewAvailSpace
.height
, deltaISize
, deltaICoord
);
320 // Update the root span position
321 mRootSpan
->mIStart
+= deltaICoord
;
322 mRootSpan
->mIEnd
+= deltaICoord
;
323 mRootSpan
->mICoord
+= deltaICoord
;
325 // Now update the right edges of the open spans to account for any
326 // change in available space width
327 for (PerSpanData
* psd
= mCurrentSpan
; psd
; psd
= psd
->mParent
) {
328 psd
->mIEnd
+= deltaISize
;
329 psd
->mContainsFloat
= true;
331 printf(" span %p: oldIEnd=%d newIEnd=%d\n",
332 psd
, psd
->mIEnd
- deltaISize
, psd
->mIEnd
);
335 NS_ASSERTION(mRootSpan
->mContainsFloat
&&
336 mRootSpan
->mIStart
== availSpace
.IStart(lineWM
) &&
337 mRootSpan
->mIEnd
== availSpace
.IEnd(lineWM
),
338 "root span was updated incorrectly?");
340 // Update frame bounds
341 // Note: Only adjust the outermost frames (the ones that are direct
342 // children of the block), not the ones in the child spans. The reason
343 // is simple: the frames in the spans have coordinates local to their
344 // parent therefore they are moved when their parent span is moved.
345 if (deltaICoord
!= 0) {
346 for (PerFrameData
* pfd
= mRootSpan
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
347 pfd
->mBounds
.IStart(lineWM
) += deltaICoord
;
351 mBStartEdge
= availSpace
.BStart(lineWM
);
352 mImpactedByFloats
= true;
354 mLastFloatWasLetterFrame
= nsGkAtoms::letterFrame
== aFloatFrame
->GetType();
357 nsLineLayout::PerSpanData
*
358 nsLineLayout::NewPerSpanData()
360 PerSpanData
* psd
= mSpanFreeList
;
363 PL_ARENA_ALLOCATE(mem
, &mArena
, sizeof(PerSpanData
));
365 NS_RUNTIMEABORT("OOM");
367 psd
= reinterpret_cast<PerSpanData
*>(mem
);
370 mSpanFreeList
= psd
->mNextFreeSpan
;
372 psd
->mParent
= nullptr;
373 psd
->mFrame
= nullptr;
374 psd
->mFirstFrame
= nullptr;
375 psd
->mLastFrame
= nullptr;
376 psd
->mContainsFloat
= false;
377 psd
->mZeroEffectiveSpanBox
= false;
378 psd
->mHasNonemptyContent
= false;
387 nsLineLayout::BeginSpan(nsIFrame
* aFrame
,
388 const nsHTMLReflowState
* aSpanReflowState
,
389 nscoord aIStart
, nscoord aIEnd
,
392 NS_ASSERTION(aIEnd
!= NS_UNCONSTRAINEDSIZE
,
393 "should no longer be using unconstrained sizes");
395 nsFrame::IndentBy(stdout
, mSpanDepth
+1);
396 nsFrame::ListTag(stdout
, aFrame
);
397 printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aIStart
, aIEnd
);
400 PerSpanData
* psd
= NewPerSpanData();
401 // Link up span frame's pfd to point to its child span data
402 PerFrameData
* pfd
= mCurrentSpan
->mLastFrame
;
403 NS_ASSERTION(pfd
->mFrame
== aFrame
, "huh?");
408 psd
->mParent
= mCurrentSpan
;
409 psd
->mReflowState
= aSpanReflowState
;
410 psd
->mIStart
= aIStart
;
411 psd
->mICoord
= aIStart
;
413 psd
->mBaseline
= aBaseline
;
415 nsIFrame
* frame
= aSpanReflowState
->frame
;
416 psd
->mNoWrap
= !frame
->StyleText()->WhiteSpaceCanWrap(frame
);
417 psd
->mWritingMode
= aSpanReflowState
->GetWritingMode();
419 // Switch to new span
425 nsLineLayout::EndSpan(nsIFrame
* aFrame
)
427 NS_ASSERTION(mSpanDepth
> 0, "end-span without begin-span");
429 nsFrame::IndentBy(stdout
, mSpanDepth
);
430 nsFrame::ListTag(stdout
, aFrame
);
431 printf(": EndSpan width=%d\n", mCurrentSpan
->mICoord
- mCurrentSpan
->mIStart
);
433 PerSpanData
* psd
= mCurrentSpan
;
434 nscoord iSizeResult
= psd
->mLastFrame
? (psd
->mICoord
- psd
->mIStart
) : 0;
437 mCurrentSpan
->mReflowState
= nullptr; // no longer valid so null it out!
438 mCurrentSpan
= mCurrentSpan
->mParent
;
443 nsLineLayout::GetCurrentSpanCount() const
445 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
447 PerFrameData
* pfd
= mRootSpan
->mFirstFrame
;
448 while (nullptr != pfd
) {
456 nsLineLayout::SplitLineTo(int32_t aNewCount
)
458 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
460 #ifdef REALLY_NOISY_PUSHING
461 printf("SplitLineTo %d (current count=%d); before:\n", aNewCount
,
462 GetCurrentSpanCount());
463 DumpPerSpanData(mRootSpan
, 1);
465 PerSpanData
* psd
= mRootSpan
;
466 PerFrameData
* pfd
= psd
->mFirstFrame
;
467 while (nullptr != pfd
) {
468 if (--aNewCount
== 0) {
469 // Truncate list at pfd (we keep pfd, but anything following is freed)
470 PerFrameData
* next
= pfd
->mNext
;
471 pfd
->mNext
= nullptr;
472 psd
->mLastFrame
= pfd
;
474 // Now release all of the frames following pfd
476 while (nullptr != pfd
) {
478 pfd
->mNext
= mFrameFreeList
;
479 mFrameFreeList
= pfd
;
483 if (nullptr != pfd
->mSpan
) {
484 FreeSpan(pfd
->mSpan
);
493 printf("SplitLineTo %d (current count=%d); after:\n", aNewCount
,
494 GetCurrentSpanCount());
495 DumpPerSpanData(mRootSpan
, 1);
500 nsLineLayout::PushFrame(nsIFrame
* aFrame
)
502 PerSpanData
* psd
= mCurrentSpan
;
503 NS_ASSERTION(psd
->mLastFrame
->mFrame
== aFrame
, "pushing non-last frame");
505 #ifdef REALLY_NOISY_PUSHING
506 nsFrame::IndentBy(stdout
, mSpanDepth
);
507 printf("PushFrame %p, before:\n", psd
);
508 DumpPerSpanData(psd
, 1);
511 // Take the last frame off of the span's frame list
512 PerFrameData
* pfd
= psd
->mLastFrame
;
513 if (pfd
== psd
->mFirstFrame
) {
514 // We are pushing away the only frame...empty the list
515 psd
->mFirstFrame
= nullptr;
516 psd
->mLastFrame
= nullptr;
519 PerFrameData
* prevFrame
= pfd
->mPrev
;
520 prevFrame
->mNext
= nullptr;
521 psd
->mLastFrame
= prevFrame
;
524 // Now free it, and if it has a span, free that too
525 pfd
->mNext
= mFrameFreeList
;
526 mFrameFreeList
= pfd
;
530 if (nullptr != pfd
->mSpan
) {
531 FreeSpan(pfd
->mSpan
);
534 nsFrame::IndentBy(stdout
, mSpanDepth
);
535 printf("PushFrame: %p after:\n", psd
);
536 DumpPerSpanData(psd
, 1);
541 nsLineLayout::FreeSpan(PerSpanData
* psd
)
544 PerFrameData
* pfd
= psd
->mFirstFrame
;
545 while (nullptr != pfd
) {
546 if (nullptr != pfd
->mSpan
) {
547 FreeSpan(pfd
->mSpan
);
549 PerFrameData
* next
= pfd
->mNext
;
550 pfd
->mNext
= mFrameFreeList
;
551 mFrameFreeList
= pfd
;
558 // Now put the span on the free list since it's free too
559 psd
->mNextFreeSpan
= mSpanFreeList
;
567 nsLineLayout::IsZeroBSize()
569 PerSpanData
* psd
= mCurrentSpan
;
570 PerFrameData
* pfd
= psd
->mFirstFrame
;
571 while (nullptr != pfd
) {
572 if (0 != pfd
->mBounds
.BSize(psd
->mWritingMode
)) {
580 nsLineLayout::PerFrameData
*
581 nsLineLayout::NewPerFrameData(nsIFrame
* aFrame
)
583 PerFrameData
* pfd
= mFrameFreeList
;
586 PL_ARENA_ALLOCATE(mem
, &mArena
, sizeof(PerFrameData
));
588 NS_RUNTIMEABORT("OOM");
590 pfd
= reinterpret_cast<PerFrameData
*>(mem
);
593 mFrameFreeList
= pfd
->mNext
;
595 pfd
->mSpan
= nullptr;
596 pfd
->mNext
= nullptr;
597 pfd
->mPrev
= nullptr;
598 pfd
->mFlags
= 0; // all flags default to false
599 pfd
->mFrame
= aFrame
;
601 WritingMode frameWM
= aFrame
->GetWritingMode();
602 WritingMode lineWM
= mRootSpan
->mWritingMode
;
603 pfd
->mBounds
= LogicalRect(lineWM
);
604 pfd
->mMargin
= LogicalMargin(frameWM
);
605 pfd
->mBorderPadding
= LogicalMargin(frameWM
);
606 pfd
->mOffsets
= LogicalMargin(frameWM
);
609 pfd
->mBlockDirAlign
= 0xFF;
616 nsLineLayout::LineIsBreakable() const
618 // XXX mTotalPlacedFrames should go away and we should just use
619 // mLineIsEmpty here instead
620 if ((0 != mTotalPlacedFrames
) || mImpactedByFloats
) {
626 // Checks all four sides for percentage units. This means it should
627 // only be used for things (margin, padding) where percentages on top
628 // and bottom depend on the *width* just like percentages on left and
631 HasPercentageUnitSide(const nsStyleSides
& aSides
)
633 NS_FOR_CSS_SIDES(side
) {
634 if (aSides
.Get(side
).HasPercent())
641 IsPercentageAware(const nsIFrame
* aFrame
)
643 NS_ASSERTION(aFrame
, "null frame is not allowed");
645 nsIAtom
*fType
= aFrame
->GetType();
646 if (fType
== nsGkAtoms::textFrame
) {
647 // None of these things can ever be true for text frames.
651 // Some of these things don't apply to non-replaced inline frames
652 // (that is, fType == nsGkAtoms::inlineFrame), but we won't bother making
653 // things unnecessarily complicated, since they'll probably be set
656 const nsStyleMargin
* margin
= aFrame
->StyleMargin();
657 if (HasPercentageUnitSide(margin
->mMargin
)) {
661 const nsStylePadding
* padding
= aFrame
->StylePadding();
662 if (HasPercentageUnitSide(padding
->mPadding
)) {
666 // Note that borders can't be aware of percentages
668 const nsStylePosition
* pos
= aFrame
->StylePosition();
670 if ((pos
->WidthDependsOnContainer() &&
671 pos
->mWidth
.GetUnit() != eStyleUnit_Auto
) ||
672 pos
->MaxWidthDependsOnContainer() ||
673 pos
->MinWidthDependsOnContainer() ||
674 pos
->OffsetHasPercent(NS_SIDE_RIGHT
) ||
675 pos
->OffsetHasPercent(NS_SIDE_LEFT
)) {
679 if (eStyleUnit_Auto
== pos
->mWidth
.GetUnit()) {
680 // We need to check for frames that shrink-wrap when they're auto
682 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
683 if (disp
->mDisplay
== NS_STYLE_DISPLAY_INLINE_BLOCK
||
684 disp
->mDisplay
== NS_STYLE_DISPLAY_INLINE_TABLE
||
685 fType
== nsGkAtoms::HTMLButtonControlFrame
||
686 fType
== nsGkAtoms::gfxButtonControlFrame
||
687 fType
== nsGkAtoms::fieldSetFrame
||
688 fType
== nsGkAtoms::comboboxDisplayFrame
) {
692 // Per CSS 2.1, section 10.3.2:
693 // If 'height' and 'width' both have computed values of 'auto' and
694 // the element has an intrinsic ratio but no intrinsic height or
695 // width and the containing block's width does not itself depend
696 // on the replaced element's width, then the used value of 'width'
697 // is calculated from the constraint equation used for
698 // block-level, non-replaced elements in normal flow.
699 nsIFrame
*f
= const_cast<nsIFrame
*>(aFrame
);
700 if (f
->GetIntrinsicRatio() != nsSize(0, 0) &&
701 // Some percents are treated like 'auto', so check != coord
702 pos
->mHeight
.GetUnit() != eStyleUnit_Coord
) {
703 const IntrinsicSize
&intrinsicSize
= f
->GetIntrinsicSize();
704 if (intrinsicSize
.width
.GetUnit() == eStyleUnit_None
&&
705 intrinsicSize
.height
.GetUnit() == eStyleUnit_None
) {
715 nsLineLayout::ReflowFrame(nsIFrame
* aFrame
,
716 nsReflowStatus
& aReflowStatus
,
717 nsHTMLReflowMetrics
* aMetrics
,
720 // Initialize OUT parameter
721 aPushedFrame
= false;
723 PerFrameData
* pfd
= NewPerFrameData(aFrame
);
724 PerSpanData
* psd
= mCurrentSpan
;
725 psd
->AppendFrame(pfd
);
727 #ifdef REALLY_NOISY_REFLOW
728 nsFrame::IndentBy(stdout
, mSpanDepth
);
729 printf("%p: Begin ReflowFrame pfd=%p ", psd
, pfd
);
730 nsFrame::ListTag(stdout
, aFrame
);
734 if (mCurrentSpan
== mRootSpan
) {
735 pfd
->mFrame
->Properties().Remove(nsIFrame::LineBaselineOffset());
739 pfd
->mFrame
->Properties().Get(nsIFrame::LineBaselineOffset(), &hasLineOffset
);
740 NS_ASSERTION(!hasLineOffset
, "LineBaselineOffset was set but was not expected");
744 mTextJustificationNumSpaces
= 0;
745 mTextJustificationNumLetters
= 0;
747 // Stash copies of some of the computed state away for later
748 // (block-direction alignment, for example)
749 WritingMode frameWM
= aFrame
->GetWritingMode();
750 WritingMode lineWM
= mRootSpan
->mWritingMode
;
752 // NOTE: While the inline direction coordinate remains relative to the
753 // parent span, the block direction coordinate is fixed at the top
754 // edge for the line. During VerticalAlignFrames we will repair this
755 // so that the block direction coordinate is properly set and relative
756 // to the appropriate span.
757 pfd
->mBounds
.IStart(lineWM
) = psd
->mICoord
;
758 pfd
->mBounds
.BStart(lineWM
) = mBStartEdge
;
760 // We want to guarantee that we always make progress when
761 // formatting. Therefore, if the object being placed on the line is
762 // too big for the line, but it is the only thing on the line and is not
763 // impacted by a float, then we go ahead and place it anyway. (If the line
764 // is impacted by one or more floats, then it is safe to break because
765 // we can move the line down below float(s).)
767 // Capture this state *before* we reflow the frame in case it clears
768 // the state out. We need to know how to treat the current frame
770 bool notSafeToBreak
= LineIsEmpty() && !mImpactedByFloats
;
772 // Figure out whether we're talking about a textframe here
773 nsIAtom
* frameType
= aFrame
->GetType();
774 bool isText
= frameType
== nsGkAtoms::textFrame
;
776 // Inline-ish and text-ish things don't compute their width;
777 // everything else does. We need to give them an available width that
778 // reflects the space left on the line.
779 NS_WARN_IF_FALSE(psd
->mIEnd
!= NS_UNCONSTRAINEDSIZE
,
780 "have unconstrained width; this should only result from "
781 "very large sizes, not attempts at intrinsic width "
783 nscoord availableSpaceOnLine
= psd
->mIEnd
- psd
->mICoord
;
785 // Setup reflow state for reflowing the frame
786 Maybe
<nsHTMLReflowState
> reflowStateHolder
;
788 // Compute the available size for the frame. This available width
789 // includes room for the side margins.
790 // For now, set the available block-size to unconstrained always.
791 LogicalSize availSize
= mBlockReflowState
->ComputedSize(frameWM
);
792 availSize
.BSize(frameWM
) = NS_UNCONSTRAINEDSIZE
;
793 reflowStateHolder
.emplace(mPresContext
, *psd
->mReflowState
,
795 nsHTMLReflowState
& reflowState
= *reflowStateHolder
;
796 reflowState
.mLineLayout
= this;
797 reflowState
.mFlags
.mIsTopOfPage
= mIsTopOfPage
;
798 if (reflowState
.ComputedWidth() == NS_UNCONSTRAINEDSIZE
)
799 reflowState
.AvailableWidth() = availableSpaceOnLine
;
800 WritingMode stateWM
= reflowState
.GetWritingMode();
802 reflowState
.ComputedLogicalMargin().ConvertTo(frameWM
, stateWM
);
803 pfd
->mBorderPadding
=
804 reflowState
.ComputedLogicalBorderPadding().ConvertTo(frameWM
, stateWM
);
805 pfd
->SetFlag(PFD_RELATIVEPOS
,
806 reflowState
.mStyleDisplay
->IsRelativelyPositionedStyle());
807 if (pfd
->GetFlag(PFD_RELATIVEPOS
)) {
809 reflowState
.ComputedLogicalOffsets().ConvertTo(frameWM
, stateWM
);
812 // Calculate whether the the frame should have a start margin and
813 // subtract the margin from the available width if necessary.
814 // The margin will be applied to the starting inline coordinates of
815 // the frame in CanPlaceFrame() after reflowing the frame.
816 AllowForStartMargin(pfd
, reflowState
);
818 // if isText(), no need to propagate NS_FRAME_IS_DIRTY from the parent,
819 // because reflow doesn't look at the dirty bits on the frame being reflowed.
821 // See if this frame depends on the width of its containing block. If
822 // so, disable resize reflow optimizations for the line. (Note that,
823 // to be conservative, we do this if we *try* to fit a frame on a
824 // line, even if we don't succeed.) (Note also that we can only make
825 // this IsPercentageAware check *after* we've constructed our
826 // nsHTMLReflowState, because that construction may be what forces aFrame
827 // to lazily initialize its (possibly-percent-valued) intrinsic size.)
828 if (mGotLineBox
&& IsPercentageAware(aFrame
)) {
829 mLineBox
->DisableResizeReflowOptimization();
832 // Let frame know that are reflowing it. Note that we don't bother
833 // positioning the frame yet, because we're probably going to end up
834 // moving it when we do the block-direction alignment
835 aFrame
->WillReflow(mPresContext
);
837 // Adjust spacemanager coordinate system for the frame.
838 nsHTMLReflowMetrics
metrics(lineWM
);
840 metrics
.ISize(lineWM
) = nscoord(0xdeadbeef);
841 metrics
.BSize(lineWM
) = nscoord(0xdeadbeef);
843 nsRect physicalBounds
= pfd
->mBounds
.GetPhysicalRect(lineWM
, mContainerWidth
);
844 nscoord tx
= physicalBounds
.x
;
845 nscoord ty
= physicalBounds
.y
;
846 mFloatManager
->Translate(tx
, ty
);
848 int32_t savedOptionalBreakOffset
;
849 gfxBreakPriority savedOptionalBreakPriority
;
850 nsIContent
* savedOptionalBreakContent
=
851 GetLastOptionalBreakPosition(&savedOptionalBreakOffset
,
852 &savedOptionalBreakPriority
);
855 aFrame
->Reflow(mPresContext
, metrics
, *reflowStateHolder
, aReflowStatus
);
857 static_cast<nsTextFrame
*>(aFrame
)->
858 ReflowText(*this, availableSpaceOnLine
, psd
->mReflowState
->rendContext
,
859 metrics
, aReflowStatus
);
862 pfd
->mJustificationNumSpaces
= mTextJustificationNumSpaces
;
863 pfd
->mJustificationNumLetters
= mTextJustificationNumLetters
;
865 // See if the frame is a placeholderFrame and if it is process
866 // the float. At the same time, check if the frame has any non-collapsed-away
868 bool placedFloat
= false;
871 isEmpty
= pfd
->mFrame
->IsEmpty();
873 if (nsGkAtoms::placeholderFrame
== frameType
) {
875 pfd
->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE
, true);
876 nsIFrame
* outOfFlowFrame
= nsLayoutUtils::GetFloatFromPlaceholder(aFrame
);
877 if (outOfFlowFrame
) {
878 // Add mTrimmableWidth to the available width since if the line ends
879 // here, the width of the inline content will be reduced by
881 nscoord availableWidth
= psd
->mIEnd
- (psd
->mICoord
- mTrimmableWidth
);
883 // If we place floats after inline content where there's
884 // no break opportunity, we don't know how much additional
885 // width is required for the non-breaking content after the float,
886 // so we can't know whether the float plus that content will fit
887 // on the line. So for now, don't place floats after inline
888 // content where there's no break opportunity. This is incorrect
889 // but hopefully rare. Fixing it will require significant
890 // restructuring of line layout.
891 // We might as well allow zero-width floats to be placed, though.
894 placedFloat
= AddFloat(outOfFlowFrame
, availableWidth
);
895 NS_ASSERTION(!(outOfFlowFrame
->GetType() == nsGkAtoms::letterFrame
&&
896 GetFirstLetterStyleOK()),
897 "FirstLetterStyle set on line with floating first letter");
901 // Note non-empty text-frames for inline frame compatibility hackery
902 pfd
->SetFlag(PFD_ISTEXTFRAME
, true);
903 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(pfd
->mFrame
);
904 isEmpty
= !textFrame
->HasNoncollapsedCharacters();
906 pfd
->SetFlag(PFD_ISNONEMPTYTEXTFRAME
, true);
907 nsIContent
* content
= textFrame
->GetContent();
909 const nsTextFragment
* frag
= content
->GetText();
911 pfd
->SetFlag(PFD_ISNONWHITESPACETEXTFRAME
,
912 !content
->TextIsOnlyWhitespace());
916 else if (nsGkAtoms::brFrame
== frameType
) {
917 pfd
->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE
, true);
920 if (nsGkAtoms::letterFrame
==frameType
) {
921 pfd
->SetFlag(PFD_ISLETTERFRAME
, true);
924 isEmpty
= !pfd
->mSpan
->mHasNonemptyContent
&& pfd
->mFrame
->IsSelfEmpty();
926 isEmpty
= pfd
->mFrame
->IsEmpty();
931 mFloatManager
->Translate(-tx
, -ty
);
933 NS_ASSERTION(metrics
.ISize(lineWM
) >= 0, "bad inline size");
934 NS_ASSERTION(metrics
.BSize(lineWM
) >= 0,"bad block size");
935 if (metrics
.ISize(lineWM
) < 0) {
936 metrics
.ISize(lineWM
) = 0;
938 if (metrics
.BSize(lineWM
) < 0) {
939 metrics
.BSize(lineWM
) = 0;
943 // Note: break-before means ignore the reflow metrics since the
944 // frame will be reflowed another time.
945 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus
)) {
946 if (CRAZY_SIZE(metrics
.ISize(lineWM
)) ||
947 CRAZY_SIZE(metrics
.BSize(lineWM
))) {
948 printf("nsLineLayout: ");
949 nsFrame::ListTag(stdout
, aFrame
);
950 printf(" metrics=%d,%d!\n", metrics
.Width(), metrics
.Height());
952 if ((metrics
.Width() == nscoord(0xdeadbeef)) ||
953 (metrics
.Height() == nscoord(0xdeadbeef))) {
954 printf("nsLineLayout: ");
955 nsFrame::ListTag(stdout
, aFrame
);
956 printf(" didn't set w/h %d,%d!\n", metrics
.Width(), metrics
.Height());
961 // Unlike with non-inline reflow, the overflow area here does *not*
962 // include the accumulation of the frame's bounds and its inline
963 // descendants' bounds. Nor does it include the outline area; it's
964 // just the union of the bounds of any absolute children. That is
965 // added in later by nsLineLayout::ReflowInlineFrames.
966 pfd
->mOverflowAreas
= metrics
.mOverflowAreas
;
968 pfd
->mBounds
.ISize(lineWM
) = metrics
.ISize(lineWM
);
969 pfd
->mBounds
.BSize(lineWM
) = metrics
.BSize(lineWM
);
971 // Size the frame, but |RelativePositionFrames| will size the view.
972 aFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
974 // Tell the frame that we're done reflowing it
975 aFrame
->DidReflow(mPresContext
,
976 isText
? nullptr : reflowStateHolder
.ptr(),
977 nsDidReflowStatus::FINISHED
);
983 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus
)) {
984 // If frame is complete and has a next-in-flow, we need to delete
985 // them now. Do not do this when a break-before is signaled because
986 // the frame is going to get reflowed again (and may end up wanting
987 // a next-in-flow where it ends up).
988 if (NS_FRAME_IS_COMPLETE(aReflowStatus
)) {
989 nsIFrame
* kidNextInFlow
= aFrame
->GetNextInFlow();
990 if (nullptr != kidNextInFlow
) {
991 // Remove all of the childs next-in-flows. Make sure that we ask
992 // the right parent to do the removal (it's possible that the
993 // parent is not this because we are executing pullup code)
994 kidNextInFlow
->GetParent()->
995 DeleteNextInFlowChild(kidNextInFlow
, true);
999 // Check whether this frame breaks up text runs. All frames break up text
1000 // runs (hence return false here) except for text frames and inline containers.
1001 bool continuingTextRun
= aFrame
->CanContinueTextRun();
1003 // Clear any residual mTrimmableWidth if this isn't a text frame
1004 if (!continuingTextRun
&& !pfd
->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE
)) {
1005 mTrimmableWidth
= 0;
1008 // See if we can place the frame. If we can't fit it, then we
1010 bool optionalBreakAfterFits
;
1011 NS_ASSERTION(isText
||
1012 !reflowStateHolder
->IsFloating(),
1013 "How'd we get a floated inline frame? "
1014 "The frame ctor should've dealt with this.");
1015 if (CanPlaceFrame(pfd
, notSafeToBreak
, continuingTextRun
,
1016 savedOptionalBreakContent
!= nullptr, metrics
,
1017 aReflowStatus
, &optionalBreakAfterFits
)) {
1019 psd
->mHasNonemptyContent
= true;
1020 mLineIsEmpty
= false;
1022 // nonempty leaf content has been placed
1023 mLineAtStart
= false;
1027 // Place the frame, updating aBounds with the final size and
1028 // location. Then apply the bottom+right margins (as
1029 // appropriate) to the frame.
1030 PlaceFrame(pfd
, metrics
);
1031 PerSpanData
* span
= pfd
->mSpan
;
1033 // The frame we just finished reflowing is an inline
1034 // container. It needs its child frames aligned in the block direction,
1035 // so do most of it now.
1036 VerticalAlignFrames(span
);
1039 if (!continuingTextRun
) {
1040 if (!psd
->mNoWrap
&& (!LineIsEmpty() || placedFloat
)) {
1041 // record soft break opportunity after this content that can't be
1042 // part of a text run. This is not a text frame so we know
1043 // that offset INT32_MAX means "after the content".
1044 if (NotifyOptionalBreakPosition(aFrame
->GetContent(), INT32_MAX
, optionalBreakAfterFits
, gfxBreakPriority::eNormalBreak
)) {
1045 // If this returns true then we are being told to actually break here.
1046 aReflowStatus
= NS_INLINE_LINE_BREAK_AFTER(aReflowStatus
);
1053 aPushedFrame
= true;
1054 // Undo any saved break positions that the frame might have told us about,
1055 // since we didn't end up placing it
1056 RestoreSavedBreakPosition(savedOptionalBreakContent
,
1057 savedOptionalBreakOffset
,
1058 savedOptionalBreakPriority
);
1065 #ifdef REALLY_NOISY_REFLOW
1066 nsFrame::IndentBy(stdout
, mSpanDepth
);
1067 printf("End ReflowFrame ");
1068 nsFrame::ListTag(stdout
, aFrame
);
1069 printf(" status=%x\n", aReflowStatus
);
1074 nsLineLayout::AllowForStartMargin(PerFrameData
* pfd
,
1075 nsHTMLReflowState
& aReflowState
)
1077 NS_ASSERTION(!aReflowState
.IsFloating(),
1078 "How'd we get a floated inline frame? "
1079 "The frame ctor should've dealt with this.");
1081 WritingMode frameWM
= pfd
->mFrame
->GetWritingMode();
1083 // Only apply start-margin on the first-in flow for inline frames,
1084 // and make sure to not apply it to any inline other than the first
1085 // in an ib split. Note that the ib sibling (block-in-inline
1086 // sibling) annotations only live on the first continuation, but we
1087 // don't want to apply the start margin for later continuations
1088 // anyway. For box-decoration-break:clone we apply the start-margin
1089 // on all continuations.
1090 if ((pfd
->mFrame
->GetPrevContinuation() ||
1091 pfd
->mFrame
->FrameIsNonFirstInIBSplit()) &&
1092 aReflowState
.mStyleBorder
->mBoxDecorationBreak
==
1093 NS_STYLE_BOX_DECORATION_BREAK_SLICE
) {
1094 // Zero this out so that when we compute the max-element-width of
1095 // the frame we will properly avoid adding in the starting margin.
1096 pfd
->mMargin
.IStart(frameWM
) = 0;
1098 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE
!= aReflowState
.AvailableWidth(),
1099 "have unconstrained width; this should only result from "
1100 "very large sizes, not attempts at intrinsic width "
1102 if (NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedWidth()) {
1103 // For inline-ish and text-ish things (which don't compute widths
1104 // in the reflow state), adjust available width to account for the
1105 // start margin. The end margin will be accounted for when we
1106 // finish flowing the frame.
1107 aReflowState
.AvailableWidth() -= pfd
->mMargin
.IStart(frameWM
);
1113 nsLineLayout::GetCurrentFrameInlineDistanceFromBlock()
1117 for (psd
= mCurrentSpan
; psd
; psd
= psd
->mParent
) {
1124 * See if the frame can be placed now that we know it's desired size.
1125 * We can always place the frame if the line is empty. Note that we
1126 * know that the reflow-status is not a break-before because if it was
1127 * ReflowFrame above would have returned false, preventing this method
1128 * from being called. The logic in this method assumes that.
1130 * Note that there is no check against the Y coordinate because we
1131 * assume that the caller will take care of that.
1134 nsLineLayout::CanPlaceFrame(PerFrameData
* pfd
,
1135 bool aNotSafeToBreak
,
1136 bool aFrameCanContinueTextRun
,
1137 bool aCanRollBackBeforeFrame
,
1138 nsHTMLReflowMetrics
& aMetrics
,
1139 nsReflowStatus
& aStatus
,
1140 bool* aOptionalBreakAfterFits
)
1142 NS_PRECONDITION(pfd
&& pfd
->mFrame
, "bad args, null pointers for frame data");
1144 *aOptionalBreakAfterFits
= true;
1146 WritingMode frameWM
= pfd
->mFrame
->GetWritingMode();
1147 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1149 * We want to only apply the end margin if we're the last continuation and
1150 * either not in an {ib} split or the last inline in it. In all other
1151 * cases we want to zero it out. That means zeroing it out if any of these
1153 * 1) The frame is not complete (in this case it will get a next-in-flow)
1154 * 2) The frame is complete but has a non-fluid continuation on its
1155 * continuation chain. Note that if it has a fluid continuation, that
1156 * continuation will get destroyed later, so we don't want to drop the
1157 * end-margin in that case.
1158 * 3) The frame is in an {ib} split and is not the last part.
1160 * However, none of that applies if this is a letter frame (XXXbz why?)
1162 * For box-decoration-break:clone we apply the end margin on all
1163 * continuations (that are not letter frames).
1165 if ((NS_FRAME_IS_NOT_COMPLETE(aStatus
) ||
1166 pfd
->mFrame
->LastInFlow()->GetNextContinuation() ||
1167 pfd
->mFrame
->FrameIsNonLastInIBSplit()) &&
1168 !pfd
->GetFlag(PFD_ISLETTERFRAME
) &&
1169 pfd
->mFrame
->StyleBorder()->mBoxDecorationBreak
==
1170 NS_STYLE_BOX_DECORATION_BREAK_SLICE
) {
1171 pfd
->mMargin
.IEnd(frameWM
) = 0;
1174 // Convert the frame's margins to the line's writing mode and apply
1175 // the start margin to the frame bounds.
1176 LogicalMargin usedMargins
= pfd
->mMargin
.ConvertTo(lineWM
, frameWM
);
1177 nscoord startMargin
= usedMargins
.IStart(lineWM
);
1178 nscoord endMargin
= usedMargins
.IEnd(lineWM
);
1180 pfd
->mBounds
.IStart(lineWM
) += startMargin
;
1182 PerSpanData
* psd
= mCurrentSpan
;
1184 // When wrapping is off, everything fits.
1188 #ifdef NOISY_CAN_PLACE_FRAME
1189 if (nullptr != psd
->mFrame
) {
1190 nsFrame::ListTag(stdout
, psd
->mFrame
->mFrame
);
1193 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
1195 printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak
? "true" : "false");
1196 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1197 printf(" frameWidth=%d, margins=%d,%d\n",
1198 pfd
->mBounds
.IEnd(lineWM
) + endMargin
- psd
->mICoord
,
1199 startMargin
, endMargin
);
1202 // Set outside to true if the result of the reflow leads to the
1203 // frame sticking outside of our available area.
1204 bool outside
= pfd
->mBounds
.IEnd(lineWM
) - mTrimmableWidth
+ endMargin
>
1207 // If it fits, it fits
1208 #ifdef NOISY_CAN_PLACE_FRAME
1209 printf(" ==> inside\n");
1213 *aOptionalBreakAfterFits
= false;
1215 // When it doesn't fit, check for a few special conditions where we
1216 // allow it to fit anyway.
1217 if (0 == startMargin
+ pfd
->mBounds
.ISize(lineWM
) + endMargin
) {
1218 // Empty frames always fit right where they are
1219 #ifdef NOISY_CAN_PLACE_FRAME
1220 printf(" ==> empty frame fits\n");
1225 #ifdef FIX_BUG_50257
1226 // another special case: always place a BR
1227 if (nsGkAtoms::brFrame
== pfd
->mFrame
->GetType()) {
1228 #ifdef NOISY_CAN_PLACE_FRAME
1229 printf(" ==> BR frame fits\n");
1235 if (aNotSafeToBreak
) {
1236 // There are no frames on the line that take up width and the line is
1237 // not impacted by floats, so we must allow the current frame to be
1238 // placed on the line
1239 #ifdef NOISY_CAN_PLACE_FRAME
1240 printf(" ==> not-safe and not-impacted fits: ");
1241 while (nullptr != psd
) {
1242 printf("<psd=%p x=%d left=%d> ", psd
, psd
->mICoord
, psd
->mIStart
);
1250 // Special check for span frames
1251 if (pfd
->mSpan
&& pfd
->mSpan
->mContainsFloat
) {
1252 // If the span either directly or indirectly contains a float then
1253 // it fits. Why? It's kind of complicated, but here goes:
1255 // 1. CanPlaceFrame is used for all frame placements on a line,
1256 // and in a span. This includes recursively placement of frames
1257 // inside of spans, and the span itself. Because the logic always
1258 // checks for room before proceeding (the code above here), the
1259 // only things on a line will be those things that "fit".
1261 // 2. Before a float is placed on a line, the line has to be empty
1262 // (otherwise it's a "below current line" float and will be placed
1265 // Therefore, if the span directly or indirectly has a float
1266 // then it means that at the time of the placement of the float
1267 // the line was empty. Because of #1, only the frames that fit can
1268 // be added after that point, therefore we can assume that the
1269 // current span being placed has fit.
1271 // So how do we get here and have a span that should already fit
1272 // and yet doesn't: Simple: span's that have the no-wrap attribute
1273 // set on them and contain a float and are placed where they
1274 // don't naturally fit.
1278 if (aFrameCanContinueTextRun
) {
1279 // Let it fit, but we reserve the right to roll back.
1280 // Note that we usually won't get here because a text frame will break
1281 // itself to avoid exceeding the available width.
1282 // We'll only get here for text frames that couldn't break early enough.
1283 #ifdef NOISY_CAN_PLACE_FRAME
1284 printf(" ==> placing overflowing textrun, requesting backup\n");
1287 // We will want to try backup.
1292 #ifdef NOISY_CAN_PLACE_FRAME
1293 printf(" ==> didn't fit\n");
1295 aStatus
= NS_INLINE_LINE_BREAK_BEFORE();
1300 * Place the frame. Update running counters.
1303 nsLineLayout::PlaceFrame(PerFrameData
* pfd
, nsHTMLReflowMetrics
& aMetrics
)
1305 WritingMode frameWM
= pfd
->mFrame
->GetWritingMode();
1306 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1308 // Record ascent and update max-ascent and max-descent values
1309 if (aMetrics
.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE
) {
1310 pfd
->mAscent
= pfd
->mFrame
->GetLogicalBaseline(lineWM
);
1312 pfd
->mAscent
= aMetrics
.BlockStartAscent();
1315 // Advance to next inline coordinate
1316 mCurrentSpan
->mICoord
= pfd
->mBounds
.IEnd(lineWM
) +
1317 pfd
->mMargin
.ConvertTo(lineWM
, frameWM
).IEnd(lineWM
);
1319 // Count the number of non-placeholder frames on the line...
1320 if (pfd
->mFrame
->GetType() == nsGkAtoms::placeholderFrame
) {
1321 NS_ASSERTION(pfd
->mBounds
.ISize(lineWM
) == 0 &&
1322 pfd
->mBounds
.BSize(lineWM
) == 0,
1323 "placeholders should have 0 width/height (checking "
1324 "placeholders were never counted by the old code in "
1327 mTotalPlacedFrames
++;
1332 nsLineLayout::AddBulletFrame(nsIFrame
* aFrame
,
1333 const nsHTMLReflowMetrics
& aMetrics
)
1335 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
1336 NS_ASSERTION(mGotLineBox
, "must have line box");
1338 nsIFrame
*blockFrame
= mBlockReflowState
->frame
;
1339 NS_ASSERTION(blockFrame
->IsFrameOfType(nsIFrame::eBlockFrame
),
1340 "must be for block");
1341 if (!static_cast<nsBlockFrame
*>(blockFrame
)->BulletIsEmpty()) {
1343 mLineBox
->SetHasBullet();
1346 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1347 PerFrameData
* pfd
= NewPerFrameData(aFrame
);
1348 mRootSpan
->AppendFrame(pfd
);
1349 pfd
->SetFlag(PFD_ISBULLET
, true);
1350 if (aMetrics
.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE
) {
1351 pfd
->mAscent
= aFrame
->GetLogicalBaseline(lineWM
);
1353 pfd
->mAscent
= aMetrics
.BlockStartAscent();
1356 // Note: block-coord value will be updated during block-direction alignment
1357 pfd
->mBounds
= LogicalRect(lineWM
, aFrame
->GetRect(), mContainerWidth
);
1358 pfd
->mOverflowAreas
= aMetrics
.mOverflowAreas
;
1363 nsLineLayout::DumpPerSpanData(PerSpanData
* psd
, int32_t aIndent
)
1365 nsFrame::IndentBy(stdout
, aIndent
);
1366 printf("%p: left=%d x=%d right=%d\n", static_cast<void*>(psd
),
1367 psd
->mIStart
, psd
->mICoord
, psd
->mIEnd
);
1368 PerFrameData
* pfd
= psd
->mFirstFrame
;
1369 while (nullptr != pfd
) {
1370 nsFrame::IndentBy(stdout
, aIndent
+1);
1371 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1372 nsRect rect
= pfd
->mBounds
.GetPhysicalRect(psd
->mWritingMode
,
1374 printf(" %d,%d,%d,%d\n", rect
.x
, rect
.y
, rect
.width
, rect
.height
);
1376 DumpPerSpanData(pfd
->mSpan
, aIndent
+ 1);
1383 #define VALIGN_OTHER 0
1384 #define VALIGN_TOP 1
1385 #define VALIGN_BOTTOM 2
1388 nsLineLayout::VerticalAlignLine()
1390 // Synthesize a PerFrameData for the block frame
1391 PerFrameData
rootPFD(mBlockReflowState
->frame
->GetWritingMode());
1392 rootPFD
.mFrame
= mBlockReflowState
->frame
;
1393 rootPFD
.mAscent
= 0;
1394 mRootSpan
->mFrame
= &rootPFD
;
1396 // Partially place the children of the block frame. The baseline for
1397 // this operation is set to zero so that the y coordinates for all
1398 // of the placed children will be relative to there.
1399 PerSpanData
* psd
= mRootSpan
;
1400 VerticalAlignFrames(psd
);
1402 // *** Note that comments here still use the anachronistic term
1403 // "line-height" when we really mean "size of the line in the block
1404 // direction", "vertical-align" when we really mean "alignment in
1405 // the block direction", and "top" and "bottom" when we really mean
1406 // "block start" and "block end". This is partly for brevity and
1407 // partly to retain the association with the CSS line-height and
1408 // vertical-align properties.
1410 // Compute the line-height. The line-height will be the larger of:
1412 // [1] maxBCoord - minBCoord (the distance between the first child's
1413 // block-start edge and the last child's block-end edge)
1415 // [2] the maximum logical box block size (since not every frame may have
1416 // participated in #1; for example: "top" and "botttom" aligned frames)
1418 // [3] the minimum line height ("line-height" property set on the
1420 nscoord lineBSize
= psd
->mMaxBCoord
- psd
->mMinBCoord
;
1422 // Now that the line-height is computed, we need to know where the
1423 // baseline is in the line. Position baseline so that mMinBCoord is just
1424 // inside the start of the line box.
1425 nscoord baselineBCoord
;
1426 if (psd
->mMinBCoord
< 0) {
1427 baselineBCoord
= mBStartEdge
- psd
->mMinBCoord
;
1430 baselineBCoord
= mBStartEdge
;
1433 // It's also possible that the line block-size isn't tall enough because
1434 // of "top" and "bottom" aligned elements that were not accounted for in
1437 // The CSS2 spec doesn't really say what happens when to the
1438 // baseline in this situations. What we do is if the largest start
1439 // aligned box block size is greater than the line block-size then we leave
1440 // the baseline alone. If the largest end aligned box is greater
1441 // than the line block-size then we slide the baseline forward by the extra
1444 // Navigator 4 gives precedence to the first top/bottom aligned
1445 // object. We just let block end aligned objects win.
1446 if (lineBSize
< mMaxEndBoxBSize
) {
1447 // When the line is shorter than the maximum block start aligned box
1448 nscoord extra
= mMaxEndBoxBSize
- lineBSize
;
1449 baselineBCoord
+= extra
;
1450 lineBSize
= mMaxEndBoxBSize
;
1452 if (lineBSize
< mMaxStartBoxBSize
) {
1453 lineBSize
= mMaxStartBoxBSize
;
1455 #ifdef NOISY_BLOCKDIR_ALIGN
1456 printf(" [line]==> lineBSize=%d baselineBCoord=%d\n", lineBSize
, baselineBCoord
);
1459 // Now position all of the frames in the root span. We will also
1460 // recurse over the child spans and place any frames we find with
1461 // vertical-align: top or bottom.
1462 // XXX PERFORMANCE: set a bit per-span to avoid the extra work
1463 // (propagate it upward too)
1464 WritingMode lineWM
= psd
->mWritingMode
;
1465 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1466 if (pfd
->mBlockDirAlign
== VALIGN_OTHER
) {
1467 pfd
->mBounds
.BStart(lineWM
) += baselineBCoord
;
1468 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
1471 PlaceTopBottomFrames(psd
, -mBStartEdge
, lineBSize
);
1473 // Fill in returned line-box and max-element-width data
1474 mLineBox
->SetBounds(lineWM
,
1475 psd
->mIStart
, mBStartEdge
,
1476 psd
->mICoord
- psd
->mIStart
, lineBSize
,
1479 mFinalLineBSize
= lineBSize
;
1480 mLineBox
->SetLogicalAscent(baselineBCoord
- mBStartEdge
);
1481 #ifdef NOISY_BLOCKDIR_ALIGN
1483 " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
1484 mLineBox
->GetBounds().IStart(lineWM
), mLineBox
->GetBounds().BStart(lineWM
),
1485 mLineBox
->GetBounds().ISize(lineWM
), mLineBox
->GetBounds().BSize(lineWM
),
1486 mFinalLineBSize
, mLineBox
->GetLogicalAscent());
1489 // Undo root-span mFrame pointer to prevent brane damage later on...
1490 mRootSpan
->mFrame
= nullptr;
1493 // Place frames with CSS property vertical-align: top or bottom.
1495 nsLineLayout::PlaceTopBottomFrames(PerSpanData
* psd
,
1496 nscoord aDistanceFromStart
,
1499 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1500 PerSpanData
* span
= pfd
->mSpan
;
1502 NS_ASSERTION(0xFF != pfd
->mBlockDirAlign
, "umr");
1504 WritingMode frameWM
= pfd
->mFrame
->GetWritingMode();
1505 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1506 switch (pfd
->mBlockDirAlign
) {
1509 pfd
->mBounds
.BStart(lineWM
) = -aDistanceFromStart
- span
->mMinBCoord
;
1512 pfd
->mBounds
.BStart(lineWM
) =
1513 -aDistanceFromStart
+ pfd
->mMargin
.BStart(frameWM
);
1515 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
1516 #ifdef NOISY_BLOCKDIR_ALIGN
1518 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1519 printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n",
1520 pfd
->mBounds
.BStart(lineWM
), aDistanceFromStart
,
1521 span
? pfd
->mBorderPadding
.BStart(frameWM
) : 0,
1522 span
? span
->mBStartLeading
: 0);
1527 // Compute bottom leading
1528 pfd
->mBounds
.BStart(lineWM
) =
1529 -aDistanceFromStart
+ aLineBSize
- span
->mMaxBCoord
;
1532 pfd
->mBounds
.BStart(lineWM
) = -aDistanceFromStart
+ aLineBSize
-
1533 pfd
->mMargin
.BEnd(frameWM
) - pfd
->mBounds
.BSize(lineWM
);
1535 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
1536 #ifdef NOISY_BLOCKDIR_ALIGN
1538 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1539 printf(": y=%d\n", pfd
->mBounds
.BStart(lineWM
));
1544 nscoord fromStart
= aDistanceFromStart
+ pfd
->mBounds
.BStart(lineWM
);
1545 PlaceTopBottomFrames(span
, fromStart
, aLineBSize
);
1551 GetInflationForBlockDirAlignment(nsIFrame
* aFrame
,
1552 nscoord aInflationMinFontSize
)
1554 if (aFrame
->IsSVGText()) {
1555 const nsIFrame
* container
=
1556 nsLayoutUtils::GetClosestFrameOfType(aFrame
, nsGkAtoms::svgTextFrame
);
1557 NS_ASSERTION(container
, "expected to find an ancestor SVGTextFrame");
1559 static_cast<const SVGTextFrame
*>(container
)->GetFontSizeScaleFactor();
1561 return nsLayoutUtils::FontSizeInflationInner(aFrame
, aInflationMinFontSize
);
1564 #define BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM nscoord_MAX
1565 #define BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM nscoord_MIN
1567 // Place frames in the block direction within a given span (CSS property
1568 // vertical-align) Note: this doesn't place frames with vertical-align:
1569 // top or bottom as those have to wait until the entire line box block
1570 // size is known. This is called after the span frame has finished being
1571 // reflowed so that we know its block size.
1573 nsLineLayout::VerticalAlignFrames(PerSpanData
* psd
)
1575 // Get parent frame info
1576 PerFrameData
* spanFramePFD
= psd
->mFrame
;
1577 nsIFrame
* spanFrame
= spanFramePFD
->mFrame
;
1579 // Get the parent frame's font for all of the frames in this span
1580 nsRefPtr
<nsFontMetrics
> fm
;
1582 GetInflationForBlockDirAlignment(spanFrame
, mInflationMinFontSize
);
1583 nsLayoutUtils::GetFontMetricsForFrame(spanFrame
, getter_AddRefs(fm
),
1585 mBlockReflowState
->rendContext
->SetFont(fm
);
1587 bool preMode
= mStyleText
->WhiteSpaceIsSignificant();
1589 // See if the span is an empty continuation. It's an empty continuation iff:
1590 // - it has a prev-in-flow
1591 // - it has no next in flow
1592 // - it's zero sized
1593 WritingMode frameWM
= spanFramePFD
->mFrame
->GetWritingMode();
1594 WritingMode lineWM
= mRootSpan
->mWritingMode
;
1595 bool emptyContinuation
= psd
!= mRootSpan
&&
1596 spanFrame
->GetPrevInFlow() && !spanFrame
->GetNextInFlow() &&
1597 spanFramePFD
->mBounds
.IsZeroSize();
1599 #ifdef NOISY_BLOCKDIR_ALIGN
1600 printf("[%sSpan]", (psd
== mRootSpan
)?"Root":"");
1601 nsFrame::ListTag(stdout
, spanFrame
);
1602 printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s",
1603 preMode
? "yes" : "no",
1604 mPresContext
->CompatibilityMode() != eCompatibility_NavQuirks
? "yes" : "no",
1605 spanFramePFD
->mBounds
.ISize(lineWM
),
1606 spanFramePFD
->mBounds
.BSize(lineWM
),
1607 emptyContinuation
? "yes" : "no");
1608 if (psd
!= mRootSpan
) {
1609 WritingMode frameWM
= spanFramePFD
->mFrame
->GetWritingMode();
1610 printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
1611 spanFramePFD
->mBorderPadding
.Top(frameWM
),
1612 spanFramePFD
->mBorderPadding
.Right(frameWM
),
1613 spanFramePFD
->mBorderPadding
.Bottom(frameWM
),
1614 spanFramePFD
->mBorderPadding
.Left(frameWM
),
1615 spanFramePFD
->mMargin
.Top(frameWM
),
1616 spanFramePFD
->mMargin
.Right(frameWM
),
1617 spanFramePFD
->mMargin
.Bottom(frameWM
),
1618 spanFramePFD
->mMargin
.Left(frameWM
));
1623 // Compute the span's mZeroEffectiveSpanBox flag. What we are trying
1624 // to determine is how we should treat the span: should it act
1625 // "normally" according to css2 or should it effectively
1628 // In general, if the document being processed is in full standards
1629 // mode then it should act normally (with one exception). The
1630 // exception case is when a span is continued and yet the span is
1631 // empty (e.g. compressed whitespace). For this kind of span we treat
1632 // it as if it were not there so that it doesn't impact the
1635 // In almost standards mode or quirks mode, we should sometimes make
1636 // it disappear. The cases that matter are those where the span
1637 // contains no real text elements that would provide an ascent and
1638 // descent and height. However, if css style elements have been
1639 // applied to the span (border/padding/margin) so that it's clear the
1640 // document author is intending css2 behavior then we act as if strict
1643 // This code works correctly for preMode, because a blank line
1644 // in PRE mode is encoded as a text node with a LF in it, since
1645 // text nodes with only whitespace are considered in preMode.
1647 // Much of this logic is shared with the various implementations of
1648 // nsIFrame::IsEmpty since they need to duplicate the way it makes
1649 // some lines empty. However, nsIFrame::IsEmpty can't be reused here
1650 // since this code sets zeroEffectiveSpanBox even when there are
1651 // non-empty children.
1652 bool zeroEffectiveSpanBox
= false;
1653 // XXXldb If we really have empty continuations, then all these other
1654 // checks don't make sense for them.
1655 // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that
1656 // it agrees with this code. (If it doesn't agree, it probably should.)
1657 if ((emptyContinuation
||
1658 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
) &&
1659 ((psd
== mRootSpan
) ||
1660 (spanFramePFD
->mBorderPadding
.IsEmpty() &&
1661 spanFramePFD
->mMargin
.IsEmpty()))) {
1662 // This code handles an issue with compatibility with non-css
1663 // conformant browsers. In particular, there are some cases
1664 // where the font-size and line-height for a span must be
1665 // ignored and instead the span must *act* as if it were zero
1666 // sized. In general, if the span contains any non-compressed
1667 // text then we don't use this logic.
1668 // However, this is not propagated outwards, since (in compatibility
1669 // mode) we don't want big line heights for things like
1670 // <p><font size="-1">Text</font></p>
1672 // We shouldn't include any whitespace that collapses, unless we're
1673 // preformatted (in which case it shouldn't, but the width=0 test is
1674 // perhaps incorrect). This includes whitespace at the beginning of
1675 // a line and whitespace preceded (?) by other whitespace.
1676 // See bug 134580 and bug 155333.
1677 zeroEffectiveSpanBox
= true;
1678 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1679 if (pfd
->GetFlag(PFD_ISTEXTFRAME
) &&
1680 (pfd
->GetFlag(PFD_ISNONWHITESPACETEXTFRAME
) || preMode
||
1681 pfd
->mBounds
.ISize(mRootSpan
->mWritingMode
) != 0)) {
1682 zeroEffectiveSpanBox
= false;
1687 psd
->mZeroEffectiveSpanBox
= zeroEffectiveSpanBox
;
1689 // Setup baselineBCoord, minBCoord, and maxBCoord
1690 nscoord baselineBCoord
, minBCoord
, maxBCoord
;
1691 if (psd
== mRootSpan
) {
1692 // Use a zero baselineBCoord since we don't yet know where the baseline
1693 // will be (until we know how tall the line is; then we will
1694 // know). In addition, use extreme values for the minBCoord and maxBCoord
1695 // values so that only the child frames will impact their values
1696 // (since these are children of the block, there is no span box to
1697 // provide initial values).
1699 minBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
1700 maxBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
1701 #ifdef NOISY_BLOCKDIR_ALIGN
1702 printf("[RootSpan]");
1703 nsFrame::ListTag(stdout
, spanFrame
);
1704 printf(": pass1 valign frames: topEdge=%d minLineBSize=%d zeroEffectiveSpanBox=%s\n",
1705 mBStartEdge
, mMinLineBSize
,
1706 zeroEffectiveSpanBox
? "yes" : "no");
1710 // Compute the logical block size for this span. The logical block size
1711 // is based on the "line-height" value, not the font-size. Also
1712 // compute the top leading.
1714 GetInflationForBlockDirAlignment(spanFrame
, mInflationMinFontSize
);
1715 nscoord logicalBSize
= nsHTMLReflowState::
1716 CalcLineHeight(spanFrame
->GetContent(), spanFrame
->StyleContext(),
1717 mBlockReflowState
->ComputedHeight(),
1719 nscoord contentBSize
= spanFramePFD
->mBounds
.BSize(lineWM
) -
1720 spanFramePFD
->mBorderPadding
.BStart(frameWM
) -
1721 spanFramePFD
->mBorderPadding
.BEnd(frameWM
);
1723 // Special-case for a ::first-letter frame, set the line height to
1724 // the frame block size if the user has left line-height == normal
1725 if (spanFramePFD
->GetFlag(PFD_ISLETTERFRAME
) &&
1726 !spanFrame
->GetPrevInFlow() &&
1727 spanFrame
->StyleText()->mLineHeight
.GetUnit() == eStyleUnit_Normal
) {
1728 logicalBSize
= spanFramePFD
->mBounds
.BSize(lineWM
);
1731 nscoord leading
= logicalBSize
- contentBSize
;
1732 psd
->mBStartLeading
= leading
/ 2;
1733 psd
->mBEndLeading
= leading
- psd
->mBStartLeading
;
1734 psd
->mLogicalBSize
= logicalBSize
;
1736 if (zeroEffectiveSpanBox
) {
1737 // When the span-box is to be ignored, zero out the initial
1738 // values so that the span doesn't impact the final line
1739 // height. The contents of the span can impact the final line
1742 // Note that things are readjusted for this span after its children
1744 minBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
1745 maxBCoord
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
1749 // The initial values for the min and max block coord values are in the
1750 // span's coordinate space, and cover the logical block size of the span.
1751 // If there are child frames in this span that stick out of this area
1752 // then the minBCoord and maxBCoord are updated by the amount of logical
1753 // blockSize that is outside this range.
1754 minBCoord
= spanFramePFD
->mBorderPadding
.BStart(frameWM
) -
1755 psd
->mBStartLeading
;
1756 maxBCoord
= minBCoord
+ psd
->mLogicalBSize
;
1759 // This is the distance from the top edge of the parents visual
1760 // box to the baseline. The span already computed this for us,
1762 *psd
->mBaseline
= baselineBCoord
= spanFramePFD
->mAscent
;
1765 #ifdef NOISY_BLOCKDIR_ALIGN
1766 printf("[%sSpan]", (psd
== mRootSpan
)?"Root":"");
1767 nsFrame::ListTag(stdout
, spanFrame
);
1768 printf(": baseLine=%d logicalBSize=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n",
1769 baselineBCoord
, psd
->mLogicalBSize
, psd
->mBStartLeading
,
1770 spanFramePFD
->mBounds
.BSize(lineWM
),
1771 spanFramePFD
->mBorderPadding
.Top(frameWM
),
1772 spanFramePFD
->mBorderPadding
.Bottom(frameWM
),
1773 zeroEffectiveSpanBox
? "yes" : "no");
1777 nscoord maxStartBoxBSize
= 0;
1778 nscoord maxEndBoxBSize
= 0;
1779 PerFrameData
* pfd
= psd
->mFirstFrame
;
1780 while (nullptr != pfd
) {
1781 nsIFrame
* frame
= pfd
->mFrame
;
1782 WritingMode frameWM
= frame
->GetWritingMode();
1784 // sanity check (see bug 105168, non-reproducible crashes from null frame)
1785 NS_ASSERTION(frame
, "null frame in PerFrameData - something is very very bad");
1790 // Compute the logical block size of the frame
1791 nscoord logicalBSize
;
1792 PerSpanData
* frameSpan
= pfd
->mSpan
;
1794 // For span frames the logical-block-size and start-leading were
1795 // pre-computed when the span was reflowed.
1796 logicalBSize
= frameSpan
->mLogicalBSize
;
1799 // For other elements the logical block size is the same as the
1800 // frame's block size plus its margins.
1801 logicalBSize
= pfd
->mBounds
.BSize(lineWM
) +
1802 pfd
->mMargin
.BStartEnd(frameWM
);
1803 if (logicalBSize
< 0 &&
1804 mPresContext
->CompatibilityMode() == eCompatibility_NavQuirks
) {
1805 pfd
->mAscent
-= logicalBSize
;
1810 // Get vertical-align property ("vertical-align" is the CSS name for
1811 // block-direction align)
1812 const nsStyleCoord
& verticalAlign
=
1813 frame
->StyleTextReset()->mVerticalAlign
;
1814 uint8_t verticalAlignEnum
= frame
->VerticalAlignEnum();
1815 #ifdef NOISY_BLOCKDIR_ALIGN
1817 nsFrame::ListTag(stdout
, frame
);
1818 printf(": verticalAlignUnit=%d (enum == %d",
1819 verticalAlign
.GetUnit(),
1820 ((eStyleUnit_Enumerated
== verticalAlign
.GetUnit())
1821 ? verticalAlign
.GetIntValue()
1823 if (verticalAlignEnum
!= nsIFrame::eInvalidVerticalAlign
) {
1824 printf(", after SVG dominant-baseline conversion == %d",
1830 if (verticalAlignEnum
!= nsIFrame::eInvalidVerticalAlign
) {
1831 switch (verticalAlignEnum
) {
1833 case NS_STYLE_VERTICAL_ALIGN_BASELINE
:
1835 // The element's baseline is aligned with the baseline of
1837 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
- pfd
->mAscent
;
1838 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
1842 case NS_STYLE_VERTICAL_ALIGN_SUB
:
1844 // Lower the baseline of the box to the subscript offset
1845 // of the parent's box. This is identical to the baseline
1846 // alignment except for the addition of the subscript
1847 // offset to the baseline BCoord.
1848 nscoord parentSubscript
= fm
->SubscriptOffset();
1849 nscoord revisedBaselineBCoord
= baselineBCoord
+ parentSubscript
;
1850 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
- pfd
->mAscent
;
1851 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
1855 case NS_STYLE_VERTICAL_ALIGN_SUPER
:
1857 // Raise the baseline of the box to the superscript offset
1858 // of the parent's box. This is identical to the baseline
1859 // alignment except for the subtraction of the superscript
1860 // offset to the baseline BCoord.
1861 nscoord parentSuperscript
= fm
->SuperscriptOffset();
1862 nscoord revisedBaselineBCoord
= baselineBCoord
- parentSuperscript
;
1863 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
- pfd
->mAscent
;
1864 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
1868 case NS_STYLE_VERTICAL_ALIGN_TOP
:
1870 pfd
->mBlockDirAlign
= VALIGN_TOP
;
1871 nscoord subtreeBSize
= logicalBSize
;
1873 subtreeBSize
= frameSpan
->mMaxBCoord
- frameSpan
->mMinBCoord
;
1874 NS_ASSERTION(subtreeBSize
>= logicalBSize
,
1875 "unexpected subtree block size");
1877 if (subtreeBSize
> maxStartBoxBSize
) {
1878 maxStartBoxBSize
= subtreeBSize
;
1883 case NS_STYLE_VERTICAL_ALIGN_BOTTOM
:
1885 pfd
->mBlockDirAlign
= VALIGN_BOTTOM
;
1886 nscoord subtreeBSize
= logicalBSize
;
1888 subtreeBSize
= frameSpan
->mMaxBCoord
- frameSpan
->mMinBCoord
;
1889 NS_ASSERTION(subtreeBSize
>= logicalBSize
,
1890 "unexpected subtree block size");
1892 if (subtreeBSize
> maxEndBoxBSize
) {
1893 maxEndBoxBSize
= subtreeBSize
;
1898 case NS_STYLE_VERTICAL_ALIGN_MIDDLE
:
1900 // Align the midpoint of the frame with 1/2 the parents
1901 // x-height above the baseline.
1902 nscoord parentXHeight
= fm
->XHeight();
1904 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
-
1905 (parentXHeight
+ pfd
->mBounds
.BSize(lineWM
))/2;
1908 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
-
1909 (parentXHeight
+ logicalBSize
)/2 +
1910 pfd
->mMargin
.BStart(frameWM
);
1912 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
1916 case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
:
1918 // The top of the logical box is aligned with the top of
1919 // the parent element's text.
1920 // XXX For vertical text we will need a new API to get the logical
1922 nscoord parentAscent
= fm
->MaxAscent();
1924 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
- parentAscent
-
1925 pfd
->mBorderPadding
.BStart(frameWM
) + frameSpan
->mBStartLeading
;
1928 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
- parentAscent
+
1929 pfd
->mMargin
.BStart(frameWM
);
1931 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
1935 case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
:
1937 // The bottom of the logical box is aligned with the
1938 // bottom of the parent elements text.
1939 nscoord parentDescent
= fm
->MaxDescent();
1941 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
+ parentDescent
-
1942 pfd
->mBounds
.BSize(lineWM
) +
1943 pfd
->mBorderPadding
.BEnd(frameWM
) -
1944 frameSpan
->mBEndLeading
;
1947 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
+ parentDescent
-
1948 pfd
->mBounds
.BSize(lineWM
) -
1949 pfd
->mMargin
.BEnd(frameWM
);
1951 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
1955 case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
:
1957 // Align the midpoint of the frame with the baseline of the parent.
1959 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
-
1960 pfd
->mBounds
.BSize(lineWM
)/2;
1963 pfd
->mBounds
.BStart(lineWM
) = baselineBCoord
- logicalBSize
/2 +
1964 pfd
->mMargin
.BStart(frameWM
);
1966 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
1971 // We have either a coord, a percent, or a calc().
1972 nscoord pctBasis
= 0;
1973 if (verticalAlign
.HasPercent()) {
1974 // Percentages are like lengths, except treated as a percentage
1975 // of the elements line block size value.
1977 GetInflationForBlockDirAlignment(frame
, mInflationMinFontSize
);
1978 pctBasis
= nsHTMLReflowState::CalcLineHeight(frame
->GetContent(),
1979 frame
->StyleContext(), mBlockReflowState
->ComputedBSize(),
1983 nsRuleNode::ComputeCoordPercentCalc(verticalAlign
, pctBasis
);
1984 // According to the CSS2 spec (10.8.1), a positive value
1985 // "raises" the box by the given distance while a negative value
1986 // "lowers" the box by the given distance (with zero being the
1987 // baseline). Since Y coordinates increase towards the bottom of
1988 // the screen we reverse the sign.
1989 nscoord revisedBaselineBCoord
= baselineBCoord
- offset
;
1990 pfd
->mBounds
.BStart(lineWM
) = revisedBaselineBCoord
- pfd
->mAscent
;
1991 pfd
->mBlockDirAlign
= VALIGN_OTHER
;
1994 // Update minBCoord/maxBCoord for frames that we just placed. Do not factor
1995 // text into the equation.
1996 if (pfd
->mBlockDirAlign
== VALIGN_OTHER
) {
1997 // Text frames do not contribute to the min/max Y values for the
1998 // line (instead their parent frame's font-size contributes).
1999 // XXXrbs -- relax this restriction because it causes text frames
2000 // to jam together when 'font-size-adjust' is enabled
2001 // and layout is using dynamic font heights (bug 20394)
2002 // -- Note #1: With this code enabled and with the fact that we are not
2003 // using Em[Ascent|Descent] as nsDimensions for text metrics in
2004 // GFX mean that the discussion in bug 13072 cannot hold.
2005 // -- Note #2: We still don't want empty-text frames to interfere.
2006 // For example in quirks mode, avoiding empty text frames prevents
2007 // "tall" lines around elements like <hr> since the rules of <hr>
2008 // in quirks.css have pseudo text contents with LF in them.
2010 if (!pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2012 // Only consider non empty text frames when line-height=normal
2013 bool canUpdate
= !pfd
->GetFlag(PFD_ISTEXTFRAME
);
2014 if (!canUpdate
&& pfd
->GetFlag(PFD_ISNONWHITESPACETEXTFRAME
)) {
2016 frame
->StyleText()->mLineHeight
.GetUnit() == eStyleUnit_Normal
;
2020 nscoord blockStart
, blockEnd
;
2022 // For spans that were are now placing, use their position
2023 // plus their already computed min-Y and max-Y values for
2024 // computing blockStart and blockEnd.
2025 blockStart
= pfd
->mBounds
.BStart(lineWM
) + frameSpan
->mMinBCoord
;
2026 blockEnd
= pfd
->mBounds
.BStart(lineWM
) + frameSpan
->mMaxBCoord
;
2029 blockStart
= pfd
->mBounds
.BStart(lineWM
) -
2030 pfd
->mMargin
.BStart(frameWM
);
2031 blockEnd
= blockStart
+ logicalBSize
;
2034 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
&&
2036 // Check if it's a BR frame that is not alone on its line (it
2037 // is given a block size of zero to indicate this), and if so reset
2038 // blockStart and blockEnd so that BR frames don't influence the line.
2039 if (nsGkAtoms::brFrame
== frame
->GetType()) {
2040 blockStart
= BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
;
2041 blockEnd
= BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
;
2044 if (blockStart
< minBCoord
) minBCoord
= blockStart
;
2045 if (blockEnd
> maxBCoord
) maxBCoord
= blockEnd
;
2046 #ifdef NOISY_BLOCKDIR_ALIGN
2047 printf(" [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minBCoord=%d maxBCoord=%d\n",
2048 pfd
->mAscent
, pfd
->mBounds
.BSize(lineWM
),
2049 pfd
->mBorderPadding
.Top(frameWM
),
2050 pfd
->mBorderPadding
.Bottom(frameWM
),
2052 frameSpan
? frameSpan
->mBStartLeading
: 0,
2053 pfd
->mBounds
.BStart(lineWM
), minBCoord
, maxBCoord
);
2056 if (psd
!= mRootSpan
) {
2057 frame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
2063 // Factor in the minimum line block-size when handling the root-span for
2065 if (psd
== mRootSpan
) {
2066 // We should factor in the block element's minimum line-height (as
2067 // defined in section 10.8.1 of the css2 spec) assuming that
2068 // mZeroEffectiveSpanBox is not set on the root span. This only happens
2069 // in some cases in quirks mode:
2070 // (1) if the root span contains non-whitespace text directly (this
2071 // is handled by mZeroEffectiveSpanBox
2072 // (2) if this line has a bullet
2073 // (3) if this is the last line of an LI, DT, or DD element
2074 // (The last line before a block also counts, but not before a
2075 // BR) (NN4/IE5 quirk)
2077 // (1) and (2) above
2078 bool applyMinLH
= !psd
->mZeroEffectiveSpanBox
|| mHasBullet
;
2079 bool isLastLine
= (!mLineBox
->IsLineWrapped() && !mLineEndsInBR
);
2080 if (!applyMinLH
&& isLastLine
) {
2081 nsIContent
* blockContent
= mRootSpan
->mFrame
->mFrame
->GetContent();
2083 nsIAtom
*blockTagAtom
= blockContent
->Tag();
2084 // (3) above, if the last line of LI, DT, or DD
2085 if (blockTagAtom
== nsGkAtoms::li
||
2086 blockTagAtom
== nsGkAtoms::dt
||
2087 blockTagAtom
== nsGkAtoms::dd
) {
2093 if (psd
->mHasNonemptyContent
|| preMode
|| mHasBullet
) {
2094 #ifdef NOISY_BLOCKDIR_ALIGN
2095 printf(" [span]==> adjusting min/maxBCoord: currentValues: %d,%d", minBCoord
, maxBCoord
);
2097 nscoord minimumLineBSize
= mMinLineBSize
;
2098 nscoord blockStart
=
2099 -nsLayoutUtils::GetCenteredFontBaseline(fm
, minimumLineBSize
);
2100 nscoord blockEnd
= blockStart
+ minimumLineBSize
;
2102 if (blockStart
< minBCoord
) minBCoord
= blockStart
;
2103 if (blockEnd
> maxBCoord
) maxBCoord
= blockEnd
;
2105 #ifdef NOISY_BLOCKDIR_ALIGN
2106 printf(" new values: %d,%d\n", minBCoord
, maxBCoord
);
2108 #ifdef NOISY_BLOCKDIR_ALIGN
2109 printf(" Used mMinLineBSize: %d, blockStart: %d, blockEnd: %d\n", mMinLineBSize
, blockStart
, blockEnd
);
2114 // [1] BR's on empty lines stop working
2115 // [2] May not honor css2's notion of handling empty elements
2116 // [3] blank lines in a pre-section ("\n") (handled with preMode)
2118 // XXX Are there other problems with this?
2119 #ifdef NOISY_BLOCKDIR_ALIGN
2120 printf(" [span]==> zapping min/maxBCoord: currentValues: %d,%d newValues: 0,0\n",
2121 minBCoord
, maxBCoord
);
2123 minBCoord
= maxBCoord
= 0;
2128 if ((minBCoord
== BLOCKDIR_ALIGN_FRAMES_NO_MINIMUM
) ||
2129 (maxBCoord
== BLOCKDIR_ALIGN_FRAMES_NO_MAXIMUM
)) {
2130 minBCoord
= maxBCoord
= baselineBCoord
;
2133 if ((psd
!= mRootSpan
) && (psd
->mZeroEffectiveSpanBox
)) {
2134 #ifdef NOISY_BLOCKDIR_ALIGN
2135 printf(" [span]adjusting for zeroEffectiveSpanBox\n");
2136 printf(" Original: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
2137 minBCoord
, maxBCoord
, spanFramePFD
->mBounds
.BSize(frameWM
),
2138 spanFramePFD
->mAscent
,
2139 psd
->mLogicalBSize
, psd
->mBStartLeading
, psd
->mBEndLeading
);
2141 nscoord goodMinBCoord
= spanFramePFD
->mBorderPadding
.BStart(frameWM
) -
2142 psd
->mBStartLeading
;
2143 nscoord goodMaxBCoord
= goodMinBCoord
+ psd
->mLogicalBSize
;
2145 // For cases like the one in bug 714519 (text-decoration placement
2146 // or making nsLineLayout::IsZeroBSize() handle
2147 // vertical-align:top/bottom on a descendant of the line that's not
2148 // a child of it), we want to treat elements that are
2149 // vertical-align: top or bottom somewhat like children for the
2150 // purposes of this quirk. To some extent, this is guessing, since
2151 // they might end up being aligned anywhere. However, we'll guess
2152 // that they'll be placed aligned with the top or bottom of this
2153 // frame (as though this frame is the only thing in the line).
2154 // (Guessing isn't crazy, since all we're doing is reducing the
2155 // scope of a quirk and making the behavior more standards-like.)
2156 if (maxStartBoxBSize
> maxBCoord
- minBCoord
) {
2157 // Distribute maxStartBoxBSize to ascent (baselineBCoord - minBCoord), and
2158 // then to descent (maxBCoord - baselineBCoord) by adjusting minBCoord or
2159 // maxBCoord, but not to exceed goodMinBCoord and goodMaxBCoord.
2160 nscoord distribute
= maxStartBoxBSize
- (maxBCoord
- minBCoord
);
2161 nscoord ascentSpace
= std::max(minBCoord
- goodMinBCoord
, 0);
2162 if (distribute
> ascentSpace
) {
2163 distribute
-= ascentSpace
;
2164 minBCoord
-= ascentSpace
;
2165 nscoord descentSpace
= std::max(goodMaxBCoord
- maxBCoord
, 0);
2166 if (distribute
> descentSpace
) {
2167 maxBCoord
+= descentSpace
;
2169 maxBCoord
+= distribute
;
2172 minBCoord
-= distribute
;
2175 if (maxEndBoxBSize
> maxBCoord
- minBCoord
) {
2176 // Likewise, but preferring descent to ascent.
2177 nscoord distribute
= maxEndBoxBSize
- (maxBCoord
- minBCoord
);
2178 nscoord descentSpace
= std::max(goodMaxBCoord
- maxBCoord
, 0);
2179 if (distribute
> descentSpace
) {
2180 distribute
-= descentSpace
;
2181 maxBCoord
+= descentSpace
;
2182 nscoord ascentSpace
= std::max(minBCoord
- goodMinBCoord
, 0);
2183 if (distribute
> ascentSpace
) {
2184 minBCoord
-= ascentSpace
;
2186 minBCoord
-= distribute
;
2189 maxBCoord
+= distribute
;
2193 if (minBCoord
> goodMinBCoord
) {
2194 nscoord adjust
= minBCoord
- goodMinBCoord
; // positive
2196 // shrink the logical extents
2197 psd
->mLogicalBSize
-= adjust
;
2198 psd
->mBStartLeading
-= adjust
;
2200 if (maxBCoord
< goodMaxBCoord
) {
2201 nscoord adjust
= goodMaxBCoord
- maxBCoord
;
2202 psd
->mLogicalBSize
-= adjust
;
2203 psd
->mBEndLeading
-= adjust
;
2205 if (minBCoord
> 0) {
2207 // shrink the content by moving its block start down. This is tricky,
2208 // since the block start is the 0 for many coordinates, so what we do is
2209 // move everything else up.
2210 spanFramePFD
->mAscent
-= minBCoord
; // move the baseline up
2211 spanFramePFD
->mBounds
.BSize(lineWM
) -= minBCoord
; // move the block end up
2212 psd
->mBStartLeading
+= minBCoord
;
2213 *psd
->mBaseline
-= minBCoord
;
2215 pfd
= psd
->mFirstFrame
;
2216 while (nullptr != pfd
) {
2217 pfd
->mBounds
.BStart(lineWM
) -= minBCoord
; // move all the children
2219 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
2222 maxBCoord
-= minBCoord
; // since minBCoord is in the frame's own
2223 // coordinate system
2226 if (maxBCoord
< spanFramePFD
->mBounds
.BSize(lineWM
)) {
2227 nscoord adjust
= spanFramePFD
->mBounds
.BSize(lineWM
) - maxBCoord
;
2228 spanFramePFD
->mBounds
.BSize(lineWM
) -= adjust
; // move the bottom up
2229 psd
->mBEndLeading
+= adjust
;
2231 #ifdef NOISY_BLOCKDIR_ALIGN
2232 printf(" New: minBCoord=%d, maxBCoord=%d, bSize=%d, ascent=%d, logicalBSize=%d, topLeading=%d, bottomLeading=%d\n",
2233 minBCoord
, maxBCoord
, spanFramePFD
->mBounds
.BSize(lineWM
),
2234 spanFramePFD
->mAscent
,
2235 psd
->mLogicalBSize
, psd
->mBStartLeading
, psd
->mBEndLeading
);
2239 psd
->mMinBCoord
= minBCoord
;
2240 psd
->mMaxBCoord
= maxBCoord
;
2241 #ifdef NOISY_BLOCKDIR_ALIGN
2242 printf(" [span]==> minBCoord=%d maxBCoord=%d delta=%d maxStartBoxBSize=%d maxEndBoxBSize=%d\n",
2243 minBCoord
, maxBCoord
, maxBCoord
- minBCoord
, maxStartBoxBSize
, maxEndBoxBSize
);
2245 if (maxStartBoxBSize
> mMaxStartBoxBSize
) {
2246 mMaxStartBoxBSize
= maxStartBoxBSize
;
2248 if (maxEndBoxBSize
> mMaxEndBoxBSize
) {
2249 mMaxEndBoxBSize
= maxEndBoxBSize
;
2253 static void SlideSpanFrameRect(nsIFrame
* aFrame
, nscoord aDeltaWidth
)
2255 // This should not use nsIFrame::MovePositionBy because it happens
2256 // prior to relative positioning. In particular, because
2257 // nsBlockFrame::PlaceLine calls aLineLayout.TrimTrailingWhiteSpace()
2258 // prior to calling aLineLayout.RelativePositionFrames().
2259 nsPoint p
= aFrame
->GetPosition();
2261 aFrame
->SetPosition(p
);
2265 nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData
* psd
,
2266 nscoord
* aDeltaISize
)
2268 PerFrameData
* pfd
= psd
->mFirstFrame
;
2274 while (nullptr != pfd
) {
2275 #ifdef REALLY_NOISY_TRIM
2276 nsFrame::ListTag(stdout
, (psd
== mRootSpan
2277 ? mBlockReflowState
->frame
2278 : psd
->mFrame
->mFrame
));
2279 printf(": attempting trim of ");
2280 nsFrame::ListTag(stdout
, pfd
->mFrame
);
2283 PerSpanData
* childSpan
= pfd
->mSpan
;
2284 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2286 // Maybe the child span has the trailing white-space in it?
2287 if (TrimTrailingWhiteSpaceIn(childSpan
, aDeltaISize
)) {
2288 nscoord deltaISize
= *aDeltaISize
;
2290 // Adjust the child spans frame size
2291 pfd
->mBounds
.ISize(lineWM
) -= deltaISize
;
2292 if (psd
!= mRootSpan
) {
2293 // When the child span is not a direct child of the block
2294 // we need to update the child spans frame rectangle
2295 // because it most likely will not be done again. Spans
2296 // that are direct children of the block will be updated
2297 // later, however, because the VerticalAlignFrames method
2298 // will be run after this method.
2299 nsIFrame
* f
= pfd
->mFrame
;
2300 LogicalRect
r(lineWM
, f
->GetRect(), mContainerWidth
);
2301 r
.ISize(lineWM
) -= deltaISize
;
2302 f
->SetRect(lineWM
, r
, mContainerWidth
);
2305 // Adjust the inline end edge of the span that contains the child span
2306 psd
->mICoord
-= deltaISize
;
2308 // Slide any frames that follow the child span over by the
2309 // correct amount. The only thing that can follow the child
2310 // span is empty stuff, so we are just making things
2311 // sensible (keeping the combined area honest).
2312 while (pfd
->mNext
) {
2314 pfd
->mBounds
.IStart(lineWM
) -= deltaISize
;
2315 if (psd
!= mRootSpan
) {
2316 // When the child span is not a direct child of the block
2317 // we need to update the child span's frame rectangle
2318 // because it most likely will not be done again. Spans
2319 // that are direct children of the block will be updated
2320 // later, however, because the VerticalAlignFrames method
2321 // will be run after this method.
2322 SlideSpanFrameRect(pfd
->mFrame
, deltaISize
);
2329 else if (!pfd
->GetFlag(PFD_ISTEXTFRAME
) &&
2330 !pfd
->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE
)) {
2331 // If we hit a frame on the end that's not text and not a placeholder,
2332 // then there is no trailing whitespace to trim. Stop the search.
2336 else if (pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2337 // Call TrimTrailingWhiteSpace even on empty textframes because they
2338 // might have a soft hyphen which should now appear, changing the frame's
2340 nsTextFrame::TrimOutput trimOutput
= static_cast<nsTextFrame
*>(pfd
->mFrame
)->
2341 TrimTrailingWhiteSpace(mBlockReflowState
->rendContext
);
2343 nsFrame::ListTag(stdout
, (psd
== mRootSpan
2344 ? mBlockReflowState
->frame
2345 : psd
->mFrame
->mFrame
));
2346 printf(": trim of ");
2347 nsFrame::ListTag(stdout
, pfd
->mFrame
);
2348 printf(" returned %d\n", trimOutput
.mDeltaWidth
);
2350 if (trimOutput
.mLastCharIsJustifiable
&& pfd
->mJustificationNumSpaces
> 0) {
2351 pfd
->mJustificationNumSpaces
--;
2354 if (trimOutput
.mChanged
) {
2355 pfd
->SetFlag(PFD_RECOMPUTEOVERFLOW
, true);
2358 if (trimOutput
.mDeltaWidth
) {
2359 pfd
->mBounds
.ISize(lineWM
) -= trimOutput
.mDeltaWidth
;
2361 // See if the text frame has already been placed in its parent
2362 if (psd
!= mRootSpan
) {
2363 // The frame was already placed during psd's
2364 // reflow. Update the frames rectangle now.
2365 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
2368 // Adjust containing span's right edge
2369 psd
->mICoord
-= trimOutput
.mDeltaWidth
;
2371 // Slide any frames that follow the text frame over by the
2372 // right amount. The only thing that can follow the text
2373 // frame is empty stuff, so we are just making things
2374 // sensible (keeping the combined area honest).
2375 while (pfd
->mNext
) {
2377 pfd
->mBounds
.IStart(lineWM
) -= trimOutput
.mDeltaWidth
;
2378 if (psd
!= mRootSpan
) {
2379 // When the child span is not a direct child of the block
2380 // we need to update the child spans frame rectangle
2381 // because it most likely will not be done again. Spans
2382 // that are direct children of the block will be updated
2383 // later, however, because the VerticalAlignFrames method
2384 // will be run after this method.
2385 SlideSpanFrameRect(pfd
->mFrame
, trimOutput
.mDeltaWidth
);
2390 if (pfd
->GetFlag(PFD_ISNONEMPTYTEXTFRAME
) || trimOutput
.mChanged
) {
2391 // Pass up to caller so they can shrink their span
2392 *aDeltaISize
= trimOutput
.mDeltaWidth
;
2404 nsLineLayout::TrimTrailingWhiteSpace()
2406 PerSpanData
* psd
= mRootSpan
;
2408 TrimTrailingWhiteSpaceIn(psd
, &deltaISize
);
2409 return 0 != deltaISize
;
2413 nsLineLayout::ComputeJustificationWeights(PerSpanData
* aPSD
,
2414 int32_t* aNumSpaces
,
2415 int32_t* aNumLetters
)
2417 NS_ASSERTION(aPSD
, "null arg");
2418 NS_ASSERTION(aNumSpaces
, "null arg");
2419 NS_ASSERTION(aNumLetters
, "null arg");
2420 int32_t numSpaces
= 0;
2421 int32_t numLetters
= 0;
2423 for (PerFrameData
* pfd
= aPSD
->mFirstFrame
; pfd
!= nullptr; pfd
= pfd
->mNext
) {
2425 if (true == pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2426 numSpaces
+= pfd
->mJustificationNumSpaces
;
2427 numLetters
+= pfd
->mJustificationNumLetters
;
2429 else if (pfd
->mSpan
!= nullptr) {
2431 int32_t spanLetters
;
2433 ComputeJustificationWeights(pfd
->mSpan
, &spanSpaces
, &spanLetters
);
2435 numSpaces
+= spanSpaces
;
2436 numLetters
+= spanLetters
;
2440 *aNumSpaces
= numSpaces
;
2441 *aNumLetters
= numLetters
;
2445 nsLineLayout::ApplyFrameJustification(PerSpanData
* aPSD
, FrameJustificationState
* aState
)
2447 NS_ASSERTION(aPSD
, "null arg");
2448 NS_ASSERTION(aState
, "null arg");
2450 nscoord deltaICoord
= 0;
2451 for (PerFrameData
* pfd
= aPSD
->mFirstFrame
; pfd
!= nullptr; pfd
= pfd
->mNext
) {
2452 // Don't reposition bullets (and other frames that occur out of X-order?)
2453 if (!pfd
->GetFlag(PFD_ISBULLET
)) {
2455 WritingMode lineWM
= mRootSpan
->mWritingMode
;
2457 pfd
->mBounds
.IStart(lineWM
) += deltaICoord
;
2459 if (true == pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2460 if (aState
->mTotalWidthForSpaces
> 0 &&
2461 aState
->mTotalNumSpaces
> 0) {
2462 aState
->mNumSpacesProcessed
+= pfd
->mJustificationNumSpaces
;
2464 nscoord newAllocatedWidthForSpaces
=
2465 (aState
->mTotalWidthForSpaces
*aState
->mNumSpacesProcessed
)
2466 /aState
->mTotalNumSpaces
;
2468 dw
+= newAllocatedWidthForSpaces
- aState
->mWidthForSpacesProcessed
;
2470 aState
->mWidthForSpacesProcessed
= newAllocatedWidthForSpaces
;
2473 if (aState
->mTotalWidthForLetters
> 0 &&
2474 aState
->mTotalNumLetters
> 0) {
2475 aState
->mNumLettersProcessed
+= pfd
->mJustificationNumLetters
;
2477 nscoord newAllocatedWidthForLetters
=
2478 (aState
->mTotalWidthForLetters
*aState
->mNumLettersProcessed
)
2479 /aState
->mTotalNumLetters
;
2481 dw
+= newAllocatedWidthForLetters
- aState
->mWidthForLettersProcessed
;
2483 aState
->mWidthForLettersProcessed
= newAllocatedWidthForLetters
;
2487 pfd
->SetFlag(PFD_RECOMPUTEOVERFLOW
, true);
2491 if (nullptr != pfd
->mSpan
) {
2492 dw
+= ApplyFrameJustification(pfd
->mSpan
, aState
);
2496 pfd
->mBounds
.ISize(lineWM
) += dw
;
2499 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
2505 // Align inline frames within the line according to the CSS text-align
2508 nsLineLayout::TextAlignLine(nsLineBox
* aLine
,
2512 * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller
2513 * only in cases where the last line needs special handling.
2515 PerSpanData
* psd
= mRootSpan
;
2516 WritingMode lineWM
= psd
->mWritingMode
;
2517 NS_WARN_IF_FALSE(psd
->mIEnd
!= NS_UNCONSTRAINEDSIZE
,
2518 "have unconstrained width; this should only result from "
2519 "very large sizes, not attempts at intrinsic width "
2521 nscoord availISize
= psd
->mIEnd
- psd
->mIStart
;
2522 nscoord remainingISize
= availISize
- aLine
->ISize();
2523 #ifdef NOISY_INLINEDIR_ALIGN
2524 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
2525 printf(": availISize=%d lineBounds.IStart=%d lineISize=%d delta=%d\n",
2526 availISize
, aLine
->IStart(), aLine
->ISize(), remainingISize
);
2529 // 'text-align-last: auto' is equivalent to the value of the 'text-align'
2530 // property except when 'text-align' is set to 'justify', in which case it
2531 // is 'justify' when 'text-justify' is 'distribute' and 'start' otherwise.
2533 // XXX: the code below will have to change when we implement text-justify
2536 uint8_t textAlign
= mStyleText
->mTextAlign
;
2537 bool textAlignTrue
= mStyleText
->mTextAlignTrue
;
2539 textAlignTrue
= mStyleText
->mTextAlignLastTrue
;
2540 if (mStyleText
->mTextAlignLast
== NS_STYLE_TEXT_ALIGN_AUTO
) {
2541 if (textAlign
== NS_STYLE_TEXT_ALIGN_JUSTIFY
) {
2542 textAlign
= NS_STYLE_TEXT_ALIGN_DEFAULT
;
2545 textAlign
= mStyleText
->mTextAlignLast
;
2549 if ((remainingISize
> 0 || textAlignTrue
) &&
2550 !(mBlockReflowState
->frame
->IsSVGText())) {
2552 switch (textAlign
) {
2553 case NS_STYLE_TEXT_ALIGN_JUSTIFY
:
2557 ComputeJustificationWeights(psd
, &numSpaces
, &numLetters
);
2559 if (numSpaces
> 0) {
2560 FrameJustificationState state
=
2561 { numSpaces
, numLetters
, remainingISize
, 0, 0, 0, 0, 0 };
2563 // Apply the justification, and make sure to update our linebox
2564 // width to account for it.
2565 aLine
->ExpandBy(ApplyFrameJustification(psd
, &state
),
2569 // Fall through to the default case if we could not justify to fill
2572 case NS_STYLE_TEXT_ALIGN_DEFAULT
:
2573 // default alignment is to start edge so do nothing
2576 case NS_STYLE_TEXT_ALIGN_LEFT
:
2577 case NS_STYLE_TEXT_ALIGN_MOZ_LEFT
:
2578 if (!lineWM
.IsBidiLTR()) {
2579 dx
= remainingISize
;
2583 case NS_STYLE_TEXT_ALIGN_RIGHT
:
2584 case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
:
2585 if (lineWM
.IsBidiLTR()) {
2586 dx
= remainingISize
;
2590 case NS_STYLE_TEXT_ALIGN_END
:
2591 dx
= remainingISize
;
2594 case NS_STYLE_TEXT_ALIGN_CENTER
:
2595 case NS_STYLE_TEXT_ALIGN_MOZ_CENTER
:
2596 dx
= remainingISize
/ 2;
2602 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
2603 pfd
->mBounds
.IStart(lineWM
) += dx
;
2604 pfd
->mFrame
->SetRect(lineWM
, pfd
->mBounds
, mContainerWidth
);
2606 aLine
->IndentBy(dx
, mContainerWidth
);
2609 if (mPresContext
->BidiEnabled() &&
2610 (!mPresContext
->IsVisualMode() || !lineWM
.IsBidiLTR())) {
2611 nsBidiPresUtils::ReorderFrames(psd
->mFirstFrame
->mFrame
,
2612 aLine
->GetChildCount(),
2613 lineWM
, mContainerWidth
);
2618 nsLineLayout::RelativePositionFrames(nsOverflowAreas
& aOverflowAreas
)
2620 RelativePositionFrames(mRootSpan
, aOverflowAreas
);
2624 nsLineLayout::RelativePositionFrames(PerSpanData
* psd
, nsOverflowAreas
& aOverflowAreas
)
2626 nsOverflowAreas overflowAreas
;
2627 WritingMode wm
= psd
->mWritingMode
;
2628 if (nullptr != psd
->mFrame
) {
2629 // The span's overflow areas come in three parts:
2630 // -- this frame's width and height
2631 // -- pfd->mOverflowAreas, which is the area of a bullet or the union
2632 // of a relatively positioned frame's absolute children
2633 // -- the bounds of all inline descendants
2634 // The former two parts are computed right here, we gather the descendants
2636 // At this point psd->mFrame->mBounds might be out of date since
2637 // bidi reordering can move and resize the frames. So use the frame's
2638 // rect instead of mBounds.
2639 nsRect
adjustedBounds(nsPoint(0, 0), psd
->mFrame
->mFrame
->GetSize());
2641 overflowAreas
.ScrollableOverflow().UnionRect(
2642 psd
->mFrame
->mOverflowAreas
.ScrollableOverflow(), adjustedBounds
);
2643 overflowAreas
.VisualOverflow().UnionRect(
2644 psd
->mFrame
->mOverflowAreas
.VisualOverflow(), adjustedBounds
);
2647 LogicalRect
rect(wm
, psd
->mIStart
, mBStartEdge
,
2648 psd
->mICoord
- psd
->mIStart
, mFinalLineBSize
);
2649 // The minimum combined area for the frames that are direct
2650 // children of the block starts at the upper left corner of the
2651 // line and is sized to match the size of the line's bounding box
2652 // (the same size as the values returned from VerticalAlignFrames)
2653 overflowAreas
.VisualOverflow() = rect
.GetPhysicalRect(wm
, mContainerWidth
);
2654 overflowAreas
.ScrollableOverflow() = overflowAreas
.VisualOverflow();
2657 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
2658 nsIFrame
* frame
= pfd
->mFrame
;
2659 nsPoint origin
= frame
->GetPosition();
2661 // Adjust the origin of the frame
2662 if (pfd
->GetFlag(PFD_RELATIVEPOS
)) {
2663 //XXX temporary until ApplyRelativePositioning can handle logical offsets
2664 nsMargin physicalOffsets
=
2665 pfd
->mOffsets
.GetPhysicalMargin(pfd
->mFrame
->GetWritingMode());
2666 // right and bottom are handled by
2667 // nsHTMLReflowState::ComputeRelativeOffsets
2668 nsHTMLReflowState::ApplyRelativePositioning(pfd
->mFrame
,
2671 frame
->SetPosition(origin
);
2674 // We must position the view correctly before positioning its
2675 // descendants so that widgets are positioned properly (since only
2676 // some views have widgets).
2677 if (frame
->HasView())
2678 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext
, frame
,
2679 frame
->GetView(), pfd
->mOverflowAreas
.VisualOverflow(),
2680 NS_FRAME_NO_SIZE_VIEW
);
2682 // Note: the combined area of a child is in its coordinate
2683 // system. We adjust the childs combined area into our coordinate
2684 // system before computing the aggregated value by adding in
2685 // <b>x</b> and <b>y</b> which were computed above.
2688 // Compute a new combined area for the child span before
2689 // aggregating it into our combined area.
2690 RelativePositionFrames(pfd
->mSpan
, r
);
2692 r
= pfd
->mOverflowAreas
;
2693 if (pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2694 // We need to recompute overflow areas in two cases:
2695 // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming
2696 // (2) When there are text decorations, since we can't recompute the
2697 // overflow area until Reflow and VerticalAlignLine have finished
2698 if (pfd
->GetFlag(PFD_RECOMPUTEOVERFLOW
) ||
2699 frame
->StyleContext()->HasTextDecorationLines()) {
2700 nsTextFrame
* f
= static_cast<nsTextFrame
*>(frame
);
2701 r
= f
->RecomputeOverflow(*mBlockReflowState
);
2703 frame
->FinishAndStoreOverflow(r
, frame
->GetSize());
2706 // If we have something that's not an inline but with a complex frame
2707 // hierarchy inside that contains views, they need to be
2709 // All descendant views must be repositioned even if this frame
2710 // does have a view in case this frame's view does not have a
2711 // widget and some of the descendant views do have widgets --
2712 // otherwise the widgets won't be repositioned.
2713 nsContainerFrame::PositionChildViews(frame
);
2716 // Do this here (rather than along with setting the overflow rect
2717 // below) so we get leaf frames as well. No need to worry
2718 // about the root span, since it doesn't have a frame.
2719 if (frame
->HasView())
2720 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext
, frame
,
2723 NS_FRAME_NO_MOVE_VIEW
);
2725 overflowAreas
.UnionWith(r
+ origin
);
2728 // If we just computed a spans combined area, we need to update its
2731 PerFrameData
* spanPFD
= psd
->mFrame
;
2732 nsIFrame
* frame
= spanPFD
->mFrame
;
2733 frame
->FinishAndStoreOverflow(overflowAreas
, frame
->GetSize());
2735 aOverflowAreas
= overflowAreas
;
2739 nsLineLayout::AdvanceICoord(nscoord aAmount
)
2741 mCurrentSpan
->mICoord
+= aAmount
;
2745 nsLineLayout::GetWritingMode()
2747 return mRootSpan
->mWritingMode
;
2751 nsLineLayout::GetCurrentICoord()
2753 return mCurrentSpan
->mICoord
;