Back to index

lightning-sunbird  0.9+nobinonly
nsLineBox.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 // vim:cindent:ts=2:et:sw=2:
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   L. David Baron <dbaron@dbaron.org>
00025  *   Pierre Phaneuf <pp@ludusdesign.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 #include "nsLineBox.h"
00041 #include "nsSpaceManager.h"
00042 #include "nsLineLayout.h"
00043 #include "prprf.h"
00044 #include "nsBlockFrame.h"
00045 #include "nsITextContent.h"
00046 #include "nsLayoutAtoms.h"
00047 #include "nsFrameManager.h"
00048 
00049 #ifdef DEBUG
00050 static PRInt32 ctorCount;
00051 PRInt32 nsLineBox::GetCtorCount() { return ctorCount; }
00052 #endif
00053 
00054 MOZ_DECL_CTOR_COUNTER(nsLineBox)
00055 
00056 nsLineBox::nsLineBox(nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock)
00057   : mFirstChild(aFrame),
00058     mBounds(0, 0, 0, 0),
00059     mMaxElementWidth(0),
00060     mMaximumWidth(-1),
00061     mData(nsnull)
00062 {
00063   MOZ_COUNT_CTOR(nsLineBox);
00064 #ifdef DEBUG
00065   ++ctorCount;
00066   NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
00067   nsIFrame* f = aFrame;
00068   for (PRInt32 n = aCount; n > 0; f = f->GetNextSibling(), --n) {
00069     NS_ASSERTION(aIsBlock == f->GetStyleDisplay()->IsBlockLevel(),
00070                  "wrong kind of child frame");
00071   }
00072 #endif
00073 
00074   mAllFlags = 0;
00075 #if NS_STYLE_CLEAR_NONE > 0
00076   mFlags.mBreakType = NS_STYLE_CLEAR_NONE;
00077 #endif
00078   SetChildCount(aCount);
00079   MarkDirty();
00080   mFlags.mBlock = aIsBlock;
00081 }
00082 
00083 nsLineBox::~nsLineBox()
00084 {
00085   MOZ_COUNT_DTOR(nsLineBox);
00086   Cleanup();
00087 }
00088 
00089 nsLineBox*
00090 NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame,
00091               PRInt32 aCount, PRBool aIsBlock)
00092 {
00093   return new (aPresShell)nsLineBox(aFrame, aCount, aIsBlock);
00094 }
00095 
00096 // Overloaded new operator. Uses an arena (which comes from the presShell)
00097 // to perform the allocation.
00098 void* 
00099 nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
00100 {
00101   return aPresShell->AllocateFrame(sz);
00102 }
00103 
00104 // Overloaded delete operator. Doesn't actually free the memory, because we
00105 // use an arena
00106 void 
00107 nsLineBox::operator delete(void* aPtr, size_t sz)
00108 {
00109 }
00110 
00111 void
00112 nsLineBox::Destroy(nsIPresShell* aPresShell)
00113 {
00114   // Destroy the object. This won't actually free the memory, though
00115   delete this;
00116 
00117   // Have the pres shell recycle the memory
00118   aPresShell->FreeFrame(sizeof(*this), (void*)this);
00119 }
00120 
00121 void
00122 nsLineBox::Cleanup()
00123 {
00124   if (mData) {
00125     if (IsBlock()) {
00126       delete mBlockData;
00127     }
00128     else {
00129       delete mInlineData;
00130     }
00131     mData = nsnull;
00132   }
00133 }
00134 
00135 #ifdef DEBUG
00136 static void
00137 ListFloats(FILE* out, PRInt32 aIndent, const nsFloatCacheList& aFloats)
00138 {
00139   nsAutoString frameName;
00140   nsFloatCache* fc = aFloats.Head();
00141   while (fc) {
00142     nsFrame::IndentBy(out, aIndent);
00143     nsPlaceholderFrame* ph = fc->mPlaceholder;
00144     if (nsnull != ph) {
00145       fprintf(out, "placeholder@%p ", NS_STATIC_CAST(void*, ph));
00146       nsIFrame* frame = ph->GetOutOfFlowFrame();
00147       if (nsnull != frame) {
00148         nsIFrameDebug*  frameDebug;
00149 
00150         if (NS_SUCCEEDED(frame->QueryInterface(NS_GET_IID(nsIFrameDebug), (void**)&frameDebug))) {
00151           frameDebug->GetFrameName(frameName);
00152           fputs(NS_LossyConvertUCS2toASCII(frameName).get(), out);
00153         }
00154       }
00155       fprintf(out, " %s region={%d,%d,%d,%d} combinedArea={%d,%d,%d,%d}",
00156               fc->mIsCurrentLineFloat ? "cl" : "bcl",
00157               fc->mRegion.x, fc->mRegion.y,
00158               fc->mRegion.width, fc->mRegion.height,
00159               fc->mCombinedArea.x, fc->mCombinedArea.y,
00160               fc->mCombinedArea.width, fc->mCombinedArea.height);
00161 
00162       fprintf(out, "\n");
00163     }
00164     fc = fc->Next();
00165   }
00166 }
00167 #endif
00168 
00169 #ifdef DEBUG
00170 const char *
00171 BreakTypeToString(PRUint8 aBreakType)
00172 {
00173   switch (aBreakType) {
00174   case NS_STYLE_CLEAR_NONE: return "nobr";
00175   case NS_STYLE_CLEAR_LEFT: return "leftbr";
00176   case NS_STYLE_CLEAR_RIGHT: return "rightbr";
00177   case NS_STYLE_CLEAR_LEFT_AND_RIGHT: return "leftbr+rightbr";
00178   case NS_STYLE_CLEAR_LINE: return "linebr";
00179   case NS_STYLE_CLEAR_BLOCK: return "blockbr";
00180   case NS_STYLE_CLEAR_COLUMN: return "columnbr";
00181   case NS_STYLE_CLEAR_PAGE: return "pagebr";
00182   default:
00183     break;
00184   }
00185   return "unknown";
00186 }
00187 
00188 char*
00189 nsLineBox::StateToString(char* aBuf, PRInt32 aBufSize) const
00190 {
00191   PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
00192               IsBlock() ? "block" : "inline",
00193               IsDirty() ? "dirty" : "clean",
00194               IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
00195               IsImpactedByFloat() ? "impacted" : "not impacted",
00196               IsLineWrapped() ? "wrapped" : "not wrapped",
00197               BreakTypeToString(GetBreakTypeBefore()),
00198               BreakTypeToString(GetBreakTypeAfter()),
00199               mAllFlags);
00200   return aBuf;
00201 }
00202 
00203 void
00204 nsLineBox::List(nsPresContext* aPresContext, FILE* out, PRInt32 aIndent) const
00205 {
00206   PRInt32 i;
00207 
00208   for (i = aIndent; --i >= 0; ) fputs("  ", out);
00209   char cbuf[100];
00210   fprintf(out, "line %p: count=%d state=%s ",
00211           NS_STATIC_CAST(const void*, this), GetChildCount(),
00212           StateToString(cbuf, sizeof(cbuf)));
00213   if (IsBlock() && !GetCarriedOutBottomMargin().IsZero()) {
00214     fprintf(out, "bm=%d ", GetCarriedOutBottomMargin().get());
00215   }
00216   if (0 != mMaxElementWidth) {
00217     fprintf(out, "mew=%d ", mMaxElementWidth);
00218   }
00219   fprintf(out, "{%d,%d,%d,%d} ",
00220           mBounds.x, mBounds.y, mBounds.width, mBounds.height);
00221   if (mData) {
00222     fprintf(out, "ca={%d,%d,%d,%d} ",
00223             mData->mCombinedArea.x, mData->mCombinedArea.y,
00224             mData->mCombinedArea.width, mData->mCombinedArea.height);
00225   }
00226   fprintf(out, "<\n");
00227 
00228   nsIFrame* frame = mFirstChild;
00229   PRInt32 n = GetChildCount();
00230   while (--n >= 0) {
00231     nsIFrameDebug*  frameDebug;
00232 
00233     if (NS_SUCCEEDED(frame->QueryInterface(NS_GET_IID(nsIFrameDebug), (void**)&frameDebug))) {
00234       frameDebug->List(aPresContext, out, aIndent + 1);
00235     }
00236     frame = frame->GetNextSibling();
00237   }
00238 
00239   for (i = aIndent; --i >= 0; ) fputs("  ", out);
00240   if (HasFloats()) {
00241     fputs("> floats <\n", out);
00242     ListFloats(out, aIndent + 1, mInlineData->mFloats);
00243     for (i = aIndent; --i >= 0; ) fputs("  ", out);
00244   }
00245   fputs(">\n", out);
00246 }
00247 #endif
00248 
00249 nsIFrame*
00250 nsLineBox::LastChild() const
00251 {
00252   nsIFrame* frame = mFirstChild;
00253   PRInt32 n = GetChildCount() - 1;
00254   while (--n >= 0) {
00255     frame = frame->GetNextSibling();
00256   }
00257   return frame;
00258 }
00259 
00260 PRBool
00261 nsLineBox::IsLastChild(nsIFrame* aFrame) const
00262 {
00263   nsIFrame* lastFrame = LastChild();
00264   return aFrame == lastFrame;
00265 }
00266 
00267 PRInt32
00268 nsLineBox::IndexOf(nsIFrame* aFrame) const
00269 {
00270   PRInt32 i, n = GetChildCount();
00271   nsIFrame* frame = mFirstChild;
00272   for (i = 0; i < n; i++) {
00273     if (frame == aFrame) {
00274       return i;
00275     }
00276     frame = frame->GetNextSibling();
00277   }
00278   return -1;
00279 }
00280 
00281 PRBool
00282 nsLineBox::IsEmpty() const
00283 {
00284   if (IsBlock())
00285     return mFirstChild->IsEmpty();
00286 
00287   PRInt32 n;
00288   nsIFrame *kid;
00289   for (n = GetChildCount(), kid = mFirstChild;
00290        n > 0;
00291        --n, kid = kid->GetNextSibling())
00292   {
00293     if (!kid->IsEmpty())
00294       return PR_FALSE;
00295   }
00296   return PR_TRUE;
00297 }
00298 
00299 PRBool
00300 nsLineBox::CachedIsEmpty()
00301 {
00302   if (mFlags.mDirty) {
00303     return IsEmpty();
00304   }
00305   
00306   if (mFlags.mEmptyCacheValid) {
00307     return mFlags.mEmptyCacheState;
00308   }
00309 
00310   PRBool result = IsEmpty();
00311   mFlags.mEmptyCacheValid = PR_TRUE;
00312   mFlags.mEmptyCacheState = result;
00313   return result;
00314 }
00315 
00316 void
00317 nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines)
00318 {
00319   if (! aLines.empty()) {
00320     // Delete our child frames before doing anything else. In particular
00321     // we do all of this before our base class releases it's hold on the
00322     // view.
00323     for (nsIFrame* child = aLines.front()->mFirstChild; child; ) {
00324       nsIFrame* nextChild = child->GetNextSibling();
00325       child->Destroy(aPresContext);
00326       child = nextChild;
00327     }
00328 
00329     nsIPresShell *shell = aPresContext->PresShell();
00330 
00331     do {
00332       nsLineBox* line = aLines.front();
00333       aLines.pop_front();
00334       line->Destroy(shell);
00335     } while (! aLines.empty());
00336   }
00337 }
00338 
00339 nsLineBox*
00340 nsLineBox::FindLineContaining(nsLineList& aLines, nsIFrame* aFrame,
00341                               PRInt32* aFrameIndexInLine)
00342 {
00343   NS_PRECONDITION(aFrameIndexInLine && !aLines.empty() && aFrame, "null ptr");
00344   for (nsLineList::iterator line = aLines.begin(),
00345                             line_end = aLines.end();
00346        line != line_end;
00347        ++line)
00348   {
00349     PRInt32 ix = line->IndexOf(aFrame);
00350     if (ix >= 0) {
00351       *aFrameIndexInLine = ix;
00352       return line;
00353     }
00354   }
00355   *aFrameIndexInLine = -1;
00356   return nsnull;
00357 }
00358 
00359 PRBool
00360 nsLineBox::RFindLineContaining(nsIFrame* aFrame,
00361                                const nsLineList::iterator& aBegin,
00362                                nsLineList::iterator& aEnd,
00363                                PRInt32* aFrameIndexInLine)
00364 {
00365   NS_PRECONDITION(aFrame, "null ptr");
00366   while (aBegin != aEnd) {
00367     --aEnd;
00368     PRInt32 ix = aEnd->IndexOf(aFrame);
00369     if (ix >= 0) {
00370       *aFrameIndexInLine = ix;
00371       return PR_TRUE;
00372     }
00373   }
00374   *aFrameIndexInLine = -1;
00375   return PR_FALSE;
00376 }
00377 
00378 nsCollapsingMargin
00379 nsLineBox::GetCarriedOutBottomMargin() const
00380 {
00381   NS_ASSERTION(IsBlock(),
00382                "GetCarriedOutBottomMargin called on non-block line.");
00383   return (IsBlock() && mBlockData)
00384     ? mBlockData->mCarriedOutBottomMargin
00385     : nsCollapsingMargin();
00386 }
00387 
00388 PRBool
00389 nsLineBox::SetCarriedOutBottomMargin(nsCollapsingMargin aValue)
00390 {
00391   PRBool changed = PR_FALSE;
00392   if (IsBlock()) {
00393     if (!aValue.IsZero()) {
00394       if (!mBlockData) {
00395         mBlockData = new ExtraBlockData(mBounds);
00396       }
00397       if (mBlockData) {
00398         changed = aValue != mBlockData->mCarriedOutBottomMargin;
00399         mBlockData->mCarriedOutBottomMargin = aValue;
00400       }
00401     }
00402     else if (mBlockData) {
00403       changed = aValue != mBlockData->mCarriedOutBottomMargin;
00404       mBlockData->mCarriedOutBottomMargin = aValue;
00405       MaybeFreeData();
00406     }
00407   }
00408   return changed;
00409 }
00410 
00411 void
00412 nsLineBox::MaybeFreeData()
00413 {
00414   if (mData && (mData->mCombinedArea == mBounds)) {
00415     if (IsInline()) {
00416       if (mInlineData->mFloats.IsEmpty()) {
00417         delete mInlineData;
00418         mInlineData = nsnull;
00419       }
00420     }
00421     else if (mBlockData->mCarriedOutBottomMargin.IsZero()) {
00422       delete mBlockData;
00423       mBlockData = nsnull;
00424     }
00425   }
00426 }
00427 
00428 // XXX get rid of this???
00429 nsFloatCache*
00430 nsLineBox::GetFirstFloat()
00431 {
00432   NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
00433   return mInlineData ? mInlineData->mFloats.Head() : nsnull;
00434 }
00435 
00436 // XXX this might be too eager to free memory
00437 void
00438 nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
00439 {
00440   NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
00441   if (IsInline() && mInlineData) {
00442     if (mInlineData->mFloats.NotEmpty()) {
00443       aFreeList.Append(mInlineData->mFloats);
00444     }
00445     MaybeFreeData();
00446   }
00447 }
00448 
00449 void
00450 nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
00451 { 
00452   NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
00453   if (IsInline()) {
00454     if (aFreeList.NotEmpty()) {
00455       if (!mInlineData) {
00456         mInlineData = new ExtraInlineData(mBounds);
00457       }
00458       if (mInlineData) {
00459         mInlineData->mFloats.Append(aFreeList);
00460       }
00461     }
00462   }
00463 }
00464 
00465 PRBool
00466 nsLineBox::RemoveFloat(nsIFrame* aFrame)
00467 {
00468   NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
00469   if (IsInline() && mInlineData) {
00470     nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
00471     if (fc) {
00472       // Note: the placeholder is part of the line's child list
00473       // and will be removed later.
00474       mInlineData->mFloats.Remove(fc);
00475       MaybeFreeData();
00476       return PR_TRUE;
00477     }
00478   }
00479   return PR_FALSE;
00480 }
00481 
00482 void
00483 nsLineBox::SetCombinedArea(const nsRect& aCombinedArea)
00484 {  
00485   NS_ASSERTION(aCombinedArea.width >= 0, "illegal width for combined area");
00486   NS_ASSERTION(aCombinedArea.height >= 0, "illegal height for combined area");
00487   if (aCombinedArea != mBounds) {
00488     if (mData) {
00489       mData->mCombinedArea = aCombinedArea;
00490     }
00491     else {
00492       if (IsInline()) {
00493         mInlineData = new ExtraInlineData(aCombinedArea);
00494       }
00495       else {
00496         mBlockData = new ExtraBlockData(aCombinedArea);
00497       }
00498     }
00499   }
00500   else {
00501     if (mData) {
00502       // Store away new value so that MaybeFreeData compares against
00503       // the right value.
00504       mData->mCombinedArea = aCombinedArea;
00505     }
00506     MaybeFreeData();
00507   }
00508 }
00509 
00510 //----------------------------------------------------------------------
00511 
00512 
00513 static nsLineBox* gDummyLines[1];
00514 
00515 nsLineIterator::nsLineIterator()
00516 {
00517   mLines = gDummyLines;
00518   mNumLines = 0;
00519   mIndex = 0;
00520   mRightToLeft = PR_FALSE;
00521 }
00522 
00523 nsLineIterator::~nsLineIterator()
00524 {
00525   if (mLines != gDummyLines) {
00526     delete [] mLines;
00527   }
00528 }
00529 
00530 NS_IMPL_ISUPPORTS2(nsLineIterator, nsILineIterator, nsILineIteratorNavigator)
00531 
00532 nsresult
00533 nsLineIterator::Init(nsLineList& aLines, PRBool aRightToLeft)
00534 {
00535   mRightToLeft = aRightToLeft;
00536 
00537   // Count the lines
00538   PRInt32 numLines = aLines.size();
00539   if (0 == numLines) {
00540     // Use gDummyLines so that we don't need null pointer checks in
00541     // the accessor methods
00542     mLines = gDummyLines;
00543     return NS_OK;
00544   }
00545 
00546   // Make a linear array of the lines
00547   mLines = new nsLineBox*[numLines];
00548   if (!mLines) {
00549     // Use gDummyLines so that we don't need null pointer checks in
00550     // the accessor methods
00551     mLines = gDummyLines;
00552     return NS_ERROR_OUT_OF_MEMORY;
00553   }
00554   nsLineBox** lp = mLines;
00555   for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
00556        line != line_end;
00557        ++line)
00558   {
00559     *lp++ = line;
00560   }
00561   mNumLines = numLines;
00562   return NS_OK;
00563 }
00564 
00565 NS_IMETHODIMP
00566 nsLineIterator::GetNumLines(PRInt32* aResult)
00567 {
00568   NS_PRECONDITION(aResult, "null OUT ptr");
00569   if (!aResult) {
00570     return NS_ERROR_NULL_POINTER;
00571   }
00572   *aResult = mNumLines;
00573   return NS_OK;
00574 }
00575 
00576 NS_IMETHODIMP
00577 nsLineIterator::GetDirection(PRBool* aIsRightToLeft)
00578 {
00579   NS_PRECONDITION(aIsRightToLeft, "null OUT ptr");
00580   if (!aIsRightToLeft) {
00581     return NS_ERROR_NULL_POINTER;
00582   }
00583   *aIsRightToLeft = mRightToLeft;
00584   return NS_OK;
00585 }
00586 
00587 NS_IMETHODIMP
00588 nsLineIterator::GetLine(PRInt32 aLineNumber,
00589                         nsIFrame** aFirstFrameOnLine,
00590                         PRInt32* aNumFramesOnLine,
00591                         nsRect& aLineBounds,
00592                         PRUint32* aLineFlags)
00593 {
00594   NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
00595   NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
00596   NS_ENSURE_ARG_POINTER(aLineFlags);
00597 
00598   if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
00599     *aFirstFrameOnLine = nsnull;
00600     *aNumFramesOnLine = 0;
00601     aLineBounds.SetRect(0, 0, 0, 0);
00602     return NS_OK;
00603   }
00604   nsLineBox* line = mLines[aLineNumber];
00605   *aFirstFrameOnLine = line->mFirstChild;
00606   *aNumFramesOnLine = line->GetChildCount();
00607   aLineBounds = line->mBounds;
00608 
00609   PRUint32 flags = 0;
00610   if (line->IsBlock()) {
00611     flags |= NS_LINE_FLAG_IS_BLOCK;
00612   }
00613   else {
00614     if (line->HasBreakAfter())
00615       flags |= NS_LINE_FLAG_ENDS_IN_BREAK;
00616   }
00617   *aLineFlags = flags;
00618 
00619   return NS_OK;
00620 }
00621 
00622 NS_IMETHODIMP
00623 nsLineIterator::FindLineContaining(nsIFrame* aFrame,
00624                                    PRInt32* aLineNumberResult)
00625 {
00626   nsLineBox* line = mLines[0];
00627   PRInt32 lineNumber = 0;
00628   while (lineNumber != mNumLines) {
00629     if (line->Contains(aFrame)) {
00630       *aLineNumberResult = lineNumber;
00631       return NS_OK;
00632     }
00633     line = mLines[++lineNumber];
00634   }
00635   *aLineNumberResult = -1;
00636   return NS_OK;
00637 }
00638 
00639 NS_IMETHODIMP
00640 nsLineIterator::FindLineAt(nscoord aY,
00641                            PRInt32* aLineNumberResult)
00642 {
00643   nsLineBox* line = mLines[0];
00644   if (!line || (aY < line->mBounds.y)) {
00645     *aLineNumberResult = -1;
00646     return NS_OK;
00647   }
00648   PRInt32 lineNumber = 0;
00649   while (lineNumber != mNumLines) {
00650     if ((aY >= line->mBounds.y) && (aY < line->mBounds.YMost())) {
00651       *aLineNumberResult = lineNumber;
00652       return NS_OK;
00653     }
00654     line = mLines[++lineNumber];
00655   }
00656   *aLineNumberResult = mNumLines;
00657   return NS_OK;
00658 }
00659 
00660 #ifdef IBMBIDI
00661 NS_IMETHODIMP
00662 nsLineIterator::CheckLineOrder(PRInt32                  aLine,
00663                                PRBool                   *aIsReordered,
00664                                nsIFrame                 **aFirstVisual,
00665                                nsIFrame                 **aLastVisual)
00666 {
00667   PRInt32   currentLine, saveLine, testLine;
00668   nscoord   saveX;
00669   nsIFrame  *checkFrame;
00670   nsIFrame  *firstFrame;
00671   nsIFrame  *leftmostFrame;
00672   nsIFrame  *rightmostFrame;
00673   nscoord   minX, maxX;
00674   PRInt32   lineFrameCount;
00675   PRUint32  lineFlags;
00676 
00677   nsresult  result = NS_OK;
00678 
00679   // an RTL paragraph is always considered as reordered
00680   // in an LTR paragraph, find out by examining the coordinates of each frame in the line
00681   if (mRightToLeft)
00682     *aIsReordered = PR_TRUE;
00683   else {
00684     *aIsReordered = PR_FALSE;
00685 
00686     // Check the preceding and following line, since we might be moving into them
00687     for (currentLine = PR_MAX(0, aLine-1); currentLine < aLine+1; currentLine++) {
00688 
00689       nsLineBox* line = mLines[currentLine];
00690       if (!line)
00691         break;
00692 
00693       checkFrame = line->mFirstChild;
00694 
00695       result = FindLineContaining(checkFrame, &saveLine);
00696       if (NS_FAILED(result))
00697         return result;
00698       saveX = checkFrame->GetRect().x;
00699       lineFrameCount = line->GetChildCount();
00700 
00701       for (; checkFrame; checkFrame = checkFrame->GetNextSibling()) {
00702         result = FindLineContaining(checkFrame, &testLine);
00703         if (NS_FAILED(result))
00704           return result;
00705         if (testLine != saveLine) {
00706           *aIsReordered = PR_TRUE;
00707           break;
00708         }
00709 
00710         nsRect checkRect = checkFrame->GetRect();
00711         // If the origin of any frame is less than the previous frame, the line is reordered
00712         if (checkRect.x < saveX) {
00713           *aIsReordered = PR_TRUE;
00714           break;
00715         }
00716         saveX = checkRect.x;
00717         lineFrameCount--;
00718         if (0 == lineFrameCount)
00719           break;
00720       }
00721       if (*aIsReordered)
00722         break;
00723     }
00724   }
00725 
00726   // If the line is reordered, identify the first and last frames on the line
00727   if (*aIsReordered) {
00728     nsRect nonUsedRect;
00729     result = GetLine(aLine, &firstFrame, &lineFrameCount, nonUsedRect, &lineFlags);
00730     if (NS_FAILED(result))
00731       return result;
00732 
00733     leftmostFrame = rightmostFrame = firstFrame;
00734     maxX = minX = firstFrame->GetRect().x;
00735 
00736     for (;lineFrameCount > 1;lineFrameCount--) {
00737       firstFrame = firstFrame->GetNextSibling();
00738 
00739       nsRect checkRect = firstFrame->GetRect();
00740       if (checkRect.x > maxX) {
00741         maxX = checkRect.x;
00742         rightmostFrame = firstFrame;
00743       }
00744       if (checkRect.x < minX) {
00745         minX = checkRect.x;
00746         leftmostFrame = firstFrame;
00747       }
00748     }
00749     if (mRightToLeft) {
00750       *aFirstVisual = rightmostFrame;
00751       *aLastVisual = leftmostFrame;
00752     }
00753     else {
00754       *aFirstVisual = leftmostFrame;
00755       *aLastVisual = rightmostFrame;
00756     }
00757   }
00758   return result;
00759 }
00760 #endif // IBMBIDI
00761 
00762 NS_IMETHODIMP
00763 nsLineIterator::FindFrameAt(PRInt32 aLineNumber,
00764                             nscoord aX,
00765                             nsIFrame** aFrameFound,
00766                             PRBool* aXIsBeforeFirstFrame,
00767                             PRBool* aXIsAfterLastFrame)
00768 {
00769   NS_PRECONDITION(aFrameFound && aXIsBeforeFirstFrame && aXIsAfterLastFrame,
00770                   "null OUT ptr");
00771   if (!aFrameFound || !aXIsBeforeFirstFrame || !aXIsAfterLastFrame) {
00772     return NS_ERROR_NULL_POINTER;
00773   }
00774   if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
00775     return NS_ERROR_INVALID_ARG;
00776   }
00777 
00778   nsLineBox* line = mLines[aLineNumber];
00779   if (!line) {
00780     *aFrameFound = nsnull;
00781     *aXIsBeforeFirstFrame = PR_TRUE;
00782     *aXIsAfterLastFrame = PR_FALSE;
00783     return NS_OK;
00784   }
00785 
00786   if (line->mBounds.width == 0 && line->mBounds.height == 0)
00787     return NS_ERROR_FAILURE;
00788 
00789   nsIFrame* frame = line->mFirstChild;
00790   nsIFrame* closestFromLeft = nsnull;
00791   nsIFrame* closestFromRight = nsnull;
00792   PRInt32 n = line->GetChildCount();
00793   while (n--) {
00794     nsRect rect = frame->GetRect();
00795     if (rect.width > 0) {
00796       // If aX is inside this frame - this is it
00797       if (rect.x <= aX && rect.XMost() > aX) {
00798         closestFromLeft = closestFromRight = frame;
00799         break;
00800       }
00801       if (rect.x < aX) {
00802         if (!closestFromLeft || 
00803             rect.XMost() > closestFromLeft->GetRect().XMost())
00804           closestFromLeft = frame;
00805       }
00806       else {
00807         if (!closestFromRight ||
00808             rect.x < closestFromRight->GetRect().x)
00809           closestFromRight = frame;
00810       }
00811     }
00812     frame = frame->GetNextSibling();
00813   }
00814   if (!closestFromLeft && !closestFromRight) {
00815     // All frames were zero-width. Just take the first one.
00816     closestFromLeft = closestFromRight = line->mFirstChild;
00817   }
00818   *aXIsBeforeFirstFrame = mRightToLeft ? !closestFromRight : !closestFromLeft;
00819   *aXIsAfterLastFrame = mRightToLeft ? !closestFromLeft : !closestFromRight;
00820   if (closestFromLeft == closestFromRight) {
00821     *aFrameFound = closestFromLeft;
00822   }
00823   else if (!closestFromLeft) {
00824     *aFrameFound = closestFromRight;
00825   }
00826   else if (!closestFromRight) {
00827     *aFrameFound = closestFromLeft;
00828   }
00829   else { // we're between two frames
00830     nscoord delta = closestFromRight->GetRect().x - closestFromLeft->GetRect().XMost();
00831     if (aX < closestFromLeft->GetRect().XMost() + delta/2)
00832       *aFrameFound = closestFromLeft;
00833     else
00834       *aFrameFound = closestFromRight;
00835   }
00836   return NS_OK;
00837 }
00838 
00839 NS_IMETHODIMP
00840 nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32 aLineNumber)
00841 {
00842   aFrame = aFrame->GetNextSibling();
00843   return NS_OK;
00844 }
00845 
00846 //----------------------------------------------------------------------
00847 
00848 nsFloatCacheList::~nsFloatCacheList()
00849 {
00850   nsFloatCache* fc = mHead;
00851   while (fc) {
00852     nsFloatCache* next = fc->mNext;
00853     delete fc;
00854     fc = next;
00855   }
00856   mHead = nsnull;
00857 }
00858 
00859 nsFloatCache*
00860 nsFloatCacheList::Tail() const
00861 {
00862   nsFloatCache* fc = mHead;
00863   while (fc) {
00864     if (!fc->mNext) {
00865       break;
00866     }
00867     fc = fc->mNext;
00868   }
00869   return fc;
00870 }
00871 
00872 void
00873 nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
00874 {
00875   NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
00876   
00877   nsFloatCache* tail = Tail();
00878   if (tail) {
00879     tail->mNext = aList.mHead;
00880   }
00881   else {
00882     mHead = aList.mHead;
00883   }
00884   aList.mHead = nsnull;
00885   aList.mTail = nsnull;
00886 }
00887 
00888 nsFloatCache*
00889 nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
00890 {
00891   nsFloatCache* fc = mHead;
00892   while (fc) {
00893     if (fc->mPlaceholder->GetOutOfFlowFrame() == aOutOfFlowFrame) {
00894       break;
00895     }
00896     fc = fc->Next();
00897   }
00898   return fc;
00899 }
00900 
00901 void
00902 nsFloatCacheList::Remove(nsFloatCache* aElement)
00903 {
00904   nsFloatCache** fcp = &mHead;
00905   nsFloatCache* fc;
00906   while (nsnull != (fc = *fcp)) {
00907     if (fc == aElement) {
00908       *fcp = fc->mNext;
00909       break;
00910     }
00911     fcp = &fc->mNext;
00912   }
00913 }
00914 
00915 //----------------------------------------------------------------------
00916 
00917 void
00918 nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
00919 {
00920   NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
00921   
00922   if (mTail) {
00923     mTail->mNext = aList.mHead;
00924   }
00925   else {
00926     mHead = aList.mHead;
00927   }
00928   mTail = aList.Tail();
00929   aList.mHead = nsnull;
00930 }
00931 
00932 nsFloatCache*
00933 nsFloatCacheFreeList::Alloc()
00934 {
00935   nsFloatCache* fc = mHead;
00936   if (mHead) {
00937     if (mHead == mTail) {
00938       mHead = mTail = nsnull;
00939     }
00940     else {
00941       mHead = fc->mNext;
00942     }
00943     fc->mNext = nsnull;
00944   }
00945   else {
00946     fc = new nsFloatCache();
00947   }
00948   return fc;
00949 }
00950 
00951 void
00952 nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
00953 {
00954   aFloat->mNext = nsnull;
00955   if (mTail) {
00956     mTail->mNext = aFloat;
00957     mTail = aFloat;
00958   }
00959   else {
00960     mHead = mTail = aFloat;
00961   }
00962 }
00963 
00964 //----------------------------------------------------------------------
00965 
00966 MOZ_DECL_CTOR_COUNTER(nsFloatCache)
00967 
00968 nsFloatCache::nsFloatCache()
00969   : mPlaceholder(nsnull),
00970     mIsCurrentLineFloat(PR_TRUE),
00971     mMargins(0, 0, 0, 0),
00972     mOffsets(0, 0, 0, 0),
00973     mCombinedArea(0, 0, 0, 0),
00974     mNext(nsnull)
00975 {
00976   MOZ_COUNT_CTOR(nsFloatCache);
00977 }
00978 
00979 #ifdef NS_BUILD_REFCNT_LOGGING
00980 nsFloatCache::~nsFloatCache()
00981 {
00982   MOZ_COUNT_DTOR(nsFloatCache);
00983 }
00984 #endif