Back to index

lightning-sunbird  0.9+nobinonly
nsLineLayout.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Steve Clark <buster@netscape.com>
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *   L. David Baron <dbaron@dbaron.org>
00026  *   Robert O'Callahan <roc+moz@cs.cmu.edu>
00027  *   IBM Corporation
00028  *   Mats Palmgren <mats.palmgren@bredband.net>
00029  *
00030  * Alternatively, the contents of this file may be used under the terms of
00031  * either of the GNU General Public License Version 2 or later (the "GPL"),
00032  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00033  * in which case the provisions of the GPL or the LGPL are applicable instead
00034  * of those above. If you wish to allow use of your version of this file only
00035  * under the terms of either the GPL or the LGPL, and not to allow others to
00036  * use your version of this file under the terms of the MPL, indicate your
00037  * decision by deleting the provisions above and replace them with the notice
00038  * and other provisions required by the GPL or the LGPL. If you do not delete
00039  * the provisions above, a recipient may use your version of this file under
00040  * the terms of any one of the MPL, the GPL or the LGPL.
00041  *
00042  * ***** END LICENSE BLOCK ***** */
00043 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
00044 #include "plarena.h"
00045 
00046 #include "nsCOMPtr.h"
00047 #include "nsLineLayout.h"
00048 #include "nsBlockFrame.h"
00049 #include "nsInlineFrame.h"
00050 #include "nsStyleConsts.h"
00051 #include "nsHTMLContainerFrame.h"
00052 #include "nsSpaceManager.h"
00053 #include "nsStyleContext.h"
00054 #include "nsPresContext.h"
00055 #include "nsIFontMetrics.h"
00056 #include "nsIRenderingContext.h"
00057 #include "nsLayoutAtoms.h"
00058 #include "nsPlaceholderFrame.h"
00059 #include "nsReflowPath.h"
00060 #include "nsIDocument.h"
00061 #include "nsIHTMLDocument.h"
00062 #include "nsIContent.h"
00063 #include "nsITextContent.h"
00064 #include "nsIView.h"
00065 #include "nsIViewManager.h"
00066 #include "nsHTMLAtoms.h"
00067 #include "nsTextFragment.h"
00068 #include "nsBidiUtils.h"
00069 #include "nsLayoutUtils.h"
00070 
00071 #ifdef DEBUG
00072 #undef  NOISY_HORIZONTAL_ALIGN
00073 #undef  NOISY_VERTICAL_ALIGN
00074 #undef  REALLY_NOISY_VERTICAL_ALIGN
00075 #undef  NOISY_REFLOW
00076 #undef  REALLY_NOISY_REFLOW
00077 #undef  NOISY_PUSHING
00078 #undef  REALLY_NOISY_PUSHING
00079 #undef  DEBUG_ADD_TEXT
00080 #undef  NOISY_MAX_ELEMENT_SIZE
00081 #undef  REALLY_NOISY_MAX_ELEMENT_SIZE
00082 #undef  NOISY_CAN_PLACE_FRAME
00083 #undef  NOISY_TRIM
00084 #undef  REALLY_NOISY_TRIM
00085 #endif
00086 
00087 //----------------------------------------------------------------------
00088 
00089 #define FIX_BUG_50257
00090 
00091 #define PLACED_LEFT  0x1
00092 #define PLACED_RIGHT 0x2
00093 
00094 #define HACK_MEW
00095 //#undef HACK_MEW
00096 #ifdef HACK_MEW
00097 static nscoord AccumulateImageSizes(nsPresContext& aPresContext, nsIFrame& aFrame)
00098 {
00099   nscoord sizes = 0;
00100 
00101   // see if aFrame is an image frame first
00102   if (aFrame.GetType() == nsLayoutAtoms::imageFrame) {
00103     sizes += aFrame.GetSize().width;
00104   } else {
00105     // see if there are children to process
00106     // XXX: process alternate child lists?
00107     nsIFrame* child = aFrame.GetFirstChild(nsnull);
00108     while (child) {
00109       // recurse: note that we already know we are in a child frame, so no need to track further
00110       sizes += AccumulateImageSizes(aPresContext, *child);
00111       // now next sibling
00112       child = child->GetNextSibling();
00113     }
00114   }
00115 
00116   return sizes;
00117 }
00118 
00119 static PRBool InUnconstrainedTableCell(const nsHTMLReflowState& aBlockReflowState)
00120 {
00121   PRBool result = PR_FALSE;
00122 
00123   // get the parent reflow state
00124   const nsHTMLReflowState* parentReflowState = aBlockReflowState.parentReflowState;
00125   if (parentReflowState) {
00126     // check if the frame is a tablecell
00127     NS_ASSERTION(parentReflowState->mStyleDisplay, "null styleDisplay in parentReflowState unexpected");
00128     if (parentReflowState->mStyleDisplay &&
00129         parentReflowState->mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL) {
00130       // see if width is unconstrained or percent
00131       NS_ASSERTION(parentReflowState->mStylePosition, "null stylePosition in parentReflowState unexpected");
00132       if(parentReflowState->mStylePosition) {
00133         switch(parentReflowState->mStylePosition->mWidth.GetUnit()) {
00134           case eStyleUnit_Auto :
00135           case eStyleUnit_Null :
00136             result = PR_TRUE;
00137             break;
00138           default:
00139             result = PR_FALSE;
00140             break;
00141         }
00142       }
00143     }
00144   }
00145   return result;
00146 }
00147 
00148 #endif
00149 
00150 MOZ_DECL_CTOR_COUNTER(nsLineLayout)
00151 
00152 nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
00153                            nsSpaceManager* aSpaceManager,
00154                            const nsHTMLReflowState* aOuterReflowState,
00155                            PRBool aComputeMaxElementWidth)
00156   : mPresContext(aPresContext),
00157     mSpaceManager(aSpaceManager),
00158     mBlockReflowState(aOuterReflowState),
00159     mBlockRS(nsnull),/* XXX temporary */
00160     mMinLineHeight(0),
00161     mComputeMaxElementWidth(aComputeMaxElementWidth),
00162     mTextIndent(0),
00163     mWordFrames(0)
00164 {
00165   MOZ_COUNT_CTOR(nsLineLayout);
00166 
00167   // Stash away some style data that we need
00168   mStyleText = aOuterReflowState->frame->GetStyleText();
00169   mTextAlign = mStyleText->mTextAlign;
00170   mLineNumber = 0;
00171   mColumn = 0;
00172   mFlags = 0; // default all flags to false except those that follow here...
00173   SetFlag(LL_ENDSINWHITESPACE, PR_TRUE);
00174   mPlacedFloats = 0;
00175   mTotalPlacedFrames = 0;
00176   mTopEdge = 0;
00177 
00178   // Instead of always pre-initializing the free-lists for frames and
00179   // spans, we do it on demand so that situations that only use a few
00180   // frames and spans won't waste alot of time in unneeded
00181   // initialization.
00182   PL_INIT_ARENA_POOL(&mArena, "nsLineLayout", 1024);
00183   mFrameFreeList = nsnull;
00184   mSpanFreeList = nsnull;
00185 
00186   mCurrentSpan = mRootSpan = nsnull;
00187   mSpanDepth = 0;
00188 
00189   mCompatMode = mPresContext->CompatibilityMode();
00190 }
00191 
00192 nsLineLayout::~nsLineLayout()
00193 {
00194   MOZ_COUNT_DTOR(nsLineLayout);
00195 
00196   NS_ASSERTION(nsnull == mRootSpan, "bad line-layout user");
00197 
00198   delete mWordFrames; // operator delete for this class just returns
00199 
00200   // PL_FreeArenaPool takes our memory and puts in on a global free list so
00201   // that the next time an arena makes an allocation it will not have to go
00202   // all the way down to malloc.  This is desirable as this class is created
00203   // and destroyed in a tight loop.
00204   //
00205   // I looked at the code.  It is not technically necessary to call
00206   // PL_FinishArenaPool() after PL_FreeArenaPool(), but from an API
00207   // standpoint, I think we are susposed to.  It will be very fast anyway,
00208   // since PL_FreeArenaPool() has done all the work.
00209   PL_FreeArenaPool(&mArena);
00210   PL_FinishArenaPool(&mArena);
00211 }
00212 
00213 void*
00214 nsLineLayout::ArenaDeque::operator new(size_t aSize, PLArenaPool &aPool)
00215 {
00216   void *mem;
00217 
00218   PL_ARENA_ALLOCATE(mem, &aPool, aSize);
00219   return mem;
00220 }
00221 
00222 PRBool nsLineLayout::AllocateDeque()
00223 {
00224   mWordFrames = new(mArena) ArenaDeque;
00225 
00226   return mWordFrames != nsnull;
00227 }
00228 
00229 // Find out if the frame has a non-null prev-in-flow, i.e., whether it
00230 // is a continuation.
00231 inline PRBool
00232 HasPrevInFlow(nsIFrame *aFrame)
00233 {
00234   nsIFrame *prevInFlow = aFrame->GetPrevInFlow();
00235   return prevInFlow != nsnull;
00236 }
00237 
00238 void
00239 nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY,
00240                               nscoord aWidth, nscoord aHeight,
00241                               PRBool aImpactedByFloats,
00242                               PRBool aIsTopOfPage)
00243 {
00244   NS_ASSERTION(nsnull == mRootSpan, "bad linelayout user");
00245 #ifdef DEBUG
00246   if ((aWidth != NS_UNCONSTRAINEDSIZE) && CRAZY_WIDTH(aWidth)) {
00247     NS_NOTREACHED("bad width");
00248     nsFrame::ListTag(stdout, mBlockReflowState->frame);
00249     printf(": Init: bad caller: width WAS %d(0x%x)\n",
00250            aWidth, aWidth);
00251   }
00252   if ((aHeight != NS_UNCONSTRAINEDSIZE) && CRAZY_HEIGHT(aHeight)) {
00253     NS_NOTREACHED("bad height");
00254     nsFrame::ListTag(stdout, mBlockReflowState->frame);
00255     printf(": Init: bad caller: height WAS %d(0x%x)\n",
00256            aHeight, aHeight);
00257   }
00258 #endif
00259 #ifdef NOISY_REFLOW
00260   nsFrame::ListTag(stdout, mBlockReflowState->frame);
00261   printf(": BeginLineReflow: %d,%d,%d,%d impacted=%s %s\n",
00262          aX, aY, aWidth, aHeight,
00263          aImpactedByFloats?"true":"false",
00264          aIsTopOfPage ? "top-of-page" : "");
00265 #endif
00266 #ifdef DEBUG
00267   mSpansAllocated = mSpansFreed = mFramesAllocated = mFramesFreed = 0;
00268 #endif
00269 
00270   mColumn = 0;
00271   
00272   SetFlag(LL_ENDSINWHITESPACE, PR_TRUE);
00273   SetFlag(LL_UNDERSTANDSNWHITESPACE, PR_FALSE);
00274   SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE);
00275   SetFlag(LL_ISTOPOFPAGE, aIsTopOfPage);
00276   SetFlag(LL_UPDATEDBAND, PR_FALSE);
00277   mPlacedFloats = 0;
00278   SetFlag(LL_IMPACTEDBYFLOATS, aImpactedByFloats);
00279   mTotalPlacedFrames = 0;
00280   SetFlag(LL_CANPLACEFLOAT, PR_TRUE);
00281   SetFlag(LL_LINEENDSINBR, PR_FALSE);
00282   mSpanDepth = 0;
00283   mMaxTopBoxHeight = mMaxBottomBoxHeight = 0;
00284 
00285   ForgetWordFrames();
00286 
00287   PerSpanData* psd;
00288   NewPerSpanData(&psd);
00289   mCurrentSpan = mRootSpan = psd;
00290   psd->mReflowState = mBlockReflowState;
00291   psd->mLeftEdge = aX;
00292   psd->mX = aX;
00293   if (NS_UNCONSTRAINEDSIZE == aWidth) {
00294     psd->mRightEdge = NS_UNCONSTRAINEDSIZE;
00295   }
00296   else {
00297     psd->mRightEdge = aX + aWidth;
00298   }
00299 
00300   mTopEdge = aY;
00301 
00302   switch (mStyleText->mWhiteSpace) {
00303   case NS_STYLE_WHITESPACE_PRE:
00304   case NS_STYLE_WHITESPACE_NOWRAP:
00305     psd->mNoWrap = PR_TRUE;
00306     break;
00307   default:
00308     psd->mNoWrap = PR_FALSE;
00309     break;
00310   }
00311   psd->mDirection = mBlockReflowState->mStyleVisibility->mDirection;
00312   psd->mChangedFrameDirection = PR_FALSE;
00313 
00314   // If this is the first line of a block then see if the text-indent
00315   // property amounts to anything.
00316   
00317   if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowState->frame)) {
00318     nscoord indent = 0;
00319     nsStyleUnit unit = mStyleText->mTextIndent.GetUnit();
00320     if (eStyleUnit_Coord == unit) {
00321       indent = mStyleText->mTextIndent.GetCoordValue();
00322     }
00323     else if (eStyleUnit_Percent == unit) {
00324       nscoord width =
00325         nsHTMLReflowState::GetContainingBlockContentWidth(mBlockReflowState);
00326       if ((0 != width) && (NS_UNCONSTRAINEDSIZE != width)) {
00327         indent = nscoord(mStyleText->mTextIndent.GetPercentValue() * width);
00328       }
00329     }
00330 
00331     mTextIndent = indent;
00332 
00333     if (NS_STYLE_DIRECTION_RTL == psd->mDirection) {
00334       if (NS_UNCONSTRAINEDSIZE != psd->mRightEdge) {
00335         psd->mRightEdge -= indent;
00336       }
00337     }
00338     else {
00339       psd->mX += indent;
00340     }
00341   }
00342 }
00343 
00344 void
00345 nsLineLayout::EndLineReflow()
00346 {
00347 #ifdef NOISY_REFLOW
00348   nsFrame::ListTag(stdout, mBlockReflowState->frame);
00349   printf(": EndLineReflow: width=%d\n", mRootSpan->mX - mRootSpan->mLeftEdge);
00350 #endif
00351 
00352   FreeSpan(mRootSpan);
00353   mCurrentSpan = mRootSpan = nsnull;
00354 
00355   NS_ASSERTION(mSpansAllocated == mSpansFreed, "leak");
00356   NS_ASSERTION(mFramesAllocated == mFramesFreed, "leak");
00357 
00358 #if 0
00359   static PRInt32 maxSpansAllocated = NS_LINELAYOUT_NUM_SPANS;
00360   static PRInt32 maxFramesAllocated = NS_LINELAYOUT_NUM_FRAMES;
00361   if (mSpansAllocated > maxSpansAllocated) {
00362     printf("XXX: saw a line with %d spans\n", mSpansAllocated);
00363     maxSpansAllocated = mSpansAllocated;
00364   }
00365   if (mFramesAllocated > maxFramesAllocated) {
00366     printf("XXX: saw a line with %d frames\n", mFramesAllocated);
00367     maxFramesAllocated = mFramesAllocated;
00368   }
00369 #endif
00370 }
00371 
00372 // XXX swtich to a single mAvailLineWidth that we adjust as each frame
00373 // on the line is placed. Each span can still have a per-span mX that
00374 // tracks where a child frame is going in its span; they don't need a
00375 // per-span mLeftEdge?
00376 
00377 void
00378 nsLineLayout::UpdateBand(nscoord aX, nscoord aY,
00379                          nscoord aWidth, nscoord aHeight,
00380                          PRBool aPlacedLeftFloat,
00381                          nsIFrame* aFloatFrame)
00382 {
00383 #ifdef REALLY_NOISY_REFLOW
00384   printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p placedLeft=%s\n  will set mImpacted to PR_TRUE\n",
00385          aX, aY, aWidth, aHeight, aFloatFrame, aPlacedLeftFloat?"true":"false");
00386 #endif
00387   PerSpanData* psd = mRootSpan;
00388   NS_PRECONDITION(psd->mX == psd->mLeftEdge, "update-band called late");
00389 #ifdef DEBUG
00390   if ((aWidth != NS_UNCONSTRAINEDSIZE) && CRAZY_WIDTH(aWidth)) {
00391     nsFrame::ListTag(stdout, mBlockReflowState->frame);
00392     printf(": UpdateBand: bad caller: width WAS %d(0x%x)\n",
00393            aWidth, aWidth);
00394   }
00395   if ((aHeight != NS_UNCONSTRAINEDSIZE) && CRAZY_HEIGHT(aHeight)) {
00396     nsFrame::ListTag(stdout, mBlockReflowState->frame);
00397     printf(": UpdateBand: bad caller: height WAS %d(0x%x)\n",
00398            aHeight, aHeight);
00399   }
00400 #endif
00401 
00402   // Compute the difference between last times width and the new width
00403   nscoord deltaWidth = 0;
00404   if (NS_UNCONSTRAINEDSIZE != psd->mRightEdge) {
00405     NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aWidth, "switched constraints");
00406     nscoord oldWidth = psd->mRightEdge - psd->mLeftEdge;
00407     deltaWidth = aWidth - oldWidth;
00408   }
00409 #ifdef NOISY_REFLOW
00410   nsFrame::ListTag(stdout, mBlockReflowState->frame);
00411   printf(": UpdateBand: %d,%d,%d,%d deltaWidth=%d %s float\n",
00412          aX, aY, aWidth, aHeight, deltaWidth,
00413          aPlacedLeftFloat ? "left" : "right");
00414 #endif
00415 
00416   psd->mLeftEdge = aX;
00417   psd->mX = aX;
00418   if (NS_UNCONSTRAINEDSIZE == aWidth) {
00419     psd->mRightEdge = NS_UNCONSTRAINEDSIZE;
00420   }
00421   else {
00422     psd->mRightEdge = aX + aWidth;
00423   }
00424   mTopEdge = aY;
00425   SetFlag(LL_UPDATEDBAND, PR_TRUE);
00426   mPlacedFloats |= (aPlacedLeftFloat ? PLACED_LEFT : PLACED_RIGHT);
00427   SetFlag(LL_IMPACTEDBYFLOATS, PR_TRUE);
00428 
00429   SetFlag(LL_LASTFLOATWASLETTERFRAME,
00430           nsLayoutAtoms::letterFrame == aFloatFrame->GetType());
00431 
00432   // Now update all of the open spans...
00433   mRootSpan->mContainsFloat = PR_TRUE;              // make sure mRootSpan gets updated too
00434   psd = mCurrentSpan;
00435   while (psd != mRootSpan) {
00436     NS_ASSERTION(nsnull != psd, "null ptr");
00437     if (nsnull == psd) {
00438       break;
00439     }
00440     NS_ASSERTION(psd->mX == psd->mLeftEdge, "bad float placement");
00441     if (NS_UNCONSTRAINEDSIZE == aWidth) {
00442       psd->mRightEdge = NS_UNCONSTRAINEDSIZE;
00443     }
00444     else {
00445       psd->mRightEdge += deltaWidth;
00446     }
00447     psd->mContainsFloat = PR_TRUE;
00448 #ifdef NOISY_REFLOW
00449     printf("  span %p: oldRightEdge=%d newRightEdge=%d\n",
00450            psd, psd->mRightEdge - deltaWidth, psd->mRightEdge);
00451 #endif
00452     psd = psd->mParent;
00453   }
00454 }
00455 
00456 // Note: Only adjust the outermost frames (the ones that are direct
00457 // children of the block), not the ones in the child spans. The reason
00458 // is simple: the frames in the spans have coordinates local to their
00459 // parent therefore they are moved when their parent span is moved.
00460 void
00461 nsLineLayout::UpdateFrames()
00462 {
00463   NS_ASSERTION(nsnull != mRootSpan, "UpdateFrames with no active spans");
00464 
00465   PerSpanData* psd = mRootSpan;
00466   if (PLACED_LEFT & mPlacedFloats) {
00467     PerFrameData* pfd = psd->mFirstFrame;
00468     while (nsnull != pfd) {
00469       pfd->mBounds.x = psd->mX;
00470       pfd = pfd->mNext;
00471     }
00472   }
00473 }
00474 
00475 nsresult
00476 nsLineLayout::NewPerSpanData(PerSpanData** aResult)
00477 {
00478   PerSpanData* psd = mSpanFreeList;
00479   if (nsnull == psd) {
00480     void *mem;
00481     PL_ARENA_ALLOCATE(mem, &mArena, sizeof(PerSpanData));
00482     if (nsnull == mem) {
00483       return NS_ERROR_OUT_OF_MEMORY;
00484     }
00485     psd = NS_REINTERPRET_CAST(PerSpanData*, mem);
00486   }
00487   else {
00488     mSpanFreeList = psd->mNextFreeSpan;
00489   }
00490   psd->mParent = nsnull;
00491   psd->mFrame = nsnull;
00492   psd->mFirstFrame = nsnull;
00493   psd->mLastFrame = nsnull;
00494   psd->mContainsFloat = PR_FALSE;
00495   psd->mZeroEffectiveSpanBox = PR_FALSE;
00496 
00497 #ifdef DEBUG
00498   mSpansAllocated++;
00499 #endif
00500   *aResult = psd;
00501   return NS_OK;
00502 }
00503 
00504 nsresult
00505 nsLineLayout::BeginSpan(nsIFrame* aFrame,
00506                         const nsHTMLReflowState* aSpanReflowState,
00507                         nscoord aLeftEdge,
00508                         nscoord aRightEdge)
00509 {
00510 #ifdef NOISY_REFLOW
00511   nsFrame::IndentBy(stdout, mSpanDepth+1);
00512   nsFrame::ListTag(stdout, aFrame);
00513   printf(": BeginSpan leftEdge=%d rightEdge=%d\n", aLeftEdge, aRightEdge);
00514 #endif
00515 
00516   PerSpanData* psd;
00517   nsresult rv = NewPerSpanData(&psd);
00518   if (NS_SUCCEEDED(rv)) {
00519     // Link up span frame's pfd to point to its child span data
00520     PerFrameData* pfd = mCurrentSpan->mLastFrame;
00521     NS_ASSERTION(pfd->mFrame == aFrame, "huh?");
00522     pfd->mSpan = psd;
00523 
00524     // Init new span
00525     psd->mFrame = pfd;
00526     psd->mParent = mCurrentSpan;
00527     psd->mReflowState = aSpanReflowState;
00528     psd->mLeftEdge = aLeftEdge;
00529     psd->mX = aLeftEdge;
00530     psd->mRightEdge = aRightEdge;
00531 
00532     const nsStyleText* styleText = aSpanReflowState->frame->GetStyleText();
00533     switch (styleText->mWhiteSpace) {
00534     case NS_STYLE_WHITESPACE_PRE:
00535     case NS_STYLE_WHITESPACE_NOWRAP:
00536         psd->mNoWrap = PR_TRUE;
00537         break;
00538     default:
00539         psd->mNoWrap = PR_FALSE;
00540         break;
00541     }
00542     psd->mDirection = aSpanReflowState->mStyleVisibility->mDirection;
00543     psd->mChangedFrameDirection = PR_FALSE;
00544 
00545     // Switch to new span
00546     mCurrentSpan = psd;
00547     mSpanDepth++;
00548   }
00549   return rv;
00550 }
00551 
00552 void
00553 nsLineLayout::EndSpan(nsIFrame* aFrame,
00554                       nsSize& aSizeResult,
00555                       nscoord* aMaxElementWidth)
00556 {
00557   NS_ASSERTION(mSpanDepth > 0, "end-span without begin-span");
00558 #ifdef NOISY_REFLOW
00559   nsFrame::IndentBy(stdout, mSpanDepth);
00560   nsFrame::ListTag(stdout, aFrame);
00561   printf(": EndSpan width=%d\n", mCurrentSpan->mX - mCurrentSpan->mLeftEdge);
00562 #endif
00563   PerSpanData* psd = mCurrentSpan;
00564   nscoord width = 0;
00565   nscoord maxHeight = 0;
00566   nscoord maxElementWidth = 0;
00567   if (nsnull != psd->mLastFrame) {
00568     width = psd->mX - psd->mLeftEdge;
00569     PerFrameData* pfd = psd->mFirstFrame;
00570     while (nsnull != pfd) {
00571       /* there's one oddball case we need to guard against
00572        * if we're reflowed with NS_UNCONSTRAINEDSIZE
00573        * then the last frame will not contribute to the max element size height
00574        * if it is a text frame that only contains whitespace
00575        */
00576       if (NS_UNCONSTRAINEDSIZE != psd->mRightEdge ||  // it's not an unconstrained reflow
00577           pfd->mNext ||                               // or it's not the last frame in the span
00578           !pfd->GetFlag(PFD_ISTEXTFRAME) ||           // or it's not a text frame
00579           pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)  // or it contains something other than whitespace
00580          ) {
00581         if (pfd->mBounds.height > maxHeight) maxHeight = pfd->mBounds.height;
00582 
00583         // Compute max-element-width if necessary
00584         if (aMaxElementWidth) {
00585           nscoord mw = pfd->mMaxElementWidth;
00586           // add only fixed margins to the MEW
00587           if (pfd->mMargin.left) {
00588             if (pfd->mFrame->GetStyleMargin()->mMargin.GetLeftUnit() ==
00589                 eStyleUnit_Coord)
00590               mw += pfd->mMargin.left;
00591           }
00592           if (pfd->mMargin.right) {
00593             if (pfd->mFrame->GetStyleMargin()->mMargin.GetRightUnit() ==
00594                 eStyleUnit_Coord)
00595               mw += pfd->mMargin.right;
00596           }
00597           if (maxElementWidth < mw) {
00598             maxElementWidth = mw;
00599           }
00600         }
00601       }
00602       pfd = pfd->mNext;
00603     }
00604   }
00605   aSizeResult.width = width;
00606   aSizeResult.height = maxHeight;
00607   if (aMaxElementWidth) {
00608     if (psd->mNoWrap) {
00609       // When we have a non-breakable span, it's max-element-width
00610       // width is its entire width.
00611       *aMaxElementWidth = width;
00612     }
00613     else {
00614       *aMaxElementWidth = maxElementWidth;
00615     }
00616   }
00617 
00618   mSpanDepth--;
00619   mCurrentSpan->mReflowState = nsnull;  // no longer valid so null it out!
00620   mCurrentSpan = mCurrentSpan->mParent;
00621 }
00622 
00623 PRInt32
00624 nsLineLayout::GetCurrentSpanCount() const
00625 {
00626   NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
00627   PRInt32 count = 0;
00628   PerFrameData* pfd = mRootSpan->mFirstFrame;
00629   while (nsnull != pfd) {
00630     count++;
00631     pfd = pfd->mNext;
00632   }
00633   return count;
00634 }
00635 
00636 void
00637 nsLineLayout::SplitLineTo(PRInt32 aNewCount)
00638 {
00639   NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
00640 
00641 #ifdef REALLY_NOISY_PUSHING
00642   printf("SplitLineTo %d (current count=%d); before:\n", aNewCount,
00643          GetCurrentSpanCount());
00644   DumpPerSpanData(mRootSpan, 1);
00645 #endif
00646   PerSpanData* psd = mRootSpan;
00647   PerFrameData* pfd = psd->mFirstFrame;
00648   while (nsnull != pfd) {
00649     if (--aNewCount == 0) {
00650       // Truncate list at pfd (we keep pfd, but anything following is freed)
00651       PerFrameData* next = pfd->mNext;
00652       pfd->mNext = nsnull;
00653       psd->mLastFrame = pfd;
00654 
00655       // Now release all of the frames following pfd
00656       pfd = next;
00657       while (nsnull != pfd) {
00658         next = pfd->mNext;
00659         pfd->mNext = mFrameFreeList;
00660         mFrameFreeList = pfd;
00661 #ifdef DEBUG
00662         mFramesFreed++;
00663 #endif
00664         if (nsnull != pfd->mSpan) {
00665           FreeSpan(pfd->mSpan);
00666         }
00667         pfd = next;
00668       }
00669       break;
00670     }
00671     pfd = pfd->mNext;
00672   }
00673 #ifdef NOISY_PUSHING
00674   printf("SplitLineTo %d (current count=%d); after:\n", aNewCount,
00675          GetCurrentSpanCount());
00676   DumpPerSpanData(mRootSpan, 1);
00677 #endif
00678 }
00679 
00680 void
00681 nsLineLayout::PushFrame(nsIFrame* aFrame)
00682 {
00683   PerSpanData* psd = mCurrentSpan;
00684   NS_ASSERTION(psd->mLastFrame->mFrame == aFrame, "pushing non-last frame");
00685 
00686 #ifdef REALLY_NOISY_PUSHING
00687   nsFrame::IndentBy(stdout, mSpanDepth);
00688   printf("PushFrame %p, before:\n", psd);
00689   DumpPerSpanData(psd, 1);
00690 #endif
00691 
00692   // Take the last frame off of the span's frame list
00693   PerFrameData* pfd = psd->mLastFrame;
00694   if (pfd == psd->mFirstFrame) {
00695     // We are pushing away the only frame...empty the list
00696     psd->mFirstFrame = nsnull;
00697     psd->mLastFrame = nsnull;
00698   }
00699   else {
00700     PerFrameData* prevFrame = pfd->mPrev;
00701     prevFrame->mNext = nsnull;
00702     psd->mLastFrame = prevFrame;
00703   }
00704 
00705   // Now free it, and if it has a span, free that too
00706   pfd->mNext = mFrameFreeList;
00707   mFrameFreeList = pfd;
00708 #ifdef DEBUG
00709   mFramesFreed++;
00710 #endif
00711   if (nsnull != pfd->mSpan) {
00712     FreeSpan(pfd->mSpan);
00713   }
00714 #ifdef NOISY_PUSHING
00715   nsFrame::IndentBy(stdout, mSpanDepth);
00716   printf("PushFrame: %p after:\n", psd);
00717   DumpPerSpanData(psd, 1);
00718 #endif
00719 }
00720 
00721 void
00722 nsLineLayout::FreeSpan(PerSpanData* psd)
00723 {
00724   // Free its frames
00725   PerFrameData* pfd = psd->mFirstFrame;
00726   while (nsnull != pfd) {
00727     if (nsnull != pfd->mSpan) {
00728       FreeSpan(pfd->mSpan);
00729     }
00730     PerFrameData* next = pfd->mNext;
00731     pfd->mNext = mFrameFreeList;
00732     mFrameFreeList = pfd;
00733 #ifdef DEBUG
00734     mFramesFreed++;
00735 #endif
00736     pfd = next;
00737   }
00738 
00739   // Now put the span on the free list since its free too
00740   psd->mNextFreeSpan = mSpanFreeList;
00741   mSpanFreeList = psd;
00742 #ifdef DEBUG
00743   mSpansFreed++;
00744 #endif
00745 }
00746 
00747 PRBool
00748 nsLineLayout::IsZeroHeight()
00749 {
00750   PerSpanData* psd = mCurrentSpan;
00751   PerFrameData* pfd = psd->mFirstFrame;
00752   while (nsnull != pfd) {
00753     if (0 != pfd->mBounds.height) {
00754       return PR_FALSE;
00755     }
00756     pfd = pfd->mNext;
00757   }
00758   return PR_TRUE;
00759 }
00760 
00761 nsresult
00762 nsLineLayout::NewPerFrameData(PerFrameData** aResult)
00763 {
00764   PerFrameData* pfd = mFrameFreeList;
00765   if (nsnull == pfd) {
00766     void *mem;
00767     PL_ARENA_ALLOCATE(mem, &mArena, sizeof(PerFrameData));
00768     if (nsnull == mem) {
00769       return NS_ERROR_OUT_OF_MEMORY;
00770     }
00771     pfd = NS_REINTERPRET_CAST(PerFrameData*, mem);
00772   }
00773   else {
00774     mFrameFreeList = pfd->mNext;
00775   }
00776   pfd->mSpan = nsnull;
00777   pfd->mNext = nsnull;
00778   pfd->mPrev = nsnull;
00779   pfd->mFrame = nsnull;
00780   pfd->mFlags = 0;  // all flags default to false
00781 
00782 #ifdef DEBUG
00783   pfd->mVerticalAlign = 0xFF;
00784   mFramesAllocated++;
00785 #endif
00786   *aResult = pfd;
00787   return NS_OK;
00788 }
00789 
00790 PRBool
00791 nsLineLayout::CanPlaceFloatNow() const
00792 {
00793   return GetFlag(LL_CANPLACEFLOAT);
00794 }
00795 
00796 PRBool
00797 nsLineLayout::LineIsBreakable() const
00798 {
00799   if ((0 != mTotalPlacedFrames) || GetFlag(LL_IMPACTEDBYFLOATS)) {
00800     return PR_TRUE;
00801   }
00802   return PR_FALSE;
00803 }
00804 
00805 nsresult
00806 nsLineLayout::ReflowFrame(nsIFrame* aFrame,
00807                           nsReflowStatus& aReflowStatus,
00808                           nsHTMLReflowMetrics* aMetrics,
00809                           PRBool& aPushedFrame)
00810 {
00811   // Initialize OUT parameter
00812   aPushedFrame = PR_FALSE;
00813 
00814   PerFrameData* pfd;
00815   nsresult rv = NewPerFrameData(&pfd);
00816   if (NS_FAILED(rv)) {
00817     return rv;
00818   }
00819   PerSpanData* psd = mCurrentSpan;
00820   psd->AppendFrame(pfd);
00821 
00822 #ifdef REALLY_NOISY_REFLOW
00823   nsFrame::IndentBy(stdout, mSpanDepth);
00824   printf("%p: Begin ReflowFrame pfd=%p ", psd, pfd);
00825   nsFrame::ListTag(stdout, aFrame);
00826   printf("\n");
00827 #endif
00828 
00829   // Compute the available size for the frame. This available width
00830   // includes room for the side margins.
00831   nsSize availSize;
00832   if (NS_UNCONSTRAINEDSIZE == psd->mRightEdge) {
00833     availSize.width = NS_UNCONSTRAINEDSIZE;
00834   }
00835   else {
00836     availSize.width = psd->mRightEdge - psd->mX;
00837     if (psd->mNoWrap) {
00838       // Make up a width to use for reflowing into.  XXX what value to
00839       // use? for tables, we want to limit it; for other elements
00840       // (e.g. text) it can be unlimited...
00841       availSize.width = psd->mReflowState->availableWidth;
00842     }
00843   }
00844   // For now, set the available height to unconstrained always.
00845   // XXX inline blocks and tables won't be able to break across pages/
00846   // columns, but it's not clear how to handle that anyway
00847   availSize.height = NS_UNCONSTRAINEDSIZE;
00848 
00849   // Get reflow reason set correctly. It's possible that a child was
00850   // created and then it was decided that it could not be reflowed
00851   // (for example, a block frame that isn't at the start of a
00852   // line). In this case the reason will be wrong so we need to check
00853   // the frame state.
00854   const nsHTMLReflowState* rs = psd->mReflowState;
00855   nsReflowReason reason = eReflowReason_Resize;
00856   if (NS_FRAME_FIRST_REFLOW & aFrame->GetStateBits()) {
00857     reason = eReflowReason_Initial;
00858   }
00859   else if (rs->reason == eReflowReason_Initial &&
00860            mBlockReflowState->reason == eReflowReason_StyleChange) {
00861     // The frame we're about to reflow is an _old_ frame that was
00862     // pushed inside a _new_ parent (overflow).
00863 
00864     // So we propagate the same 'style change' that led to creating
00865     // the new overflow parent to which this frame is now the child
00866     reason = eReflowReason_StyleChange;
00867   }
00868   else if (rs->reason == eReflowReason_Incremental) { // XXX
00869     // XXXwaterson (above) previously used mBlockReflowState rather
00870     // than psd->mReflowState.
00871 
00872     // If the frame we're about to reflow is on the reflow path, then
00873     // propagate the reflow as `incremental' so it unwinds correctly
00874     // to the target frames below us.
00875     PRBool frameIsOnReflowPath = rs->path->HasChild(aFrame);
00876     if (frameIsOnReflowPath)
00877       reason = eReflowReason_Incremental;
00878 
00879     // But...if the incremental reflow command is a StyleChanged
00880     // reflow and its target is the current span, change the reason
00881     // to `style change', so that it propagates through the entire
00882     // subtree.
00883     nsHTMLReflowCommand* rc = rs->path->mReflowCommand;
00884     if (rc) {
00885       nsReflowType type = rc->Type();
00886       if (type == eReflowType_StyleChanged) {
00887         nsIFrame* parentFrame = psd->mFrame
00888           ? psd->mFrame->mFrame
00889           : mBlockReflowState->frame;
00890         if (rc->GetTarget() == parentFrame) {
00891           reason = eReflowReason_StyleChange;
00892         }
00893       }
00894       else if (type == eReflowType_ReflowDirty &&
00895                (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
00896                !frameIsOnReflowPath) {
00897         reason = eReflowReason_Dirty;
00898       }
00899     }
00900   }
00901   else if (rs->reason == eReflowReason_StyleChange) {
00902     reason = eReflowReason_StyleChange;
00903   }
00904   else if (rs->reason == eReflowReason_Dirty) {
00905     if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY)
00906       reason = eReflowReason_Dirty;
00907   }
00908 
00909   // Setup reflow state for reflowing the frame
00910   nsHTMLReflowState reflowState(mPresContext, *psd->mReflowState,
00911                                 aFrame, availSize, reason);
00912   reflowState.mLineLayout = this;
00913   reflowState.mFlags.mIsTopOfPage = GetFlag(LL_ISTOPOFPAGE);
00914   SetFlag(LL_UNDERSTANDSNWHITESPACE, PR_FALSE);
00915   mTextJustificationNumSpaces = 0;
00916   mTextJustificationNumLetters = 0;
00917 
00918   // Stash copies of some of the computed state away for later
00919   // (vertical alignment, for example)
00920   pfd->mFrame = aFrame;
00921   pfd->mMargin = reflowState.mComputedMargin;
00922   pfd->mBorderPadding = reflowState.mComputedBorderPadding;
00923   pfd->mFrameType = reflowState.mFrameType;
00924   pfd->SetFlag(PFD_RELATIVEPOS,
00925                (reflowState.mStyleDisplay->mPosition == NS_STYLE_POSITION_RELATIVE));
00926   if (pfd->GetFlag(PFD_RELATIVEPOS)) {
00927     pfd->mOffsets = reflowState.mComputedOffsets;
00928   }
00929 
00930   // NOTE: While the x coordinate remains relative to the parent span,
00931   // the y coordinate is fixed at the top edge for the line. During
00932   // VerticalAlignFrames we will repair this so that the y coordinate
00933   // is properly set and relative to the appropriate span.
00934   pfd->mBounds.x = psd->mX;
00935   pfd->mBounds.y = mTopEdge;
00936 
00937   // We want to guarantee that we always make progress when
00938   // formatting. Therefore, if the object being placed on the line is
00939   // too big for the line, but it is the only thing on the line
00940   // (including counting floats) then we go ahead and place it
00941   // anyway. Its also true that if the object is a part of a larger
00942   // object (a multiple frame word) then we will place it on the line
00943   // too.
00944   //
00945   // Capture this state *before* we reflow the frame in case it clears
00946   // the state out. We need to know how to treat the current frame
00947   // when breaking.
00948   PRBool notSafeToBreak = CanPlaceFloatNow() || InWord();
00949 
00950   // Apply start margins (as appropriate) to the frame computing the
00951   // new starting x,y coordinates for the frame.
00952   ApplyStartMargin(pfd, reflowState);
00953 
00954   // Let frame know that are reflowing it. Note that we don't bother
00955   // positioning the frame yet, because we're probably going to end up
00956   // moving it when we do the vertical alignment
00957   nscoord x = pfd->mBounds.x;
00958   nscoord y = pfd->mBounds.y;
00959 
00960   aFrame->WillReflow(mPresContext);
00961 
00962   // Adjust spacemanager coordinate system for the frame. The
00963   // spacemanager coordinates are <b>inside</b> the current spans
00964   // border+padding, but the x/y coordinates are not (recall that
00965   // frame coordinates are relative to the parents origin and that the
00966   // parents border/padding is <b>inside</b> the parent
00967   // frame. Therefore we have to subtract out the parents
00968   // border+padding before translating.
00969   nsHTMLReflowMetrics metrics(mComputeMaxElementWidth);
00970 #ifdef DEBUG
00971   metrics.width = nscoord(0xdeadbeef);
00972   metrics.height = nscoord(0xdeadbeef);
00973   metrics.ascent = nscoord(0xdeadbeef);
00974   metrics.descent = nscoord(0xdeadbeef);
00975   if (mComputeMaxElementWidth) {
00976     metrics.mMaxElementWidth = nscoord(0xdeadbeef);
00977   }
00978 #endif
00979   nscoord tx = x - psd->mReflowState->mComputedBorderPadding.left;
00980   nscoord ty = y - psd->mReflowState->mComputedBorderPadding.top;
00981   mSpaceManager->Translate(tx, ty);
00982 
00983 #ifdef IBMBIDI
00984   PRInt32 start, end;
00985 
00986   if (mPresContext->BidiEnabled()) {
00987     if (aFrame->GetStateBits() & NS_FRAME_IS_BIDI) {
00988       aFrame->GetOffsets(start, end);
00989     }
00990   }
00991 #endif // IBMBIDI
00992 
00993   nsIAtom* frameType = aFrame->GetType();
00994 
00995   rv = aFrame->Reflow(mPresContext, metrics, reflowState, aReflowStatus);
00996   if (NS_FAILED(rv)) {
00997     NS_WARNING( "Reflow of frame failed in nsLineLayout" );
00998     return rv;
00999   }
01000 
01001   pfd->mJustificationNumSpaces = mTextJustificationNumSpaces;
01002   pfd->mJustificationNumLetters = mTextJustificationNumLetters;
01003 
01004   // XXX See if the frame is a placeholderFrame and if it is process
01005   // the float.
01006   if (frameType) {
01007     if (nsLayoutAtoms::placeholderFrame == frameType) {
01008       pfd->SetFlag(PFD_ISPLACEHOLDERFRAME, PR_TRUE);
01009       nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame);
01010       if (outOfFlowFrame) {
01011         nsPlaceholderFrame* placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, aFrame);
01012         PRBool didPlace;
01013         if (eReflowReason_Incremental == reason) {
01014           didPlace = InitFloat(placeholder, aReflowStatus);
01015         }
01016         else {
01017           didPlace = AddFloat(placeholder, aReflowStatus);
01018         }
01019         if (!didPlace) {
01020           aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
01021         }
01022         if (outOfFlowFrame->GetType() == nsLayoutAtoms::letterFrame) {
01023           SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE);
01024         }
01025       }
01026     }
01027     else if (nsLayoutAtoms::textFrame == frameType) {
01028       // Note non-empty text-frames for inline frame compatability hackery
01029       pfd->SetFlag(PFD_ISTEXTFRAME, PR_TRUE);
01030       // XXX An empty text frame at the end of the line seems not
01031       // to have zero width.
01032       if (metrics.width) {
01033         pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, PR_TRUE);
01034         nsIContent* content = pfd->mFrame->GetContent();
01035 
01036         nsCOMPtr<nsITextContent> textContent
01037           = do_QueryInterface(content);
01038         if (textContent) {
01039           pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME,
01040                        !textContent->IsOnlyWhitespace());
01041 // fix for bug 40882
01042 #ifdef IBMBIDI
01043           if (mPresContext->BidiEnabled()) {
01044             const nsTextFragment* frag = textContent->Text();
01045             if (frag->Is2b()) {
01046               //PRBool isVisual;
01047               //mPresContext->IsVisualMode(isVisual);
01048               PRUnichar ch = /*(isVisual) ?
01049                               *(frag->Get2b() + frag->GetLength() - 1) :*/ *frag->Get2b();
01050               if (IS_BIDI_DIACRITIC(ch)) {
01051                 mPresContext->PropertyTable()->SetProperty(aFrame,
01052                            nsLayoutAtoms::endsInDiacritic, NS_INT32_TO_PTR(ch),
01053                                                            nsnull, nsnull);
01054               }
01055             }
01056           }
01057 #endif // IBMBIDI
01058         }
01059       }
01060     }
01061     else if (nsLayoutAtoms::letterFrame==frameType) {
01062       pfd->SetFlag(PFD_ISLETTERFRAME, PR_TRUE);
01063     }
01064   }
01065 
01066   mSpaceManager->Translate(-tx, -ty);
01067 
01068   NS_ASSERTION(metrics.width>=0, "bad width");
01069   NS_ASSERTION(metrics.height>=0,"bad height");
01070   if (metrics.width<0) metrics.width=0;
01071   if (metrics.height<0) metrics.height=0;
01072 
01073 #ifdef DEBUG
01074   // Note: break-before means ignore the reflow metrics since the
01075   // frame will be reflowed another time.
01076   if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
01077     if (CRAZY_WIDTH(metrics.width) || CRAZY_HEIGHT(metrics.height)) {
01078       printf("nsLineLayout: ");
01079       nsFrame::ListTag(stdout, aFrame);
01080       printf(" metrics=%d,%d!\n", metrics.width, metrics.height);
01081     }
01082     if (mComputeMaxElementWidth &&
01083         (nscoord(0xdeadbeef) == metrics.mMaxElementWidth)) {
01084       printf("nsLineLayout: ");
01085       nsFrame::ListTag(stdout, aFrame);
01086       printf(" didn't set max-element-width!\n");
01087     }
01088 #ifdef REALLY_NOISY_MAX_ELEMENT_SIZE
01089     // Note: there are common reflow situations where this *correctly*
01090     // occurs; so only enable this debug noise when you really need to
01091     // analyze in detail.
01092     if (mComputeMaxElementWidth &&
01093         (metrics.mMaxElementWidth > metrics.width)) {
01094       printf("nsLineLayout: ");
01095       nsFrame::ListTag(stdout, aFrame);
01096       printf(": WARNING: maxElementWidth=%d > metrics=%d\n",
01097              metrics.mMaxElementWidth, metrics.width);
01098     }
01099 #endif
01100     if ((metrics.width == nscoord(0xdeadbeef)) ||
01101         (metrics.height == nscoord(0xdeadbeef)) ||
01102         (metrics.ascent == nscoord(0xdeadbeef)) ||
01103         (metrics.descent == nscoord(0xdeadbeef))) {
01104       printf("nsLineLayout: ");
01105       nsFrame::ListTag(stdout, aFrame);
01106       printf(" didn't set whad %d,%d,%d,%d!\n", metrics.width, metrics.height,
01107              metrics.ascent, metrics.descent);
01108     }
01109   }
01110 #endif
01111 #ifdef DEBUG
01112   if (nsBlockFrame::gNoisyMaxElementWidth) {
01113     nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
01114     if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
01115       if (mComputeMaxElementWidth) {
01116         printf("  ");
01117         nsFrame::ListTag(stdout, aFrame);
01118         printf(": maxElementWidth=%d wh=%d,%d,\n",
01119                metrics.mMaxElementWidth,
01120                metrics.width, metrics.height);
01121       }
01122     }
01123   }
01124 #endif
01125 
01126   // Unlike with non-inline reflow, the overflow area here does *not*
01127   // include the accumulation of the frame's bounds and its inline
01128   // descendants' bounds. Nor does it include the outline area; it's
01129   // just the union of the bounds of any absolute children. That is
01130   // added in later by nsLineLayout::ReflowInlineFrames.
01131   pfd->mCombinedArea = metrics.mOverflowArea;
01132 
01133   pfd->mBounds.width = metrics.width;
01134   pfd->mBounds.height = metrics.height;
01135   if (mComputeMaxElementWidth) {
01136     pfd->mMaxElementWidth = metrics.mMaxElementWidth;
01137   }
01138 
01139   // Size the frame, but |RelativePositionFrames| will size the view.
01140   aFrame->SetSize(nsSize(metrics.width, metrics.height));
01141 
01142   // Tell the frame that we're done reflowing it
01143   aFrame->DidReflow(mPresContext, &reflowState, NS_FRAME_REFLOW_FINISHED);
01144 
01145   if (aMetrics) {
01146     *aMetrics = metrics;
01147   }
01148 
01149   if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
01150     // If frame is complete and has a next-in-flow, we need to delete
01151     // them now. Do not do this when a break-before is signaled because
01152     // the frame is going to get reflowed again (and may end up wanting
01153     // a next-in-flow where it ends up).
01154     if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
01155       nsIFrame* kidNextInFlow = aFrame->GetNextInFlow();
01156       if (nsnull != kidNextInFlow) {
01157         // Remove all of the childs next-in-flows. Make sure that we ask
01158         // the right parent to do the removal (it's possible that the
01159         // parent is not this because we are executing pullup code)
01160         nsHTMLContainerFrame* parent = NS_STATIC_CAST(nsHTMLContainerFrame*,
01161                                                       kidNextInFlow->GetParent());
01162         parent->DeleteNextInFlowChild(mPresContext, kidNextInFlow);
01163       }
01164     }
01165 
01166     // See if we can place the frame. If we can't fit it, then we
01167     // return now.
01168     if (CanPlaceFrame(pfd, reflowState, notSafeToBreak, metrics, aReflowStatus)) {
01169       // Place the frame, updating aBounds with the final size and
01170       // location.  Then apply the bottom+right margins (as
01171       // appropriate) to the frame.
01172       PlaceFrame(pfd, metrics);
01173       PerSpanData* span = pfd->mSpan;
01174       if (span) {
01175         // The frame we just finished reflowing is an inline
01176         // container.  It needs its child frames vertically aligned,
01177         // so do most of it now.
01178         VerticalAlignFrames(span);
01179       }
01180     }
01181     else {
01182       PushFrame(aFrame);
01183       aPushedFrame = PR_TRUE;
01184     }
01185   }
01186   else {
01187     PushFrame(aFrame);
01188   }
01189 
01190 #ifdef REALLY_NOISY_REFLOW
01191   nsFrame::IndentBy(stdout, mSpanDepth);
01192   printf("End ReflowFrame ");
01193   nsFrame::ListTag(stdout, aFrame);
01194   printf(" status=%x\n", aReflowStatus);
01195 #endif
01196 
01197   if (aFrame->GetStateBits() & NS_FRAME_IS_BIDI) {
01198     // Since aReflowStatus may change, check it at the end
01199     if (NS_INLINE_IS_BREAK_BEFORE(aReflowStatus) ) {
01200       aFrame->AdjustOffsetsForBidi(start, end);
01201     }
01202     else if (!NS_FRAME_IS_COMPLETE(aReflowStatus) ) {
01203       PRInt32 newEnd;
01204       aFrame->GetOffsets(start, newEnd);
01205       if (newEnd != end) {
01206         nsIFrame* nextInFlow = aFrame->GetNextInFlow();
01207         if (nextInFlow) {
01208           nextInFlow->GetOffsets(start, end);
01209           nextInFlow->AdjustOffsetsForBidi(newEnd, end);
01210         } // nextInFlow
01211       } // newEnd != end
01212     } // !NS_FRAME_IS_COMPLETE(aReflowStatus)
01213   } // isBidiFrame
01214 
01215   return rv;
01216 }
01217 
01218 void
01219 nsLineLayout::ApplyStartMargin(PerFrameData* pfd,
01220                                nsHTMLReflowState& aReflowState)
01221 {
01222   NS_ASSERTION(aReflowState.mStyleDisplay->mFloats == NS_STYLE_FLOAT_NONE,
01223                "How'd we get a floated inline frame? "
01224                "The frame ctor should've dealt with this.");
01225 
01226   // XXXwaterson probably not the right way to get this; e.g., embeddings, etc.
01227   PRBool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection);
01228 
01229   // Only apply start-margin on the first-in flow for inline frames
01230   if (HasPrevInFlow(pfd->mFrame)) {
01231     // Zero this out so that when we compute the max-element-width of
01232     // the frame we will properly avoid adding in the starting margin.
01233     if (ltr)
01234       pfd->mMargin.left = 0;
01235     else
01236       pfd->mMargin.right = 0;
01237   }
01238 
01239   if (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth){
01240     // Adjust available width to account for the left margin. The
01241     // right margin will be accounted for when we finish flowing the
01242     // frame.
01243     aReflowState.availableWidth -= ltr ? pfd->mMargin.left : pfd->mMargin.right;
01244   }
01245 
01246   if (ltr)
01247     pfd->mBounds.x += pfd->mMargin.left;
01248 }
01249 
01260 PRBool
01261 nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
01262                             const nsHTMLReflowState& aReflowState,
01263                             PRBool aNotSafeToBreak,
01264                             nsHTMLReflowMetrics& aMetrics,
01265                             nsReflowStatus& aStatus)
01266 {
01267   NS_PRECONDITION(pfd && pfd->mFrame, "bad args, null pointers for frame data");
01268   // Compute right margin to use
01269   if (0 != pfd->mBounds.width) {
01270     NS_ASSERTION(aReflowState.mStyleDisplay->mFloats == NS_STYLE_FLOAT_NONE,
01271                  "How'd we get a floated inline frame? "
01272                  "The frame ctor should've dealt with this.");
01273 
01274     // XXXwaterson this is probably not exactly right; e.g., embeddings, etc.
01275     PRBool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection);
01276 
01277     if (NS_FRAME_IS_NOT_COMPLETE(aStatus) && !pfd->GetFlag(PFD_ISLETTERFRAME)) {
01278       // Only apply end margin for the last-in-flow. Zero this out so
01279       // that when we compute the max-element-width of the frame we
01280       // will properly avoid adding in the end margin.
01281       if (ltr)
01282         pfd->mMargin.right = 0;
01283       else
01284         pfd->mMargin.left = 0;
01285     }
01286   }
01287   else {
01288     // Don't apply margin to empty frames.
01289     pfd->mMargin.left = pfd->mMargin.right = 0;
01290   }
01291 
01292   PerSpanData* psd = mCurrentSpan;
01293   if (psd->mNoWrap) {
01294     // When wrapping is off, everything fits.
01295     return PR_TRUE;
01296   }
01297 
01298 #ifdef NOISY_CAN_PLACE_FRAME
01299   if (nsnull != psd->mFrame) {
01300     nsFrame::ListTag(stdout, psd->mFrame->mFrame);
01301   }
01302   else {
01303     nsFrame::ListTag(stdout, mBlockReflowState->frame);
01304   } 
01305   printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak ? "true" : "false");
01306   nsFrame::ListTag(stdout, pfd->mFrame);
01307   printf(" frameWidth=%d\n", pfd->mBounds.XMost() + rightMargin - psd->mX);
01308 #endif
01309 
01310   // Set outside to PR_TRUE if the result of the reflow leads to the
01311   // frame sticking outside of our available area.
01312   PRBool outside = pfd->mBounds.XMost() + pfd->mMargin.right > psd->mRightEdge;
01313   if (!outside) {
01314     // If it fits, it fits
01315 #ifdef NOISY_CAN_PLACE_FRAME
01316     printf("   ==> inside\n");
01317 #endif
01318     return PR_TRUE;
01319   }
01320 
01321   // When it doesn't fit, check for a few special conditions where we
01322   // allow it to fit anyway.
01323   if (0 == pfd->mMargin.left + pfd->mBounds.width + pfd->mMargin.right) {
01324     // Empty frames always fit right where they are
01325 #ifdef NOISY_CAN_PLACE_FRAME
01326     printf("   ==> empty frame fits\n");
01327 #endif
01328     return PR_TRUE;
01329   }
01330 
01331 #ifdef FIX_BUG_50257
01332   // another special case:  always place a BR
01333   if (nsLayoutAtoms::brFrame == pfd->mFrame->GetType()) {
01334 #ifdef NOISY_CAN_PLACE_FRAME
01335     printf("   ==> BR frame fits\n");
01336 #endif
01337     return PR_TRUE;
01338   }
01339 #endif
01340 
01341   if (aNotSafeToBreak) {
01342     // There are no frames on the line or we are in the first word on
01343     // the line. If the line isn't impacted by a float then the
01344     // current frame fits.
01345     if (!GetFlag(LL_IMPACTEDBYFLOATS)) {
01346 #ifdef NOISY_CAN_PLACE_FRAME
01347       printf("   ==> not-safe and not-impacted fits: ");
01348       while (nsnull != psd) {
01349         printf("<psd=%p x=%d left=%d> ", psd, psd->mX, psd->mLeftEdge);
01350         psd = psd->mParent;
01351       }
01352       printf("\n");
01353 #endif
01354       return PR_TRUE;
01355     }
01356     else if (GetFlag(LL_LASTFLOATWASLETTERFRAME)) {
01357       // Another special case: see if the float is a letter
01358       // frame. If it is, then allow the frame next to it to fit.
01359       if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) {
01360         // This must be the first piece of non-empty text (because
01361         // aNotSafeToBreak is true) or its a piece of text that is
01362         // part of a larger word.
01363         pfd->SetFlag(PFD_ISSTICKY, PR_TRUE);
01364       }
01365       else if (pfd->mSpan) {
01366         PerFrameData* pf = pfd->mSpan->mFirstFrame;
01367         while (pf) {
01368           if (pf->GetFlag(PFD_ISSTICKY)) {
01369             // If one of the spans children was sticky then the span
01370             // itself is sticky.
01371             pfd->SetFlag(PFD_ISSTICKY, PR_TRUE);
01372           }
01373           pf = pf->mNext;
01374         }
01375       }
01376 
01377       if (pfd->GetFlag(PFD_ISSTICKY)) {
01378 #ifdef NOISY_CAN_PLACE_FRAME
01379         printf("   ==> last float was letter frame && frame is sticky\n");
01380 #endif
01381         return PR_TRUE;
01382       }
01383     }
01384   }
01385 
01386   // If this is a piece of text inside a letter frame...
01387   if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) {
01388     if (psd->mFrame && psd->mFrame->GetFlag(PFD_ISLETTERFRAME)) {
01389       nsIFrame* prevInFlow = psd->mFrame->mFrame->GetPrevInFlow();
01390       if (prevInFlow) {
01391         nsIFrame* prevPrevInFlow = prevInFlow->GetPrevInFlow();
01392         if (!prevPrevInFlow) {
01393           // And it's the first continuation of the letter frame...
01394           // Then make sure that the text fits
01395           return PR_TRUE;
01396         }
01397       }
01398     }
01399   }
01400   else if (pfd->GetFlag(PFD_ISLETTERFRAME)) {
01401     // If this is the first continuation of the letter frame...
01402     nsIFrame* prevInFlow = pfd->mFrame->GetPrevInFlow();
01403     if (prevInFlow) {
01404       nsIFrame* prevPrevInFlow = prevInFlow->GetPrevInFlow();
01405       if (!prevPrevInFlow) {
01406         return PR_TRUE;
01407       }
01408     }
01409   }
01410 
01411   // Special check for span frames
01412   if (pfd->mSpan && pfd->mSpan->mContainsFloat) {
01413     // If the span either directly or indirectly contains a float then
01414     // it fits. Why? It's kind of complicated, but here goes:
01415     //
01416     // 1. CanPlaceFrame is used for all frame placements on a line,
01417     // and in a span. This includes recursively placement of frames
01418     // inside of spans, and the span itself. Because the logic always
01419     // checks for room before proceeding (the code above here), the
01420     // only things on a line will be those things that "fit".
01421     //
01422     // 2. Before a float is placed on a line, the line has to be empty
01423     // (otherwise its a "below current line" flaoter and will be placed
01424     // after the line).
01425     //
01426     // Therefore, if the span directly or indirectly has a float
01427     // then it means that at the time of the placement of the float
01428     // the line was empty. Because of #1, only the frames that fit can
01429     // be added after that point, therefore we can assume that the
01430     // current span being placed has fit.
01431     //
01432     // So how do we get here and have a span that should already fit
01433     // and yet doesn't: Simple: span's that have the no-wrap attribute
01434     // set on them and contain a float and are placed where they
01435     // don't naturally fit.
01436     return PR_TRUE;
01437  }
01438 
01439 #ifdef NOISY_CAN_PLACE_FRAME
01440   printf("   ==> didn't fit\n");
01441 #endif
01442   aStatus = NS_INLINE_LINE_BREAK_BEFORE();
01443   return PR_FALSE;
01444 }
01445 
01449 void
01450 nsLineLayout::PlaceFrame(PerFrameData* pfd, nsHTMLReflowMetrics& aMetrics)
01451 {
01452   // If frame is zero width then do not apply its left and right margins.
01453   PerSpanData* psd = mCurrentSpan;
01454   PRBool emptyFrame = PR_FALSE;
01455   if ((0 == pfd->mBounds.width) && (0 == pfd->mBounds.height)) {
01456     pfd->mBounds.x = psd->mX;
01457     pfd->mBounds.y = mTopEdge;
01458     emptyFrame = PR_TRUE;
01459   }
01460 
01461   // Record ascent and update max-ascent and max-descent values
01462   pfd->mAscent = aMetrics.ascent;
01463   pfd->mDescent = aMetrics.descent;
01464 
01465   // If the band was updated during the reflow of that frame then we
01466   // need to adjust any prior frames that were reflowed.
01467   if (GetFlag(LL_UPDATEDBAND) && InBlockContext()) {
01468     UpdateFrames();
01469     SetFlag(LL_UPDATEDBAND, PR_FALSE);
01470   }
01471 
01472   // Advance to next X coordinate
01473   psd->mX = pfd->mBounds.XMost() + pfd->mMargin.right;
01474   psd->mRightEdge = PR_MAX(psd->mRightEdge, psd->mX);
01475 
01476   // If the frame is a not aware of white-space and it takes up some
01477   // width, disable leading white-space compression for the next frame
01478   // to be reflowed.
01479   if ((!GetFlag(LL_UNDERSTANDSNWHITESPACE)) && pfd->mBounds.width) {
01480     SetFlag(LL_ENDSINWHITESPACE, PR_FALSE);
01481   }
01482 
01483   // Count the number of non-empty frames on the line...
01484   if (!emptyFrame) {
01485     mTotalPlacedFrames++;
01486   }
01487   if (psd->mX != psd->mLeftEdge || pfd->mBounds.x != psd->mLeftEdge) {
01488     // As soon as a frame placed on the line advances an X coordinate
01489     // of any span we can no longer place a float on the line.
01490     SetFlag(LL_CANPLACEFLOAT, PR_FALSE);
01491   }
01492 }
01493 
01494 nsresult
01495 nsLineLayout::AddBulletFrame(nsIFrame* aFrame,
01496                              const nsHTMLReflowMetrics& aMetrics)
01497 {
01498   NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user");
01499 
01500   PerFrameData* pfd;
01501   nsresult rv = NewPerFrameData(&pfd);
01502   if (NS_SUCCEEDED(rv)) {
01503     mRootSpan->AppendFrame(pfd);
01504     pfd->mFrame = aFrame;
01505     pfd->mMargin.SizeTo(0, 0, 0, 0);
01506     pfd->mBorderPadding.SizeTo(0, 0, 0, 0);
01507     pfd->mFrameType = NS_CSS_FRAME_TYPE_INLINE|NS_FRAME_REPLACED_ELEMENT;
01508     pfd->mFlags = 0;  // all flags default to false
01509     pfd->SetFlag(PFD_ISBULLET, PR_TRUE);
01510     pfd->mAscent = aMetrics.ascent;
01511     pfd->mDescent = aMetrics.descent;
01512 
01513     // Note: y value will be updated during vertical alignment
01514     pfd->mBounds = aFrame->GetRect();
01515     pfd->mCombinedArea = aMetrics.mOverflowArea;
01516     if (mComputeMaxElementWidth) {
01517       pfd->mMaxElementWidth = aMetrics.width;
01518     }
01519   }
01520   return rv;
01521 }
01522 
01523 #ifdef DEBUG
01524 void
01525 nsLineLayout::DumpPerSpanData(PerSpanData* psd, PRInt32 aIndent)
01526 {
01527   nsFrame::IndentBy(stdout, aIndent);
01528   printf("%p: left=%d x=%d right=%d\n", NS_STATIC_CAST(void*, psd),
01529          psd->mLeftEdge, psd->mX, psd->mRightEdge);
01530   PerFrameData* pfd = psd->mFirstFrame;
01531   while (nsnull != pfd) {
01532     nsFrame::IndentBy(stdout, aIndent+1);
01533     nsFrame::ListTag(stdout, pfd->mFrame);
01534     printf(" %d,%d,%d,%d\n", pfd->mBounds.x, pfd->mBounds.y,
01535            pfd->mBounds.width, pfd->mBounds.height);
01536     if (pfd->mSpan) {
01537       DumpPerSpanData(pfd->mSpan, aIndent + 1);
01538     }
01539     pfd = pfd->mNext;
01540   }
01541 }
01542 #endif
01543 
01544 #define VALIGN_OTHER  0
01545 #define VALIGN_TOP    1
01546 #define VALIGN_BOTTOM 2
01547 
01548 PRBool
01549 nsLineLayout::IsPercentageUnitSides(const nsStyleSides* aSides)
01550 {
01551   return eStyleUnit_Percent == aSides->GetLeftUnit()
01552       || eStyleUnit_Percent == aSides->GetRightUnit()
01553       || eStyleUnit_Percent == aSides->GetTopUnit()
01554       || eStyleUnit_Percent == aSides->GetBottomUnit();
01555 }
01556 
01557 PRBool
01558 nsLineLayout::IsPercentageAwareReplacedElement(nsPresContext *aPresContext, 
01559                                                nsIFrame* aFrame)
01560 {
01561   if (aFrame->GetStateBits() & NS_FRAME_REPLACED_ELEMENT)
01562   {
01563     nsIAtom* frameType = aFrame->GetType();
01564     if (nsLayoutAtoms::brFrame != frameType && 
01565         nsLayoutAtoms::textFrame != frameType)
01566     {
01567       const nsStyleMargin* margin = aFrame->GetStyleMargin();
01568       if (IsPercentageUnitSides(&margin->mMargin)) {
01569         return PR_TRUE;
01570       }
01571 
01572       const nsStylePadding* padding = aFrame->GetStylePadding();
01573       if (IsPercentageUnitSides(&padding->mPadding)) {
01574         return PR_TRUE;
01575       }
01576 
01577       // Borders aren't percentage aware
01578 
01579       const nsStylePosition* pos = aFrame->GetStylePosition();
01580       if (eStyleUnit_Percent == pos->mWidth.GetUnit()
01581         || eStyleUnit_Percent == pos->mMaxWidth.GetUnit()
01582         || eStyleUnit_Percent == pos->mMinWidth.GetUnit()
01583         || eStyleUnit_Percent == pos->mHeight.GetUnit()
01584         || eStyleUnit_Percent == pos->mMinHeight.GetUnit()
01585         || eStyleUnit_Percent == pos->mMaxHeight.GetUnit()
01586         || IsPercentageUnitSides(&pos->mOffset)) { // XXX need more here!!!
01587         return PR_TRUE;
01588       }
01589     }
01590   }
01591 
01592   return PR_FALSE;
01593 }
01594 
01595 PRBool IsPercentageAwareFrame(nsPresContext *aPresContext, nsIFrame *aFrame)
01596 {
01597   if (aFrame->GetStateBits() & NS_FRAME_REPLACED_ELEMENT) {
01598     if (nsLineLayout::IsPercentageAwareReplacedElement(aPresContext, aFrame)) {
01599       return PR_TRUE;
01600     }
01601   }
01602   else
01603   {
01604     nsIFrame *child = aFrame->GetFirstChild(nsnull);
01605     if (child)
01606     { // aFrame is an inline container frame, check my frame state
01607       if (aFrame->GetStateBits() & NS_INLINE_FRAME_CONTAINS_PERCENT_AWARE_CHILD) {
01608         return PR_TRUE;
01609       }
01610     }
01611     // else it's a frame we just don't care about
01612   }  
01613   return PR_FALSE;
01614 }
01615 
01616 
01617 void
01618 nsLineLayout::VerticalAlignLine(nsLineBox* aLineBox,
01619                                 nscoord* aMaxElementWidthResult)
01620 {
01621   // Synthesize a PerFrameData for the block frame
01622   PerFrameData rootPFD;
01623   rootPFD.mFrame = mBlockReflowState->frame;
01624   rootPFD.mFrameType = mBlockReflowState->mFrameType;
01625   rootPFD.mAscent = 0;
01626   rootPFD.mDescent = 0;
01627   mRootSpan->mFrame = &rootPFD;
01628   mLineBox = aLineBox;
01629 
01630   // Partially place the children of the block frame. The baseline for
01631   // this operation is set to zero so that the y coordinates for all
01632   // of the placed children will be relative to there.
01633   PerSpanData* psd = mRootSpan;
01634   VerticalAlignFrames(psd);
01635 
01636   // Compute the line-height. The line-height will be the larger of:
01637   //
01638   // [1] maxY - minY (the distance between the highest childs top edge
01639   // and the lowest childs bottom edge)
01640   //
01641   // [2] the maximum logical box height (since not every frame may have
01642   // participated in #1; for example: top/bottom aligned frames)
01643   //
01644   // [3] the minimum line height (line-height property set on the
01645   // block frame)
01646   nscoord lineHeight = psd->mMaxY - psd->mMinY;
01647 
01648   // Now that the line-height is computed, we need to know where the
01649   // baseline is in the line. Position baseline so that mMinY is just
01650   // inside the top of the line box.
01651   nscoord baselineY;
01652   if (psd->mMinY < 0) {
01653     baselineY = mTopEdge - psd->mMinY;
01654   }
01655   else {
01656     baselineY = mTopEdge;
01657   }
01658 
01659   // It's also possible that the line-height isn't tall enough because
01660   // of top/bottom aligned elements that were not accounted for in
01661   // min/max Y.
01662   //
01663   // The CSS2 spec doesn't really say what happens when to the
01664   // baseline in this situations. What we do is if the largest top
01665   // aligned box height is greater than the line-height then we leave
01666   // the baseline alone. If the largest bottom aligned box is greater
01667   // than the line-height then we slide the baseline down by the extra
01668   // amount.
01669   //
01670   // Navigator 4 gives precedence to the first top/bottom aligned
01671   // object.  We just let bottom aligned objects win.
01672   if (lineHeight < mMaxBottomBoxHeight) {
01673     // When the line is shorter than the maximum top aligned box
01674     nscoord extra = mMaxBottomBoxHeight - lineHeight;
01675     baselineY += extra;
01676     lineHeight = mMaxBottomBoxHeight;
01677   }
01678   if (lineHeight < mMaxTopBoxHeight) {
01679     lineHeight = mMaxTopBoxHeight;
01680   }
01681 #ifdef NOISY_VERTICAL_ALIGN
01682   printf("  [line]==> lineHeight=%d baselineY=%d\n", lineHeight, baselineY);
01683 #endif
01684 
01685   // Now position all of the frames in the root span. We will also
01686   // recurse over the child spans and place any top/bottom aligned
01687   // frames we find.
01688   // XXX PERFORMANCE: set a bit per-span to avoid the extra work
01689   // (propagate it upward too)
01690   PerFrameData* pfd = psd->mFirstFrame;
01691   nscoord maxElementWidth = 0;
01692   PRBool prevFrameAccumulates = PR_FALSE;
01693   nscoord accumulatedWidth = 0;
01694 #ifdef HACK_MEW
01695   PRBool strictMode = InStrictMode();
01696   PRBool inUnconstrainedTable = InUnconstrainedTableCell(*mBlockReflowState);
01697 #endif
01698 #ifdef DEBUG
01699   int frameCount = 0;
01700 #endif
01701 
01702   nscoord indent = mTextIndent; // Used for the first frame.
01703 
01704   while (nsnull != pfd) {
01705 
01706     // Compute max-element-width if necessary
01707     if (mComputeMaxElementWidth) {
01708 
01709       nscoord mw = pfd->mMaxElementWidth + indent;
01710       // add only fixed margins to the MEW
01711       if (pfd->mMargin.left) {
01712         if (pfd->mFrame->GetStyleMargin()->mMargin.GetLeftUnit() ==
01713             eStyleUnit_Coord)
01714           mw += pfd->mMargin.left;
01715       }
01716       if (pfd->mMargin.right) {
01717         if (pfd->mFrame->GetStyleMargin()->mMargin.GetRightUnit() ==
01718             eStyleUnit_Coord)
01719           mw += pfd->mMargin.right;
01720       }
01721 
01722       // Zero |indent| after including the 'text-indent' only for the
01723       // frame that is indented.
01724       indent = 0;
01725 
01726       if (psd->mNoWrap) {
01727         maxElementWidth += mw;
01728       }
01729       else {
01730 
01731 #ifdef HACK_MEW
01732 
01733 #ifdef DEBUG
01734       if (nsBlockFrame::gNoisyMaxElementWidth) 
01735         frameCount++;
01736 #endif
01737         // if in Quirks mode and in a table cell with an unconstrained width, then emulate an IE
01738         // quirk to keep consecutive images from breaking the line
01739         // - see bugs 54565, 32191, and their many dups
01740         // XXX - reconsider how textFrame text measurement happens and have it take into account
01741         //       image frames as well, thus eliminating the need for this code
01742         if (!strictMode && inUnconstrainedTable ) {
01743 
01744           nscoord imgSizes = AccumulateImageSizes(*mPresContext, *pfd->mFrame);
01745           PRBool curFrameAccumulates = (imgSizes > 0) || 
01746                                        (pfd->mMaxElementWidth == pfd->mBounds.width &&
01747                                         pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME));
01748             // NOTE: we check for the maxElementWidth == the boundsWidth to detect when
01749             //       a textframe has whitespace in it and thus should not be used as the basis
01750             //       for accumulating the image width
01751             // - this is to handle images in a text run
01752 
01753           if(prevFrameAccumulates && curFrameAccumulates) {
01754             accumulatedWidth += mw;
01755           } else {
01756             accumulatedWidth = mw;
01757           } 
01758           // now update the prevFrame
01759           prevFrameAccumulates = curFrameAccumulates;
01760         
01761 #ifdef DEBUG
01762           if (nsBlockFrame::gNoisyMaxElementWidth) {
01763             nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent);
01764             printf("(%d) last frame's MEW=%d | Accumulated MEW=%d\n", frameCount, mw, accumulatedWidth);
01765           }
01766 #endif 
01767 
01768           mw = accumulatedWidth;
01769         }
01770 
01771 #endif // HACK_MEW
01772 
01773         // and finally reset the max element width
01774         if (maxElementWidth < mw) {
01775           maxElementWidth = mw;
01776         }
01777       }
01778     }
01779     PerSpanData* span = pfd->mSpan;
01780 #ifdef DEBUG
01781     NS_ASSERTION(0xFF != pfd->mVerticalAlign, "umr");
01782 #endif
01783     switch (pfd->mVerticalAlign) {
01784       case VALIGN_TOP:
01785         if (span) {
01786           pfd->mBounds.y = mTopEdge - pfd->mBorderPadding.top +
01787             span->mTopLeading;
01788         }
01789         else {
01790           pfd->mBounds.y = mTopEdge + pfd->mMargin.top;
01791         }
01792         break;
01793       case VALIGN_BOTTOM:
01794         if (span) {
01795           // Compute bottom leading
01796           pfd->mBounds.y = mTopEdge + lineHeight -
01797             pfd->mBounds.height + pfd->mBorderPadding.bottom -
01798             span->mBottomLeading;
01799         }
01800         else {
01801           pfd->mBounds.y = mTopEdge + lineHeight - pfd->mMargin.bottom -
01802             pfd->mBounds.height;
01803         }
01804         break;
01805       case VALIGN_OTHER:
01806         pfd->mBounds.y += baselineY;
01807         break;
01808     }
01809     pfd->mFrame->SetRect(pfd->mBounds);
01810 #ifdef NOISY_VERTICAL_ALIGN
01811     printf("  [child of line]");
01812     nsFrame::ListTag(stdout, pfd->mFrame);
01813     printf(": y=%d\n", pfd->mBounds.y);
01814 #endif
01815     if (span) {
01816       nscoord distanceFromTop = pfd->mBounds.y - mTopEdge;
01817       PlaceTopBottomFrames(span, distanceFromTop, lineHeight);
01818     }
01819     // check to see if the frame is an inline replace element
01820     // and if it is percent-aware.  If so, mark the line.
01821     if ((PR_FALSE==aLineBox->ResizeReflowOptimizationDisabled()) &&
01822          pfd->mFrameType & NS_CSS_FRAME_TYPE_INLINE)
01823     {
01824       if (IsPercentageAwareFrame(mPresContext, pfd->mFrame))
01825         aLineBox->DisableResizeReflowOptimization();
01826     }
01827     pfd = pfd->mNext;
01828   }
01829 
01830   // Fill in returned line-box and max-element-width data
01831   aLineBox->mBounds.x = psd->mLeftEdge;
01832   aLineBox->mBounds.y = mTopEdge;
01833   aLineBox->mBounds.width = psd->mX - psd->mLeftEdge;
01834   aLineBox->mBounds.height = lineHeight;
01835   mFinalLineHeight = lineHeight;
01836   *aMaxElementWidthResult = maxElementWidth;
01837   aLineBox->SetAscent(baselineY - mTopEdge);
01838 #ifdef NOISY_VERTICAL_ALIGN
01839   printf(
01840     "  [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d mew=%d\n",
01841     aLineBox->mBounds.x, aLineBox->mBounds.y,
01842     aLineBox->mBounds.width, aLineBox->mBounds.height,
01843     mFinalLineHeight, aLineBox->GetAscent(),
01844     *aMaxElementWidthResult);
01845 #endif
01846 
01847   // Undo root-span mFrame pointer to prevent brane damage later on...
01848   mRootSpan->mFrame = nsnull;
01849   mLineBox = nsnull;
01850 }
01851 
01852 void
01853 nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd,
01854                                    nscoord aDistanceFromTop,
01855                                    nscoord aLineHeight)
01856 {
01857   PerFrameData* pfd = psd->mFirstFrame;
01858   while (nsnull != pfd) {
01859     PerSpanData* span = pfd->mSpan;
01860 #ifdef DEBUG
01861     NS_ASSERTION(0xFF != pfd->mVerticalAlign, "umr");
01862 #endif
01863     switch (pfd->mVerticalAlign) {
01864       case VALIGN_TOP:
01865         if (span) {
01866           pfd->mBounds.y = -aDistanceFromTop - pfd->mBorderPadding.top +
01867             span->mTopLeading;
01868         }
01869         else {
01870           pfd->mBounds.y = -aDistanceFromTop + pfd->mMargin.top;
01871         }
01872         pfd->mFrame->SetRect(pfd->mBounds);
01873 #ifdef NOISY_VERTICAL_ALIGN
01874         printf("    ");
01875         nsFrame::ListTag(stdout, pfd->mFrame);
01876         printf(": y=%d dTop=%d [bp.top=%d topLeading=%d]\n",
01877                pfd->mBounds.y, aDistanceFromTop,
01878                span ? pfd->mBorderPadding.top : 0,
01879                span ? span->mTopLeading : 0);
01880 #endif
01881         break;
01882       case VALIGN_BOTTOM:
01883         if (span) {
01884           // Compute bottom leading
01885           pfd->mBounds.y = -aDistanceFromTop + aLineHeight -
01886             pfd->mBounds.height + pfd->mBorderPadding.bottom -
01887             span->mBottomLeading;
01888         }
01889         else {
01890           pfd->mBounds.y = -aDistanceFromTop + aLineHeight -
01891             pfd->mMargin.bottom - pfd->mBounds.height;
01892         }
01893         pfd->mFrame->SetRect(pfd->mBounds);
01894 #ifdef NOISY_VERTICAL_ALIGN
01895         printf("    ");
01896         nsFrame::ListTag(stdout, pfd->mFrame);
01897         printf(": y=%d\n", pfd->mBounds.y);
01898 #endif
01899         break;
01900     }
01901     if (span) {
01902       nscoord distanceFromTop = aDistanceFromTop + pfd->mBounds.y;
01903       PlaceTopBottomFrames(span, distanceFromTop, aLineHeight);
01904     }
01905     pfd = pfd->mNext;
01906   }
01907 }
01908 
01909 #define VERTICAL_ALIGN_FRAMES_NO_MINIMUM 32767
01910 #define VERTICAL_ALIGN_FRAMES_NO_MAXIMUM -32768
01911 
01912 // Vertically place frames within a given span. Note: this doesn't
01913 // place top/bottom aligned frames as those have to wait until the
01914 // entire line box height is known. This is called after the span
01915 // frame has finished being reflowed so that we know its height.
01916 void
01917 nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
01918 {
01919   // Get parent frame info
01920   PerFrameData* spanFramePFD = psd->mFrame;
01921   nsIFrame* spanFrame = spanFramePFD->mFrame;
01922 
01923   // Get the parent frame's font for all of the frames in this span
01924   nsStyleContext* styleContext = spanFrame->GetStyleContext();
01925   nsIRenderingContext* rc = mBlockReflowState->rendContext;
01926   SetFontFromStyle(mBlockReflowState->rendContext, styleContext);
01927   nsCOMPtr<nsIFontMetrics> fm;
01928   rc->GetFontMetrics(*getter_AddRefs(fm));
01929 
01930   PRBool preMode = (mStyleText->mWhiteSpace == NS_STYLE_WHITESPACE_PRE) ||
01931     (mStyleText->mWhiteSpace == NS_STYLE_WHITESPACE_MOZ_PRE_WRAP);
01932 
01933   // See if the span is an empty continuation. It's an empty continuation iff:
01934   // - it has a prev-in-flow
01935   // - it has no next in flow
01936   // - it's zero sized
01937   nsIFrame* spanNextInFlow = spanFrame->GetNextInFlow();
01938   nsIFrame* spanPrevInFlow = spanFrame->GetPrevInFlow();
01939   PRBool emptyContinuation = spanPrevInFlow && !spanNextInFlow &&
01940     (0 == spanFramePFD->mBounds.width) && (0 == spanFramePFD->mBounds.height);
01941 
01942 #ifdef NOISY_VERTICAL_ALIGN
01943   printf("[%sSpan]", (psd == mRootSpan)?"Root":"");
01944   nsFrame::ListTag(stdout, spanFrame);
01945   printf(": preMode=%s strictMode=%s w/h=%d,%d emptyContinuation=%s",
01946          preMode ? "yes" : "no",
01947          InStrictMode() ? "yes" : "no",
01948          spanFramePFD->mBounds.width, spanFramePFD->mBounds.height,
01949          emptyContinuation ? "yes" : "no");
01950   if (psd != mRootSpan) {
01951     printf(" bp=%d,%d,%d,%d margin=%d,%d,%d,%d",
01952            spanFramePFD->mBorderPadding.top,
01953            spanFramePFD->mBorderPadding.right,
01954            spanFramePFD->mBorderPadding.bottom,
01955            spanFramePFD->mBorderPadding.left,
01956            spanFramePFD->mMargin.top,
01957            spanFramePFD->mMargin.right,
01958            spanFramePFD->mMargin.bottom,
01959            spanFramePFD->mMargin.left);
01960   }
01961   printf("\n");
01962 #endif
01963 
01964   // Compute the span's mZeroEffectiveSpanBox flag. What we are trying
01965   // to determine is how we should treat the span: should it act
01966   // "normally" according to css2 or should it effectively
01967   // "disappear".
01968   //
01969   // In general, if the document being processed is in full standards
01970   // mode then it should act normally (with one exception). The
01971   // exception case is when a span is continued and yet the span is
01972   // empty (e.g. compressed whitespace). For this kind of span we treat
01973   // it as if it were not there so that it doesn't impact the
01974   // line-height.
01975   //
01976   // In almost standards mode or quirks mode, we should sometimes make
01977   // it disappear. The cases that matter are those where the span
01978   // contains no real text elements that would provide an ascent and
01979   // descent and height. However, if css style elements have been
01980   // applied to the span (border/padding/margin) so that it's clear the
01981   // document author is intending css2 behavior then we act as if strict
01982   // mode is set.
01983   //
01984   // This code works correctly for preMode, because a blank line
01985   // in PRE mode is encoded as a text node with a LF in it, since
01986   // text nodes with only whitespace are considered in preMode.
01987   //
01988   // Much of this logic is shared with the various implementations of
01989   // nsIFrame::IsEmpty since they need to duplicate the way it makes
01990   // some lines empty.  However, nsIFrame::IsEmpty can't be reused here
01991   // since this code sets zeroEffectiveSpanBox even when there are
01992   // non-empty children.
01993   PRBool zeroEffectiveSpanBox = PR_FALSE;
01994   // XXXldb If we really have empty continuations, then all these other
01995   // checks don't make sense for them.
01996   // XXXldb This should probably just use nsIFrame::IsSelfEmpty, assuming that
01997   // it agrees with this code.  (If it doesn't agree, it probably should.)
01998   if ((emptyContinuation || mCompatMode != eCompatibility_FullStandards) &&
01999       ((psd == mRootSpan) ||
02000        ((0 == spanFramePFD->mBorderPadding.top) &&
02001         (0 == spanFramePFD->mBorderPadding.right) &&
02002         (0 == spanFramePFD->mBorderPadding.bottom) &&
02003         (0 == spanFramePFD->mBorderPadding.left) &&
02004         (0 == spanFramePFD->mMargin.top) &&
02005         (0 == spanFramePFD->mMargin.right) &&
02006         (0 == spanFramePFD->mMargin.bottom) &&
02007         (0 == spanFramePFD->mMargin.left)))) {
02008     // This code handles an issue with compatability with non-css
02009     // conformant browsers. In particular, there are some cases
02010     // where the font-size and line-height for a span must be
02011     // ignored and instead the span must *act* as if it were zero
02012     // sized. In general, if the span contains any non-compressed
02013     // text then we don't use this logic.
02014     // However, this is not propagated outwards, since (in compatibility
02015     // mode) we don't want big line heights for things like
02016     // <p><font size="-1">Text</font></p>
02017 
02018     // We shouldn't include any whitespace that collapses, unless we're
02019     // preformatted (in which case it shouldn't, but the width=0 test is
02020     // perhaps incorrect).  This includes whitespace at the beginning of
02021     // a line and whitespace preceded (?) by other whitespace.
02022     // See bug 134580 and bug 155333.
02023     zeroEffectiveSpanBox = PR_TRUE;
02024     for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
02025       if (pfd->GetFlag(PFD_ISTEXTFRAME) &&
02026           (pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME) || preMode ||
02027            pfd->mBounds.width != 0)) {
02028         zeroEffectiveSpanBox = PR_FALSE;
02029         break;
02030       }
02031     }
02032   }
02033   psd->mZeroEffectiveSpanBox = zeroEffectiveSpanBox;
02034 
02035   // Setup baselineY, minY, and maxY
02036   nscoord baselineY, minY, maxY;
02037   if (psd == mRootSpan) {
02038     // Use a zero baselineY since we don't yet know where the baseline
02039     // will be (until we know how tall the line is; then we will
02040     // know). In addition, use extreme values for the minY and maxY
02041     // values so that only the child frames will impact their values
02042     // (since these are children of the block, there is no span box to
02043     // provide initial values).
02044     baselineY = 0;
02045     minY = VERTICAL_ALIGN_FRAMES_NO_MINIMUM;
02046     maxY = VERTICAL_ALIGN_FRAMES_NO_MAXIMUM;
02047 #ifdef NOISY_VERTICAL_ALIGN
02048     printf("[RootSpan]");
02049     nsFrame::ListTag(stdout, spanFrame);
02050     printf(": pass1 valign frames: topEdge=%d minLineHeight=%d zeroEffectiveSpanBox=%s\n",
02051            mTopEdge, mMinLineHeight,
02052            zeroEffectiveSpanBox ? "yes" : "no");
02053 #endif
02054   }
02055   else {
02056     // Compute the logical height for this span. The logical height
02057     // is based on the line-height value, not the font-size. Also
02058     // compute the top leading.
02059     nscoord logicalHeight =
02060       nsHTMLReflowState::CalcLineHeight(mPresContext, rc, spanFrame);
02061     nscoord contentHeight = spanFramePFD->mBounds.height -
02062       spanFramePFD->mBorderPadding.top - spanFramePFD->mBorderPadding.bottom;
02063 
02064     // Special-case for a ::first-letter frame, set the line height to
02065     // the frame height if the user has left line-height == normal 
02066     if (spanFramePFD->GetFlag(PFD_ISLETTERFRAME) && !spanPrevInFlow &&
02067         spanFrame->GetStyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal) {
02068       logicalHeight = spanFramePFD->mBounds.height;
02069     }
02070 
02071     nscoord leading = logicalHeight - contentHeight;
02072     psd->mTopLeading = leading / 2;
02073     psd->mBottomLeading = leading - psd->mTopLeading;
02074     psd->mLogicalHeight = logicalHeight;
02075 
02076     if (zeroEffectiveSpanBox) {
02077       // When the span-box is to be ignored, zero out the initial
02078       // values so that the span doesn't impact the final line
02079       // height. The contents of the span can impact the final line
02080       // height.
02081 
02082       // Note that things are readjusted for this span after its children
02083       // are reflowed
02084       minY = VERTICAL_ALIGN_FRAMES_NO_MINIMUM;
02085       maxY = VERTICAL_ALIGN_FRAMES_NO_MAXIMUM;
02086     }
02087     else {
02088 
02089       // The initial values for the min and max Y values are in the spans
02090       // coordinate space, and cover the logical height of the span. If
02091       // there are child frames in this span that stick out of this area
02092       // then the minY and maxY are updated by the amount of logical
02093       // height that is outside this range.
02094       minY = spanFramePFD->mBorderPadding.top - psd->mTopLeading;
02095       maxY = minY + psd->mLogicalHeight;
02096     }
02097 
02098     // This is the distance from the top edge of the parents visual
02099     // box to the baseline. The span already computed this for us,
02100     // so just use it.
02101     baselineY = spanFramePFD->mAscent;
02102 
02103 
02104 #ifdef NOISY_VERTICAL_ALIGN
02105     printf("[%sSpan]", (psd == mRootSpan)?"Root":"");
02106     nsFrame::ListTag(stdout, spanFrame);
02107     printf(": baseLine=%d logicalHeight=%d topLeading=%d h=%d bp=%d,%d zeroEffectiveSpanBox=%s\n",
02108            baselineY, psd->mLogicalHeight, psd->mTopLeading,
02109            spanFramePFD->mBounds.height,
02110            spanFramePFD->mBorderPadding.top, spanFramePFD->mBorderPadding.bottom,
02111            zeroEffectiveSpanBox ? "yes" : "no");
02112 #endif
02113   }
02114 
02115   nscoord maxTopBoxHeight = 0;
02116   nscoord maxBottomBoxHeight = 0;
02117   PerFrameData* pfd = psd->mFirstFrame;
02118   while (nsnull != pfd) {
02119     nsIFrame* frame = pfd->mFrame;
02120 
02121     // sanity check (see bug 105168, non-reproducable crashes from null frame)
02122     NS_ASSERTION(frame, "null frame in PerFrameData - something is very very bad");
02123     if (!frame) {
02124       return;
02125     }
02126 
02127     // Compute the logical height of the frame
02128     nscoord logicalHeight;
02129     nscoord topLeading;
02130     PerSpanData* frameSpan = pfd->mSpan;
02131     if (frameSpan) {
02132       // For span frames the logical-height and top-leading was
02133       // pre-computed when the span was reflowed.
02134       logicalHeight = frameSpan->mLogicalHeight;
02135       topLeading = frameSpan->mTopLeading;
02136     }
02137     else {
02138       // For other elements the logical height is the same as the
02139       // frames height plus its margins.
02140       logicalHeight = pfd->mBounds.height + pfd->mMargin.top +
02141         pfd->mMargin.bottom;
02142       topLeading = 0;
02143     }
02144 
02145     // Get vertical-align property
02146     const nsStyleTextReset* textStyle = frame->GetStyleTextReset();
02147     nsStyleUnit verticalAlignUnit = textStyle->mVerticalAlign.GetUnit();
02148 #ifdef NOISY_VERTICAL_ALIGN
02149     printf("  [frame]");
02150     nsFrame::ListTag(stdout, frame);
02151     printf(": verticalAlignUnit=%d (enum == %d)\n",
02152            verticalAlignUnit,
02153            ((eStyleUnit_Enumerated == verticalAlignUnit)
02154             ? textStyle->mVerticalAlign.GetIntValue()
02155             : -1));
02156 #endif
02157 
02158     PRUint8 verticalAlignEnum;
02159     nscoord parentAscent, parentDescent, parentXHeight;
02160     nscoord parentSuperscript, parentSubscript;
02161     nscoord coordOffset, percentOffset, elementLineHeight;
02162     nscoord revisedBaselineY;
02163     switch (verticalAlignUnit) {
02164       case eStyleUnit_Enumerated:
02165       default:
02166         if (eStyleUnit_Enumerated == verticalAlignUnit) {
02167           verticalAlignEnum = textStyle->mVerticalAlign.GetIntValue();
02168         }
02169         else {
02170           verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BASELINE;
02171         }
02172         switch (verticalAlignEnum) {
02173           default:
02174           case NS_STYLE_VERTICAL_ALIGN_BASELINE:
02175             // The elements baseline is aligned with the baseline of
02176             // the parent.
02177             if (frameSpan) {
02178               // XXX explain
02179               pfd->mBounds.y = baselineY - pfd->mAscent;
02180             }
02181             else {
02182               // For non-span elements the borders, padding and
02183               // margins are significant. Use the visual box height
02184               // and the bottom margin as the distance off of the
02185               // baseline.
02186               pfd->mBounds.y = baselineY - pfd->mAscent - pfd->mMargin.bottom;
02187             }
02188             pfd->mVerticalAlign = VALIGN_OTHER;
02189             break;
02190 
02191           case NS_STYLE_VERTICAL_ALIGN_SUB:
02192             // Lower the baseline of the box to the subscript offset
02193             // of the parent's box. This is identical to the baseline
02194             // alignment except for the addition of the subscript
02195             // offset to the baseline Y.
02196             fm->GetSubscriptOffset(parentSubscript);
02197             revisedBaselineY = baselineY + parentSubscript;
02198             if (frameSpan) {
02199               pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
02200             }
02201             else {
02202               pfd->mBounds.y = revisedBaselineY - pfd->mAscent -
02203                 pfd->mMargin.bottom;
02204             }
02205             pfd->mVerticalAlign = VALIGN_OTHER;
02206             break;
02207 
02208           case NS_STYLE_VERTICAL_ALIGN_SUPER:
02209             // Raise the baseline of the box to the superscript offset
02210             // of the parent's box. This is identical to the baseline
02211             // alignment except for the subtraction of the superscript
02212             // offset to the baseline Y.
02213             fm->GetSuperscriptOffset(parentSuperscript);
02214             revisedBaselineY = baselineY - parentSuperscript;
02215             if (frameSpan) {
02216               pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
02217             }
02218             else {
02219               pfd->mBounds.y = revisedBaselineY - pfd->mAscent -
02220                 pfd->mMargin.bottom;
02221             }
02222             pfd->mVerticalAlign = VALIGN_OTHER;
02223             break;
02224 
02225           case NS_STYLE_VERTICAL_ALIGN_TOP:
02226             pfd->mVerticalAlign = VALIGN_TOP;
02227             if (logicalHeight > maxTopBoxHeight) {
02228               maxTopBoxHeight = logicalHeight;
02229             }
02230             break;
02231 
02232           case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
02233             pfd->mVerticalAlign = VALIGN_BOTTOM;
02234             if (logicalHeight > maxBottomBoxHeight) {
02235               maxBottomBoxHeight = logicalHeight;
02236             }
02237             break;
02238 
02239           case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
02240             // Align the midpoint of the frame with 1/2 the parents
02241             // x-height above the baseline.
02242             fm->GetXHeight(parentXHeight);
02243             if (frameSpan) {
02244               pfd->mBounds.y = baselineY -
02245                 (parentXHeight + pfd->mBounds.height)/2;
02246             }
02247             else {
02248               pfd->mBounds.y = baselineY - (parentXHeight + logicalHeight)/2 +
02249                 pfd->mMargin.top;
02250             }
02251             pfd->mVerticalAlign = VALIGN_OTHER;
02252             break;
02253 
02254           case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
02255             // The top of the logical box is aligned with the top of
02256             // the parent elements text.
02257             fm->GetMaxAscent(parentAscent);
02258             if (frameSpan) {
02259               pfd->mBounds.y = baselineY - parentAscent -
02260                 pfd->mBorderPadding.top + frameSpan->mTopLeading;
02261             }
02262             else {
02263               pfd->mBounds.y = baselineY - parentAscent + pfd->mMargin.top;
02264             }
02265             pfd->mVerticalAlign = VALIGN_OTHER;
02266             break;
02267 
02268           case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
02269             // The bottom of the logical box is aligned with the
02270             // bottom of the parent elements text.
02271             fm->GetMaxDescent(parentDescent);
02272             if (frameSpan) {
02273               pfd->mBounds.y = baselineY + parentDescent -
02274                 pfd->mBounds.height + pfd->mBorderPadding.bottom -
02275                 frameSpan->mBottomLeading;
02276             }
02277             else {
02278               pfd->mBounds.y = baselineY + parentDescent -
02279                 pfd->mBounds.height - pfd->mMargin.bottom;
02280             }
02281             pfd->mVerticalAlign = VALIGN_OTHER;
02282             break;
02283 
02284           case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
02285             // Align the midpoint of the frame with the baseline of the parent.
02286             if (frameSpan) {
02287               pfd->mBounds.y = baselineY - pfd->mBounds.height/2;
02288             }
02289             else {
02290               pfd->mBounds.y = baselineY - logicalHeight/2 + pfd->mMargin.top;
02291             }
02292             pfd->mVerticalAlign = VALIGN_OTHER;
02293             break;       
02294         }
02295         break;
02296 
02297       case eStyleUnit_Coord:
02298         // According to the CSS2 spec (10.8.1), a positive value
02299         // "raises" the box by the given distance while a negative value
02300         // "lowers" the box by the given distance (with zero being the
02301         // baseline). Since Y coordinates increase towards the bottom of
02302         // the screen we reverse the sign.
02303         coordOffset = textStyle->mVerticalAlign.GetCoordValue();
02304         revisedBaselineY = baselineY - coordOffset;
02305         if (frameSpan) {
02306           pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
02307         }
02308         else {
02309           pfd->mBounds.y = revisedBaselineY - pfd->mAscent -
02310             pfd->mMargin.bottom;
02311         }
02312         pfd->mVerticalAlign = VALIGN_OTHER;
02313         break;
02314 
02315       case eStyleUnit_Percent:
02316         // Similar to a length value (eStyleUnit_Coord) except that the
02317         // percentage is a function of the elements line-height value.
02318         elementLineHeight =
02319           nsHTMLReflowState::CalcLineHeight(mPresContext, rc, frame);
02320         percentOffset = nscoord(
02321           textStyle->mVerticalAlign.GetPercentValue() * elementLineHeight
02322           );
02323         revisedBaselineY = baselineY - percentOffset;
02324         if (frameSpan) {
02325           pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
02326         }
02327         else {
02328           pfd->mBounds.y = revisedBaselineY - pfd->mAscent -
02329             pfd->mMargin.bottom;
02330         }
02331         pfd->mVerticalAlign = VALIGN_OTHER;
02332         break;
02333     }
02334 
02335     // Update minY/maxY for frames that we just placed. Do not factor
02336     // text into the equation.
02337     if (pfd->mVerticalAlign == VALIGN_OTHER) {
02338       // Text frames do not contribute to the min/max Y values for the
02339       // line (instead their parent frame's font-size contributes).
02340       // XXXrbs -- relax this restriction because it causes text frames
02341       //           to jam together when 'font-size-adjust' is enabled
02342       //           and layout is using dynamic font heights (bug 20394)
02343       //        -- Note #1: With this code enabled and with the fact that we are not
02344       //           using Em[Ascent|Descent] as nsDimensions for text metrics in
02345       //           GFX mean that the discussion in bug 13072 cannot hold.
02346       //        -- Note #2: We still don't want empty-text frames to interfere.
02347       //           For example in quirks mode, avoiding empty text frames prevents
02348       //           "tall" lines around elements like <hr> since the rules of <hr>
02349       //           in quirks.css have pseudo text contents with LF in them.
02350 #if 0
02351       if (!pfd->GetFlag(PFD_ISTEXTFRAME)) {
02352 #else
02353       // Only consider non empty text frames when line-height=normal
02354       PRBool canUpdate = !pfd->GetFlag(PFD_ISTEXTFRAME);
02355       if (!canUpdate && pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) {
02356         nsStyleUnit lhUnit = frame->GetStyleText()->mLineHeight.GetUnit();
02357         canUpdate = lhUnit == eStyleUnit_Normal || lhUnit == eStyleUnit_Null;
02358       }
02359       if (canUpdate) {
02360 #endif
02361         nscoord yTop, yBottom;
02362         if (frameSpan) {
02363           // For spans that were are now placing, use their position
02364           // plus their already computed min-Y and max-Y values for
02365           // computing yTop and yBottom.
02366           yTop = pfd->mBounds.y + frameSpan->mMinY;
02367           yBottom = pfd->mBounds.y + frameSpan->mMaxY;
02368         }
02369         else {
02370           yTop = pfd->mBounds.y - pfd->mMargin.top;
02371           yBottom = yTop + logicalHeight;
02372         }
02373         if (!preMode &&
02374             GetCompatMode() != eCompatibility_FullStandards &&
02375             !logicalHeight) {
02376           // Check if it's a BR frame that is not alone on its line (it
02377           // is given a height of zero to indicate this), and if so reset
02378           // yTop and yBottom so that BR frames don't influence the line.
02379           if (nsLayoutAtoms::brFrame == frame->GetType()) {
02380             yTop = VERTICAL_ALIGN_FRAMES_NO_MINIMUM;
02381             yBottom = VERTICAL_ALIGN_FRAMES_NO_MAXIMUM;
02382           }
02383         }
02384         if (yTop < minY) minY = yTop;
02385         if (yBottom > maxY) maxY = yBottom;
02386 #ifdef NOISY_VERTICAL_ALIGN
02387         printf("     [frame]raw: a=%d d=%d h=%d bp=%d,%d logical: h=%d leading=%d y=%d minY=%d maxY=%d\n",
02388                pfd->mAscent, pfd->mDescent, pfd->mBounds.height,
02389                pfd->mBorderPadding.top, pfd->mBorderPadding.bottom,
02390                logicalHeight,
02391                pfd->mSpan ? topLeading : 0,
02392                pfd->mBounds.y, minY, maxY);
02393 #endif
02394       }
02395       if (psd != mRootSpan) {
02396         frame->SetRect(pfd->mBounds);
02397       }
02398     }
02399     pfd = pfd->mNext;
02400   }
02401 
02402   // Factor in the minimum line-height when handling the root-span for
02403   // the block.
02404   if (psd == mRootSpan) {
02405     // We should factor in the block element's minimum line-height (as
02406     // defined in section 10.8.1 of the css2 spec) assuming that
02407     // mZeroEffectiveSpanBox is not set on the root span.  This only happens
02408     // in some cases in quirks mode:
02409     //  (1) if the root span contains non-whitespace text directly (this
02410     //      is handled by mZeroEffectiveSpanBox
02411     //  (2) if this is the first line of an LI element (whether or not
02412     //      there is a bullet (NN4/IE5 quirk)
02413     //  (3) if this is the last line of an LI, DT, or DD element
02414     //      (The last line before a block also counts, but not before a
02415     //      BR) (NN4/IE5 quirk)
02416     PRBool applyMinLH = !(psd->mZeroEffectiveSpanBox); // (1) above
02417     PRBool isFirstLine = !mLineNumber; // if the line number is 0
02418     PRBool isLastLine = (!mLineBox->IsLineWrapped() && !GetFlag(LL_LINEENDSINBR));
02419     PRBool foundLI = PR_FALSE;  // hack to fix bug 50480.
02420     //XXX: rather than remembering if we've found an LI, we really should be checking
02421     //     for the existence of a bullet frame.  Likewise, the code below should not
02422     //     be checking for any particular content tag type, but rather should
02423     //     be checking for the existence of a bullet frame to determine if it's a list element or not.
02424     if (!applyMinLH && (isFirstLine || isLastLine)) {
02425       nsIContent* blockContent = mRootSpan->mFrame->mFrame->GetContent();
02426       if (blockContent) {
02427         nsIAtom *blockTagAtom = blockContent->Tag();
02428         // (2) above, if the first line of LI
02429         if (isFirstLine && blockTagAtom == nsHTMLAtoms::li) {
02430           // if the line is empty, then don't force the min height
02431           // (see bug 75963)
02432           if (!IsZeroHeight()) {
02433             applyMinLH = PR_TRUE;
02434             foundLI = PR_TRUE;
02435           }
02436         }
02437         // (3) above, if the last line of LI, DT, or DD
02438         else if (!applyMinLH && isLastLine &&
02439                  ((blockTagAtom == nsHTMLAtoms::li) ||
02440                   (blockTagAtom == nsHTMLAtoms::dt) ||
02441                   (blockTagAtom == nsHTMLAtoms::dd))) {
02442           applyMinLH = PR_TRUE;
02443         }
02444       }
02445     }
02446     if (applyMinLH) {
02447       if ((psd->mX != psd->mLeftEdge) || preMode || foundLI) {
02448 #ifdef NOISY_VERTICAL_ALIGN
02449         printf("  [span]==> adjusting min/maxY: currentValues: %d,%d", minY, maxY);
02450 #endif
02451         nscoord minimumLineHeight = mMinLineHeight;
02452         nscoord fontAscent, fontHeight;
02453         fm->GetMaxAscent(fontAscent);
02454         fm->GetHeight(fontHeight);
02455 
02456         nscoord leading = minimumLineHeight - fontHeight;
02457         nscoord yTop = -fontAscent - leading/2;
02458         nscoord yBottom = yTop + minimumLineHeight;
02459         if (yTop < minY) minY = yTop;
02460         if (yBottom > maxY) maxY = yBottom;
02461 
02462 #ifdef NOISY_VERTICAL_ALIGN
02463         printf(" new values: %d,%d\n", minY, maxY);
02464 #endif
02465       }
02466       else {
02467         // XXX issues:
02468         // [1] BR's on empty lines stop working
02469         // [2] May not honor css2's notion of handling empty elements
02470         // [3] blank lines in a pre-section ("\n") (handled with preMode)
02471 
02472         // XXX Are there other problems with this?
02473 #ifdef NOISY_VERTICAL_ALIGN
02474         printf("  [span]==> zapping min/maxY: currentValues: %d,%d newValues: 0,0\n",
02475                minY, maxY);
02476 #endif
02477         minY = maxY = 0;
02478       }
02479     }
02480   }
02481 
02482   if ((minY == VERTICAL_ALIGN_FRAMES_NO_MINIMUM) ||
02483       (maxY == VERTICAL_ALIGN_FRAMES_NO_MINIMUM)) {
02484     minY = maxY = baselineY;
02485   }
02486 
02487   if ((psd != mRootSpan) && (psd->mZeroEffectiveSpanBox)) {
02488 #ifdef NOISY_VERTICAL_ALIGN
02489     printf("   [span]adjusting for zeroEffectiveSpanBox\n");
02490     printf("     Original: minY=%d, maxY=%d, height=%d, ascent=%d, descent=%d, logicalHeight=%d, topLeading=%d, bottomLeading=%d\n",
02491            minY, maxY, spanFramePFD->mBounds.height,
02492            spanFramePFD->mAscent, spanFramePFD->mDescent,
02493            psd->mLogicalHeight, psd->mTopLeading, psd->mBottomLeading);
02494 #endif
02495     nscoord goodMinY = spanFramePFD->mBorderPadding.top - psd->mTopLeading;
02496     nscoord goodMaxY = goodMinY + psd->mLogicalHeight;
02497     if (minY > goodMinY) {
02498       nscoord adjust = minY - goodMinY; // positive
02499 
02500       // shrink the logical extents
02501       psd->mLogicalHeight -= adjust;
02502       psd->mTopLeading -= adjust;
02503     }
02504     if (maxY < goodMaxY) {
02505       nscoord adjust = goodMaxY - maxY;
02506       psd->mLogicalHeight -= adjust;
02507       psd->mBottomLeading -= adjust;
02508     }
02509     if (minY > 0) {
02510 
02511       // shrink the content by moving its top down.  This is tricky, since
02512       // the top is the 0 for many coordinates, so what we do is
02513       // move everything else up.
02514       spanFramePFD->mAscent -= minY; // move the baseline up
02515       spanFramePFD->mBounds.height -= minY; // move the bottom up
02516       psd->mTopLeading += minY;
02517 
02518       pfd = psd->mFirstFrame;
02519       while (nsnull != pfd) {
02520         pfd->mBounds.y -= minY; // move all the children back up
02521         pfd->mFrame->SetRect(pfd->mBounds);
02522         pfd = pfd->mNext;
02523       }
02524       maxY -= minY; // since minY is in the frame's own coordinate system
02525       minY = 0;
02526     }
02527     if (maxY < spanFramePFD->mBounds.height) {
02528       nscoord adjust = spanFramePFD->mBounds.height - maxY;
02529       spanFramePFD->mBounds.height -= adjust; // move the bottom up
02530       spanFramePFD->mDescent -= adjust;
02531       psd->mBottomLeading += adjust;
02532     }
02533 #ifdef NOISY_VERTICAL_ALIGN
02534     printf("     New: minY=%d, maxY=%d, height=%d, ascent=%d, descent=%d, logicalHeight=%d, topLeading=%d, bottomLeading=%d\n",
02535            minY, maxY, spanFramePFD->mBounds.height,
02536            spanFramePFD->mAscent, spanFramePFD->mDescent,
02537            psd->mLogicalHeight, psd->mTopLeading, psd->mBottomLeading);
02538 #endif
02539   }
02540 
02541   psd->mMinY = minY;
02542   psd->mMaxY = maxY;
02543 #ifdef NOISY_VERTICAL_ALIGN
02544   printf("  [span]==> minY=%d maxY=%d delta=%d maxTopBoxHeight=%d maxBottomBoxHeight=%d\n",
02545          minY, maxY, maxY - minY, maxTopBoxHeight, maxBottomBoxHeight);
02546 #endif
02547   if (maxTopBoxHeight > mMaxTopBoxHeight) {
02548     mMaxTopBoxHeight = maxTopBoxHeight;
02549   }
02550   if (maxBottomBoxHeight > mMaxBottomBoxHeight) {
02551     mMaxBottomBoxHeight = maxBottomBoxHeight;
02552   }
02553 }
02554 
02555 PRBool
02556 nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
02557                                        nscoord* aDeltaWidth)
02558 {
02559 #ifndef IBMBIDI
02560 // XXX what about NS_STYLE_DIRECTION_RTL?
02561   if (NS_STYLE_DIRECTION_RTL == psd->mDirection) {
02562     *aDeltaWidth = 0;
02563     return PR_TRUE;
02564   }
02565 #endif
02566 
02567   PerFrameData* pfd = psd->mFirstFrame;
02568   if (!pfd) {
02569     *aDeltaWidth = 0;
02570     return PR_FALSE;
02571   }
02572   pfd = pfd->Last();
02573   while (nsnull != pfd) {
02574 #ifdef REALLY_NOISY_TRIM
02575     nsFrame::ListTag(stdout, (psd == mRootSpan
02576                               ? mBlockReflowState->frame
02577                               : psd->mFrame->mFrame));
02578     printf(": attempting trim of ");
02579     nsFrame::ListTag(stdout, pfd->mFrame);
02580     printf("\n");
02581 #endif
02582     PerSpanData* childSpan = pfd->mSpan;
02583     if (childSpan) {
02584       // Maybe the child span has the trailing white-space in it?
02585       if (TrimTrailingWhiteSpaceIn(childSpan, aDeltaWidth)) {
02586         nscoord deltaWidth = *aDeltaWidth;
02587         if (deltaWidth) {
02588           // Adjust the child spans frame size
02589           pfd->mBounds.width -= deltaWidth;
02590           if (psd != mRootSpan) {
02591             // When the child span is not a direct child of the block
02592             // we need to update the child spans frame rectangle
02593             // because it most likely will not be done again. Spans
02594             // that are direct children of the block will be updated
02595             // later, however, because the VerticalAlignFrames method
02596             // will be run after this method.
02597             nsIFrame* f = pfd->mFrame;
02598             nsRect r = f->GetRect();
02599             r.width -= deltaWidth;
02600             f->SetRect(r);
02601           }
02602 
02603           // Adjust the right edge of the span that contains the child span
02604           psd->mX -= deltaWidth;
02605 
02606           // Slide any frames that follow the child span over by the
02607           // right amount. The only thing that can follow the child
02608           // span is empty stuff, so we are just making things
02609           // sensible (keeping the combined area honest).
02610           while (pfd->mNext) {
02611             pfd = pfd->mNext;
02612             pfd->mBounds.x -= deltaWidth;
02613           }
02614         }
02615         return PR_TRUE;
02616       }
02617     }
02618     else if (!pfd->GetFlag(PFD_ISTEXTFRAME) &&
02619              !pfd->GetFlag(PFD_ISPLACEHOLDERFRAME)) {
02620       // If we hit a frame on the end that's not text and not a placeholder,
02621       // then there is no trailing whitespace to trim. Stop the search.
02622       *aDeltaWidth = 0;
02623       return PR_TRUE;
02624     }
02625     else if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) {
02626       nscoord deltaWidth = 0;
02627       PRBool lastCharIsJustifiable = PR_FALSE;
02628       pfd->mFrame->TrimTrailingWhiteSpace(mPresContext,
02629                                           *mBlockReflowState->rendContext,
02630                                           deltaWidth,
02631                                           lastCharIsJustifiable);
02632 #ifdef NOISY_TRIM
02633       nsFrame::ListTag(stdout, (psd == mRootSpan
02634                                 ? mBlockReflowState->frame
02635                                 : psd->mFrame->mFrame));
02636       printf(": trim of ");
02637       nsFrame::ListTag(stdout, pfd->mFrame);
02638       printf(" returned %d\n", deltaWidth);
02639 #endif
02640       if (lastCharIsJustifiable && pfd->mJustificationNumSpaces > 0) {
02641         pfd->mJustificationNumSpaces--;
02642       }
02643 
02644       if (deltaWidth) {
02645         pfd->mBounds.width -= deltaWidth;
02646         if (0 == pfd->mBounds.width) {
02647           pfd->mMaxElementWidth = 0;
02648         }
02649 
02650         // See if the text frame has already been placed in its parent
02651         if (psd != mRootSpan) {
02652           // The frame was already placed during psd's
02653           // reflow. Update the frames rectangle now.
02654           pfd->mFrame->SetRect(pfd->mBounds);
02655         }
02656 
02657         // Adjust containing span's right edge
02658         psd->mX -= deltaWidth;
02659 
02660         // Slide any frames that follow the text frame over by the
02661         // right amount. The only thing that can follow the text
02662         // frame is empty stuff, so we are just making things
02663         // sensible (keeping the combined area honest).
02664         while (pfd->mNext) {
02665           pfd = pfd->mNext;
02666           pfd->mBounds.x -= deltaWidth;
02667         }
02668       }
02669 
02670       // Pass up to caller so they can shrink their span
02671       *aDeltaWidth = deltaWidth;
02672       return PR_TRUE;
02673     }
02674     pfd = pfd->mPrev;
02675   }
02676 
02677   *aDeltaWidth = 0;
02678   return PR_FALSE;
02679 }
02680 
02681 PRBool
02682 nsLineLayout::TrimTrailingWhiteSpace()
02683 {
02684   PerSpanData* psd = mRootSpan;
02685   nscoord deltaWidth;
02686   TrimTrailingWhiteSpaceIn(psd, &deltaWidth);
02687   return 0 != deltaWidth;
02688 }
02689 
02690 void
02691 nsLineLayout::ComputeJustificationWeights(PerSpanData* aPSD,
02692                                           PRInt32* aNumSpaces,
02693                                           PRInt32* aNumLetters)
02694 {
02695   NS_ASSERTION(aPSD, "null arg");
02696   NS_ASSERTION(aNumSpaces, "null arg");
02697   NS_ASSERTION(aNumLetters, "null arg");
02698   PRInt32 numSpaces = 0;
02699   PRInt32 numLetters = 0;
02700 
02701   for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nsnull; pfd = pfd->mNext) {
02702 
02703     if (PR_TRUE == pfd->GetFlag(PFD_ISTEXTFRAME)) {
02704       numSpaces += pfd->mJustificationNumSpaces;
02705       numLetters += pfd->mJustificationNumLetters;
02706     }
02707     else if (pfd->mSpan != nsnull) {
02708       PRInt32 spanSpaces;
02709       PRInt32 spanLetters;
02710 
02711       ComputeJustificationWeights(pfd->mSpan, &spanSpaces, &spanLetters);
02712 
02713       numSpaces += spanSpaces;
02714       numLetters += spanLetters;
02715     }
02716   }
02717 
02718   *aNumSpaces = numSpaces;
02719   *aNumLetters = numLetters;
02720 }
02721 
02722 nscoord 
02723 nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, FrameJustificationState* aState)
02724 {
02725   NS_ASSERTION(aPSD, "null arg");
02726   NS_ASSERTION(aState, "null arg");
02727 
02728   nscoord deltaX = 0;
02729   for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nsnull; pfd = pfd->mNext) {
02730     // Don't reposition bullets (and other frames that occur out of X-order?)
02731     if (!pfd->GetFlag(PFD_ISBULLET)) {
02732       nscoord dw = 0;
02733       
02734       pfd->mBounds.x += deltaX;
02735       
02736       if (PR_TRUE == pfd->GetFlag(PFD_ISTEXTFRAME)) {
02737         if (aState->mTotalWidthForSpaces > 0 &&
02738             aState->mTotalNumSpaces > 0) {
02739           aState->mNumSpacesProcessed += pfd->mJustificationNumSpaces;
02740 
02741           nscoord newAllocatedWidthForSpaces =
02742             (aState->mTotalWidthForSpaces*aState->mNumSpacesProcessed)
02743               /aState->mTotalNumSpaces;
02744           
02745           dw += newAllocatedWidthForSpaces - aState->mWidthForSpacesProcessed;
02746 
02747           aState->mWidthForSpacesProcessed = newAllocatedWidthForSpaces;
02748         }
02749 
02750         if (aState->mTotalWidthForLetters > 0 &&
02751             aState->mTotalNumLetters > 0) {
02752           aState->mNumLettersProcessed += pfd->mJustificationNumLetters;
02753 
02754           nscoord newAllocatedWidthForLetters =
02755             (aState->mTotalWidthForLetters*aState->mNumLettersProcessed)
02756               /aState->mTotalNumLetters;
02757           
02758           dw += newAllocatedWidthForLetters - aState->mWidthForLettersProcessed;
02759 
02760           aState->mWidthForLettersProcessed = newAllocatedWidthForLetters;
02761         }
02762       }
02763       else {
02764         if (nsnull != pfd->mSpan) {
02765           dw += ApplyFrameJustification(pfd->mSpan, aState);
02766         }
02767       }
02768       
02769       pfd->mBounds.width += dw;
02770 
02771       deltaX += dw;
02772       pfd->mFrame->SetRect(pfd->mBounds);
02773     }
02774   }
02775   return deltaX;
02776 }
02777 
02778 PRBool
02779 nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds,
02780                                     PRBool aAllowJustify,
02781                                     PRBool aShrinkWrapWidth)
02782 {
02783   PerSpanData* psd = mRootSpan;
02784   nscoord availWidth = psd->mRightEdge;
02785   if (NS_UNCONSTRAINEDSIZE == availWidth) {
02786     // Don't bother horizontal aligning on pass1 table reflow
02787 #ifdef NOISY_HORIZONTAL_ALIGN
02788     nsFrame::ListTag(stdout, mBlockReflowState->frame);
02789     printf(": skipping horizontal alignment in pass1 table reflow\n");
02790 #endif
02791     return PR_TRUE;
02792   }
02793   availWidth -= psd->mLeftEdge;
02794   nscoord remainingWidth = availWidth - aLineBounds.width;
02795 #ifdef NOISY_HORIZONTAL_ALIGN
02796     nsFrame::ListTag(stdout, mBlockReflowState->frame);
02797     printf(": availWidth=%d lineWidth=%d delta=%d\n",
02798            availWidth, aLineBounds.width, remainingWidth);
02799 #endif
02800 #ifdef IBMBIDI
02801   nscoord dx = 0;
02802 #endif
02803 
02804   // XXXldb What if it's less than 0??
02805   if (remainingWidth > 0)
02806   {
02807 #ifndef IBMBIDI
02808     nscoord dx = 0;
02809 #endif
02810     switch (mTextAlign) {
02811       case NS_STYLE_TEXT_ALIGN_DEFAULT:
02812         if (NS_STYLE_DIRECTION_LTR == psd->mDirection) {
02813           // default alignment for left-to-right is left so do nothing
02814           break;
02815         }
02816         // Fall through to align right case for default alignment
02817         // used when the direction is right-to-left.
02818 
02819       case NS_STYLE_TEXT_ALIGN_RIGHT:
02820       case NS_STYLE_TEXT_ALIGN_MOZ_RIGHT:
02821         dx = remainingWidth;
02822         break;
02823 
02824       case NS_STYLE_TEXT_ALIGN_LEFT:
02825       case NS_STYLE_TEXT_ALIGN_MOZ_LEFT:
02826         break;
02827 
02828       case NS_STYLE_TEXT_ALIGN_JUSTIFY:
02829         // If this is not the last line then go ahead and justify the
02830         // frames in the line. If it is the last line then if the
02831         // direction is right-to-left then we right-align the frames.
02832         if (aAllowJustify) {
02833           if (!aShrinkWrapWidth) {
02834             PRInt32 numSpaces;
02835             PRInt32 numLetters;
02836             
02837             ComputeJustificationWeights(psd, &numSpaces, &numLetters);
02838 
02839             if (numSpaces > 0) {
02840               FrameJustificationState state = { numSpaces, numLetters, remainingWidth, 0, 0, 0, 0, 0 };
02841 
02842               ApplyFrameJustification(psd, &state);
02843             }
02844           }
02845         }
02846         else if (NS_STYLE_DIRECTION_RTL == psd->mDirection) {
02847           // right align the frames
02848           dx = remainingWidth;
02849         }
02850         break;
02851 
02852       case NS_STYLE_TEXT_ALIGN_CENTER:
02853       case NS_STYLE_TEXT_ALIGN_MOZ_CENTER:
02854         dx = remainingWidth / 2;
02855         break;
02856     }
02857 #ifdef IBMBIDI
02858   }
02859   PRBool isRTL = ( (NS_STYLE_DIRECTION_RTL == psd->mDirection)
02860                 && (!psd->mChangedFrameDirection) );
02861   if (dx || isRTL) {
02862     PerFrameData* bulletPfd = nsnull;
02863     nscoord maxX = aLineBounds.XMost() + dx;
02864     PRBool isVisualRTL = PR_FALSE;
02865 
02866     if (isRTL) {
02867       if (psd->mLastFrame->GetFlag(PFD_ISBULLET) )
02868         bulletPfd = psd->mLastFrame;
02869   
02870       psd->mChangedFrameDirection = PR_TRUE;
02871 
02872       isVisualRTL = mPresContext->IsVisualMode();
02873     }
02874     if (dx || isVisualRTL)
02875 #else
02876     if (0 != dx)
02877 #endif
02878     {
02879       // If we need to move the frames but we're shrink wrapping, then
02880       // we need to wait until the final width is known
02881       if (aShrinkWrapWidth) {
02882         return PR_FALSE;
02883       }
02884       for (PerFrameData* pfd = psd->mFirstFrame; pfd
02885 #ifdef IBMBIDI
02886            && bulletPfd != pfd
02887 #endif
02888            ; pfd = pfd->mNext) {
02889 #ifdef IBMBIDI
02890         if (isVisualRTL) {
02891           // XXXldb Ugh.  Could we handle this earlier so we don't get here?
02892           maxX = pfd->mBounds.x = maxX - (pfd->mMargin.left + pfd->mBounds.width + pfd->mMargin.right);
02893         }
02894         else
02895 #endif // IBMBIDI
02896           pfd->mBounds.x += dx;
02897         pfd->mFrame->SetRect(pfd->mBounds);
02898       }
02899       aLineBounds.x += dx;
02900     }
02901 #ifndef IBMBIDI
02902     if ((NS_STYLE_DIRECTION_RTL == psd->mDirection) &&
02903         !psd->mChangedFrameDirection) {
02904       psd->mChangedFrameDirection = PR_TRUE;
02905   
02906       /* Assume that all frames have been right aligned.*/
02907       if (aShrinkWrapWidth) {
02908         return PR_FALSE;
02909       }
02910       PerFrameData* pfd = psd->mFirstFrame;
02911       PRUint32 maxX = psd->mRightEdge;
02912       while (nsnull != pfd) {
02913         pfd->mBounds.x = maxX - (pfd->mMargin.left + pfd->mBounds.width + pfd->mMargin.right);
02914         pfd->mFrame->SetRect(pfd->mBounds);
02915         maxX = pfd->mBounds.x;
02916         pfd = pfd->mNext;
02917       }
02918     }
02919 #endif // ndef IBMBIDI
02920   }
02921 
02922   return PR_TRUE;
02923 }
02924 
02925 void
02926 nsLineLayout::RelativePositionFrames(nsRect& aCombinedArea)
02927 {
02928   RelativePositionFrames(mRootSpan, aCombinedArea);
02929 }
02930 
02931 void
02932 nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea)
02933 {
02934   nsRect combinedAreaResult;
02935   if (nsnull != psd->mFrame) {
02936     // The span's overflow area comes in three parts:
02937     // -- this frame's width and height
02938     // -- the pfd->mCombinedArea, which is the area of a bullet or the union
02939     // of a relatively positioned frame's absolute children
02940     // -- the bounds of all inline descendants
02941     // The former two parts are computed right here, we gather the descendants
02942     // below.
02943     nsRect adjustedBounds(0, 0, psd->mFrame->mBounds.width,
02944                           psd->mFrame->mBounds.height);
02945     combinedAreaResult.UnionRect(psd->mFrame->mCombinedArea, adjustedBounds);
02946   }
02947   else {
02948     // The minimum combined area for the frames that are direct
02949     // children of the block starts at the upper left corner of the
02950     // line and is sized to match the size of the line's bounding box
02951     // (the same size as the values returned from VerticalAlignFrames)
02952     combinedAreaResult.x = psd->mLeftEdge;
02953     // If this turns out to be negative, the rect will be treated as empty.
02954     // Which is just fine.
02955     combinedAreaResult.width = psd->mX - combinedAreaResult.x;
02956     combinedAreaResult.y = mTopEdge;
02957     combinedAreaResult.height = mFinalLineHeight;
02958   }
02959 
02960   for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) {
02961     nsPoint origin = nsPoint(pfd->mBounds.x, pfd->mBounds.y);
02962     nsIFrame* frame = pfd->mFrame;
02963 
02964     // Adjust the origin of the frame
02965     if (pfd->GetFlag(PFD_RELATIVEPOS)) {
02966       // right and bottom are handled by
02967       // nsHTMLReflowState::ComputeRelativeOffsets
02968       nsPoint change(pfd->mOffsets.left, pfd->mOffsets.top);
02969       frame->SetPosition(frame->GetPosition() + change);
02970       origin += change;
02971     }
02972 
02973     // We must position the view correctly before positioning its
02974     // descendants so that widgets are positioned properly (since only
02975     // some views have widgets).
02976     if (frame->HasView())
02977       nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame,
02978                                                  frame->GetView(),
02979                                                  &pfd->mCombinedArea, //ignored
02980                                                  NS_FRAME_NO_SIZE_VIEW);
02981 
02982     // Note: the combined area of a child is in its coordinate
02983     // system. We adjust the childs combined area into our coordinate
02984     // system before computing the aggregated value by adding in
02985     // <b>x</b> and <b>y</b> which were computed above.
02986     nsRect r;
02987     if (pfd->mSpan) {
02988       // Compute a new combined area for the child span before
02989       // aggregating it into our combined area.
02990       RelativePositionFrames(pfd->mSpan, r);
02991     } else {
02992       // For simple text frames we take the union of the combined area
02993       // and the width/height. I think the combined area should always
02994       // equal the bounds in this case, but this is safe.
02995       nsRect adjustedBounds(0, 0, pfd->mBounds.width, pfd->mBounds.height);
02996       r.UnionRect(pfd->mCombinedArea, adjustedBounds);
02997 
02998       // If we have something that's not an inline but with a complex frame
02999       // hierarchy inside that contains views, they need to be
03000       // positioned.
03001       // All descendant views must be repositioned even if this frame
03002       // does have a view in case this frame's view does not have a
03003       // widget and some of the descendant views do have widgets --
03004       // otherwise the widgets won't be repositioned.
03005       nsContainerFrame::PositionChildViews(frame);
03006     }
03007 
03008     // Do this here (rather than along with NS_FRAME_OUTSIDE_CHILDREN
03009     // handling below) so we get leaf frames as well.  No need to worry
03010     // about the root span, since it doesn't have a frame.
03011     if (frame->HasView())
03012       nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, frame,
03013                                                  frame->GetView(), &r,
03014                                                  NS_FRAME_NO_MOVE_VIEW);
03015 
03016     combinedAreaResult.UnionRect(combinedAreaResult, r + origin);
03017   }
03018 
03019   // If we just computed a spans combined area, we need to update its
03020   // NS_FRAME_OUTSIDE_CHILDREN bit..
03021   if (psd->mFrame) {
03022     PerFrameData* spanPFD = psd->mFrame;
03023     nsIFrame* frame = spanPFD->mFrame;
03024     frame->FinishAndStoreOverflow(&combinedAreaResult, frame->GetSize());
03025   }
03026   aCombinedArea = combinedAreaResult;
03027 }
03028 
03029 void
03030 nsLineLayout::ForgetWordFrame(nsIFrame* aFrame)
03031 {
03032   if (mWordFrames && 0 != mWordFrames->GetSize()) {
03033     NS_ASSERTION((void*)aFrame == mWordFrames->PeekFront(), "forget-word-frame");
03034     mWordFrames->PopFront();
03035   }
03036 }
03037 
03038 nsIFrame*
03039 nsLineLayout::FindNextText(nsPresContext* aPresContext, nsIFrame* aFrame)
03040 {
03041   // Grovel through the frame hierarchy to find a text frame that is
03042   // "adjacent" to aFrame.
03043 
03044   // So this is kind of funky. During reflow, overflow frames will
03045   // have their parent pointers set up lazily. We assume that, on
03046   // entry, aFrame has it's parent pointer set correctly (as do all of
03047   // its ancestors). Starting from that, we need to make sure that as
03048   // we traverse through frames trying to find the next text frame, we
03049   // leave the frames with their parent pointers set correctly, so the
03050   // *next* time we come through here, we're good to go.
03051 
03052   // Build a path from the enclosing block frame down to aFrame. We'll
03053   // use this to walk the frame tree. (XXXwaterson if I was clever, I
03054   // wouldn't need to build this up before hand, and could incorporate
03055   // this logic into the walking code directly.)
03056   nsAutoVoidArray stack;
03057   for (;;) {
03058     stack.InsertElementAt(aFrame, 0);
03059 
03060     aFrame = aFrame->GetParent();
03061 
03062     NS_ASSERTION(aFrame, "wow, no block frame found");
03063     if (! aFrame)
03064       break;
03065 
03066     if (NS_STYLE_DISPLAY_INLINE != aFrame->GetStyleDisplay()->mDisplay)
03067       break;
03068   }
03069 
03070   // Using the path we've built up, walk the frame tree looking for
03071   // the text frame that follows aFrame.
03072   PRInt32 count;
03073   while ((count = stack.Count()) != 0) {
03074     PRInt32 lastIndex = count - 1;
03075     nsIFrame* top = NS_STATIC_CAST(nsIFrame*, stack.ElementAt(lastIndex));
03076 
03077     // If this is a frame that'll break a word, then bail.
03078     PRBool canContinue;
03079     top->CanContinueTextRun(canContinue);
03080     if (! canContinue)
03081       return nsnull;
03082 
03083     // Advance to top's next sibling
03084     nsIFrame* next = top->GetNextSibling();
03085 
03086     if (! next) {
03087       // No more siblings. Pop the top element to walk back up the
03088       // frame tree.
03089       stack.RemoveElementAt(lastIndex);
03090       continue;
03091     }
03092 
03093     // We know top's parent is good, but next's might not be. So let's
03094     // set it to be sure.
03095     next->SetParent(top->GetParent());
03096 
03097     // Save next at the top of the stack...
03098     stack.ReplaceElementAt(next, lastIndex);
03099 
03100     // ...and prowl down to next's deepest child. We'll need to check
03101     // for potential run-busters "on the way down", too.
03102     for (;;) {
03103       next->CanContinueTextRun(canContinue);
03104       if (! canContinue)
03105         return nsnull;
03106 
03107       nsIFrame* child = next->GetFirstChild(nsnull);
03108 
03109       if (! child)
03110         break;
03111 
03112       stack.AppendElement(child);
03113       next = child;
03114     }
03115 
03116     // Ignore continuing frames
03117     if (HasPrevInFlow(next))
03118       continue;
03119 
03120     // If this is a text frame, return it.
03121     if (nsLayoutAtoms::textFrame == next->GetType())
03122       return next;
03123   }
03124 
03125   // If we get here, then there are no more text frames in this block.
03126   return nsnull;
03127 }
03128 
03129 
03130 PRBool
03131 nsLineLayout::TreatFrameAsBlock(nsIFrame* aFrame)
03132 {
03133   const nsStyleDisplay* display = aFrame->GetStyleDisplay();
03134   if (NS_STYLE_POSITION_ABSOLUTE == display->mPosition) {
03135     return PR_FALSE;
03136   }
03137   if (NS_STYLE_FLOAT_NONE != display->mFloats) {
03138     return PR_FALSE;
03139   }
03140   switch (display->mDisplay) {
03141   case NS_STYLE_DISPLAY_BLOCK:
03142   case NS_STYLE_DISPLAY_LIST_ITEM:
03143   case NS_STYLE_DISPLAY_RUN_IN:
03144   case NS_STYLE_DISPLAY_COMPACT:
03145   case NS_STYLE_DISPLAY_TABLE:
03146     return PR_TRUE;
03147   }
03148   return PR_FALSE;
03149 }