1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Steve Clark <buster@netscape.com>
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * L. David Baron <dbaron@dbaron.org>
26 * Robert O'Callahan <roc+moz@cs.cmu.edu>
28 * Mats Palmgren <mats.palmgren@bredband.net>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either of the GNU General Public License Version 2 or later (the "GPL"),
32 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
44 /* state and methods used while laying out a single line of a block frame */
46 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
50 #include "nsLineLayout.h"
51 #include "nsBlockFrame.h"
52 #include "nsInlineFrame.h"
53 #include "nsStyleConsts.h"
54 #include "nsHTMLContainerFrame.h"
55 #include "nsFloatManager.h"
56 #include "nsStyleContext.h"
57 #include "nsPresContext.h"
58 #include "nsIFontMetrics.h"
59 #include "nsIThebesFontMetrics.h"
60 #include "nsIRenderingContext.h"
61 #include "nsGkAtoms.h"
62 #include "nsPlaceholderFrame.h"
63 #include "nsIDocument.h"
64 #include "nsIHTMLDocument.h"
65 #include "nsIContent.h"
66 #include "nsTextFragment.h"
67 #include "nsBidiUtils.h"
68 #include "nsLayoutUtils.h"
69 #include "nsTextFrame.h"
70 #include "nsCSSRendering.h"
74 #undef NOISY_HORIZONTAL_ALIGN
75 #undef NOISY_VERTICAL_ALIGN
76 #undef REALLY_NOISY_VERTICAL_ALIGN
78 #undef REALLY_NOISY_REFLOW
80 #undef REALLY_NOISY_PUSHING
82 #undef NOISY_MAX_ELEMENT_SIZE
83 #undef REALLY_NOISY_MAX_ELEMENT_SIZE
84 #undef NOISY_CAN_PLACE_FRAME
86 #undef REALLY_NOISY_TRIM
89 //----------------------------------------------------------------------
93 nsLineLayout::nsLineLayout(nsPresContext
* aPresContext
,
94 nsFloatManager
* aFloatManager
,
95 const nsHTMLReflowState
* aOuterReflowState
,
96 const nsLineList::iterator
* aLine
)
97 : mPresContext(aPresContext
),
98 mFloatManager(aFloatManager
),
99 mBlockReflowState(aOuterReflowState
),
100 mLastOptionalBreakContent(nsnull
),
101 mForceBreakContent(nsnull
),
102 mBlockRS(nsnull
),/* XXX temporary */
103 mLastOptionalBreakPriority(eNoBreak
),
104 mLastOptionalBreakContentOffset(-1),
105 mForceBreakContentOffset(-1),
109 NS_ASSERTION(aFloatManager
|| aOuterReflowState
->frame
->GetType() ==
110 nsGkAtoms::letterFrame
,
111 "float manager should be present");
112 MOZ_COUNT_CTOR(nsLineLayout
);
114 // Stash away some style data that we need
115 mStyleText
= aOuterReflowState
->frame
->GetStyleText();
116 mTextAlign
= mStyleText
->mTextAlign
;
118 mFlags
= 0; // default all flags to false except those that follow here...
119 mTotalPlacedFrames
= 0;
123 // Instead of always pre-initializing the free-lists for frames and
124 // spans, we do it on demand so that situations that only use a few
125 // frames and spans won't waste a lot of time in unneeded
127 PL_INIT_ARENA_POOL(&mArena
, "nsLineLayout", 1024);
128 mFrameFreeList
= nsnull
;
129 mSpanFreeList
= nsnull
;
131 mCurrentSpan
= mRootSpan
= nsnull
;
135 SetFlag(LL_GOTLINEBOX
, PR_TRUE
);
140 nsLineLayout::~nsLineLayout()
142 MOZ_COUNT_DTOR(nsLineLayout
);
144 NS_ASSERTION(nsnull
== mRootSpan
, "bad line-layout user");
146 // PL_FreeArenaPool takes our memory and puts in on a global free list so
147 // that the next time an arena makes an allocation it will not have to go
148 // all the way down to malloc. This is desirable as this class is created
149 // and destroyed in a tight loop.
151 // I looked at the code. It is not technically necessary to call
152 // PL_FinishArenaPool() after PL_FreeArenaPool(), but from an API
153 // standpoint, I think we are susposed to. It will be very fast anyway,
154 // since PL_FreeArenaPool() has done all the work.
155 PL_FreeArenaPool(&mArena
);
156 PL_FinishArenaPool(&mArena
);
159 // Find out if the frame has a non-null prev-in-flow, i.e., whether it
160 // is a continuation.
162 HasPrevInFlow(nsIFrame
*aFrame
)
164 nsIFrame
*prevInFlow
= aFrame
->GetPrevInFlow();
165 return prevInFlow
!= nsnull
;
169 nsLineLayout::BeginLineReflow(nscoord aX
, nscoord aY
,
170 nscoord aWidth
, nscoord aHeight
,
171 PRBool aImpactedByFloats
,
174 NS_ASSERTION(nsnull
== mRootSpan
, "bad linelayout user");
175 NS_WARN_IF_FALSE(aWidth
!= NS_UNCONSTRAINEDSIZE
,
176 "have unconstrained width; this should only result from "
177 "very large sizes, not attempts at intrinsic width "
180 if ((aWidth
!= NS_UNCONSTRAINEDSIZE
) && CRAZY_WIDTH(aWidth
)) {
181 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
182 printf(": Init: bad caller: width WAS %d(0x%x)\n",
185 if ((aHeight
!= NS_UNCONSTRAINEDSIZE
) && CRAZY_HEIGHT(aHeight
)) {
186 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
187 printf(": Init: bad caller: height WAS %d(0x%x)\n",
192 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
193 printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n",
194 aX
, aY
, aWidth
, aHeight
,
195 aImpactedByFloats
?"true":"false",
196 aIsTopOfPage
? "top-of-page" : "");
199 mSpansAllocated
= mSpansFreed
= mFramesAllocated
= mFramesFreed
= 0;
202 SetFlag(LL_FIRSTLETTERSTYLEOK
, PR_FALSE
);
203 SetFlag(LL_ISTOPOFPAGE
, aIsTopOfPage
);
204 SetFlag(LL_IMPACTEDBYFLOATS
, aImpactedByFloats
);
205 mTotalPlacedFrames
= 0;
206 SetFlag(LL_LINEISEMPTY
, PR_TRUE
);
207 SetFlag(LL_LINEATSTART
, PR_TRUE
);
208 SetFlag(LL_LINEENDSINBR
, PR_FALSE
);
210 mMaxTopBoxHeight
= mMaxBottomBoxHeight
= 0;
212 if (GetFlag(LL_GOTLINEBOX
)) {
213 mLineBox
->ClearHasBullet();
217 NewPerSpanData(&psd
);
218 mCurrentSpan
= mRootSpan
= psd
;
219 psd
->mReflowState
= mBlockReflowState
;
222 psd
->mRightEdge
= aX
+ aWidth
;
226 psd
->mNoWrap
= !mStyleText
->WhiteSpaceCanWrap();
227 psd
->mDirection
= mBlockReflowState
->mStyleVisibility
->mDirection
;
228 psd
->mChangedFrameDirection
= PR_FALSE
;
230 // If this is the first line of a block then see if the text-indent
231 // property amounts to anything.
233 if (0 == mLineNumber
&& !HasPrevInFlow(mBlockReflowState
->frame
)) {
234 const nsStyleCoord
&textIndent
= mStyleText
->mTextIndent
;
235 nscoord pctBasis
= 0;
236 if (textIndent
.HasPercent()) {
238 nsHTMLReflowState::GetContainingBlockContentWidth(mBlockReflowState
);
240 if (GetFlag(LL_GOTLINEBOX
)) {
241 mLineBox
->DisableResizeReflowOptimization();
244 nscoord indent
= nsRuleNode::ComputeCoordPercentCalc(textIndent
, pctBasis
);
246 mTextIndent
= indent
;
248 if (NS_STYLE_DIRECTION_RTL
== psd
->mDirection
) {
249 psd
->mRightEdge
-= indent
;
258 nsLineLayout::EndLineReflow()
261 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
262 printf(": EndLineReflow: width=%d\n", mRootSpan
->mX
- mRootSpan
->mLeftEdge
);
266 mCurrentSpan
= mRootSpan
= nsnull
;
268 NS_ASSERTION(mSpansAllocated
== mSpansFreed
, "leak");
269 NS_ASSERTION(mFramesAllocated
== mFramesFreed
, "leak");
272 static PRInt32 maxSpansAllocated
= NS_LINELAYOUT_NUM_SPANS
;
273 static PRInt32 maxFramesAllocated
= NS_LINELAYOUT_NUM_FRAMES
;
274 if (mSpansAllocated
> maxSpansAllocated
) {
275 printf("XXX: saw a line with %d spans\n", mSpansAllocated
);
276 maxSpansAllocated
= mSpansAllocated
;
278 if (mFramesAllocated
> maxFramesAllocated
) {
279 printf("XXX: saw a line with %d frames\n", mFramesAllocated
);
280 maxFramesAllocated
= mFramesAllocated
;
285 // XXX swtich to a single mAvailLineWidth that we adjust as each frame
286 // on the line is placed. Each span can still have a per-span mX that
287 // tracks where a child frame is going in its span; they don't need a
288 // per-span mLeftEdge?
291 nsLineLayout::UpdateBand(const nsRect
& aNewAvailSpace
,
292 nsIFrame
* aFloatFrame
)
294 #ifdef REALLY_NOISY_REFLOW
295 printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p\n will set mImpacted to PR_TRUE\n",
296 aNewAvailSpace
.x
, aNewAvailSpace
.y
,
297 aNewAvailSpace
.width
, aNewAvailSpace
.height
,
301 if ((aNewAvailSpace
.width
!= NS_UNCONSTRAINEDSIZE
) && CRAZY_WIDTH(aNewAvailSpace
.width
)) {
302 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
303 printf(": UpdateBand: bad caller: width WAS %d(0x%x)\n",
304 aNewAvailSpace
.width
, aNewAvailSpace
.width
);
306 if ((aNewAvailSpace
.height
!= NS_UNCONSTRAINEDSIZE
) && CRAZY_HEIGHT(aNewAvailSpace
.height
)) {
307 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
308 printf(": UpdateBand: bad caller: height WAS %d(0x%x)\n",
309 aNewAvailSpace
.height
, aNewAvailSpace
.height
);
313 // Compute the difference between last times width and the new width
314 NS_WARN_IF_FALSE(mRootSpan
->mRightEdge
!= NS_UNCONSTRAINEDSIZE
&&
315 aNewAvailSpace
.width
!= NS_UNCONSTRAINEDSIZE
,
316 "have unconstrained width; this should only result from "
317 "very large sizes, not attempts at intrinsic width "
319 // The root span's mLeftEdge moves to aX
320 nscoord deltaX
= aNewAvailSpace
.x
- mRootSpan
->mLeftEdge
;
321 // The width of all spans changes by this much (the root span's
322 // mRightEdge moves to aX + aWidth, its new width is aWidth)
323 nscoord deltaWidth
= aNewAvailSpace
.width
- (mRootSpan
->mRightEdge
- mRootSpan
->mLeftEdge
);
325 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
326 printf(": UpdateBand: %d,%d,%d,%d deltaWidth=%d deltaX=%d\n",
327 aNewAvailSpace
.x
, aNewAvailSpace
.y
,
328 aNewAvailSpace
.width
, aNewAvailSpace
.height
, deltaWidth
, deltaX
);
331 // Update the root span position
332 mRootSpan
->mLeftEdge
+= deltaX
;
333 mRootSpan
->mRightEdge
+= deltaX
;
334 mRootSpan
->mX
+= deltaX
;
336 // Now update the right edges of the open spans to account for any
337 // change in available space width
338 for (PerSpanData
* psd
= mCurrentSpan
; psd
; psd
= psd
->mParent
) {
339 psd
->mRightEdge
+= deltaWidth
;
340 psd
->mContainsFloat
= PR_TRUE
;
341 NS_ASSERTION(psd
->mX
- mTrimmableWidth
<= psd
->mRightEdge
,
342 "We placed a float where there was no room!");
344 printf(" span %p: oldRightEdge=%d newRightEdge=%d\n",
345 psd
, psd
->mRightEdge
- deltaRightEdge
, psd
->mRightEdge
);
348 NS_ASSERTION(mRootSpan
->mContainsFloat
&&
349 mRootSpan
->mLeftEdge
== aNewAvailSpace
.x
&&
350 mRootSpan
->mRightEdge
== aNewAvailSpace
.XMost(),
351 "root span was updated incorrectly?");
353 // Update frame bounds
354 // Note: Only adjust the outermost frames (the ones that are direct
355 // children of the block), not the ones in the child spans. The reason
356 // is simple: the frames in the spans have coordinates local to their
357 // parent therefore they are moved when their parent span is moved.
359 for (PerFrameData
* pfd
= mRootSpan
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
360 pfd
->mBounds
.x
+= deltaX
;
364 mTopEdge
= aNewAvailSpace
.y
;
365 SetFlag(LL_IMPACTEDBYFLOATS
, PR_TRUE
);
367 SetFlag(LL_LASTFLOATWASLETTERFRAME
,
368 nsGkAtoms::letterFrame
== aFloatFrame
->GetType());
372 nsLineLayout::NewPerSpanData(PerSpanData
** aResult
)
374 PerSpanData
* psd
= mSpanFreeList
;
377 PL_ARENA_ALLOCATE(mem
, &mArena
, sizeof(PerSpanData
));
379 return NS_ERROR_OUT_OF_MEMORY
;
381 psd
= reinterpret_cast<PerSpanData
*>(mem
);
384 mSpanFreeList
= psd
->mNextFreeSpan
;
386 psd
->mParent
= nsnull
;
387 psd
->mFrame
= nsnull
;
388 psd
->mFirstFrame
= nsnull
;
389 psd
->mLastFrame
= nsnull
;
390 psd
->mContainsFloat
= PR_FALSE
;
391 psd
->mZeroEffectiveSpanBox
= PR_FALSE
;
392 psd
->mHasNonemptyContent
= PR_FALSE
;
402 nsLineLayout::BeginSpan(nsIFrame
* aFrame
,
403 const nsHTMLReflowState
* aSpanReflowState
,
407 NS_ASSERTION(aRightEdge
!= NS_UNCONSTRAINEDSIZE
,
408 "should no longer be using unconstrained sizes");
410 nsFrame::IndentBy(stdout
, mSpanDepth
+1);
411 nsFrame::ListTag(stdout
, aFrame
);
412 printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aLeftEdge
, aRightEdge
);
416 nsresult rv
= NewPerSpanData(&psd
);
417 if (NS_SUCCEEDED(rv
)) {
418 // Link up span frame's pfd to point to its child span data
419 PerFrameData
* pfd
= mCurrentSpan
->mLastFrame
;
420 NS_ASSERTION(pfd
->mFrame
== aFrame
, "huh?");
425 psd
->mParent
= mCurrentSpan
;
426 psd
->mReflowState
= aSpanReflowState
;
427 psd
->mLeftEdge
= aLeftEdge
;
429 psd
->mRightEdge
= aRightEdge
;
432 !aSpanReflowState
->frame
->GetStyleText()->WhiteSpaceCanWrap();
433 psd
->mDirection
= aSpanReflowState
->mStyleVisibility
->mDirection
;
434 psd
->mChangedFrameDirection
= PR_FALSE
;
436 // Switch to new span
444 nsLineLayout::EndSpan(nsIFrame
* aFrame
)
446 NS_ASSERTION(mSpanDepth
> 0, "end-span without begin-span");
448 nsFrame::IndentBy(stdout
, mSpanDepth
);
449 nsFrame::ListTag(stdout
, aFrame
);
450 printf(": EndSpan width=%d\n", mCurrentSpan
->mX
- mCurrentSpan
->mLeftEdge
);
452 PerSpanData
* psd
= mCurrentSpan
;
453 nscoord widthResult
= psd
->mLastFrame
? (psd
->mX
- psd
->mLeftEdge
) : 0;
456 mCurrentSpan
->mReflowState
= nsnull
; // no longer valid so null it out!
457 mCurrentSpan
= mCurrentSpan
->mParent
;
462 nsLineLayout::GetCurrentSpanCount() const
464 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
466 PerFrameData
* pfd
= mRootSpan
->mFirstFrame
;
467 while (nsnull
!= pfd
) {
475 nsLineLayout::SplitLineTo(PRInt32 aNewCount
)
477 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
479 #ifdef REALLY_NOISY_PUSHING
480 printf("SplitLineTo %d (current count=%d); before:\n", aNewCount
,
481 GetCurrentSpanCount());
482 DumpPerSpanData(mRootSpan
, 1);
484 PerSpanData
* psd
= mRootSpan
;
485 PerFrameData
* pfd
= psd
->mFirstFrame
;
486 while (nsnull
!= pfd
) {
487 if (--aNewCount
== 0) {
488 // Truncate list at pfd (we keep pfd, but anything following is freed)
489 PerFrameData
* next
= pfd
->mNext
;
491 psd
->mLastFrame
= pfd
;
493 // Now release all of the frames following pfd
495 while (nsnull
!= pfd
) {
497 pfd
->mNext
= mFrameFreeList
;
498 mFrameFreeList
= pfd
;
502 if (nsnull
!= pfd
->mSpan
) {
503 FreeSpan(pfd
->mSpan
);
512 printf("SplitLineTo %d (current count=%d); after:\n", aNewCount
,
513 GetCurrentSpanCount());
514 DumpPerSpanData(mRootSpan
, 1);
519 nsLineLayout::PushFrame(nsIFrame
* aFrame
)
521 PerSpanData
* psd
= mCurrentSpan
;
522 NS_ASSERTION(psd
->mLastFrame
->mFrame
== aFrame
, "pushing non-last frame");
524 #ifdef REALLY_NOISY_PUSHING
525 nsFrame::IndentBy(stdout
, mSpanDepth
);
526 printf("PushFrame %p, before:\n", psd
);
527 DumpPerSpanData(psd
, 1);
530 // Take the last frame off of the span's frame list
531 PerFrameData
* pfd
= psd
->mLastFrame
;
532 if (pfd
== psd
->mFirstFrame
) {
533 // We are pushing away the only frame...empty the list
534 psd
->mFirstFrame
= nsnull
;
535 psd
->mLastFrame
= nsnull
;
538 PerFrameData
* prevFrame
= pfd
->mPrev
;
539 prevFrame
->mNext
= nsnull
;
540 psd
->mLastFrame
= prevFrame
;
543 // Now free it, and if it has a span, free that too
544 pfd
->mNext
= mFrameFreeList
;
545 mFrameFreeList
= pfd
;
549 if (nsnull
!= pfd
->mSpan
) {
550 FreeSpan(pfd
->mSpan
);
553 nsFrame::IndentBy(stdout
, mSpanDepth
);
554 printf("PushFrame: %p after:\n", psd
);
555 DumpPerSpanData(psd
, 1);
560 nsLineLayout::FreeSpan(PerSpanData
* psd
)
563 PerFrameData
* pfd
= psd
->mFirstFrame
;
564 while (nsnull
!= pfd
) {
565 if (nsnull
!= pfd
->mSpan
) {
566 FreeSpan(pfd
->mSpan
);
568 PerFrameData
* next
= pfd
->mNext
;
569 pfd
->mNext
= mFrameFreeList
;
570 mFrameFreeList
= pfd
;
577 // Now put the span on the free list since it's free too
578 psd
->mNextFreeSpan
= mSpanFreeList
;
586 nsLineLayout::IsZeroHeight()
588 PerSpanData
* psd
= mCurrentSpan
;
589 PerFrameData
* pfd
= psd
->mFirstFrame
;
590 while (nsnull
!= pfd
) {
591 if (0 != pfd
->mBounds
.height
) {
600 nsLineLayout::NewPerFrameData(PerFrameData
** aResult
)
602 PerFrameData
* pfd
= mFrameFreeList
;
605 PL_ARENA_ALLOCATE(mem
, &mArena
, sizeof(PerFrameData
));
607 return NS_ERROR_OUT_OF_MEMORY
;
609 pfd
= reinterpret_cast<PerFrameData
*>(mem
);
612 mFrameFreeList
= pfd
->mNext
;
617 pfd
->mFrame
= nsnull
;
618 pfd
->mFlags
= 0; // all flags default to false
621 pfd
->mVerticalAlign
= 0xFF;
629 nsLineLayout::LineIsBreakable() const
631 // XXX mTotalPlacedFrames should go away and we should just use
632 // LL_LINEISEMPTY here instead
633 if ((0 != mTotalPlacedFrames
) || GetFlag(LL_IMPACTEDBYFLOATS
)) {
639 // Checks all four sides for percentage units. This means it should
640 // only be used for things (margin, padding) where percentages on top
641 // and bottom depend on the *width* just like percentages on left and
644 HasPercentageUnitSide(const nsStyleSides
& aSides
)
646 NS_FOR_CSS_SIDES(side
) {
647 if (aSides
.Get(side
).HasPercent())
654 IsPercentageAware(const nsIFrame
* aFrame
)
656 NS_ASSERTION(aFrame
, "null frame is not allowed");
658 nsIAtom
*fType
= aFrame
->GetType();
659 if (fType
== nsGkAtoms::textFrame
) {
660 // None of these things can ever be true for text frames.
664 // Some of these things don't apply to non-replaced inline frames
665 // (that is, fType == nsGkAtoms::inlineFrame || fType ==
666 // nsGkAtoms::positionedInlineFrame), but we won't bother making
667 // things unnecessarily complicated, since they'll probably be set
670 const nsStyleMargin
* margin
= aFrame
->GetStyleMargin();
671 if (HasPercentageUnitSide(margin
->mMargin
)) {
675 const nsStylePadding
* padding
= aFrame
->GetStylePadding();
676 if (HasPercentageUnitSide(padding
->mPadding
)) {
680 // Note that borders can't be aware of percentages
682 const nsStylePosition
* pos
= aFrame
->GetStylePosition();
684 if ((pos
->WidthDependsOnContainer() &&
685 pos
->mWidth
.GetUnit() != eStyleUnit_Auto
) ||
686 pos
->MaxWidthDependsOnContainer() ||
687 pos
->MinWidthDependsOnContainer() ||
688 pos
->OffsetHasPercent(NS_SIDE_RIGHT
) ||
689 pos
->OffsetHasPercent(NS_SIDE_LEFT
)) {
693 if (eStyleUnit_Auto
== pos
->mWidth
.GetUnit()) {
694 // We need to check for frames that shrink-wrap when they're auto
696 const nsStyleDisplay
* disp
= aFrame
->GetStyleDisplay();
697 if (disp
->mDisplay
== NS_STYLE_DISPLAY_INLINE_BLOCK
||
698 disp
->mDisplay
== NS_STYLE_DISPLAY_INLINE_TABLE
||
699 fType
== nsGkAtoms::HTMLButtonControlFrame
||
700 fType
== nsGkAtoms::gfxButtonControlFrame
||
701 fType
== nsGkAtoms::fieldSetFrame
||
702 fType
== nsGkAtoms::comboboxDisplayFrame
) {
706 // Handle SVG, which doesn't map width/height into style
709 fType
== nsGkAtoms::svgOuterSVGFrame
||
710 fType
== nsGkAtoms::imageFrame
||
712 fType
== nsGkAtoms::subDocumentFrame
) &&
713 const_cast<nsIFrame
*>(aFrame
)->GetIntrinsicSize().width
.GetUnit() ==
714 eStyleUnit_Percent
) {
723 nsLineLayout::ReflowFrame(nsIFrame
* aFrame
,
724 nsReflowStatus
& aReflowStatus
,
725 nsHTMLReflowMetrics
* aMetrics
,
726 PRBool
& aPushedFrame
)
728 // Initialize OUT parameter
729 aPushedFrame
= PR_FALSE
;
732 nsresult rv
= NewPerFrameData(&pfd
);
736 PerSpanData
* psd
= mCurrentSpan
;
737 psd
->AppendFrame(pfd
);
739 #ifdef REALLY_NOISY_REFLOW
740 nsFrame::IndentBy(stdout
, mSpanDepth
);
741 printf("%p: Begin ReflowFrame pfd=%p ", psd
, pfd
);
742 nsFrame::ListTag(stdout
, aFrame
);
746 // See if this frame depends on the width of its containing block. If
747 // so, disable resize reflow optimizations for the line. (Note that,
748 // to be conservative, we do this if we *try* to fit a frame on a
749 // line, even if we don't succeed.)
750 if (GetFlag(LL_GOTLINEBOX
) && IsPercentageAware(aFrame
)) {
751 mLineBox
->DisableResizeReflowOptimization();
754 mTextJustificationNumSpaces
= 0;
755 mTextJustificationNumLetters
= 0;
757 // Stash copies of some of the computed state away for later
758 // (vertical alignment, for example)
759 pfd
->mFrame
= aFrame
;
761 // NOTE: While the x coordinate remains relative to the parent span,
762 // the y coordinate is fixed at the top edge for the line. During
763 // VerticalAlignFrames we will repair this so that the y coordinate
764 // is properly set and relative to the appropriate span.
765 pfd
->mBounds
.x
= psd
->mX
;
766 pfd
->mBounds
.y
= mTopEdge
;
768 // We want to guarantee that we always make progress when
769 // formatting. Therefore, if the object being placed on the line is
770 // too big for the line, but it is the only thing on the line and is not
771 // impacted by a float, then we go ahead and place it anyway. (If the line
772 // is impacted by one or more floats, then it is safe to break because
773 // we can move the line down below float(s).)
775 // Capture this state *before* we reflow the frame in case it clears
776 // the state out. We need to know how to treat the current frame
778 PRBool notSafeToBreak
= LineIsEmpty() && !GetFlag(LL_IMPACTEDBYFLOATS
);
780 // Figure out whether we're talking about a textframe here
781 nsIAtom
* frameType
= aFrame
->GetType();
782 PRBool isText
= frameType
== nsGkAtoms::textFrame
;
784 // Compute the available size for the frame. This available width
785 // includes room for the side margins.
786 // For now, set the available height to unconstrained always.
787 nsSize
availSize(mBlockReflowState
->ComputedWidth(), NS_UNCONSTRAINEDSIZE
);
789 // Inline-ish and text-ish things don't compute their width;
790 // everything else does. We need to give them an available width that
791 // reflects the space left on the line.
792 NS_WARN_IF_FALSE(psd
->mRightEdge
!= NS_UNCONSTRAINEDSIZE
,
793 "have unconstrained width; this should only result from "
794 "very large sizes, not attempts at intrinsic width "
796 nscoord availableSpaceOnLine
= psd
->mRightEdge
- psd
->mX
;
798 // Setup reflow state for reflowing the frame
799 js::LazilyConstructed
<nsHTMLReflowState
> reflowStateHolder
;
801 reflowStateHolder
.construct(mPresContext
, *psd
->mReflowState
,
803 nsHTMLReflowState
& reflowState
= reflowStateHolder
.ref();
804 reflowState
.mLineLayout
= this;
805 reflowState
.mFlags
.mIsTopOfPage
= GetFlag(LL_ISTOPOFPAGE
);
806 if (reflowState
.ComputedWidth() == NS_UNCONSTRAINEDSIZE
)
807 reflowState
.availableWidth
= availableSpaceOnLine
;
808 pfd
->mMargin
= reflowState
.mComputedMargin
;
809 pfd
->mBorderPadding
= reflowState
.mComputedBorderPadding
;
810 pfd
->SetFlag(PFD_RELATIVEPOS
,
811 (reflowState
.mStyleDisplay
->mPosition
== NS_STYLE_POSITION_RELATIVE
));
812 if (pfd
->GetFlag(PFD_RELATIVEPOS
)) {
813 pfd
->mOffsets
= reflowState
.mComputedOffsets
;
816 // Apply start margins (as appropriate) to the frame computing the
817 // new starting x,y coordinates for the frame.
818 ApplyStartMargin(pfd
, reflowState
);
820 pfd
->mMargin
.SizeTo(0, 0, 0, 0);
821 pfd
->mBorderPadding
.SizeTo(0, 0, 0, 0);
822 pfd
->mOffsets
.SizeTo(0, 0, 0, 0);
823 // Text reflow doesn't look at the dirty bits on the frame being reflowed,
824 // so no need to propagate NS_FRAME_IS_DIRTY from the parent.
827 // Let frame know that are reflowing it. Note that we don't bother
828 // positioning the frame yet, because we're probably going to end up
829 // moving it when we do the vertical alignment
830 aFrame
->WillReflow(mPresContext
);
832 // Adjust spacemanager coordinate system for the frame.
833 nsHTMLReflowMetrics metrics
;
835 metrics
.width
= nscoord(0xdeadbeef);
836 metrics
.height
= nscoord(0xdeadbeef);
838 nscoord tx
= pfd
->mBounds
.x
;
839 nscoord ty
= pfd
->mBounds
.y
;
840 mFloatManager
->Translate(tx
, ty
);
842 PRInt32 savedOptionalBreakOffset
;
843 gfxBreakPriority savedOptionalBreakPriority
;
844 nsIContent
* savedOptionalBreakContent
=
845 GetLastOptionalBreakPosition(&savedOptionalBreakOffset
,
846 &savedOptionalBreakPriority
);
849 rv
= aFrame
->Reflow(mPresContext
, metrics
, reflowStateHolder
.ref(),
852 NS_WARNING( "Reflow of frame failed in nsLineLayout" );
856 static_cast<nsTextFrame
*>(aFrame
)->
857 ReflowText(*this, availableSpaceOnLine
, psd
->mReflowState
->rendContext
,
858 psd
->mReflowState
->mFlags
.mBlinks
, metrics
, aReflowStatus
);
861 pfd
->mJustificationNumSpaces
= mTextJustificationNumSpaces
;
862 pfd
->mJustificationNumLetters
= mTextJustificationNumLetters
;
864 // See if the frame is a placeholderFrame and if it is process
865 // the float. At the same time, check if the frame has any non-collapsed-away
867 PRBool placedFloat
= PR_FALSE
;
870 isEmpty
= pfd
->mFrame
->IsEmpty();
872 if (nsGkAtoms::placeholderFrame
== frameType
) {
874 pfd
->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE
, PR_TRUE
);
875 nsIFrame
* outOfFlowFrame
= nsLayoutUtils::GetFloatFromPlaceholder(aFrame
);
876 if (outOfFlowFrame
) {
877 // Add mTrimmableWidth to the available width since if the line ends
878 // here, the width of the inline content will be reduced by
880 nscoord availableWidth
= psd
->mRightEdge
- (psd
->mX
- mTrimmableWidth
);
882 // If we place floats after inline content where there's
883 // no break opportunity, we don't know how much additional
884 // width is required for the non-breaking content after the float,
885 // so we can't know whether the float plus that content will fit
886 // on the line. So for now, don't place floats after inline
887 // content where there's no break opportunity. This is incorrect
888 // but hopefully rare. Fixing it will require significant
889 // restructuring of line layout.
890 // We might as well allow zero-width floats to be placed, though.
893 placedFloat
= AddFloat(outOfFlowFrame
, availableWidth
);
894 NS_ASSERTION(!(outOfFlowFrame
->GetType() == nsGkAtoms::letterFrame
&&
895 GetFirstLetterStyleOK()),
896 "FirstLetterStyle set on line with floating first letter");
900 // Note non-empty text-frames for inline frame compatibility hackery
901 pfd
->SetFlag(PFD_ISTEXTFRAME
, PR_TRUE
);
902 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(pfd
->mFrame
);
903 isEmpty
= !textFrame
->HasNoncollapsedCharacters();
905 pfd
->SetFlag(PFD_ISNONEMPTYTEXTFRAME
, PR_TRUE
);
906 nsIContent
* content
= textFrame
->GetContent();
908 const nsTextFragment
* frag
= content
->GetText();
910 pfd
->SetFlag(PFD_ISNONWHITESPACETEXTFRAME
,
911 !content
->TextIsOnlyWhitespace());
915 else if (nsGkAtoms::brFrame
== frameType
) {
916 pfd
->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE
, PR_TRUE
);
919 if (nsGkAtoms::letterFrame
==frameType
) {
920 pfd
->SetFlag(PFD_ISLETTERFRAME
, PR_TRUE
);
923 isEmpty
= !pfd
->mSpan
->mHasNonemptyContent
&& pfd
->mFrame
->IsSelfEmpty();
925 isEmpty
= pfd
->mFrame
->IsEmpty();
930 mFloatManager
->Translate(-tx
, -ty
);
932 NS_ASSERTION(metrics
.width
>=0, "bad width");
933 NS_ASSERTION(metrics
.height
>=0,"bad height");
934 if (metrics
.width
<0) metrics
.width
=0;
935 if (metrics
.height
<0) metrics
.height
=0;
938 // Note: break-before means ignore the reflow metrics since the
939 // frame will be reflowed another time.
940 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus
)) {
941 if (CRAZY_WIDTH(metrics
.width
) || CRAZY_HEIGHT(metrics
.height
)) {
942 printf("nsLineLayout: ");
943 nsFrame::ListTag(stdout
, aFrame
);
944 printf(" metrics=%d,%d!\n", metrics
.width
, metrics
.height
);
946 if ((metrics
.width
== nscoord(0xdeadbeef)) ||
947 (metrics
.height
== nscoord(0xdeadbeef))) {
948 printf("nsLineLayout: ");
949 nsFrame::ListTag(stdout
, aFrame
);
950 printf(" didn't set w/h %d,%d!\n", metrics
.width
, metrics
.height
);
955 // Unlike with non-inline reflow, the overflow area here does *not*
956 // include the accumulation of the frame's bounds and its inline
957 // descendants' bounds. Nor does it include the outline area; it's
958 // just the union of the bounds of any absolute children. That is
959 // added in later by nsLineLayout::ReflowInlineFrames.
960 pfd
->mOverflowAreas
= metrics
.mOverflowAreas
;
962 pfd
->mBounds
.width
= metrics
.width
;
963 pfd
->mBounds
.height
= metrics
.height
;
965 // Size the frame, but |RelativePositionFrames| will size the view.
966 aFrame
->SetSize(nsSize(metrics
.width
, metrics
.height
));
968 // Tell the frame that we're done reflowing it
969 aFrame
->DidReflow(mPresContext
,
970 isText
? nsnull
: reflowStateHolder
.addr(),
971 NS_FRAME_REFLOW_FINISHED
);
977 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus
)) {
978 // If frame is complete and has a next-in-flow, we need to delete
979 // them now. Do not do this when a break-before is signaled because
980 // the frame is going to get reflowed again (and may end up wanting
981 // a next-in-flow where it ends up).
982 if (NS_FRAME_IS_COMPLETE(aReflowStatus
)) {
983 nsIFrame
* kidNextInFlow
= aFrame
->GetNextInFlow();
984 if (nsnull
!= kidNextInFlow
) {
985 // Remove all of the childs next-in-flows. Make sure that we ask
986 // the right parent to do the removal (it's possible that the
987 // parent is not this because we are executing pullup code)
988 nsHTMLContainerFrame
* parent
= static_cast<nsHTMLContainerFrame
*>
989 (kidNextInFlow
->GetParent());
990 parent
->DeleteNextInFlowChild(mPresContext
, kidNextInFlow
, PR_TRUE
);
994 // Check whether this frame breaks up text runs. All frames break up text
995 // runs (hence return false here) except for text frames and inline containers.
996 PRBool continuingTextRun
= aFrame
->CanContinueTextRun();
998 // Clear any residual mTrimmableWidth if this isn't a text frame
999 if (!continuingTextRun
&& !pfd
->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE
)) {
1000 mTrimmableWidth
= 0;
1003 // See if we can place the frame. If we can't fit it, then we
1005 PRBool optionalBreakAfterFits
;
1006 NS_ASSERTION(isText
||
1007 reflowStateHolder
.ref().mStyleDisplay
->mFloats
==
1008 NS_STYLE_FLOAT_NONE
,
1009 "How'd we get a floated inline frame? "
1010 "The frame ctor should've dealt with this.");
1011 // Direction is inherited, so using the psd direction is fine.
1012 // Get it off the reflow state instead of the frame to save style
1013 // data computation (especially for the text).
1015 isText
? psd
->mReflowState
->mStyleVisibility
->mDirection
:
1016 reflowStateHolder
.ref().mStyleVisibility
->mDirection
;
1017 if (CanPlaceFrame(pfd
, direction
, notSafeToBreak
, continuingTextRun
,
1018 savedOptionalBreakContent
!= nsnull
, metrics
,
1019 aReflowStatus
, &optionalBreakAfterFits
)) {
1021 psd
->mHasNonemptyContent
= PR_TRUE
;
1022 SetFlag(LL_LINEISEMPTY
, PR_FALSE
);
1024 // nonempty leaf content has been placed
1025 SetFlag(LL_LINEATSTART
, PR_FALSE
);
1029 // Place the frame, updating aBounds with the final size and
1030 // location. Then apply the bottom+right margins (as
1031 // appropriate) to the frame.
1032 PlaceFrame(pfd
, metrics
);
1033 PerSpanData
* span
= pfd
->mSpan
;
1035 // The frame we just finished reflowing is an inline
1036 // container. It needs its child frames vertically aligned,
1037 // so do most of it now.
1038 VerticalAlignFrames(span
);
1041 if (!continuingTextRun
) {
1042 if (!psd
->mNoWrap
&& (!LineIsEmpty() || placedFloat
)) {
1043 // record soft break opportunity after this content that can't be
1044 // part of a text run. This is not a text frame so we know
1045 // that offset PR_INT32_MAX means "after the content".
1046 if (NotifyOptionalBreakPosition(aFrame
->GetContent(), PR_INT32_MAX
, optionalBreakAfterFits
, eNormalBreak
)) {
1047 // If this returns true then we are being told to actually break here.
1048 aReflowStatus
= NS_INLINE_LINE_BREAK_AFTER(aReflowStatus
);
1055 aPushedFrame
= PR_TRUE
;
1056 // Undo any saved break positions that the frame might have told us about,
1057 // since we didn't end up placing it
1058 RestoreSavedBreakPosition(savedOptionalBreakContent
,
1059 savedOptionalBreakOffset
,
1060 savedOptionalBreakPriority
);
1067 #ifdef REALLY_NOISY_REFLOW
1068 nsFrame::IndentBy(stdout
, mSpanDepth
);
1069 printf("End ReflowFrame ");
1070 nsFrame::ListTag(stdout
, aFrame
);
1071 printf(" status=%x\n", aReflowStatus
);
1078 nsLineLayout::ApplyStartMargin(PerFrameData
* pfd
,
1079 nsHTMLReflowState
& aReflowState
)
1081 NS_ASSERTION(aReflowState
.mStyleDisplay
->mFloats
== NS_STYLE_FLOAT_NONE
,
1082 "How'd we get a floated inline frame? "
1083 "The frame ctor should've dealt with this.");
1085 // XXXwaterson probably not the right way to get this; e.g., embeddings, etc.
1086 PRBool ltr
= (NS_STYLE_DIRECTION_LTR
== aReflowState
.mStyleVisibility
->mDirection
);
1088 // Only apply start-margin on the first-in flow for inline frames,
1089 // and make sure to not apply it to any inline other than the first
1090 // in an ib split. Note that the ib special sibling annotations
1091 // only live on the first continuation, but we don't want to apply
1092 // the start margin for later continuations anyway.
1093 if (pfd
->mFrame
->GetPrevContinuation() ||
1094 nsLayoutUtils::FrameIsNonFirstInIBSplit(pfd
->mFrame
)) {
1095 // Zero this out so that when we compute the max-element-width of
1096 // the frame we will properly avoid adding in the starting margin.
1098 pfd
->mMargin
.left
= 0;
1100 pfd
->mMargin
.right
= 0;
1103 pfd
->mBounds
.x
+= ltr
? pfd
->mMargin
.left
: pfd
->mMargin
.right
;
1105 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE
!= aReflowState
.availableWidth
,
1106 "have unconstrained width; this should only result from "
1107 "very large sizes, not attempts at intrinsic width "
1109 if (NS_UNCONSTRAINEDSIZE
== aReflowState
.ComputedWidth()) {
1110 // For inline-ish and text-ish things (which don't compute widths
1111 // in the reflow state), adjust available width to account for the
1112 // left margin. The right margin will be accounted for when we
1113 // finish flowing the frame.
1114 aReflowState
.availableWidth
-= ltr
? pfd
->mMargin
.left
: pfd
->mMargin
.right
;
1120 nsLineLayout::GetCurrentFrameXDistanceFromBlock()
1124 for (psd
= mCurrentSpan
; psd
; psd
= psd
->mParent
) {
1131 * See if the frame can be placed now that we know it's desired size.
1132 * We can always place the frame if the line is empty. Note that we
1133 * know that the reflow-status is not a break-before because if it was
1134 * ReflowFrame above would have returned false, preventing this method
1135 * from being called. The logic in this method assumes that.
1137 * Note that there is no check against the Y coordinate because we
1138 * assume that the caller will take care of that.
1141 nsLineLayout::CanPlaceFrame(PerFrameData
* pfd
,
1142 PRUint8 aFrameDirection
,
1143 PRBool aNotSafeToBreak
,
1144 PRBool aFrameCanContinueTextRun
,
1145 PRBool aCanRollBackBeforeFrame
,
1146 nsHTMLReflowMetrics
& aMetrics
,
1147 nsReflowStatus
& aStatus
,
1148 PRBool
* aOptionalBreakAfterFits
)
1150 NS_PRECONDITION(pfd
&& pfd
->mFrame
, "bad args, null pointers for frame data");
1152 *aOptionalBreakAfterFits
= PR_TRUE
;
1153 // Compute right margin to use
1154 if (0 != pfd
->mBounds
.width
) {
1155 // XXXwaterson this is probably not exactly right; e.g., embeddings, etc.
1156 PRBool ltr
= (NS_STYLE_DIRECTION_LTR
== aFrameDirection
);
1159 * We want to only apply the end margin if we're the last continuation and
1160 * either not in an {ib} split or the last inline in it. In all other
1161 * cases we want to zero it out. That means zeroing it out if any of these
1163 * 1) The frame is not complete (in this case it will get a next-in-flow)
1164 * 2) The frame is complete but has a non-fluid continuation on its
1165 * continuation chain. Note that if it has a fluid continuation, that
1166 * continuation will get destroyed later, so we don't want to drop the
1167 * end-margin in that case.
1168 * 3) The frame is in an {ib} split and is not the last part.
1170 * However, none of that applies if this is a letter frame (XXXbz why?)
1172 if ((NS_FRAME_IS_NOT_COMPLETE(aStatus
) ||
1173 pfd
->mFrame
->GetLastInFlow()->GetNextContinuation() ||
1174 nsLayoutUtils::FrameIsNonLastInIBSplit(pfd
->mFrame
))
1175 && !pfd
->GetFlag(PFD_ISLETTERFRAME
)) {
1177 pfd
->mMargin
.right
= 0;
1179 pfd
->mMargin
.left
= 0;
1183 // Don't apply margin to empty frames.
1184 pfd
->mMargin
.left
= pfd
->mMargin
.right
= 0;
1187 PerSpanData
* psd
= mCurrentSpan
;
1189 // When wrapping is off, everything fits.
1193 PRBool ltr
= NS_STYLE_DIRECTION_LTR
== aFrameDirection
;
1194 nscoord endMargin
= ltr
? pfd
->mMargin
.right
: pfd
->mMargin
.left
;
1196 #ifdef NOISY_CAN_PLACE_FRAME
1197 if (nsnull
!= psd
->mFrame
) {
1198 nsFrame::ListTag(stdout
, psd
->mFrame
->mFrame
);
1201 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
1203 printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak
? "true" : "false");
1204 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1205 printf(" frameWidth=%d\n", pfd
->mBounds
.XMost() + endMargin
- psd
->mX
);
1208 // Set outside to PR_TRUE if the result of the reflow leads to the
1209 // frame sticking outside of our available area.
1210 PRBool outside
= pfd
->mBounds
.XMost() - mTrimmableWidth
+ endMargin
> psd
->mRightEdge
;
1212 // If it fits, it fits
1213 #ifdef NOISY_CAN_PLACE_FRAME
1214 printf(" ==> inside\n");
1218 *aOptionalBreakAfterFits
= PR_FALSE
;
1220 // When it doesn't fit, check for a few special conditions where we
1221 // allow it to fit anyway.
1222 if (0 == pfd
->mMargin
.left
+ pfd
->mBounds
.width
+ pfd
->mMargin
.right
) {
1223 // Empty frames always fit right where they are
1224 #ifdef NOISY_CAN_PLACE_FRAME
1225 printf(" ==> empty frame fits\n");
1230 #ifdef FIX_BUG_50257
1231 // another special case: always place a BR
1232 if (nsGkAtoms::brFrame
== pfd
->mFrame
->GetType()) {
1233 #ifdef NOISY_CAN_PLACE_FRAME
1234 printf(" ==> BR frame fits\n");
1240 if (aNotSafeToBreak
) {
1241 // There are no frames on the line that take up width and the line is
1242 // not impacted by floats, so we must allow the current frame to be
1243 // placed on the line
1244 #ifdef NOISY_CAN_PLACE_FRAME
1245 printf(" ==> not-safe and not-impacted fits: ");
1246 while (nsnull
!= psd
) {
1247 printf("<psd=%p x=%d left=%d> ", psd
, psd
->mX
, psd
->mLeftEdge
);
1255 // Special check for span frames
1256 if (pfd
->mSpan
&& pfd
->mSpan
->mContainsFloat
) {
1257 // If the span either directly or indirectly contains a float then
1258 // it fits. Why? It's kind of complicated, but here goes:
1260 // 1. CanPlaceFrame is used for all frame placements on a line,
1261 // and in a span. This includes recursively placement of frames
1262 // inside of spans, and the span itself. Because the logic always
1263 // checks for room before proceeding (the code above here), the
1264 // only things on a line will be those things that "fit".
1266 // 2. Before a float is placed on a line, the line has to be empty
1267 // (otherwise it's a "below current line" float and will be placed
1270 // Therefore, if the span directly or indirectly has a float
1271 // then it means that at the time of the placement of the float
1272 // the line was empty. Because of #1, only the frames that fit can
1273 // be added after that point, therefore we can assume that the
1274 // current span being placed has fit.
1276 // So how do we get here and have a span that should already fit
1277 // and yet doesn't: Simple: span's that have the no-wrap attribute
1278 // set on them and contain a float and are placed where they
1279 // don't naturally fit.
1283 if (aFrameCanContinueTextRun
) {
1284 // Let it fit, but we reserve the right to roll back.
1285 // Note that we usually won't get here because a text frame will break
1286 // itself to avoid exceeding the available width.
1287 // We'll only get here for text frames that couldn't break early enough.
1288 #ifdef NOISY_CAN_PLACE_FRAME
1289 printf(" ==> placing overflowing textrun, requesting backup\n");
1292 // We will want to try backup.
1293 SetFlag(LL_NEEDBACKUP
, PR_TRUE
);
1297 #ifdef NOISY_CAN_PLACE_FRAME
1298 printf(" ==> didn't fit\n");
1300 aStatus
= NS_INLINE_LINE_BREAK_BEFORE();
1305 * Place the frame. Update running counters.
1308 nsLineLayout::PlaceFrame(PerFrameData
* pfd
, nsHTMLReflowMetrics
& aMetrics
)
1310 // If frame is zero width then do not apply its left and right margins.
1311 PerSpanData
* psd
= mCurrentSpan
;
1312 PRBool emptyFrame
= PR_FALSE
;
1313 if ((0 == pfd
->mBounds
.width
) && (0 == pfd
->mBounds
.height
)) {
1314 pfd
->mBounds
.x
= psd
->mX
;
1315 pfd
->mBounds
.y
= mTopEdge
;
1316 emptyFrame
= PR_TRUE
;
1319 // Record ascent and update max-ascent and max-descent values
1320 if (aMetrics
.ascent
== nsHTMLReflowMetrics::ASK_FOR_BASELINE
)
1321 pfd
->mAscent
= pfd
->mFrame
->GetBaseline();
1323 pfd
->mAscent
= aMetrics
.ascent
;
1325 PRBool ltr
= (NS_STYLE_DIRECTION_LTR
== pfd
->mFrame
->GetStyleVisibility()->mDirection
);
1326 // Advance to next X coordinate
1327 psd
->mX
= pfd
->mBounds
.XMost() + (ltr
? pfd
->mMargin
.right
: pfd
->mMargin
.left
);
1329 // Count the number of non-empty frames on the line...
1331 mTotalPlacedFrames
++;
1336 nsLineLayout::AddBulletFrame(nsIFrame
* aFrame
,
1337 const nsHTMLReflowMetrics
& aMetrics
)
1339 NS_ASSERTION(mCurrentSpan
== mRootSpan
, "bad linelayout user");
1340 NS_ASSERTION(GetFlag(LL_GOTLINEBOX
), "must have line box");
1343 nsIFrame
*blockFrame
= mBlockReflowState
->frame
;
1344 NS_ASSERTION(blockFrame
->IsFrameOfType(nsIFrame::eBlockFrame
),
1345 "must be for block");
1346 if (!static_cast<nsBlockFrame
*>(blockFrame
)->BulletIsEmpty()) {
1347 SetFlag(LL_HASBULLET
, PR_TRUE
);
1348 mLineBox
->SetHasBullet();
1352 nsresult rv
= NewPerFrameData(&pfd
);
1353 if (NS_SUCCEEDED(rv
)) {
1354 mRootSpan
->AppendFrame(pfd
);
1355 pfd
->mFrame
= aFrame
;
1356 pfd
->mMargin
.SizeTo(0, 0, 0, 0);
1357 pfd
->mBorderPadding
.SizeTo(0, 0, 0, 0);
1358 pfd
->mFlags
= 0; // all flags default to false
1359 pfd
->SetFlag(PFD_ISBULLET
, PR_TRUE
);
1360 if (aMetrics
.ascent
== nsHTMLReflowMetrics::ASK_FOR_BASELINE
)
1361 pfd
->mAscent
= aFrame
->GetBaseline();
1363 pfd
->mAscent
= aMetrics
.ascent
;
1365 // Note: y value will be updated during vertical alignment
1366 pfd
->mBounds
= aFrame
->GetRect();
1367 pfd
->mOverflowAreas
= aMetrics
.mOverflowAreas
;
1374 nsLineLayout::DumpPerSpanData(PerSpanData
* psd
, PRInt32 aIndent
)
1376 nsFrame::IndentBy(stdout
, aIndent
);
1377 printf("%p: left=%d x=%d right=%d\n", static_cast<void*>(psd
),
1378 psd
->mLeftEdge
, psd
->mX
, psd
->mRightEdge
);
1379 PerFrameData
* pfd
= psd
->mFirstFrame
;
1380 while (nsnull
!= pfd
) {
1381 nsFrame::IndentBy(stdout
, aIndent
+1);
1382 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1383 printf(" %d,%d,%d,%d\n", pfd
->mBounds
.x
, pfd
->mBounds
.y
,
1384 pfd
->mBounds
.width
, pfd
->mBounds
.height
);
1386 DumpPerSpanData(pfd
->mSpan
, aIndent
+ 1);
1393 #define VALIGN_OTHER 0
1394 #define VALIGN_TOP 1
1395 #define VALIGN_BOTTOM 2
1398 nsLineLayout::VerticalAlignLine()
1400 // Synthesize a PerFrameData for the block frame
1401 PerFrameData rootPFD
;
1402 rootPFD
.mFrame
= mBlockReflowState
->frame
;
1403 rootPFD
.mAscent
= 0;
1404 mRootSpan
->mFrame
= &rootPFD
;
1406 // Partially place the children of the block frame. The baseline for
1407 // this operation is set to zero so that the y coordinates for all
1408 // of the placed children will be relative to there.
1409 PerSpanData
* psd
= mRootSpan
;
1410 VerticalAlignFrames(psd
);
1412 // Compute the line-height. The line-height will be the larger of:
1414 // [1] maxY - minY (the distance between the highest childs top edge
1415 // and the lowest childs bottom edge)
1417 // [2] the maximum logical box height (since not every frame may have
1418 // participated in #1; for example: top/bottom aligned frames)
1420 // [3] the minimum line height (line-height property set on the
1422 nscoord lineHeight
= psd
->mMaxY
- psd
->mMinY
;
1424 // Now that the line-height is computed, we need to know where the
1425 // baseline is in the line. Position baseline so that mMinY is just
1426 // inside the top of the line box.
1428 if (psd
->mMinY
< 0) {
1429 baselineY
= mTopEdge
- psd
->mMinY
;
1432 baselineY
= mTopEdge
;
1435 // It's also possible that the line-height isn't tall enough because
1436 // of top/bottom aligned elements that were not accounted for in
1439 // The CSS2 spec doesn't really say what happens when to the
1440 // baseline in this situations. What we do is if the largest top
1441 // aligned box height is greater than the line-height then we leave
1442 // the baseline alone. If the largest bottom aligned box is greater
1443 // than the line-height then we slide the baseline down by the extra
1446 // Navigator 4 gives precedence to the first top/bottom aligned
1447 // object. We just let bottom aligned objects win.
1448 if (lineHeight
< mMaxBottomBoxHeight
) {
1449 // When the line is shorter than the maximum top aligned box
1450 nscoord extra
= mMaxBottomBoxHeight
- lineHeight
;
1452 lineHeight
= mMaxBottomBoxHeight
;
1454 if (lineHeight
< mMaxTopBoxHeight
) {
1455 lineHeight
= mMaxTopBoxHeight
;
1457 #ifdef NOISY_VERTICAL_ALIGN
1458 printf(" [line]==> lineHeight=%d baselineY=%d\n", lineHeight
, baselineY
);
1461 // Now position all of the frames in the root span. We will also
1462 // recurse over the child spans and place any top/bottom aligned
1464 // XXX PERFORMANCE: set a bit per-span to avoid the extra work
1465 // (propagate it upward too)
1466 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1467 if (pfd
->mVerticalAlign
== VALIGN_OTHER
) {
1468 pfd
->mBounds
.y
+= baselineY
;
1469 pfd
->mFrame
->SetRect(pfd
->mBounds
);
1472 PlaceTopBottomFrames(psd
, -mTopEdge
, lineHeight
);
1474 // Fill in returned line-box and max-element-width data
1475 mLineBox
->mBounds
.x
= psd
->mLeftEdge
;
1476 mLineBox
->mBounds
.y
= mTopEdge
;
1477 mLineBox
->mBounds
.width
= psd
->mX
- psd
->mLeftEdge
;
1478 mLineBox
->mBounds
.height
= lineHeight
;
1479 mFinalLineHeight
= lineHeight
;
1480 mLineBox
->SetAscent(baselineY
- mTopEdge
);
1481 #ifdef NOISY_VERTICAL_ALIGN
1483 " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n",
1484 mLineBox
->mBounds
.x
, mLineBox
->mBounds
.y
,
1485 mLineBox
->mBounds
.width
, mLineBox
->mBounds
.height
,
1486 mFinalLineHeight
, mLineBox
->GetAscent());
1489 // Undo root-span mFrame pointer to prevent brane damage later on...
1490 mRootSpan
->mFrame
= nsnull
;
1494 nsLineLayout::PlaceTopBottomFrames(PerSpanData
* psd
,
1495 nscoord aDistanceFromTop
,
1496 nscoord aLineHeight
)
1498 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1499 PerSpanData
* span
= pfd
->mSpan
;
1501 NS_ASSERTION(0xFF != pfd
->mVerticalAlign
, "umr");
1503 switch (pfd
->mVerticalAlign
) {
1506 pfd
->mBounds
.y
= -aDistanceFromTop
- span
->mMinY
;
1509 pfd
->mBounds
.y
= -aDistanceFromTop
+ pfd
->mMargin
.top
;
1511 pfd
->mFrame
->SetRect(pfd
->mBounds
);
1512 #ifdef NOISY_VERTICAL_ALIGN
1514 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1515 printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n",
1516 pfd
->mBounds
.y
, aDistanceFromTop
,
1517 span
? pfd
->mBorderPadding
.top
: 0,
1518 span
? span
->mTopLeading
: 0);
1523 // Compute bottom leading
1524 pfd
->mBounds
.y
= -aDistanceFromTop
+ aLineHeight
- span
->mMaxY
;
1527 pfd
->mBounds
.y
= -aDistanceFromTop
+ aLineHeight
-
1528 pfd
->mMargin
.bottom
- pfd
->mBounds
.height
;
1530 pfd
->mFrame
->SetRect(pfd
->mBounds
);
1531 #ifdef NOISY_VERTICAL_ALIGN
1533 nsFrame::ListTag(stdout
, pfd
->mFrame
);
1534 printf(": y=%d\n", pfd
->mBounds
.y
);
1539 nscoord distanceFromTop
= aDistanceFromTop
+ pfd
->mBounds
.y
;
1540 PlaceTopBottomFrames(span
, distanceFromTop
, aLineHeight
);
1545 #define VERTICAL_ALIGN_FRAMES_NO_MINIMUM nscoord_MAX
1546 #define VERTICAL_ALIGN_FRAMES_NO_MAXIMUM nscoord_MIN
1548 // Vertically place frames within a given span. Note: this doesn't
1549 // place top/bottom aligned frames as those have to wait until the
1550 // entire line box height is known. This is called after the span
1551 // frame has finished being reflowed so that we know its height.
1553 nsLineLayout::VerticalAlignFrames(PerSpanData
* psd
)
1555 // Get parent frame info
1556 PerFrameData
* spanFramePFD
= psd
->mFrame
;
1557 nsIFrame
* spanFrame
= spanFramePFD
->mFrame
;
1559 // Get the parent frame's font for all of the frames in this span
1560 nsStyleContext
* styleContext
= spanFrame
->GetStyleContext();
1561 nsIRenderingContext
* rc
= mBlockReflowState
->rendContext
;
1562 nsLayoutUtils::SetFontFromStyle(mBlockReflowState
->rendContext
, styleContext
);
1563 nsCOMPtr
<nsIFontMetrics
> fm
;
1564 rc
->GetFontMetrics(*getter_AddRefs(fm
));
1566 PRBool preMode
= mStyleText
->WhiteSpaceIsSignificant();
1568 // See if the span is an empty continuation. It's an empty continuation iff:
1569 // - it has a prev-in-flow
1570 // - it has no next in flow
1571 // - it's zero sized
1572 PRBool emptyContinuation
= psd
!= mRootSpan
&&
1573 spanFrame
->GetPrevInFlow() && !spanFrame
->GetNextInFlow() &&
1574 (0 == spanFramePFD
->mBounds
.width
) && (0 == spanFramePFD
->mBounds
.height
);
1576 #ifdef NOISY_VERTICAL_ALIGN
1577 printf("[%sSpan]", (psd
== mRootSpan
)?"Root":"");
1578 nsFrame::ListTag(stdout
, spanFrame
);
1579 printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s",
1580 preMode
? "yes" : "no",
1581 mPresContext
->CompatibilityMode() != eCompatibility_NavQuirks
? "yes" : "no",
1582 spanFramePFD
->mBounds
.width
, spanFramePFD
->mBounds
.height
,
1583 emptyContinuation
? "yes" : "no");
1584 if (psd
!= mRootSpan
) {
1585 printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
1586 spanFramePFD
->mBorderPadding
.top
,
1587 spanFramePFD
->mBorderPadding
.right
,
1588 spanFramePFD
->mBorderPadding
.bottom
,
1589 spanFramePFD
->mBorderPadding
.left
,
1590 spanFramePFD
->mMargin
.top
,
1591 spanFramePFD
->mMargin
.right
,
1592 spanFramePFD
->mMargin
.bottom
,
1593 spanFramePFD
->mMargin
.left
);
1598 // Compute the span's mZeroEffectiveSpanBox flag. What we are trying
1599 // to determine is how we should treat the span: should it act
1600 // "normally" according to css2 or should it effectively
1603 // In general, if the document being processed is in full standards
1604 // mode then it should act normally (with one exception). The
1605 // exception case is when a span is continued and yet the span is
1606 // empty (e.g. compressed whitespace). For this kind of span we treat
1607 // it as if it were not there so that it doesn't impact the
1610 // In almost standards mode or quirks mode, we should sometimes make
1611 // it disappear. The cases that matter are those where the span
1612 // contains no real text elements that would provide an ascent and
1613 // descent and height. However, if css style elements have been
1614 // applied to the span (border/padding/margin) so that it's clear the
1615 // document author is intending css2 behavior then we act as if strict
1618 // This code works correctly for preMode, because a blank line
1619 // in PRE mode is encoded as a text node with a LF in it, since
1620 // text nodes with only whitespace are considered in preMode.
1622 // Much of this logic is shared with the various implementations of
1623 // nsIFrame::IsEmpty since they need to duplicate the way it makes
1624 // some lines empty. However, nsIFrame::IsEmpty can't be reused here
1625 // since this code sets zeroEffectiveSpanBox even when there are
1626 // non-empty children.
1627 PRBool zeroEffectiveSpanBox
= PR_FALSE
;
1628 // XXXldb If we really have empty continuations, then all these other
1629 // checks don't make sense for them.
1630 // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that
1631 // it agrees with this code. (If it doesn't agree, it probably should.)
1632 if ((emptyContinuation
||
1633 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
) &&
1634 ((psd
== mRootSpan
) ||
1635 ((0 == spanFramePFD
->mBorderPadding
.top
) &&
1636 (0 == spanFramePFD
->mBorderPadding
.right
) &&
1637 (0 == spanFramePFD
->mBorderPadding
.bottom
) &&
1638 (0 == spanFramePFD
->mBorderPadding
.left
) &&
1639 (0 == spanFramePFD
->mMargin
.top
) &&
1640 (0 == spanFramePFD
->mMargin
.right
) &&
1641 (0 == spanFramePFD
->mMargin
.bottom
) &&
1642 (0 == spanFramePFD
->mMargin
.left
)))) {
1643 // This code handles an issue with compatibility with non-css
1644 // conformant browsers. In particular, there are some cases
1645 // where the font-size and line-height for a span must be
1646 // ignored and instead the span must *act* as if it were zero
1647 // sized. In general, if the span contains any non-compressed
1648 // text then we don't use this logic.
1649 // However, this is not propagated outwards, since (in compatibility
1650 // mode) we don't want big line heights for things like
1651 // <p><font size="-1">Text</font></p>
1653 // We shouldn't include any whitespace that collapses, unless we're
1654 // preformatted (in which case it shouldn't, but the width=0 test is
1655 // perhaps incorrect). This includes whitespace at the beginning of
1656 // a line and whitespace preceded (?) by other whitespace.
1657 // See bug 134580 and bug 155333.
1658 zeroEffectiveSpanBox
= PR_TRUE
;
1659 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
1660 if (pfd
->GetFlag(PFD_ISTEXTFRAME
) &&
1661 (pfd
->GetFlag(PFD_ISNONWHITESPACETEXTFRAME
) || preMode
||
1662 pfd
->mBounds
.width
!= 0)) {
1663 zeroEffectiveSpanBox
= PR_FALSE
;
1668 psd
->mZeroEffectiveSpanBox
= zeroEffectiveSpanBox
;
1670 // Setup baselineY, minY, and maxY
1671 nscoord baselineY
, minY
, maxY
;
1672 if (psd
== mRootSpan
) {
1673 // Use a zero baselineY since we don't yet know where the baseline
1674 // will be (until we know how tall the line is; then we will
1675 // know). In addition, use extreme values for the minY and maxY
1676 // values so that only the child frames will impact their values
1677 // (since these are children of the block, there is no span box to
1678 // provide initial values).
1680 minY
= VERTICAL_ALIGN_FRAMES_NO_MINIMUM
;
1681 maxY
= VERTICAL_ALIGN_FRAMES_NO_MAXIMUM
;
1682 #ifdef NOISY_VERTICAL_ALIGN
1683 printf("[RootSpan]");
1684 nsFrame::ListTag(stdout
, spanFrame
);
1685 printf(": pass1 valign frames: topEdge=%d minLineHeight=%d zeroEffectiveSpanBox=%s\n",
1686 mTopEdge
, mMinLineHeight
,
1687 zeroEffectiveSpanBox
? "yes" : "no");
1691 // Compute the logical height for this span. The logical height
1692 // is based on the line-height value, not the font-size. Also
1693 // compute the top leading.
1694 nscoord logicalHeight
= nsHTMLReflowState::
1695 CalcLineHeight(spanFrame
->GetStyleContext(),
1696 mBlockReflowState
->ComputedHeight());
1697 nscoord contentHeight
= spanFramePFD
->mBounds
.height
-
1698 spanFramePFD
->mBorderPadding
.top
- spanFramePFD
->mBorderPadding
.bottom
;
1700 // Special-case for a ::first-letter frame, set the line height to
1701 // the frame height if the user has left line-height == normal
1702 if (spanFramePFD
->GetFlag(PFD_ISLETTERFRAME
) &&
1703 !spanFrame
->GetPrevInFlow() &&
1704 spanFrame
->GetStyleText()->mLineHeight
.GetUnit() == eStyleUnit_Normal
) {
1705 logicalHeight
= spanFramePFD
->mBounds
.height
;
1708 nscoord leading
= logicalHeight
- contentHeight
;
1709 psd
->mTopLeading
= leading
/ 2;
1710 psd
->mBottomLeading
= leading
- psd
->mTopLeading
;
1711 psd
->mLogicalHeight
= logicalHeight
;
1713 if (zeroEffectiveSpanBox
) {
1714 // When the span-box is to be ignored, zero out the initial
1715 // values so that the span doesn't impact the final line
1716 // height. The contents of the span can impact the final line
1719 // Note that things are readjusted for this span after its children
1721 minY
= VERTICAL_ALIGN_FRAMES_NO_MINIMUM
;
1722 maxY
= VERTICAL_ALIGN_FRAMES_NO_MAXIMUM
;
1726 // The initial values for the min and max Y values are in the spans
1727 // coordinate space, and cover the logical height of the span. If
1728 // there are child frames in this span that stick out of this area
1729 // then the minY and maxY are updated by the amount of logical
1730 // height that is outside this range.
1731 minY
= spanFramePFD
->mBorderPadding
.top
- psd
->mTopLeading
;
1732 maxY
= minY
+ psd
->mLogicalHeight
;
1735 // This is the distance from the top edge of the parents visual
1736 // box to the baseline. The span already computed this for us,
1738 baselineY
= spanFramePFD
->mAscent
;
1741 #ifdef NOISY_VERTICAL_ALIGN
1742 printf("[%sSpan]", (psd
== mRootSpan
)?"Root":"");
1743 nsFrame::ListTag(stdout
, spanFrame
);
1744 printf(": baseLine=%d logicalHeight=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n",
1745 baselineY
, psd
->mLogicalHeight
, psd
->mTopLeading
,
1746 spanFramePFD
->mBounds
.height
,
1747 spanFramePFD
->mBorderPadding
.top
, spanFramePFD
->mBorderPadding
.bottom
,
1748 zeroEffectiveSpanBox
? "yes" : "no");
1752 nscoord maxTopBoxHeight
= 0;
1753 nscoord maxBottomBoxHeight
= 0;
1754 PerFrameData
* pfd
= psd
->mFirstFrame
;
1755 while (nsnull
!= pfd
) {
1756 nsIFrame
* frame
= pfd
->mFrame
;
1758 // sanity check (see bug 105168, non-reproducible crashes from null frame)
1759 NS_ASSERTION(frame
, "null frame in PerFrameData - something is very very bad");
1764 // Compute the logical height of the frame
1765 nscoord logicalHeight
;
1767 PerSpanData
* frameSpan
= pfd
->mSpan
;
1769 // For span frames the logical-height and top-leading was
1770 // pre-computed when the span was reflowed.
1771 logicalHeight
= frameSpan
->mLogicalHeight
;
1772 topLeading
= frameSpan
->mTopLeading
;
1775 // For other elements the logical height is the same as the
1776 // frames height plus its margins.
1777 logicalHeight
= pfd
->mBounds
.height
+ pfd
->mMargin
.top
+
1778 pfd
->mMargin
.bottom
;
1782 // Get vertical-align property
1783 const nsStyleCoord
& verticalAlign
=
1784 frame
->GetStyleTextReset()->mVerticalAlign
;
1785 #ifdef NOISY_VERTICAL_ALIGN
1787 nsFrame::ListTag(stdout
, frame
);
1788 printf(": verticalAlignUnit=%d (enum == %d)\n",
1790 ((eStyleUnit_Enumerated
== verticalAlign
.GetUnit())
1791 ? verticalAlign
.GetIntValue()
1795 if (verticalAlign
.GetUnit() == eStyleUnit_Enumerated
) {
1796 switch (verticalAlign
.GetIntValue()) {
1798 case NS_STYLE_VERTICAL_ALIGN_BASELINE
:
1800 // The element's baseline is aligned with the baseline of
1802 pfd
->mBounds
.y
= baselineY
- pfd
->mAscent
;
1803 pfd
->mVerticalAlign
= VALIGN_OTHER
;
1807 case NS_STYLE_VERTICAL_ALIGN_SUB
:
1809 // Lower the baseline of the box to the subscript offset
1810 // of the parent's box. This is identical to the baseline
1811 // alignment except for the addition of the subscript
1812 // offset to the baseline Y.
1813 nscoord parentSubscript
;
1814 fm
->GetSubscriptOffset(parentSubscript
);
1815 nscoord revisedBaselineY
= baselineY
+ parentSubscript
;
1816 pfd
->mBounds
.y
= revisedBaselineY
- pfd
->mAscent
;
1817 pfd
->mVerticalAlign
= VALIGN_OTHER
;
1821 case NS_STYLE_VERTICAL_ALIGN_SUPER
:
1823 // Raise the baseline of the box to the superscript offset
1824 // of the parent's box. This is identical to the baseline
1825 // alignment except for the subtraction of the superscript
1826 // offset to the baseline Y.
1827 nscoord parentSuperscript
;
1828 fm
->GetSuperscriptOffset(parentSuperscript
);
1829 nscoord revisedBaselineY
= baselineY
- parentSuperscript
;
1830 pfd
->mBounds
.y
= revisedBaselineY
- pfd
->mAscent
;
1831 pfd
->mVerticalAlign
= VALIGN_OTHER
;
1835 case NS_STYLE_VERTICAL_ALIGN_TOP
:
1837 pfd
->mVerticalAlign
= VALIGN_TOP
;
1838 nscoord subtreeHeight
= logicalHeight
;
1840 subtreeHeight
= frameSpan
->mMaxY
- frameSpan
->mMinY
;
1841 NS_ASSERTION(subtreeHeight
>= logicalHeight
,
1842 "unexpected subtree height");
1844 if (subtreeHeight
> maxTopBoxHeight
) {
1845 maxTopBoxHeight
= subtreeHeight
;
1850 case NS_STYLE_VERTICAL_ALIGN_BOTTOM
:
1852 pfd
->mVerticalAlign
= VALIGN_BOTTOM
;
1853 nscoord subtreeHeight
= logicalHeight
;
1855 subtreeHeight
= frameSpan
->mMaxY
- frameSpan
->mMinY
;
1856 NS_ASSERTION(subtreeHeight
>= logicalHeight
,
1857 "unexpected subtree height");
1859 if (subtreeHeight
> maxBottomBoxHeight
) {
1860 maxBottomBoxHeight
= subtreeHeight
;
1865 case NS_STYLE_VERTICAL_ALIGN_MIDDLE
:
1867 // Align the midpoint of the frame with 1/2 the parents
1868 // x-height above the baseline.
1869 nscoord parentXHeight
;
1870 fm
->GetXHeight(parentXHeight
);
1872 pfd
->mBounds
.y
= baselineY
-
1873 (parentXHeight
+ pfd
->mBounds
.height
)/2;
1876 pfd
->mBounds
.y
= baselineY
- (parentXHeight
+ logicalHeight
)/2 +
1879 pfd
->mVerticalAlign
= VALIGN_OTHER
;
1883 case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
:
1885 // The top of the logical box is aligned with the top of
1886 // the parent element's text.
1887 nscoord parentAscent
;
1888 fm
->GetMaxAscent(parentAscent
);
1890 pfd
->mBounds
.y
= baselineY
- parentAscent
-
1891 pfd
->mBorderPadding
.top
+ frameSpan
->mTopLeading
;
1894 pfd
->mBounds
.y
= baselineY
- parentAscent
+ pfd
->mMargin
.top
;
1896 pfd
->mVerticalAlign
= VALIGN_OTHER
;
1900 case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
:
1902 // The bottom of the logical box is aligned with the
1903 // bottom of the parent elements text.
1904 nscoord parentDescent
;
1905 fm
->GetMaxDescent(parentDescent
);
1907 pfd
->mBounds
.y
= baselineY
+ parentDescent
-
1908 pfd
->mBounds
.height
+ pfd
->mBorderPadding
.bottom
-
1909 frameSpan
->mBottomLeading
;
1912 pfd
->mBounds
.y
= baselineY
+ parentDescent
-
1913 pfd
->mBounds
.height
- pfd
->mMargin
.bottom
;
1915 pfd
->mVerticalAlign
= VALIGN_OTHER
;
1919 case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
:
1921 // Align the midpoint of the frame with the baseline of the parent.
1923 pfd
->mBounds
.y
= baselineY
- pfd
->mBounds
.height
/2;
1926 pfd
->mBounds
.y
= baselineY
- logicalHeight
/2 + pfd
->mMargin
.top
;
1928 pfd
->mVerticalAlign
= VALIGN_OTHER
;
1933 // We have either a coord, a percent, or a calc().
1934 nscoord pctBasis
= 0;
1935 if (verticalAlign
.HasPercent()) {
1936 // Percentages are like lengths, except treated as a percentage
1937 // of the elements line-height value.
1938 pctBasis
= nsHTMLReflowState::CalcLineHeight(
1939 frame
->GetStyleContext(), mBlockReflowState
->ComputedHeight());
1942 nsRuleNode::ComputeCoordPercentCalc(verticalAlign
, pctBasis
);
1943 // According to the CSS2 spec (10.8.1), a positive value
1944 // "raises" the box by the given distance while a negative value
1945 // "lowers" the box by the given distance (with zero being the
1946 // baseline). Since Y coordinates increase towards the bottom of
1947 // the screen we reverse the sign.
1948 nscoord revisedBaselineY
= baselineY
- offset
;
1949 pfd
->mBounds
.y
= revisedBaselineY
- pfd
->mAscent
;
1950 pfd
->mVerticalAlign
= VALIGN_OTHER
;
1953 // Update minY/maxY for frames that we just placed. Do not factor
1954 // text into the equation.
1955 if (pfd
->mVerticalAlign
== VALIGN_OTHER
) {
1956 // Text frames do not contribute to the min/max Y values for the
1957 // line (instead their parent frame's font-size contributes).
1958 // XXXrbs -- relax this restriction because it causes text frames
1959 // to jam together when 'font-size-adjust' is enabled
1960 // and layout is using dynamic font heights (bug 20394)
1961 // -- Note #1: With this code enabled and with the fact that we are not
1962 // using Em[Ascent|Descent] as nsDimensions for text metrics in
1963 // GFX mean that the discussion in bug 13072 cannot hold.
1964 // -- Note #2: We still don't want empty-text frames to interfere.
1965 // For example in quirks mode, avoiding empty text frames prevents
1966 // "tall" lines around elements like <hr> since the rules of <hr>
1967 // in quirks.css have pseudo text contents with LF in them.
1969 if (!pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
1971 // Only consider non empty text frames when line-height=normal
1972 PRBool canUpdate
= !pfd
->GetFlag(PFD_ISTEXTFRAME
);
1973 if (!canUpdate
&& pfd
->GetFlag(PFD_ISNONWHITESPACETEXTFRAME
)) {
1975 frame
->GetStyleText()->mLineHeight
.GetUnit() == eStyleUnit_Normal
;
1979 nscoord yTop
, yBottom
;
1981 // For spans that were are now placing, use their position
1982 // plus their already computed min-Y and max-Y values for
1983 // computing yTop and yBottom.
1984 yTop
= pfd
->mBounds
.y
+ frameSpan
->mMinY
;
1985 yBottom
= pfd
->mBounds
.y
+ frameSpan
->mMaxY
;
1988 yTop
= pfd
->mBounds
.y
- pfd
->mMargin
.top
;
1989 yBottom
= yTop
+ logicalHeight
;
1992 mPresContext
->CompatibilityMode() != eCompatibility_FullStandards
&&
1994 // Check if it's a BR frame that is not alone on its line (it
1995 // is given a height of zero to indicate this), and if so reset
1996 // yTop and yBottom so that BR frames don't influence the line.
1997 if (nsGkAtoms::brFrame
== frame
->GetType()) {
1998 yTop
= VERTICAL_ALIGN_FRAMES_NO_MINIMUM
;
1999 yBottom
= VERTICAL_ALIGN_FRAMES_NO_MAXIMUM
;
2002 if (yTop
< minY
) minY
= yTop
;
2003 if (yBottom
> maxY
) maxY
= yBottom
;
2004 #ifdef NOISY_VERTICAL_ALIGN
2005 printf(" [frame]raw: a=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minY=%d maxY=%d\n",
2006 pfd
->mAscent
, pfd
->mBounds
.height
,
2007 pfd
->mBorderPadding
.top
, pfd
->mBorderPadding
.bottom
,
2009 pfd
->mSpan
? topLeading
: 0,
2010 pfd
->mBounds
.y
, minY
, maxY
);
2013 if (psd
!= mRootSpan
) {
2014 frame
->SetRect(pfd
->mBounds
);
2020 // Factor in the minimum line-height when handling the root-span for
2022 if (psd
== mRootSpan
) {
2023 // We should factor in the block element's minimum line-height (as
2024 // defined in section 10.8.1 of the css2 spec) assuming that
2025 // mZeroEffectiveSpanBox is not set on the root span. This only happens
2026 // in some cases in quirks mode:
2027 // (1) if the root span contains non-whitespace text directly (this
2028 // is handled by mZeroEffectiveSpanBox
2029 // (2) if this line has a bullet
2030 // (3) if this is the last line of an LI, DT, or DD element
2031 // (The last line before a block also counts, but not before a
2032 // BR) (NN4/IE5 quirk)
2034 // (1) and (2) above
2035 PRBool applyMinLH
= !psd
->mZeroEffectiveSpanBox
|| GetFlag(LL_HASBULLET
);
2036 PRBool isLastLine
= (!mLineBox
->IsLineWrapped() && !GetFlag(LL_LINEENDSINBR
));
2037 if (!applyMinLH
&& isLastLine
) {
2038 nsIContent
* blockContent
= mRootSpan
->mFrame
->mFrame
->GetContent();
2040 nsIAtom
*blockTagAtom
= blockContent
->Tag();
2041 // (3) above, if the last line of LI, DT, or DD
2042 if (blockTagAtom
== nsGkAtoms::li
||
2043 blockTagAtom
== nsGkAtoms::dt
||
2044 blockTagAtom
== nsGkAtoms::dd
) {
2045 applyMinLH
= PR_TRUE
;
2050 if (psd
->mHasNonemptyContent
|| preMode
|| GetFlag(LL_HASBULLET
)) {
2051 #ifdef NOISY_VERTICAL_ALIGN
2052 printf(" [span]==> adjusting min/maxY: currentValues: %d,%d", minY
, maxY
);
2054 nscoord minimumLineHeight
= mMinLineHeight
;
2056 -nsLayoutUtils::GetCenteredFontBaseline(fm
, minimumLineHeight
);
2057 nscoord yBottom
= yTop
+ minimumLineHeight
;
2059 if (yTop
< minY
) minY
= yTop
;
2060 if (yBottom
> maxY
) maxY
= yBottom
;
2062 #ifdef NOISY_VERTICAL_ALIGN
2063 printf(" new values: %d,%d\n", minY
, maxY
);
2065 #ifdef NOISY_VERTICAL_ALIGN
2066 printf(" Used mMinLineHeight: %d, fontHeight: %d, fontAscent: %d\n", mMinLineHeight
, fontHeight
, fontAscent
);
2071 // [1] BR's on empty lines stop working
2072 // [2] May not honor css2's notion of handling empty elements
2073 // [3] blank lines in a pre-section ("\n") (handled with preMode)
2075 // XXX Are there other problems with this?
2076 #ifdef NOISY_VERTICAL_ALIGN
2077 printf(" [span]==> zapping min/maxY: currentValues: %d,%d newValues: 0,0\n",
2085 if ((minY
== VERTICAL_ALIGN_FRAMES_NO_MINIMUM
) ||
2086 (maxY
== VERTICAL_ALIGN_FRAMES_NO_MAXIMUM
)) {
2087 minY
= maxY
= baselineY
;
2090 if ((psd
!= mRootSpan
) && (psd
->mZeroEffectiveSpanBox
)) {
2091 #ifdef NOISY_VERTICAL_ALIGN
2092 printf(" [span]adjusting for zeroEffectiveSpanBox\n");
2093 printf(" Original: minY=%d, maxY=%d, height=%d, ascent=%d, logicalHeight=%d, topLeading=%d, bottomLeading=%d\n",
2094 minY
, maxY
, spanFramePFD
->mBounds
.height
,
2095 spanFramePFD
->mAscent
,
2096 psd
->mLogicalHeight
, psd
->mTopLeading
, psd
->mBottomLeading
);
2098 nscoord goodMinY
= spanFramePFD
->mBorderPadding
.top
- psd
->mTopLeading
;
2099 nscoord goodMaxY
= goodMinY
+ psd
->mLogicalHeight
;
2100 if (minY
> goodMinY
) {
2101 nscoord adjust
= minY
- goodMinY
; // positive
2103 // shrink the logical extents
2104 psd
->mLogicalHeight
-= adjust
;
2105 psd
->mTopLeading
-= adjust
;
2107 if (maxY
< goodMaxY
) {
2108 nscoord adjust
= goodMaxY
- maxY
;
2109 psd
->mLogicalHeight
-= adjust
;
2110 psd
->mBottomLeading
-= adjust
;
2114 // shrink the content by moving its top down. This is tricky, since
2115 // the top is the 0 for many coordinates, so what we do is
2116 // move everything else up.
2117 spanFramePFD
->mAscent
-= minY
; // move the baseline up
2118 spanFramePFD
->mBounds
.height
-= minY
; // move the bottom up
2119 psd
->mTopLeading
+= minY
;
2121 pfd
= psd
->mFirstFrame
;
2122 while (nsnull
!= pfd
) {
2123 pfd
->mBounds
.y
-= minY
; // move all the children back up
2124 pfd
->mFrame
->SetRect(pfd
->mBounds
);
2127 maxY
-= minY
; // since minY is in the frame's own coordinate system
2130 if (maxY
< spanFramePFD
->mBounds
.height
) {
2131 nscoord adjust
= spanFramePFD
->mBounds
.height
- maxY
;
2132 spanFramePFD
->mBounds
.height
-= adjust
; // move the bottom up
2133 psd
->mBottomLeading
+= adjust
;
2135 #ifdef NOISY_VERTICAL_ALIGN
2136 printf(" New: minY=%d, maxY=%d, height=%d, ascent=%d, logicalHeight=%d, topLeading=%d, bottomLeading=%d\n",
2137 minY
, maxY
, spanFramePFD
->mBounds
.height
,
2138 spanFramePFD
->mAscent
,
2139 psd
->mLogicalHeight
, psd
->mTopLeading
, psd
->mBottomLeading
);
2145 #ifdef NOISY_VERTICAL_ALIGN
2146 printf(" [span]==> minY=%d maxY=%d delta=%d maxTopBoxHeight=%d maxBottomBoxHeight=%d\n",
2147 minY
, maxY
, maxY
- minY
, maxTopBoxHeight
, maxBottomBoxHeight
);
2149 if (maxTopBoxHeight
> mMaxTopBoxHeight
) {
2150 mMaxTopBoxHeight
= maxTopBoxHeight
;
2152 if (maxBottomBoxHeight
> mMaxBottomBoxHeight
) {
2153 mMaxBottomBoxHeight
= maxBottomBoxHeight
;
2157 static void SlideSpanFrameRect(nsIFrame
* aFrame
, nscoord aDeltaWidth
)
2159 nsRect r
= aFrame
->GetRect();
2165 nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData
* psd
,
2166 nscoord
* aDeltaWidth
)
2169 // XXX what about NS_STYLE_DIRECTION_RTL?
2170 if (NS_STYLE_DIRECTION_RTL
== psd
->mDirection
) {
2176 PerFrameData
* pfd
= psd
->mFirstFrame
;
2182 while (nsnull
!= pfd
) {
2183 #ifdef REALLY_NOISY_TRIM
2184 nsFrame::ListTag(stdout
, (psd
== mRootSpan
2185 ? mBlockReflowState
->frame
2186 : psd
->mFrame
->mFrame
));
2187 printf(": attempting trim of ");
2188 nsFrame::ListTag(stdout
, pfd
->mFrame
);
2191 PerSpanData
* childSpan
= pfd
->mSpan
;
2193 // Maybe the child span has the trailing white-space in it?
2194 if (TrimTrailingWhiteSpaceIn(childSpan
, aDeltaWidth
)) {
2195 nscoord deltaWidth
= *aDeltaWidth
;
2197 // Adjust the child spans frame size
2198 pfd
->mBounds
.width
-= deltaWidth
;
2199 if (psd
!= mRootSpan
) {
2200 // When the child span is not a direct child of the block
2201 // we need to update the child spans frame rectangle
2202 // because it most likely will not be done again. Spans
2203 // that are direct children of the block will be updated
2204 // later, however, because the VerticalAlignFrames method
2205 // will be run after this method.
2206 nsIFrame
* f
= pfd
->mFrame
;
2207 nsRect r
= f
->GetRect();
2208 r
.width
-= deltaWidth
;
2212 // Adjust the right edge of the span that contains the child span
2213 psd
->mX
-= deltaWidth
;
2215 // Slide any frames that follow the child span over by the
2216 // right amount. The only thing that can follow the child
2217 // span is empty stuff, so we are just making things
2218 // sensible (keeping the combined area honest).
2219 while (pfd
->mNext
) {
2221 pfd
->mBounds
.x
-= deltaWidth
;
2222 if (psd
!= mRootSpan
) {
2223 // When the child span is not a direct child of the block
2224 // we need to update the child spans frame rectangle
2225 // because it most likely will not be done again. Spans
2226 // that are direct children of the block will be updated
2227 // later, however, because the VerticalAlignFrames method
2228 // will be run after this method.
2229 SlideSpanFrameRect(pfd
->mFrame
, deltaWidth
);
2236 else if (!pfd
->GetFlag(PFD_ISTEXTFRAME
) &&
2237 !pfd
->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE
)) {
2238 // If we hit a frame on the end that's not text and not a placeholder,
2239 // then there is no trailing whitespace to trim. Stop the search.
2243 else if (pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2244 // Call TrimTrailingWhiteSpace even on empty textframes because they
2245 // might have a soft hyphen which should now appear, changing the frame's
2247 nsTextFrame::TrimOutput trimOutput
= static_cast<nsTextFrame
*>(pfd
->mFrame
)->
2248 TrimTrailingWhiteSpace(mBlockReflowState
->rendContext
);
2250 nsFrame::ListTag(stdout
, (psd
== mRootSpan
2251 ? mBlockReflowState
->frame
2252 : psd
->mFrame
->mFrame
));
2253 printf(": trim of ");
2254 nsFrame::ListTag(stdout
, pfd
->mFrame
);
2255 printf(" returned %d\n", trimOutput
.mDeltaWidth
);
2257 if (trimOutput
.mLastCharIsJustifiable
&& pfd
->mJustificationNumSpaces
> 0) {
2258 pfd
->mJustificationNumSpaces
--;
2261 if (trimOutput
.mChanged
) {
2262 pfd
->SetFlag(PFD_RECOMPUTEOVERFLOW
, PR_TRUE
);
2265 if (trimOutput
.mDeltaWidth
) {
2266 pfd
->mBounds
.width
-= trimOutput
.mDeltaWidth
;
2268 // See if the text frame has already been placed in its parent
2269 if (psd
!= mRootSpan
) {
2270 // The frame was already placed during psd's
2271 // reflow. Update the frames rectangle now.
2272 pfd
->mFrame
->SetRect(pfd
->mBounds
);
2275 // Adjust containing span's right edge
2276 psd
->mX
-= trimOutput
.mDeltaWidth
;
2278 // Slide any frames that follow the text frame over by the
2279 // right amount. The only thing that can follow the text
2280 // frame is empty stuff, so we are just making things
2281 // sensible (keeping the combined area honest).
2282 while (pfd
->mNext
) {
2284 pfd
->mBounds
.x
-= trimOutput
.mDeltaWidth
;
2285 if (psd
!= mRootSpan
) {
2286 // When the child span is not a direct child of the block
2287 // we need to update the child spans frame rectangle
2288 // because it most likely will not be done again. Spans
2289 // that are direct children of the block will be updated
2290 // later, however, because the VerticalAlignFrames method
2291 // will be run after this method.
2292 SlideSpanFrameRect(pfd
->mFrame
, trimOutput
.mDeltaWidth
);
2297 if (pfd
->GetFlag(PFD_ISNONEMPTYTEXTFRAME
) || trimOutput
.mChanged
) {
2298 // Pass up to caller so they can shrink their span
2299 *aDeltaWidth
= trimOutput
.mDeltaWidth
;
2311 nsLineLayout::TrimTrailingWhiteSpace()
2313 PerSpanData
* psd
= mRootSpan
;
2315 TrimTrailingWhiteSpaceIn(psd
, &deltaWidth
);
2316 return 0 != deltaWidth
;
2320 nsLineLayout::ComputeJustificationWeights(PerSpanData
* aPSD
,
2321 PRInt32
* aNumSpaces
,
2322 PRInt32
* aNumLetters
)
2324 NS_ASSERTION(aPSD
, "null arg");
2325 NS_ASSERTION(aNumSpaces
, "null arg");
2326 NS_ASSERTION(aNumLetters
, "null arg");
2327 PRInt32 numSpaces
= 0;
2328 PRInt32 numLetters
= 0;
2330 for (PerFrameData
* pfd
= aPSD
->mFirstFrame
; pfd
!= nsnull
; pfd
= pfd
->mNext
) {
2332 if (PR_TRUE
== pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2333 numSpaces
+= pfd
->mJustificationNumSpaces
;
2334 numLetters
+= pfd
->mJustificationNumLetters
;
2336 else if (pfd
->mSpan
!= nsnull
) {
2338 PRInt32 spanLetters
;
2340 ComputeJustificationWeights(pfd
->mSpan
, &spanSpaces
, &spanLetters
);
2342 numSpaces
+= spanSpaces
;
2343 numLetters
+= spanLetters
;
2347 *aNumSpaces
= numSpaces
;
2348 *aNumLetters
= numLetters
;
2352 nsLineLayout::ApplyFrameJustification(PerSpanData
* aPSD
, FrameJustificationState
* aState
)
2354 NS_ASSERTION(aPSD
, "null arg");
2355 NS_ASSERTION(aState
, "null arg");
2358 for (PerFrameData
* pfd
= aPSD
->mFirstFrame
; pfd
!= nsnull
; pfd
= pfd
->mNext
) {
2359 // Don't reposition bullets (and other frames that occur out of X-order?)
2360 if (!pfd
->GetFlag(PFD_ISBULLET
)) {
2363 pfd
->mBounds
.x
+= deltaX
;
2365 if (PR_TRUE
== pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2366 if (aState
->mTotalWidthForSpaces
> 0 &&
2367 aState
->mTotalNumSpaces
> 0) {
2368 aState
->mNumSpacesProcessed
+= pfd
->mJustificationNumSpaces
;
2370 nscoord newAllocatedWidthForSpaces
=
2371 (aState
->mTotalWidthForSpaces
*aState
->mNumSpacesProcessed
)
2372 /aState
->mTotalNumSpaces
;
2374 dw
+= newAllocatedWidthForSpaces
- aState
->mWidthForSpacesProcessed
;
2376 aState
->mWidthForSpacesProcessed
= newAllocatedWidthForSpaces
;
2379 if (aState
->mTotalWidthForLetters
> 0 &&
2380 aState
->mTotalNumLetters
> 0) {
2381 aState
->mNumLettersProcessed
+= pfd
->mJustificationNumLetters
;
2383 nscoord newAllocatedWidthForLetters
=
2384 (aState
->mTotalWidthForLetters
*aState
->mNumLettersProcessed
)
2385 /aState
->mTotalNumLetters
;
2387 dw
+= newAllocatedWidthForLetters
- aState
->mWidthForLettersProcessed
;
2389 aState
->mWidthForLettersProcessed
= newAllocatedWidthForLetters
;
2393 pfd
->SetFlag(PFD_RECOMPUTEOVERFLOW
, PR_TRUE
);
2397 if (nsnull
!= pfd
->mSpan
) {
2398 dw
+= ApplyFrameJustification(pfd
->mSpan
, aState
);
2402 pfd
->mBounds
.width
+= dw
;
2405 pfd
->mFrame
->SetRect(pfd
->mBounds
);
2412 nsLineLayout::HorizontalAlignFrames(nsRect
& aLineBounds
,
2413 PRBool aAllowJustify
)
2415 PerSpanData
* psd
= mRootSpan
;
2416 NS_WARN_IF_FALSE(psd
->mRightEdge
!= NS_UNCONSTRAINEDSIZE
,
2417 "have unconstrained width; this should only result from "
2418 "very large sizes, not attempts at intrinsic width "
2420 nscoord availWidth
= psd
->mRightEdge
- psd
->mLeftEdge
;
2421 nscoord remainingWidth
= availWidth
- aLineBounds
.width
;
2422 #ifdef NOISY_HORIZONTAL_ALIGN
2423 nsFrame::ListTag(stdout
, mBlockReflowState
->frame
);
2424 printf(": availWidth=%d lineWidth=%d delta=%d\n",
2425 availWidth
, aLineBounds
.width
, remainingWidth
);
2429 if (remainingWidth
> 0) {
2430 switch (mTextAlign
) {
2431 case NS_STYLE_TEXT_ALIGN_JUSTIFY
:
2432 // If this is not the last line then go ahead and justify the
2433 // frames in the line.
2434 if (aAllowJustify
) {
2438 ComputeJustificationWeights(psd
, &numSpaces
, &numLetters
);
2440 if (numSpaces
> 0) {
2441 FrameJustificationState state
=
2442 { numSpaces
, numLetters
, remainingWidth
, 0, 0, 0, 0, 0 };
2444 // Apply the justification, and make sure to update our linebox
2445 // width to account for it.
2446 aLineBounds
.width
+= ApplyFrameJustification(psd
, &state
);
2447 remainingWidth
= availWidth
- aLineBounds
.width
;
2451 // Fall through to the default case if we were told not to
2452 // justify anything or could not justify to fill the space.
2454 case NS_STYLE_TEXT_ALIGN_DEFAULT
:
2455 if (NS_STYLE_DIRECTION_LTR
== psd
->mDirection
) {
2456 // default alignment for left-to-right is left so do nothing
2459 // Fall through to align right case for default alignment
2460 // used when the direction is right-to-left.
2462 case NS_STYLE_TEXT_ALIGN_RIGHT
:
2463 case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
:
2464 dx
= remainingWidth
;
2467 case NS_STYLE_TEXT_ALIGN_END
:
2468 if (NS_STYLE_DIRECTION_LTR
== psd
->mDirection
) {
2469 // Do what we do for ALIGN_RIGHT
2470 dx
= remainingWidth
;
2473 // Fall through to align left case for end alignment
2474 // used when the direction is right-to-left.
2476 case NS_STYLE_TEXT_ALIGN_LEFT
:
2477 case NS_STYLE_TEXT_ALIGN_MOZ_LEFT
:
2480 case NS_STYLE_TEXT_ALIGN_CENTER
:
2481 case NS_STYLE_TEXT_ALIGN_MOZ_CENTER
:
2482 dx
= remainingWidth
/ 2;
2486 else if (remainingWidth
< 0) {
2487 if (NS_STYLE_DIRECTION_RTL
== psd
->mDirection
) {
2488 dx
= remainingWidth
;
2490 psd
->mLeftEdge
+= dx
;
2494 if (NS_STYLE_DIRECTION_RTL
== psd
->mDirection
&&
2495 !psd
->mChangedFrameDirection
) {
2496 if (psd
->mLastFrame
->GetFlag(PFD_ISBULLET
) ) {
2497 PerFrameData
* bulletPfd
= psd
->mLastFrame
;
2498 bulletPfd
->mBounds
.x
-= remainingWidth
;
2499 bulletPfd
->mFrame
->SetRect(bulletPfd
->mBounds
);
2501 psd
->mChangedFrameDirection
= PR_TRUE
;
2505 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
2506 pfd
->mBounds
.x
+= dx
;
2507 pfd
->mFrame
->SetRect(pfd
->mBounds
);
2509 aLineBounds
.x
+= dx
;
2514 nsLineLayout::RelativePositionFrames(nsOverflowAreas
& aOverflowAreas
)
2516 RelativePositionFrames(mRootSpan
, aOverflowAreas
);
2520 nsLineLayout::RelativePositionFrames(PerSpanData
* psd
, nsOverflowAreas
& aOverflowAreas
)
2522 nsOverflowAreas overflowAreas
;
2523 if (nsnull
!= psd
->mFrame
) {
2524 // The span's overflow areas come in three parts:
2525 // -- this frame's width and height
2526 // -- pfd->mOverflowAreas, which is the area of a bullet or the union
2527 // of a relatively positioned frame's absolute children
2528 // -- the bounds of all inline descendants
2529 // The former two parts are computed right here, we gather the descendants
2531 // At this point psd->mFrame->mBounds might be out of date since
2532 // bidi reordering can move and resize the frames. So use the frame's
2533 // rect instead of mBounds.
2534 nsRect
adjustedBounds(nsPoint(0, 0), psd
->mFrame
->mFrame
->GetSize());
2536 overflowAreas
.ScrollableOverflow().UnionRect(
2537 psd
->mFrame
->mOverflowAreas
.ScrollableOverflow(), adjustedBounds
);
2539 // Text-shadow overflow
2540 if (mPresContext
->CompatibilityMode() != eCompatibility_NavQuirks
) {
2541 nsRect shadowRect
= nsLayoutUtils::GetTextShadowRectsUnion(adjustedBounds
,
2542 psd
->mFrame
->mFrame
);
2543 adjustedBounds
.UnionRect(adjustedBounds
, shadowRect
);
2546 // Text shadow is only part of visual overflow and not scrollable overflow.
2547 overflowAreas
.VisualOverflow().UnionRect(
2548 psd
->mFrame
->mOverflowAreas
.VisualOverflow(), adjustedBounds
);
2551 // The minimum combined area for the frames that are direct
2552 // children of the block starts at the upper left corner of the
2553 // line and is sized to match the size of the line's bounding box
2554 // (the same size as the values returned from VerticalAlignFrames)
2555 overflowAreas
.VisualOverflow().x
= psd
->mLeftEdge
;
2556 // If this turns out to be negative, the rect will be treated as empty.
2557 // Which is just fine.
2558 overflowAreas
.VisualOverflow().width
=
2559 psd
->mX
- overflowAreas
.VisualOverflow().x
;
2560 overflowAreas
.VisualOverflow().y
= mTopEdge
;
2561 overflowAreas
.VisualOverflow().height
= mFinalLineHeight
;
2563 overflowAreas
.ScrollableOverflow() = overflowAreas
.VisualOverflow();
2566 for (PerFrameData
* pfd
= psd
->mFirstFrame
; pfd
; pfd
= pfd
->mNext
) {
2567 nsIFrame
* frame
= pfd
->mFrame
;
2568 nsPoint origin
= frame
->GetPosition();
2570 // Adjust the origin of the frame
2571 if (pfd
->GetFlag(PFD_RELATIVEPOS
)) {
2572 // right and bottom are handled by
2573 // nsHTMLReflowState::ComputeRelativeOffsets
2574 nsPoint
change(pfd
->mOffsets
.left
, pfd
->mOffsets
.top
);
2576 frame
->SetPosition(origin
);
2579 // We must position the view correctly before positioning its
2580 // descendants so that widgets are positioned properly (since only
2581 // some views have widgets).
2582 if (frame
->HasView())
2583 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext
, frame
,
2584 frame
->GetView(), pfd
->mOverflowAreas
.VisualOverflow(),
2585 NS_FRAME_NO_SIZE_VIEW
);
2587 // Note: the combined area of a child is in its coordinate
2588 // system. We adjust the childs combined area into our coordinate
2589 // system before computing the aggregated value by adding in
2590 // <b>x</b> and <b>y</b> which were computed above.
2593 // Compute a new combined area for the child span before
2594 // aggregating it into our combined area.
2595 RelativePositionFrames(pfd
->mSpan
, r
);
2597 r
= pfd
->mOverflowAreas
;
2598 if (pfd
->GetFlag(PFD_ISTEXTFRAME
)) {
2599 if (pfd
->GetFlag(PFD_RECOMPUTEOVERFLOW
)) {
2600 nsTextFrame
* f
= static_cast<nsTextFrame
*>(frame
);
2601 r
= f
->RecomputeOverflow();
2603 frame
->FinishAndStoreOverflow(r
, frame
->GetSize());
2606 // If we have something that's not an inline but with a complex frame
2607 // hierarchy inside that contains views, they need to be
2609 // All descendant views must be repositioned even if this frame
2610 // does have a view in case this frame's view does not have a
2611 // widget and some of the descendant views do have widgets --
2612 // otherwise the widgets won't be repositioned.
2613 nsContainerFrame::PositionChildViews(frame
);
2616 // Do this here (rather than along with setting the overflow rect
2617 // below) so we get leaf frames as well. No need to worry
2618 // about the root span, since it doesn't have a frame.
2619 if (frame
->HasView())
2620 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext
, frame
,
2623 NS_FRAME_NO_MOVE_VIEW
);
2625 overflowAreas
.UnionWith(r
+ origin
);
2628 // If we just computed a spans combined area, we need to update its
2631 PerFrameData
* spanPFD
= psd
->mFrame
;
2632 nsIFrame
* frame
= spanPFD
->mFrame
;
2633 frame
->FinishAndStoreOverflow(overflowAreas
, frame
->GetSize());
2635 aOverflowAreas
= overflowAreas
;