(no bug) fix end-of-line whitespace so I can add a changeset with an a=bustage annotation
[mozilla-central.git] / layout / generic / nsLineLayout.cpp
blob7d1c0cc79aa7cb9af591e1e9aa451fe9924c0972
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
13 * License.
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.
22 * Contributor(s):
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>
27 * IBM Corporation
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)
47 #include "plarena.h"
49 #include "nsCOMPtr.h"
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"
71 #include "jstl.h"
73 #ifdef DEBUG
74 #undef NOISY_HORIZONTAL_ALIGN
75 #undef NOISY_VERTICAL_ALIGN
76 #undef REALLY_NOISY_VERTICAL_ALIGN
77 #undef NOISY_REFLOW
78 #undef REALLY_NOISY_REFLOW
79 #undef NOISY_PUSHING
80 #undef REALLY_NOISY_PUSHING
81 #undef DEBUG_ADD_TEXT
82 #undef NOISY_MAX_ELEMENT_SIZE
83 #undef REALLY_NOISY_MAX_ELEMENT_SIZE
84 #undef NOISY_CAN_PLACE_FRAME
85 #undef NOISY_TRIM
86 #undef REALLY_NOISY_TRIM
87 #endif
89 //----------------------------------------------------------------------
91 #define FIX_BUG_50257
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),
106 mMinLineHeight(0),
107 mTextIndent(0)
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;
117 mLineNumber = 0;
118 mFlags = 0; // default all flags to false except those that follow here...
119 mTotalPlacedFrames = 0;
120 mTopEdge = 0;
121 mTrimmableWidth = 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
126 // initialization.
127 PL_INIT_ARENA_POOL(&mArena, "nsLineLayout", 1024);
128 mFrameFreeList = nsnull;
129 mSpanFreeList = nsnull;
131 mCurrentSpan = mRootSpan = nsnull;
132 mSpanDepth = 0;
134 if (aLine) {
135 SetFlag(LL_GOTLINEBOX, PR_TRUE);
136 mLineBox = *aLine;
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.
161 inline PRBool
162 HasPrevInFlow(nsIFrame *aFrame)
164 nsIFrame *prevInFlow = aFrame->GetPrevInFlow();
165 return prevInFlow != nsnull;
168 void
169 nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY,
170 nscoord aWidth, nscoord aHeight,
171 PRBool aImpactedByFloats,
172 PRBool aIsTopOfPage)
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 "
178 "calculation");
179 #ifdef DEBUG
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",
183 aWidth, aWidth);
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",
188 aHeight, aHeight);
190 #endif
191 #ifdef NOISY_REFLOW
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" : "");
197 #endif
198 #ifdef DEBUG
199 mSpansAllocated = mSpansFreed = mFramesAllocated = mFramesFreed = 0;
200 #endif
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);
209 mSpanDepth = 0;
210 mMaxTopBoxHeight = mMaxBottomBoxHeight = 0;
212 if (GetFlag(LL_GOTLINEBOX)) {
213 mLineBox->ClearHasBullet();
216 PerSpanData* psd;
217 NewPerSpanData(&psd);
218 mCurrentSpan = mRootSpan = psd;
219 psd->mReflowState = mBlockReflowState;
220 psd->mLeftEdge = aX;
221 psd->mX = aX;
222 psd->mRightEdge = aX + aWidth;
224 mTopEdge = aY;
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()) {
237 pctBasis =
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;
251 else {
252 psd->mX += indent;
257 void
258 nsLineLayout::EndLineReflow()
260 #ifdef NOISY_REFLOW
261 nsFrame::ListTag(stdout, mBlockReflowState->frame);
262 printf(": EndLineReflow: width=%d\n", mRootSpan->mX - mRootSpan->mLeftEdge);
263 #endif
265 FreeSpan(mRootSpan);
266 mCurrentSpan = mRootSpan = nsnull;
268 NS_ASSERTION(mSpansAllocated == mSpansFreed, "leak");
269 NS_ASSERTION(mFramesAllocated == mFramesFreed, "leak");
271 #if 0
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;
282 #endif
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?
290 void
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,
298 aFloatFrame);
299 #endif
300 #ifdef DEBUG
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);
311 #endif
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 "
318 "calculation");
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);
324 #ifdef NOISY_REFLOW
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);
329 #endif
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!");
343 #ifdef NOISY_REFLOW
344 printf(" span %p: oldRightEdge=%d newRightEdge=%d\n",
345 psd, psd->mRightEdge - deltaRightEdge, psd->mRightEdge);
346 #endif
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.
358 if (deltaX != 0) {
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());
371 nsresult
372 nsLineLayout::NewPerSpanData(PerSpanData** aResult)
374 PerSpanData* psd = mSpanFreeList;
375 if (nsnull == psd) {
376 void *mem;
377 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(PerSpanData));
378 if (nsnull == mem) {
379 return NS_ERROR_OUT_OF_MEMORY;
381 psd = reinterpret_cast<PerSpanData*>(mem);
383 else {
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;
394 #ifdef DEBUG
395 mSpansAllocated++;
396 #endif
397 *aResult = psd;
398 return NS_OK;
401 nsresult
402 nsLineLayout::BeginSpan(nsIFrame* aFrame,
403 const nsHTMLReflowState* aSpanReflowState,
404 nscoord aLeftEdge,
405 nscoord aRightEdge)
407 NS_ASSERTION(aRightEdge != NS_UNCONSTRAINEDSIZE,
408 "should no longer be using unconstrained sizes");
409 #ifdef NOISY_REFLOW
410 nsFrame::IndentBy(stdout, mSpanDepth+1);
411 nsFrame::ListTag(stdout, aFrame);
412 printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aLeftEdge, aRightEdge);
413 #endif
415 PerSpanData* psd;
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?");
421 pfd->mSpan = psd;
423 // Init new span
424 psd->mFrame = pfd;
425 psd->mParent = mCurrentSpan;
426 psd->mReflowState = aSpanReflowState;
427 psd->mLeftEdge = aLeftEdge;
428 psd->mX = aLeftEdge;
429 psd->mRightEdge = aRightEdge;
431 psd->mNoWrap =
432 !aSpanReflowState->frame->GetStyleText()->WhiteSpaceCanWrap();
433 psd->mDirection = aSpanReflowState->mStyleVisibility->mDirection;
434 psd->mChangedFrameDirection = PR_FALSE;
436 // Switch to new span
437 mCurrentSpan = psd;
438 mSpanDepth++;
440 return rv;
443 nscoord
444 nsLineLayout::EndSpan(nsIFrame* aFrame)
446 NS_ASSERTION(mSpanDepth > 0, "end-span without begin-span");
447 #ifdef NOISY_REFLOW
448 nsFrame::IndentBy(stdout, mSpanDepth);
449 nsFrame::ListTag(stdout, aFrame);
450 printf(": EndSpan width=%d\n", mCurrentSpan->mX - mCurrentSpan->mLeftEdge);
451 #endif
452 PerSpanData* psd = mCurrentSpan;
453 nscoord widthResult = psd->mLastFrame ? (psd->mX - psd->mLeftEdge) : 0;
455 mSpanDepth--;
456 mCurrentSpan->mReflowState = nsnull; // no longer valid so null it out!
457 mCurrentSpan = mCurrentSpan->mParent;
458 return widthResult;
461 PRInt32
462 nsLineLayout::GetCurrentSpanCount() const
464 NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
465 PRInt32 count = 0;
466 PerFrameData* pfd = mRootSpan->mFirstFrame;
467 while (nsnull != pfd) {
468 count++;
469 pfd = pfd->mNext;
471 return count;
474 void
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);
483 #endif
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;
490 pfd->mNext = nsnull;
491 psd->mLastFrame = pfd;
493 // Now release all of the frames following pfd
494 pfd = next;
495 while (nsnull != pfd) {
496 next = pfd->mNext;
497 pfd->mNext = mFrameFreeList;
498 mFrameFreeList = pfd;
499 #ifdef DEBUG
500 mFramesFreed++;
501 #endif
502 if (nsnull != pfd->mSpan) {
503 FreeSpan(pfd->mSpan);
505 pfd = next;
507 break;
509 pfd = pfd->mNext;
511 #ifdef NOISY_PUSHING
512 printf("SplitLineTo %d (current count=%d); after:\n", aNewCount,
513 GetCurrentSpanCount());
514 DumpPerSpanData(mRootSpan, 1);
515 #endif
518 void
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);
528 #endif
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;
537 else {
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;
546 #ifdef DEBUG
547 mFramesFreed++;
548 #endif
549 if (nsnull != pfd->mSpan) {
550 FreeSpan(pfd->mSpan);
552 #ifdef NOISY_PUSHING
553 nsFrame::IndentBy(stdout, mSpanDepth);
554 printf("PushFrame: %p after:\n", psd);
555 DumpPerSpanData(psd, 1);
556 #endif
559 void
560 nsLineLayout::FreeSpan(PerSpanData* psd)
562 // Free its frames
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;
571 #ifdef DEBUG
572 mFramesFreed++;
573 #endif
574 pfd = next;
577 // Now put the span on the free list since it's free too
578 psd->mNextFreeSpan = mSpanFreeList;
579 mSpanFreeList = psd;
580 #ifdef DEBUG
581 mSpansFreed++;
582 #endif
585 PRBool
586 nsLineLayout::IsZeroHeight()
588 PerSpanData* psd = mCurrentSpan;
589 PerFrameData* pfd = psd->mFirstFrame;
590 while (nsnull != pfd) {
591 if (0 != pfd->mBounds.height) {
592 return PR_FALSE;
594 pfd = pfd->mNext;
596 return PR_TRUE;
599 nsresult
600 nsLineLayout::NewPerFrameData(PerFrameData** aResult)
602 PerFrameData* pfd = mFrameFreeList;
603 if (nsnull == pfd) {
604 void *mem;
605 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(PerFrameData));
606 if (nsnull == mem) {
607 return NS_ERROR_OUT_OF_MEMORY;
609 pfd = reinterpret_cast<PerFrameData*>(mem);
611 else {
612 mFrameFreeList = pfd->mNext;
614 pfd->mSpan = nsnull;
615 pfd->mNext = nsnull;
616 pfd->mPrev = nsnull;
617 pfd->mFrame = nsnull;
618 pfd->mFlags = 0; // all flags default to false
620 #ifdef DEBUG
621 pfd->mVerticalAlign = 0xFF;
622 mFramesAllocated++;
623 #endif
624 *aResult = pfd;
625 return NS_OK;
628 PRBool
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)) {
634 return PR_TRUE;
636 return PR_FALSE;
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
642 // right.
643 static PRBool
644 HasPercentageUnitSide(const nsStyleSides& aSides)
646 NS_FOR_CSS_SIDES(side) {
647 if (aSides.Get(side).HasPercent())
648 return PR_TRUE;
650 return PR_FALSE;
653 static PRBool
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.
661 return PR_FALSE;
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
668 // quite rarely.
670 const nsStyleMargin* margin = aFrame->GetStyleMargin();
671 if (HasPercentageUnitSide(margin->mMargin)) {
672 return PR_TRUE;
675 const nsStylePadding* padding = aFrame->GetStylePadding();
676 if (HasPercentageUnitSide(padding->mPadding)) {
677 return PR_TRUE;
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)) {
690 return PR_TRUE;
693 if (eStyleUnit_Auto == pos->mWidth.GetUnit()) {
694 // We need to check for frames that shrink-wrap when they're auto
695 // width.
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) {
703 return PR_TRUE;
706 // Handle SVG, which doesn't map width/height into style
707 if ((
708 #ifdef MOZ_SVG
709 fType == nsGkAtoms::svgOuterSVGFrame ||
710 fType == nsGkAtoms::imageFrame ||
711 #endif
712 fType == nsGkAtoms::subDocumentFrame) &&
713 const_cast<nsIFrame*>(aFrame)->GetIntrinsicSize().width.GetUnit() ==
714 eStyleUnit_Percent) {
715 return PR_TRUE;
719 return PR_FALSE;
722 nsresult
723 nsLineLayout::ReflowFrame(nsIFrame* aFrame,
724 nsReflowStatus& aReflowStatus,
725 nsHTMLReflowMetrics* aMetrics,
726 PRBool& aPushedFrame)
728 // Initialize OUT parameter
729 aPushedFrame = PR_FALSE;
731 PerFrameData* pfd;
732 nsresult rv = NewPerFrameData(&pfd);
733 if (NS_FAILED(rv)) {
734 return rv;
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);
743 printf("\n");
744 #endif
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
777 // when breaking.
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 "
795 "calculation");
796 nscoord availableSpaceOnLine = psd->mRightEdge - psd->mX;
798 // Setup reflow state for reflowing the frame
799 js::LazilyConstructed<nsHTMLReflowState> reflowStateHolder;
800 if (!isText) {
801 reflowStateHolder.construct(mPresContext, *psd->mReflowState,
802 aFrame, availSize);
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);
819 } else {
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;
834 #ifdef DEBUG
835 metrics.width = nscoord(0xdeadbeef);
836 metrics.height = nscoord(0xdeadbeef);
837 #endif
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);
848 if (!isText) {
849 rv = aFrame->Reflow(mPresContext, metrics, reflowStateHolder.ref(),
850 aReflowStatus);
851 if (NS_FAILED(rv)) {
852 NS_WARNING( "Reflow of frame failed in nsLineLayout" );
853 return rv;
855 } else {
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
866 // content.
867 PRBool placedFloat = PR_FALSE;
868 PRBool isEmpty;
869 if (!frameType) {
870 isEmpty = pfd->mFrame->IsEmpty();
871 } else {
872 if (nsGkAtoms::placeholderFrame == frameType) {
873 isEmpty = PR_TRUE;
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
879 // mTrimmableWidth.
880 nscoord availableWidth = psd->mRightEdge - (psd->mX - mTrimmableWidth);
881 if (psd->mNoWrap) {
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.
891 availableWidth = 0;
893 placedFloat = AddFloat(outOfFlowFrame, availableWidth);
894 NS_ASSERTION(!(outOfFlowFrame->GetType() == nsGkAtoms::letterFrame &&
895 GetFirstLetterStyleOK()),
896 "FirstLetterStyle set on line with floating first letter");
899 else if (isText) {
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();
904 if (!isEmpty) {
905 pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, PR_TRUE);
906 nsIContent* content = textFrame->GetContent();
908 const nsTextFragment* frag = content->GetText();
909 if (frag) {
910 pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME,
911 !content->TextIsOnlyWhitespace());
915 else if (nsGkAtoms::brFrame == frameType) {
916 pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, PR_TRUE);
917 isEmpty = PR_FALSE;
918 } else {
919 if (nsGkAtoms::letterFrame==frameType) {
920 pfd->SetFlag(PFD_ISLETTERFRAME, PR_TRUE);
922 if (pfd->mSpan) {
923 isEmpty = !pfd->mSpan->mHasNonemptyContent && pfd->mFrame->IsSelfEmpty();
924 } else {
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;
937 #ifdef DEBUG
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);
953 #endif
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);
973 if (aMetrics) {
974 *aMetrics = metrics;
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
1004 // return now.
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).
1014 PRUint8 direction =
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)) {
1020 if (!isEmpty) {
1021 psd->mHasNonemptyContent = PR_TRUE;
1022 SetFlag(LL_LINEISEMPTY, PR_FALSE);
1023 if (!pfd->mSpan) {
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;
1034 if (span) {
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);
1053 else {
1054 PushFrame(aFrame);
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);
1063 else {
1064 PushFrame(aFrame);
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);
1072 #endif
1074 return rv;
1077 void
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.
1097 if (ltr)
1098 pfd->mMargin.left = 0;
1099 else
1100 pfd->mMargin.right = 0;
1102 else {
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 "
1108 "calculation");
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;
1119 nscoord
1120 nsLineLayout::GetCurrentFrameXDistanceFromBlock()
1122 PerSpanData* psd;
1123 nscoord x = 0;
1124 for (psd = mCurrentSpan; psd; psd = psd->mParent) {
1125 x += psd->mX;
1127 return x;
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.
1140 PRBool
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
1162 * conditions hold:
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)) {
1176 if (ltr)
1177 pfd->mMargin.right = 0;
1178 else
1179 pfd->mMargin.left = 0;
1182 else {
1183 // Don't apply margin to empty frames.
1184 pfd->mMargin.left = pfd->mMargin.right = 0;
1187 PerSpanData* psd = mCurrentSpan;
1188 if (psd->mNoWrap) {
1189 // When wrapping is off, everything fits.
1190 return PR_TRUE;
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);
1200 else {
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);
1206 #endif
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;
1211 if (!outside) {
1212 // If it fits, it fits
1213 #ifdef NOISY_CAN_PLACE_FRAME
1214 printf(" ==> inside\n");
1215 #endif
1216 return PR_TRUE;
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");
1226 #endif
1227 return PR_TRUE;
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");
1235 #endif
1236 return PR_TRUE;
1238 #endif
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);
1248 psd = psd->mParent;
1250 printf("\n");
1251 #endif
1252 return PR_TRUE;
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
1268 // after the line).
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.
1280 return PR_TRUE;
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");
1290 #endif
1292 // We will want to try backup.
1293 SetFlag(LL_NEEDBACKUP, PR_TRUE);
1294 return PR_TRUE;
1297 #ifdef NOISY_CAN_PLACE_FRAME
1298 printf(" ==> didn't fit\n");
1299 #endif
1300 aStatus = NS_INLINE_LINE_BREAK_BEFORE();
1301 return PR_FALSE;
1305 * Place the frame. Update running counters.
1307 void
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();
1322 else
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...
1330 if (!emptyFrame) {
1331 mTotalPlacedFrames++;
1335 nsresult
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();
1351 PerFrameData* pfd;
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();
1362 else
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;
1369 return rv;
1372 #ifdef DEBUG
1373 void
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);
1385 if (pfd->mSpan) {
1386 DumpPerSpanData(pfd->mSpan, aIndent + 1);
1388 pfd = pfd->mNext;
1391 #endif
1393 #define VALIGN_OTHER 0
1394 #define VALIGN_TOP 1
1395 #define VALIGN_BOTTOM 2
1397 void
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
1421 // block frame)
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.
1427 nscoord baselineY;
1428 if (psd->mMinY < 0) {
1429 baselineY = mTopEdge - psd->mMinY;
1431 else {
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
1437 // min/max Y.
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
1444 // amount.
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;
1451 baselineY += extra;
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);
1459 #endif
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
1463 // frames we find.
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
1482 printf(
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());
1487 #endif
1489 // Undo root-span mFrame pointer to prevent brane damage later on...
1490 mRootSpan->mFrame = nsnull;
1493 void
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;
1500 #ifdef DEBUG
1501 NS_ASSERTION(0xFF != pfd->mVerticalAlign, "umr");
1502 #endif
1503 switch (pfd->mVerticalAlign) {
1504 case VALIGN_TOP:
1505 if (span) {
1506 pfd->mBounds.y = -aDistanceFromTop - span->mMinY;
1508 else {
1509 pfd->mBounds.y = -aDistanceFromTop + pfd->mMargin.top;
1511 pfd->mFrame->SetRect(pfd->mBounds);
1512 #ifdef NOISY_VERTICAL_ALIGN
1513 printf(" ");
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);
1519 #endif
1520 break;
1521 case VALIGN_BOTTOM:
1522 if (span) {
1523 // Compute bottom leading
1524 pfd->mBounds.y = -aDistanceFromTop + aLineHeight - span->mMaxY;
1526 else {
1527 pfd->mBounds.y = -aDistanceFromTop + aLineHeight -
1528 pfd->mMargin.bottom - pfd->mBounds.height;
1530 pfd->mFrame->SetRect(pfd->mBounds);
1531 #ifdef NOISY_VERTICAL_ALIGN
1532 printf(" ");
1533 nsFrame::ListTag(stdout, pfd->mFrame);
1534 printf(": y=%d\n", pfd->mBounds.y);
1535 #endif
1536 break;
1538 if (span) {
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.
1552 void
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);
1595 printf("\n");
1596 #endif
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
1601 // "disappear".
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
1608 // line-height.
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
1616 // mode is set.
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;
1664 break;
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).
1679 baselineY = 0;
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");
1688 #endif
1690 else {
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
1717 // height.
1719 // Note that things are readjusted for this span after its children
1720 // are reflowed
1721 minY = VERTICAL_ALIGN_FRAMES_NO_MINIMUM;
1722 maxY = VERTICAL_ALIGN_FRAMES_NO_MAXIMUM;
1724 else {
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,
1737 // so just use it.
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");
1749 #endif
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");
1760 if (!frame) {
1761 return;
1764 // Compute the logical height of the frame
1765 nscoord logicalHeight;
1766 nscoord topLeading;
1767 PerSpanData* frameSpan = pfd->mSpan;
1768 if (frameSpan) {
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;
1774 else {
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;
1779 topLeading = 0;
1782 // Get vertical-align property
1783 const nsStyleCoord& verticalAlign =
1784 frame->GetStyleTextReset()->mVerticalAlign;
1785 #ifdef NOISY_VERTICAL_ALIGN
1786 printf(" [frame]");
1787 nsFrame::ListTag(stdout, frame);
1788 printf(": verticalAlignUnit=%d (enum == %d)\n",
1789 verticalAlignUnit,
1790 ((eStyleUnit_Enumerated == verticalAlign.GetUnit())
1791 ? verticalAlign.GetIntValue()
1792 : -1));
1793 #endif
1795 if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
1796 switch (verticalAlign.GetIntValue()) {
1797 default:
1798 case NS_STYLE_VERTICAL_ALIGN_BASELINE:
1800 // The element's baseline is aligned with the baseline of
1801 // the parent.
1802 pfd->mBounds.y = baselineY - pfd->mAscent;
1803 pfd->mVerticalAlign = VALIGN_OTHER;
1804 break;
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;
1818 break;
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;
1832 break;
1835 case NS_STYLE_VERTICAL_ALIGN_TOP:
1837 pfd->mVerticalAlign = VALIGN_TOP;
1838 nscoord subtreeHeight = logicalHeight;
1839 if (frameSpan) {
1840 subtreeHeight = frameSpan->mMaxY - frameSpan->mMinY;
1841 NS_ASSERTION(subtreeHeight >= logicalHeight,
1842 "unexpected subtree height");
1844 if (subtreeHeight > maxTopBoxHeight) {
1845 maxTopBoxHeight = subtreeHeight;
1847 break;
1850 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
1852 pfd->mVerticalAlign = VALIGN_BOTTOM;
1853 nscoord subtreeHeight = logicalHeight;
1854 if (frameSpan) {
1855 subtreeHeight = frameSpan->mMaxY - frameSpan->mMinY;
1856 NS_ASSERTION(subtreeHeight >= logicalHeight,
1857 "unexpected subtree height");
1859 if (subtreeHeight > maxBottomBoxHeight) {
1860 maxBottomBoxHeight = subtreeHeight;
1862 break;
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);
1871 if (frameSpan) {
1872 pfd->mBounds.y = baselineY -
1873 (parentXHeight + pfd->mBounds.height)/2;
1875 else {
1876 pfd->mBounds.y = baselineY - (parentXHeight + logicalHeight)/2 +
1877 pfd->mMargin.top;
1879 pfd->mVerticalAlign = VALIGN_OTHER;
1880 break;
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);
1889 if (frameSpan) {
1890 pfd->mBounds.y = baselineY - parentAscent -
1891 pfd->mBorderPadding.top + frameSpan->mTopLeading;
1893 else {
1894 pfd->mBounds.y = baselineY - parentAscent + pfd->mMargin.top;
1896 pfd->mVerticalAlign = VALIGN_OTHER;
1897 break;
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);
1906 if (frameSpan) {
1907 pfd->mBounds.y = baselineY + parentDescent -
1908 pfd->mBounds.height + pfd->mBorderPadding.bottom -
1909 frameSpan->mBottomLeading;
1911 else {
1912 pfd->mBounds.y = baselineY + parentDescent -
1913 pfd->mBounds.height - pfd->mMargin.bottom;
1915 pfd->mVerticalAlign = VALIGN_OTHER;
1916 break;
1919 case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
1921 // Align the midpoint of the frame with the baseline of the parent.
1922 if (frameSpan) {
1923 pfd->mBounds.y = baselineY - pfd->mBounds.height/2;
1925 else {
1926 pfd->mBounds.y = baselineY - logicalHeight/2 + pfd->mMargin.top;
1928 pfd->mVerticalAlign = VALIGN_OTHER;
1929 break;
1932 } else {
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());
1941 nscoord offset =
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.
1968 #if 0
1969 if (!pfd->GetFlag(PFD_ISTEXTFRAME)) {
1970 #else
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)) {
1974 canUpdate =
1975 frame->GetStyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal;
1977 if (canUpdate) {
1978 #endif
1979 nscoord yTop, yBottom;
1980 if (frameSpan) {
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;
1987 else {
1988 yTop = pfd->mBounds.y - pfd->mMargin.top;
1989 yBottom = yTop + logicalHeight;
1991 if (!preMode &&
1992 mPresContext->CompatibilityMode() != eCompatibility_FullStandards &&
1993 !logicalHeight) {
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,
2008 logicalHeight,
2009 pfd->mSpan ? topLeading : 0,
2010 pfd->mBounds.y, minY, maxY);
2011 #endif
2013 if (psd != mRootSpan) {
2014 frame->SetRect(pfd->mBounds);
2017 pfd = pfd->mNext;
2020 // Factor in the minimum line-height when handling the root-span for
2021 // the block.
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();
2039 if (blockContent) {
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;
2049 if (applyMinLH) {
2050 if (psd->mHasNonemptyContent || preMode || GetFlag(LL_HASBULLET)) {
2051 #ifdef NOISY_VERTICAL_ALIGN
2052 printf(" [span]==> adjusting min/maxY: currentValues: %d,%d", minY, maxY);
2053 #endif
2054 nscoord minimumLineHeight = mMinLineHeight;
2055 nscoord yTop =
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);
2064 #endif
2065 #ifdef NOISY_VERTICAL_ALIGN
2066 printf(" Used mMinLineHeight: %d, fontHeight: %d, fontAscent: %d\n", mMinLineHeight, fontHeight, fontAscent);
2067 #endif
2069 else {
2070 // XXX issues:
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",
2078 minY, maxY);
2079 #endif
2080 minY = maxY = 0;
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);
2097 #endif
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;
2112 if (minY > 0) {
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);
2125 pfd = pfd->mNext;
2127 maxY -= minY; // since minY is in the frame's own coordinate system
2128 minY = 0;
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);
2140 #endif
2143 psd->mMinY = minY;
2144 psd->mMaxY = maxY;
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);
2148 #endif
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();
2160 r.x -= aDeltaWidth;
2161 aFrame->SetRect(r);
2164 PRBool
2165 nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
2166 nscoord* aDeltaWidth)
2168 #ifndef IBMBIDI
2169 // XXX what about NS_STYLE_DIRECTION_RTL?
2170 if (NS_STYLE_DIRECTION_RTL == psd->mDirection) {
2171 *aDeltaWidth = 0;
2172 return PR_TRUE;
2174 #endif
2176 PerFrameData* pfd = psd->mFirstFrame;
2177 if (!pfd) {
2178 *aDeltaWidth = 0;
2179 return PR_FALSE;
2181 pfd = pfd->Last();
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);
2189 printf("\n");
2190 #endif
2191 PerSpanData* childSpan = pfd->mSpan;
2192 if (childSpan) {
2193 // Maybe the child span has the trailing white-space in it?
2194 if (TrimTrailingWhiteSpaceIn(childSpan, aDeltaWidth)) {
2195 nscoord deltaWidth = *aDeltaWidth;
2196 if (deltaWidth) {
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;
2209 f->SetRect(r);
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) {
2220 pfd = 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);
2233 return PR_TRUE;
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.
2240 *aDeltaWidth = 0;
2241 return PR_TRUE;
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
2246 // width
2247 nsTextFrame::TrimOutput trimOutput = static_cast<nsTextFrame*>(pfd->mFrame)->
2248 TrimTrailingWhiteSpace(mBlockReflowState->rendContext);
2249 #ifdef NOISY_TRIM
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);
2256 #endif
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) {
2283 pfd = 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;
2300 return PR_TRUE;
2303 pfd = pfd->mPrev;
2306 *aDeltaWidth = 0;
2307 return PR_FALSE;
2310 PRBool
2311 nsLineLayout::TrimTrailingWhiteSpace()
2313 PerSpanData* psd = mRootSpan;
2314 nscoord deltaWidth;
2315 TrimTrailingWhiteSpaceIn(psd, &deltaWidth);
2316 return 0 != deltaWidth;
2319 void
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) {
2337 PRInt32 spanSpaces;
2338 PRInt32 spanLetters;
2340 ComputeJustificationWeights(pfd->mSpan, &spanSpaces, &spanLetters);
2342 numSpaces += spanSpaces;
2343 numLetters += spanLetters;
2347 *aNumSpaces = numSpaces;
2348 *aNumLetters = numLetters;
2351 nscoord
2352 nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState* aState)
2354 NS_ASSERTION(aPSD, "null arg");
2355 NS_ASSERTION(aState, "null arg");
2357 nscoord deltaX = 0;
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)) {
2361 nscoord dw = 0;
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;
2392 if (dw) {
2393 pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, PR_TRUE);
2396 else {
2397 if (nsnull != pfd->mSpan) {
2398 dw += ApplyFrameJustification(pfd->mSpan, aState);
2402 pfd->mBounds.width += dw;
2404 deltaX += dw;
2405 pfd->mFrame->SetRect(pfd->mBounds);
2408 return deltaX;
2411 void
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 "
2419 "calculation");
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);
2426 #endif
2427 nscoord dx = 0;
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) {
2435 PRInt32 numSpaces;
2436 PRInt32 numLetters;
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;
2448 break;
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
2457 break;
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;
2465 break;
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;
2471 break;
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:
2478 break;
2480 case NS_STYLE_TEXT_ALIGN_CENTER:
2481 case NS_STYLE_TEXT_ALIGN_MOZ_CENTER:
2482 dx = remainingWidth / 2;
2483 break;
2486 else if (remainingWidth < 0) {
2487 if (NS_STYLE_DIRECTION_RTL == psd->mDirection) {
2488 dx = remainingWidth;
2489 psd->mX += dx;
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;
2504 if (dx) {
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;
2513 void
2514 nsLineLayout::RelativePositionFrames(nsOverflowAreas& aOverflowAreas)
2516 RelativePositionFrames(mRootSpan, aOverflowAreas);
2519 void
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
2530 // below.
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);
2550 else {
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);
2575 origin += change;
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.
2591 nsOverflowAreas r;
2592 if (pfd->mSpan) {
2593 // Compute a new combined area for the child span before
2594 // aggregating it into our combined area.
2595 RelativePositionFrames(pfd->mSpan, r);
2596 } else {
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
2608 // positioned.
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,
2621 frame->GetView(),
2622 r.VisualOverflow(),
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
2629 // overflow rect...
2630 if (psd->mFrame) {
2631 PerFrameData* spanPFD = psd->mFrame;
2632 nsIFrame* frame = spanPFD->mFrame;
2633 frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize());
2635 aOverflowAreas = overflowAreas;