/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Mozilla Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are Copyright (C) 1998
 * Netscape Communications Corporation.  All Rights Reserved.
 */
#include "nsHTMLContainerFrame.h"
#include "nsFrameReflowState.h"
#include "nsLineLayout.h"
#include "nsInlineReflow.h"
#include "nsAbsoluteFrame.h"
#include "nsPlaceholderFrame.h"
#include "nsStyleConsts.h"
#include "nsHTMLIIDs.h"
#include "nsCSSRendering.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsIReflowCommand.h"
#include "nsISpaceManager.h"
#include "nsIStyleContext.h"
#include "nsIView.h"
#include "nsIFontMetrics.h"
#include "nsHTMLParts.h"
#include "nsHTMLAtoms.h"
#include "nsHTMLValue.h"
#include "nsDOMEvent.h"
#include "nsIHTMLContent.h"
#include "prprf.h"
// XXX temporary for :first-letter support
#include "nsITextContent.h"
static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */
//XXX begin
// 11-03-98: low hanging memory fruit: get rid of text-runs that have
// only one piece of text in them!
// 09-17-98: I don't like keeping mInnerBottomMargin
// 09-18-98: floating block elements don't size quite right because we
// wrap them in a body frame and the body frame doesn't honor the css
// width/height properties (among others!). The body code needs
// updating.
//XXX end
#ifdef NS_DEBUG
#undef NOISY_FIRST_LINE
#undef REALLY_NOISY_FIRST_LINE
#undef NOISY_FIRST_LETTER
#undef NOISY_MAX_ELEMENT_SIZE
#undef NOISY_RUNIN
#else
#undef NOISY_FIRST_LINE
#undef REALLY_NOISY_FIRST_LINE
#undef NOISY_FIRST_LETTER
#undef NOISY_MAX_ELEMENT_SIZE
#undef NOISY_RUNIN
#endif
/* 52b33130-0b99-11d2-932e-00805f8add32 */
#define NS_BLOCK_FRAME_CID \
{ 0x52b33130, 0x0b99, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32}}
static const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID;
class BulletFrame;
struct LineData;
class nsBlockFrame;
struct nsBlockReflowState : public nsFrameReflowState {
  nsBlockReflowState(nsIPresContext& aPresContext,
                     const nsHTMLReflowState& aReflowState,
                     const nsHTMLReflowMetrics& aMetrics);
  ~nsBlockReflowState();
  /**
   * Update the mCurrentBand data based on the current mY position.
   */
  void GetAvailableSpace();
  void InitFloater(nsPlaceholderFrame* aPlaceholderFrame);
  void AddFloater(nsPlaceholderFrame* aPlaceholderFrame);
  void PlaceFloater(nsPlaceholderFrame* aFloater, PRBool& aIsLeftFloater);
  void PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem);
  void ClearFloaters(PRUint8 aBreakType);
  PRBool IsLeftMostChild(nsIFrame* aFrame);
  nsLineLayout mLineLayout;
  nsInlineReflow* mInlineReflow;
  nsISpaceManager* mSpaceManager;
  nscoord mSpaceManagerX, mSpaceManagerY;
  nsBlockFrame* mBlock;
  nsBlockFrame* mNextInFlow;
  PRBool mInlineReflowPrepared;
  nsBlockFrame* mRunInFrame;
  nsBlockFrame* mRunInToFrame;
  PRUint8 mTextAlign;
  PRUintn mPrevMarginFlags;
// previous margin flag bits (above and beyond the carried-out-margin flags)
#define HAVE_CARRIED_MARGIN 0x100
#define ALREADY_APPLIED_BOTTOM_MARGIN 0x200
  nscoord mBottomEdge;          // maximum Y
  PRBool mUnconstrainedWidth;
  PRBool mUnconstrainedHeight;
  nscoord mY;
  nscoord mKidXMost;
  nsIFrame* mPrevChild;
  LineData* mFreeList;
  nsVoidArray mPendingFloaters;
  LineData* mCurrentLine;
  LineData* mPrevLine;
  // The next list ordinal for counting list bullets
  PRInt32 mNextListOrdinal;
  // XXX what happens if we need more than 12 trapezoids?
  struct BlockBandData : public nsBandData {
    // Trapezoids used during band processing
    nsBandTrapezoid data[12];
    // Bounding rect of available space between any left and right floaters
    nsRect          availSpace;
    BlockBandData() {
      size = 12;
      trapezoids = data;
    }
    /**
     * Computes the bounding rect of the available space, i.e. space
     * between any left and right floaters Uses the current trapezoid
     * data, see nsISpaceManager::GetBandData(). Also updates member
     * data "availSpace".
     */
    void ComputeAvailSpaceRect();
  };
  BlockBandData mCurrentBand;
};
// XXX This is vile. Make it go away
void
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
{
  mBlockReflowState->InitFloater(aFrame);
}
void
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
{
  mBlockReflowState->AddFloater(aFrame);
}
//----------------------------------------------------------------------
#define nsBlockFrameSuper nsHTMLContainerFrame
class nsBlockFrame : public nsBlockFrameSuper
{
public:
  nsBlockFrame(nsIContent* aContent, nsIFrame* aParent);
  ~nsBlockFrame();
  // nsISupports
  NS_IMETHOD  QueryInterface(const nsIID& aIID, void** aInstancePtr);
  // nsIFrame
  NS_IMETHOD SetInitialChildList(nsIPresContext& aPresContext,
                                 nsIAtom*        aListName,
                                 nsIFrame*       aChildList);
  NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext,
                                   nsIStyleContext* aParentContext);
  NS_IMETHOD FirstChild(nsIAtom* aListName, nsIFrame*& aFirstChild) const;
  NS_IMETHOD GetAdditionalChildListName(PRInt32   aIndex,
                                        nsIAtom*& aListName) const;
  NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);
  NS_IMETHOD IsSplittable(nsSplittableType& aIsSplittable) const;
  NS_IMETHOD CreateContinuingFrame(nsIPresContext&  aPresContext,
                                   nsIFrame*        aParent,
                                   nsIStyleContext* aStyleContext,
                                   nsIFrame*&       aContinuingFrame);
  NS_IMETHOD Paint(nsIPresContext&      aPresContext,
                   nsIRenderingContext& aRenderingContext,
                   const nsRect&        aDirtyRect);
  NS_IMETHOD IsPercentageBase(PRBool& aBase) const {
    aBase = PR_TRUE;
    return NS_OK;
  }
  NS_IMETHOD List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const;
  NS_IMETHOD ListTag(FILE* out) const;
  NS_IMETHOD VerifyTree() const;
  // nsIHTMLReflow
  NS_IMETHOD Reflow(nsIPresContext&          aPresContext,
                    nsHTMLReflowMetrics&     aDesiredSize,
                    const nsHTMLReflowState& aReflowState,
                    nsReflowStatus&          aStatus);
  NS_IMETHOD MoveInSpaceManager(nsIPresContext& aPresContext,
                                nsISpaceManager* aSpaceManager,
                                nscoord aDeltaX, nscoord aDeltaY);
  // nsBlockFrame
  void TakeRunInFrames(nsBlockFrame* aRunInFrame);
  PRBool ShouldPlaceBullet(LineData* aLine);
  void PlaceBullet(nsBlockReflowState& aState,
                   nscoord aMaxAscent,
                   nscoord aTopMargin);
#ifdef DO_SELECTION
  NS_IMETHOD  HandleEvent(nsIPresContext& aPresContext,
                          nsGUIEvent* aEvent,
                          nsEventStatus& aEventStatus);
  NS_IMETHOD  HandleDrag(nsIPresContext& aPresContext, 
                         nsGUIEvent*     aEvent,
                         nsEventStatus&  aEventStatus);
  nsIFrame * FindHitFrame(nsBlockFrame * aBlockFrame, 
                          const nscoord aX, const nscoord aY,
                          const nsPoint & aPoint);
#endif
  virtual PRBool DeleteChildsNextInFlow(nsIPresContext& aPresContext,
                                        nsIFrame* aNextInFlow);
  void RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame);
  void SetFlags(PRUint32 aFlags) {
    mFlags = aFlags;
  }
  void RecoverLineMargins(nsBlockReflowState& aState,
                          LineData* aPrevLine,
                          nscoord& aTopMarginResult,
                          nscoord& aBottomMarginResult);
  PRUintn CalculateMargins(nsBlockReflowState& aState,
                           LineData* aLine,
                           PRBool aInlineContext,
                           nscoord& aTopMarginResult,
                           nscoord& aBottomMarginResult);
  void SlideFrames(nsIPresContext& aPresContext,
                   nsISpaceManager* aSpaceManager,
                   LineData* aLine, nscoord aDY);
  PRBool DrainOverflowLines();
  PRBool RemoveChild(LineData* aLines, nsIFrame* aChild);
  PRIntn GetSkipSides() const;
  nsresult InitialReflow(nsBlockReflowState& aState);
  nsresult FrameAppendedReflow(nsBlockReflowState& aState);
  nsresult InsertNewFrame(nsIPresContext&  aPresContext,
                          nsBlockFrame*    aParentFrame,
                          nsIFrame*        aNewFrame,
                          nsIFrame*        aPrevSibling);
  nsresult FrameInsertedReflow(nsBlockReflowState& aState);
  nsresult FrameRemovedReflow(nsBlockReflowState& aState);
  nsresult StyleChangedReflow(nsBlockReflowState& aState);
  nsresult FindTextRuns(nsBlockReflowState& aState);
  nsresult ChildIncrementalReflow(nsBlockReflowState& aState);
  nsresult ResizeReflow(nsBlockReflowState& aState);
  void ComputeFinalSize(nsBlockReflowState&  aState,
                        nsHTMLReflowMetrics& aMetrics);
  void BuildFloaterList();
  nsresult ReflowLinesAt(nsBlockReflowState& aState, LineData* aLine);
  PRBool ReflowLine(nsBlockReflowState& aState,
                    LineData* aLine,
                    nsReflowStatus& aReflowResult);
  PRBool PlaceLine(nsBlockReflowState& aState,
                   LineData* aLine,
                   nsReflowStatus aReflowStatus);
  PRBool IsLastLine(nsBlockReflowState& aState,
                    LineData* aLine,
                    nsReflowStatus aReflowStatus);
  void FindFloaters(LineData* aLine);
  void PrepareInlineReflow(nsBlockReflowState& aState, nsIFrame* aFrame, PRBool aIsBlock);
  PRBool ReflowInlineFrame(nsBlockReflowState& aState,
                           LineData* aLine,
                           nsIFrame* aFrame,
                           nsReflowStatus& aResult,
                           PRBool& aAddedToLine);
  nsresult SplitLine(nsBlockReflowState& aState,
                     LineData* aLine,
                     nsIFrame* aFrame);
  nsBlockFrame* FindFollowingBlockFrame(nsIFrame* aFrame);
  PRBool ReflowBlockFrame(nsBlockReflowState& aState,
                          LineData* aLine,
                          nsIFrame* aFrame,
                          nsReflowStatus& aResult);
  PRBool PullFrame(nsBlockReflowState& aState,
                   LineData* aToLine,
                   LineData** aFromList,
                   PRBool aUpdateGeometricParent,
                   nsReflowStatus& aResult,
                   PRBool& aAddedToLine);
  void PushLines(nsBlockReflowState& aState);
  void ReflowFloater(nsIPresContext& aPresContext,
                     nsBlockReflowState& aState,
                     nsIFrame* aFloaterFrame);
  void PaintChildren(nsIPresContext& aPresContext,
                     nsIRenderingContext& aRenderingContext,
                     const nsRect& aDirtyRect);
  void PaintFloaters(nsIPresContext& aPresContext,
                     nsIRenderingContext& aRenderingContext,
                     const nsRect& aDirtyRect);
  nsresult AppendNewFrames(nsIPresContext& aPresContext, nsIFrame*);
  void RenumberLists(nsBlockReflowState& aState);
#ifdef NS_DEBUG
  PRBool IsChild(nsIFrame* aFrame);
#endif
  nsIStyleContext* mFirstLineStyle;
  nsIStyleContext* mFirstLetterStyle;
  LineData* mLines;
  LineData* mOverflowLines;
  // Text run information
  nsTextRun* mTextRuns;
  // For list-item frames, this is the bullet frame.
  BulletFrame* mBullet;
  // List of all floaters in this block
  nsIFrame* mFloaters;
  // Body configuration flags passed into this block when this block
  // is used by the body.
  PRUint32 mFlags;
  static nsIAtom* gFloaterAtom;
  static nsIAtom* gBulletAtom;
};
//----------------------------------------------------------------------
#ifdef REALLY_NOISY_FIRST_LINE
static void
DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
{
  fputs(gap, stdout);
  aFrame->ListTag(stdout);
  printf(": ");
  nsIStyleContext* sc;
  aFrame->GetStyleContext(sc);
  while (nsnull != sc) {
    nsIStyleContext* psc;
    printf("%p ", sc);
    psc = sc->GetParent();
    NS_RELEASE(sc);
    sc = psc;
  }
  printf("\n");
}
#endif
//----------------------------------------------------------------------
#include "nsHTMLImage.h"
class BulletFrame : public nsFrame {
public:
  BulletFrame(nsIContent* aContent, nsIFrame* aParentFrame);
  virtual ~BulletFrame();
  // nsIFrame
  NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);
  NS_IMETHOD Paint(nsIPresContext &aCX,
                   nsIRenderingContext& aRenderingContext,
                   const nsRect& aDirtyRect);
  NS_IMETHOD ListTag(FILE* out) const;
  NS_IMETHOD List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const;
  // nsIHTMLReflow
  NS_IMETHOD Reflow(nsIPresContext& aPresContext,
                    nsHTMLReflowMetrics& aMetrics,
                    const nsHTMLReflowState& aReflowState,
                    nsReflowStatus& aStatus);
  void SetListItemOrdinal(nsBlockReflowState& aBlockState);
  void GetDesiredSize(nsIPresContext* aPresContext,
                      const nsHTMLReflowState& aReflowState,
                      nsHTMLReflowMetrics& aMetrics);
  void GetListItemText(nsIPresContext& aCX,
                       const nsStyleList& aMol,
                       nsString& aResult);
  PRInt32 mOrdinal;
  nsMargin mPadding;
  nsHTMLImageLoader mImageLoader;
};
BulletFrame::BulletFrame(nsIContent* aContent, nsIFrame* aParentFrame)
  : nsFrame(aContent, aParentFrame)
{
}
BulletFrame::~BulletFrame()
{
}
NS_IMETHODIMP
BulletFrame::DeleteFrame(nsIPresContext& aPresContext)
{
  // Release image loader first so that it's refcnt can go to zero
  mImageLoader.DestroyLoader();
  return nsFrame::DeleteFrame(aPresContext);
}
NS_IMETHODIMP
BulletFrame::ListTag(FILE* out) const
{
  fprintf(out, "Bullet(%d)@%p", ContentIndexInContainer(this), this);
  return NS_OK;
}
NS_IMETHODIMP
BulletFrame::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const
{
  PRInt32 i;
  for (i = aIndent; --i >= 0; ) fputs("  ", out);
  fprintf(out, "Bullet(%d)@%p ", ContentIndexInContainer(this), this);
  nsIView* view;
  GetView(view);
  if (nsnull != view) {
    fprintf(out, " [view=%p]", view);
  }
  out << mRect;
  if (0 != mState) {
    fprintf(out, " [state=%08x]", mState);
  }
  fputs("<>\n", out);
  return NS_OK;
}
NS_METHOD
BulletFrame::Paint(nsIPresContext&      aCX,
                   nsIRenderingContext& aRenderingContext,
                   const nsRect&        aDirtyRect)
{
  const nsStyleDisplay* disp =
    (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
  nscoord width;
  if (disp->mVisible) {
    const nsStyleList* myList =
      (const nsStyleList*)mStyleContext->GetStyleData(eStyleStruct_List);
    if (myList->mListStyleImage.Length() > 0) {
      nsIImage* image = mImageLoader.GetImage();
      if (nsnull == image) {
        if (!mImageLoader.GetLoadImageFailed()) {
          // No image yet
          return NS_OK;
        }
      }
      else {
        nsRect innerArea(mPadding.left, mPadding.top,
                         mRect.width - (mPadding.left + mPadding.right),
                         mRect.height - (mPadding.top + mPadding.bottom));
        aRenderingContext.DrawImage(image, innerArea);
        return NS_OK;
      }
    }
    const nsStyleFont* myFont =
      (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
    const nsStyleColor* myColor =
      (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
    nsIFontMetrics* fm;
    aRenderingContext.SetColor(myColor->mColor);
    nsAutoString text;
    switch (myList->mListStyleType) {
    case NS_STYLE_LIST_STYLE_NONE:
      break;
    default:
    case NS_STYLE_LIST_STYLE_BASIC:
    case NS_STYLE_LIST_STYLE_DISC:
      aRenderingContext.FillEllipse(mPadding.left, mPadding.top,
                                    mRect.width - (mPadding.left + mPadding.right),
                                    mRect.height - (mPadding.top + mPadding.bottom));
      break;
    case NS_STYLE_LIST_STYLE_CIRCLE:
      aRenderingContext.DrawEllipse(mPadding.left, mPadding.top,
                                    mRect.width - (mPadding.left + mPadding.right),
                                    mRect.height - (mPadding.top + mPadding.bottom));
      break;
    case NS_STYLE_LIST_STYLE_SQUARE:
      aRenderingContext.FillRect(mPadding.left, mPadding.top,
                                 mRect.width - (mPadding.left + mPadding.right),
                                 mRect.height - (mPadding.top + mPadding.bottom));
      break;
    case NS_STYLE_LIST_STYLE_DECIMAL:
    case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
    case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
    case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
    case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
      fm = aCX.GetMetricsFor(myFont->mFont);
      GetListItemText(aCX, *myList, text);
      aRenderingContext.SetFont(fm);
      aRenderingContext.GetWidth(text, width);
      aRenderingContext.DrawString(text, mPadding.left, mPadding.top, width);
      NS_RELEASE(fm);
      break;
    }
  }
  return NS_OK;
}
void
BulletFrame::SetListItemOrdinal(nsBlockReflowState& aReflowState)
{
  // Assume that the ordinal comes from the block reflow state
  mOrdinal = aReflowState.mNextListOrdinal;
  // Try to get value directly from the list-item, if it specifies a
  // value attribute. Note: we do this with our parent's content
  // because our parent is the list-item.
  nsHTMLValue value;
  nsIContent* parentContent;
  mContentParent->GetContent(parentContent);
  nsIHTMLContent* hc;
  if (NS_OK == parentContent->QueryInterface(kIHTMLContentIID, (void**) &hc)) {
    if (NS_CONTENT_ATTR_HAS_VALUE ==
        hc->GetAttribute(nsHTMLAtoms::value, value)) {
      if (eHTMLUnit_Integer == value.GetUnit()) {
        // Use ordinal specified by the value attribute
        mOrdinal = value.GetIntValue();
        if (mOrdinal <= 0) {
          mOrdinal = 1;
        }
      }
    }
    NS_RELEASE(hc);
  }
  NS_RELEASE(parentContent);
  aReflowState.mNextListOrdinal = mOrdinal + 1;
}
static const char* gLowerRomanCharsA = "ixcm";
static const char* gUpperRomanCharsA = "IXCM";
static const char* gLowerRomanCharsB = "vld?";
static const char* gUpperRomanCharsB = "VLD?";
static const char* gLowerAlphaChars  = "abcdefghijklmnopqrstuvwxyz";
static const char* gUpperAlphaChars  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// XXX change roman/alpha to use unsigned math so that maxint and
// maxnegint will work
void
BulletFrame::GetListItemText(nsIPresContext& aCX,
                             const nsStyleList& aListStyle,
                             nsString& result)
{
  PRInt32 ordinal = mOrdinal;
  char cbuf[40];
  switch (aListStyle.mListStyleType) {
  case NS_STYLE_LIST_STYLE_DECIMAL:
    PR_snprintf(cbuf, sizeof(cbuf), "%ld", ordinal);
    result.Append(cbuf);
    break;
  case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
  case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
  {
    if (ordinal <= 0) {
      ordinal = 1;
    }
    nsAutoString addOn, decStr;
    decStr.Append(ordinal, 10);
    PRIntn len = decStr.Length();
    const PRUnichar* dp = decStr.GetUnicode();
    const PRUnichar* end = dp + len;
    PRIntn romanPos = len;
    PRIntn n;
    const char* achars;
    const char* bchars;
    if (aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_LOWER_ROMAN) {
      achars = gLowerRomanCharsA;
      bchars = gLowerRomanCharsB;
    }
    else {
      achars = gUpperRomanCharsA;
      bchars = gUpperRomanCharsB;
    }
    for (; dp < end; dp++) {
      romanPos--;
      addOn.SetLength(0);
      switch(*dp) {
      case '3':  addOn.Append(achars[romanPos]);
      case '2':  addOn.Append(achars[romanPos]);
      case '1':  addOn.Append(achars[romanPos]);
        break;
      case '4':
        addOn.Append(achars[romanPos]);
        // FALLTHROUGH
      case '5': case '6':
      case '7': case  '8':
        addOn.Append(bchars[romanPos]);
        for(n=0;n<(*dp-'5');n++) {
          addOn.Append(achars[romanPos]);
        }
        break;
      case '9':
        addOn.Append(achars[romanPos]);
        addOn.Append(achars[romanPos+1]);
        break;
      default:
        break;
      }
      result.Append(addOn);
    }
  }
  break;
  case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
  case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
  {
    PRInt32 anOffset = -1;
    PRInt32 aBase = 26;
    PRInt32 ndex=0;
    PRInt32 root=1;
    PRInt32 next=aBase;
    PRInt32 expn=1;
    const char* chars =
      (aListStyle.mListStyleType == NS_STYLE_LIST_STYLE_LOWER_ALPHA)
      ? gLowerAlphaChars : gUpperAlphaChars;
    // must be positive here...
    if (ordinal <= 0) {
      ordinal = 1;
    }
    ordinal--;          // a == 0
    // scale up in baseN; exceed current value.
    while (next<=ordinal) {
      root=next;
      next*=aBase;
      expn++;
    }
    while (0!=(expn--)) {
      ndex = ((root<=ordinal) && (0!=root)) ? (ordinal/root): 0;
      ordinal %= root;
      if (root>1)
        result.Append(chars[ndex+anOffset]);
      else
        result.Append(chars[ndex]);
      root /= aBase;
    }
  }
  break;
  }
  result.Append(".");
}
#define MIN_BULLET_SIZE 5               // from laytext.c
static nsresult
UpdateBulletCB(nsIPresContext& aPresContext, nsIFrame* aFrame, PRIntn aStatus)
{
  nsresult rv = NS_OK;
  if (NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE & aStatus) {
    // Now that the size is available, trigger a reflow of the bullet
    // frame.
    nsIPresShell* shell;
    shell = aPresContext.GetShell();
    if (nsnull != shell) {
      nsIReflowCommand* cmd;
      rv = NS_NewHTMLReflowCommand(&cmd, aFrame,
                                   nsIReflowCommand::ContentChanged);
      if (NS_OK == rv) {
        shell->EnterReflowLock();
        shell->AppendReflowCommand(cmd);
        NS_RELEASE(cmd);
        shell->ExitReflowLock();
      }
      NS_RELEASE(shell);
    }
  }
  return rv;
}
void
BulletFrame::GetDesiredSize(nsIPresContext*  aCX,
                            const nsHTMLReflowState& aReflowState,
                            nsHTMLReflowMetrics& aMetrics)
{
  const nsStyleList* myList =
    (const nsStyleList*)mStyleContext->GetStyleData(eStyleStruct_List);
  nscoord ascent;
  if (myList->mListStyleImage.Length() > 0) {
    mImageLoader.SetURL(myList->mListStyleImage);
    mImageLoader.GetDesiredSize(aCX, aReflowState, this, UpdateBulletCB,
                                aMetrics);
    if (!mImageLoader.GetLoadImageFailed()) {
      nsHTMLContainerFrame::CreateViewForFrame(*aCX, this, mStyleContext,
                                               PR_FALSE);
      aMetrics.ascent = aMetrics.height;
      aMetrics.descent = 0;
      return;
    }
  }
  const nsStyleFont* myFont =
    (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
  nsIFontMetrics* fm = aCX->GetMetricsFor(myFont->mFont);
  nscoord bulletSize;
  float p2t;
  float t2p;
  nsAutoString text;
  switch (myList->mListStyleType) {
  case NS_STYLE_LIST_STYLE_NONE:
    aMetrics.width = 0;
    aMetrics.height = 0;
    aMetrics.ascent = 0;
    aMetrics.descent = 0;
    break;
  default:
  case NS_STYLE_LIST_STYLE_DISC:
  case NS_STYLE_LIST_STYLE_CIRCLE:
  case NS_STYLE_LIST_STYLE_BASIC:
  case NS_STYLE_LIST_STYLE_SQUARE:
    t2p = aCX->GetTwipsToPixels();
    fm->GetMaxAscent(ascent);
    bulletSize = NSTwipsToIntPixels(
      (nscoord)NSToIntRound(0.8f * (float(ascent) / 2.0f)), t2p);
    if (bulletSize < 1) {
      bulletSize = MIN_BULLET_SIZE;
    }
    p2t = aCX->GetPixelsToTwips();
    bulletSize = NSIntPixelsToTwips(bulletSize, p2t);
    mPadding.bottom = ascent / 8;
    aMetrics.width = mPadding.right + bulletSize;
    aMetrics.height = mPadding.bottom + bulletSize;
    aMetrics.ascent = mPadding.bottom + bulletSize;
    aMetrics.descent = 0;
    break;
  case NS_STYLE_LIST_STYLE_DECIMAL:
  case NS_STYLE_LIST_STYLE_LOWER_ROMAN:
  case NS_STYLE_LIST_STYLE_UPPER_ROMAN:
  case NS_STYLE_LIST_STYLE_LOWER_ALPHA:
  case NS_STYLE_LIST_STYLE_UPPER_ALPHA:
    GetListItemText(*aCX, *myList, text);
    fm->GetHeight(aMetrics.height);
    aReflowState.rendContext->SetFont(fm);
    aReflowState.rendContext->GetWidth(text, aMetrics.width);
    aMetrics.width += mPadding.right;
    fm->GetMaxAscent(aMetrics.ascent);
    fm->GetMaxDescent(aMetrics.descent);
    break;
  }
  NS_RELEASE(fm);
}
NS_IMETHODIMP
BulletFrame::Reflow(nsIPresContext& aPresContext,
                    nsHTMLReflowMetrics& aMetrics,
                    const nsHTMLReflowState& aReflowState,
                    nsReflowStatus& aStatus)
{
  // Get the base size
  GetDesiredSize(&aPresContext, aReflowState, aMetrics);
  // Add in the border and padding; split the top/bottom between the
  // ascent and descent to make things look nice
  const nsStyleSpacing* space =(const nsStyleSpacing*)
    mStyleContext->GetStyleData(eStyleStruct_Spacing);
  nsMargin borderPadding;
  space->CalcBorderPaddingFor(this, borderPadding);
  aMetrics.width += borderPadding.left + borderPadding.right;
  aMetrics.height += borderPadding.top + borderPadding.bottom;
  aMetrics.ascent += borderPadding.top;
  aMetrics.descent += borderPadding.bottom;
  if (nsnull != aMetrics.maxElementSize) {
    aMetrics.maxElementSize->width = aMetrics.width;
    aMetrics.maxElementSize->height = aMetrics.height;
  }
  aStatus = NS_FRAME_COMPLETE;
  return NS_OK;
}
//----------------------------------------------------------------------
#define LINE_IS_DIRTY                 0x1
#define LINE_IS_BLOCK                 0x2
#define LINE_NEED_DID_REFLOW          0x8
#define LINE_TOP_MARGIN_IS_AUTO       0x10
#define LINE_BOTTOM_MARGIN_IS_AUTO    0x20
#define LINE_OUTSIDE_CHILDREN         0x40
struct LineData {
  LineData(nsIFrame* aFrame, PRInt32 aCount, PRUint16 flags) {
    mFirstChild = aFrame;
    mChildCount = aCount;
    mState = LINE_IS_DIRTY | LINE_NEED_DID_REFLOW | flags;
    mFloaters = nsnull;
    mNext = nsnull;
    mBounds.SetRect(0,0,0,0);
    mCombinedArea.SetRect(0,0,0,0);
    mCarriedOutTopMargin = 0;
    mCarriedOutBottomMargin = 0;
    mBreakType = NS_STYLE_CLEAR_NONE;
  }
  ~LineData();
  void List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter = nsnull,
            PRBool aOutputMe=PR_TRUE) const;
  PRInt32 ChildCount() const {
    return PRInt32(mChildCount);
  }
  nsIFrame* LastChild() const;
  PRBool IsLastChild(nsIFrame* aFrame) const;
  PRBool IsBlock() const {
    return 0 != (LINE_IS_BLOCK & mState);
  }
  void SetIsBlock() {
    mState |= LINE_IS_BLOCK;
  }
  void ClearIsBlock() {
    mState &= ~LINE_IS_BLOCK;
  }
  void SetIsBlock(PRBool aValue) {
    if (aValue) {
      SetIsBlock();
    }
    else {
      ClearIsBlock();
    }
  }
  void MarkDirty() {
    mState |= LINE_IS_DIRTY;
  }
  void ClearDirty() {
    mState &= ~LINE_IS_DIRTY;
  }
  void SetNeedDidReflow() {
    mState |= LINE_NEED_DID_REFLOW;
  }
  void ClearNeedDidReflow() {
    mState &= ~LINE_NEED_DID_REFLOW;
  }
  PRBool NeedsDidReflow() {
    return 0 != (LINE_NEED_DID_REFLOW & mState);
  }
  PRBool IsDirty() const {
    return 0 != (LINE_IS_DIRTY & mState);
  }
  void SetOutsideChildren() {
    mState |= LINE_OUTSIDE_CHILDREN;
  }
  void ClearOutsideChildren() {
    mState &= ~LINE_OUTSIDE_CHILDREN;
  }
  PRBool OutsideChildren() const {
    return 0 != (LINE_OUTSIDE_CHILDREN & mState);
  }
  PRUint16 GetState() const { return mState; }
  char* StateToString(char* aBuf, PRInt32 aBufSize) const;
  PRBool Contains(nsIFrame* aFrame) const;
  void SetMarginFlags(PRUintn aFlags) {
    mState &= ~(LINE_TOP_MARGIN_IS_AUTO|LINE_BOTTOM_MARGIN_IS_AUTO);
    if (NS_CARRIED_TOP_MARGIN_IS_AUTO & aFlags) {
      mState |= LINE_TOP_MARGIN_IS_AUTO;
    }
    if (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & aFlags) {
      mState |= LINE_BOTTOM_MARGIN_IS_AUTO;
    }
  }
  PRUintn GetMarginFlags() {
    return ((LINE_TOP_MARGIN_IS_AUTO & mState)
            ? NS_CARRIED_TOP_MARGIN_IS_AUTO : 0) |
      ((LINE_BOTTOM_MARGIN_IS_AUTO & mState) ?
       NS_CARRIED_BOTTOM_MARGIN_IS_AUTO : 0);
  }
  void UnplaceFloaters(nsISpaceManager* aSpaceManager) {
    if (nsnull != mFloaters) {
      PRInt32 i, n = mFloaters->Count();
      for (i = 0; i < n; i++) {
        nsPlaceholderFrame* pf = (nsPlaceholderFrame*) mFloaters->ElementAt(i);
        nsIFrame* floater = pf->GetAnchoredItem();
        aSpaceManager->RemoveRegion(floater);
      }
    }
  }
#ifdef NS_DEBUG
  void Verify();
#endif
  nsIFrame* mFirstChild;
  PRUint16 mChildCount;
  PRUint16 mState;
  PRUint8 mBreakType;
  nsRect mBounds;
  nsRect mCombinedArea;
  nscoord mCarriedOutTopMargin;
  nscoord mCarriedOutBottomMargin;
  nsVoidArray* mFloaters;
  LineData* mNext;
};
LineData::~LineData()
{
  if (nsnull != mFloaters) {
    delete mFloaters;
  }
}
static void
ListFloaters(FILE* out, PRInt32 aIndent, nsVoidArray* aFloaters)
{
  PRInt32 j, i, n = aFloaters->Count();
  for (i = 0; i < n; i++) {
    for (j = aIndent; --j >= 0; ) fputs("  ", out);
    nsPlaceholderFrame* ph = (nsPlaceholderFrame*) aFloaters->ElementAt(i);
    if (nsnull != ph) {
      ph->ListTag(out);
      fputs("\n", out);
      nsIFrame* frame = ph->GetAnchoredItem();
      if (nsnull != frame) {
        frame->List(out, aIndent + 1, nsnull);
      }
    }
  }
}
static void
ListTextRuns(FILE* out, PRInt32 aIndent, nsTextRun* aRuns)
{
  while (nsnull != aRuns) {
    aRuns->List(out, aIndent);
    aRuns = aRuns->GetNext();
  }
}
char*
LineData::StateToString(char* aBuf, PRInt32 aBufSize) const
{
  PR_snprintf(aBuf, aBufSize, "%s,%s",
              (mState & LINE_IS_DIRTY) ? "dirty" : "clean",
              (mState & LINE_IS_BLOCK) ? "block" : "inline");
  return aBuf;
}
void
LineData::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter,
               PRBool aOutputMe) const
{
  PRInt32 i;
  if (aOutputMe) {
    for (i = aIndent; --i >= 0; ) fputs("  ", out);
    char cbuf[100];
    fprintf(out, "line %p: count=%d state=%s ",
            this, ChildCount(), StateToString(cbuf, sizeof(cbuf)));
    if (0 != mCarriedOutTopMargin) {
      fprintf(out, "tm=%d ", mCarriedOutTopMargin);
    }
    if (0 != mCarriedOutBottomMargin) {
      fprintf(out, "bm=%d ", mCarriedOutBottomMargin);
    }
    out << mBounds;
    fprintf(out, " ca=");
    out << mCombinedArea;
    fprintf(out, " <\n");
  }
  nsIFrame* frame = mFirstChild;
  PRInt32 n = ChildCount();
  while (--n >= 0) {
    frame->List(out, aIndent + 1, aFilter);
    frame->GetNextSibling(frame);
  }
  if (aOutputMe) {
    for (i = aIndent; --i >= 0; ) fputs("  ", out);
    if (nsnull != mFloaters) {
      fputs("> floaters <\n", out);
      ListFloaters(out, aIndent + 1, mFloaters);
      for (i = aIndent; --i >= 0; ) fputs("  ", out);
    }
    fputs(">\n", out);
  }
}
nsIFrame*
LineData::LastChild() const
{
  nsIFrame* frame = mFirstChild;
  PRInt32 n = ChildCount() - 1;
  while (--n >= 0) {
    frame->GetNextSibling(frame);
  }
  return frame;
}
PRBool
LineData::IsLastChild(nsIFrame* aFrame) const
{
  nsIFrame* lastFrame = LastChild();
  return aFrame == lastFrame;
}
PRBool
LineData::Contains(nsIFrame* aFrame) const
{
  PRInt32 n = ChildCount();
  nsIFrame* frame = mFirstChild;
  while (--n >= 0) {
    if (frame == aFrame) {
      return PR_TRUE;
    }
    frame->GetNextSibling(frame);
  }
  return PR_FALSE;
}
static PRInt32
LengthOf(nsIFrame* aFrame)
{
  PRInt32 result = 0;
  while (nsnull != aFrame) {
    result++;
    aFrame->GetNextSibling(aFrame);
  }
  return result;
}
#ifdef NS_DEBUG
void
LineData::Verify()
{
  nsIFrame* lastFrame = LastChild();
  if (nsnull != lastFrame) {
    nsIFrame* nextInFlow;
    lastFrame->GetNextInFlow(nextInFlow);
    if (nsnull != mNext) {
      nsIFrame* nextSibling;
      lastFrame->GetNextSibling(nextSibling);
      NS_ASSERTION(mNext->mFirstChild == nextSibling, "bad line list");
    }
  }
  PRInt32 len = LengthOf(mFirstChild);
  NS_ASSERTION(len >= ChildCount(), "bad mChildCount");
}
static void
VerifyLines(LineData* aLine)
{
  while (nsnull != aLine) {
    aLine->Verify();
    aLine = aLine->mNext;
  }
}
static void
VerifyChildCount(LineData* aLines, PRBool aEmptyOK = PR_FALSE)
{
  if (nsnull != aLines) {
    PRInt32 childCount = LengthOf(aLines->mFirstChild);
    PRInt32 sum = 0;
    LineData* line = aLines;
    while (nsnull != line) {
      if (!aEmptyOK) {
        NS_ASSERTION(0 != line->ChildCount(), "empty line left in line list");
      }
      sum += line->ChildCount();
      line = line->mNext;
    }
    if (sum != childCount) {
      printf("Bad sibling list/line mChildCount's\n");
      LineData* line = aLines;
      while (nsnull != line) {
        line->List(stdout, 1);
        if (nsnull != line->mNext) {
          nsIFrame* lastFrame = line->LastChild();
          if (nsnull != lastFrame) {
            nsIFrame* nextSibling;
            lastFrame->GetNextSibling(nextSibling);
            if (line->mNext->mFirstChild != nextSibling) {
              printf("  [list broken: nextSibling=%p mNext->mFirstChild=%p]\n",
                     nextSibling, line->mNext->mFirstChild);
            }
          }
        }
        line = line->mNext;
      }
      NS_ASSERTION(sum == childCount, "bad sibling list/line mChildCount's");
    }
  }
}
#endif
static void
DeleteLineList(nsIPresContext& aPresContext, LineData* aLine)
{
  if (nsnull != aLine) {
    // Delete our child frames before doing anything else. In particular
    // we do all of this before our base class releases it's hold on the
    // view.
    for (nsIFrame* child = aLine->mFirstChild; child; ) {
      nsIFrame* nextChild;
      child->GetNextSibling(nextChild);
      child->DeleteFrame(aPresContext);
      child = nextChild;
    }
    while (nsnull != aLine) {
      LineData* next = aLine->mNext;
      delete aLine;
      aLine = next;
    }
  }
}
static LineData*
LastLine(LineData* aLine)
{
  if (nsnull != aLine) {
    while (nsnull != aLine->mNext) {
      aLine = aLine->mNext;
    }
  }
  return aLine;
}
static LineData*
FindLineContaining(LineData* aLine, nsIFrame* aFrame)
{
  while (nsnull != aLine) {
    if (aLine->Contains(aFrame)) {
      return aLine;
    }
    aLine = aLine->mNext;
  }
  return nsnull;
}
//----------------------------------------------------------------------
void
nsBlockReflowState::BlockBandData::ComputeAvailSpaceRect()
{
  nsBandTrapezoid*  trapezoid = data;
  if (count > 1) {
    // If there's more than one trapezoid that means there are floaters
    PRInt32 i;
    // Stop when we get to space occupied by a right floater, or when we've
    // looked at every trapezoid and none are right floaters
    for (i = 0; i < count; i++) {
      nsBandTrapezoid*  trapezoid = &data[i];
      if (trapezoid->state != nsBandTrapezoid::Available) {
        const nsStyleDisplay* display;
        if (nsBandTrapezoid::OccupiedMultiple == trapezoid->state) {
          PRInt32 j, numFrames = trapezoid->frames->Count();
          NS_ASSERTION(numFrames > 0, "bad trapezoid frame list");
          for (j = 0; j < numFrames; j++) {
            nsIFrame* f = (nsIFrame*)trapezoid->frames->ElementAt(j);
            f->GetStyleData(eStyleStruct_Display,
                            (const nsStyleStruct*&)display);
            if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
              goto foundRightFloater;
            }
          }
        } else {
          trapezoid->frame->GetStyleData(eStyleStruct_Display,
                                         (const nsStyleStruct*&)display);
          if (NS_STYLE_FLOAT_RIGHT == display->mFloats) {
            break;
          }
        }
      }
    }
  foundRightFloater:
    if (i > 0) {
      trapezoid = &data[i - 1];
    }
  }
  if (nsBandTrapezoid::Available == trapezoid->state) {
    // The trapezoid is available
    trapezoid->GetRect(availSpace);
  } else {
    const nsStyleDisplay* display;
    // The trapezoid is occupied. That means there's no available space
    trapezoid->GetRect(availSpace);
    // XXX Better handle the case of multiple frames
    if (nsBandTrapezoid::Occupied == trapezoid->state) {
      trapezoid->frame->GetStyleData(eStyleStruct_Display,
                                     (const nsStyleStruct*&)display);
      if (NS_STYLE_FLOAT_LEFT == display->mFloats) {
        availSpace.x = availSpace.XMost();
      }
    }
    availSpace.width = 0;
  }
}
//----------------------------------------------------------------------
nsBlockReflowState::nsBlockReflowState(nsIPresContext& aPresContext,
                                       const nsHTMLReflowState& aReflowState,
                                       const nsHTMLReflowMetrics& aMetrics)
  : nsFrameReflowState(aPresContext, aReflowState, aMetrics),
    mLineLayout(aPresContext, aReflowState.spaceManager)
{
  mInlineReflow = nsnull;
  mLineLayout.Init(this);
  mSpaceManager = aReflowState.spaceManager;
  // Translate into our content area and then save the 
  // coordinate system origin for later.
  mSpaceManager->Translate(mBorderPadding.left, mBorderPadding.top);
  mSpaceManager->GetTranslation(mSpaceManagerX, mSpaceManagerY);
  mPresContext = aPresContext;
  mBlock = (nsBlockFrame*) frame;
  mBlock->GetNextInFlow((nsIFrame*&)mNextInFlow);
  mKidXMost = 0;
  mRunInFrame = nsnull;
  mRunInToFrame = nsnull;
  mY = 0;
  mUnconstrainedWidth = maxSize.width == NS_UNCONSTRAINEDSIZE;
  mUnconstrainedHeight = maxSize.height == NS_UNCONSTRAINEDSIZE;
#ifdef NS_DEBUG
  if (!mUnconstrainedWidth && (maxSize.width > 100000)) {
    mBlock->ListTag(stdout);
    printf(": bad parent: maxSize WAS %d,%d\n", maxSize.width, maxSize.height);
    maxSize.width = NS_UNCONSTRAINEDSIZE;
    mUnconstrainedWidth = PR_TRUE;
  }
  if (!mUnconstrainedHeight && (maxSize.height > 100000)) {
    mBlock->ListTag(stdout);
    printf(": bad parent: maxSize WAS %d,%d\n", maxSize.width, maxSize.height);
    maxSize.height = NS_UNCONSTRAINEDSIZE;
    mUnconstrainedHeight = PR_TRUE;
  }
#endif
  mTextAlign = mStyleText->mTextAlign;
  mPrevMarginFlags = 0;
  nscoord lr = mBorderPadding.left + mBorderPadding.right;
  mY = mBorderPadding.top;
  if (eHTMLFrameConstraint_FixedContent == widthConstraint) {
    // The CSS2 spec says that the width attribute defines the width
    // of the "content area" which does not include the border
    // padding. So we add those back in.
    mBorderArea.width = minWidth + lr;
    mContentArea.width = minWidth;
  }
  else {
    if (mUnconstrainedWidth) {
      mBorderArea.width = NS_UNCONSTRAINEDSIZE;
      mContentArea.width = NS_UNCONSTRAINEDSIZE;
    }
    else {
      mBorderArea.width = maxSize.width;
      mContentArea.width = maxSize.width - lr;
    }
  }
  mBorderArea.height = maxSize.height;
  mContentArea.height = maxSize.height;
  mBottomEdge = maxSize.height;
  if (!mUnconstrainedHeight) {
    mBottomEdge -= mBorderPadding.bottom;
  }
  mPrevChild = nsnull;
  mFreeList = nsnull;
  mPrevLine = nsnull;
}
nsBlockReflowState::~nsBlockReflowState()
{
  // Restore the coordinate system
  mSpaceManager->Translate(-mBorderPadding.left, -mBorderPadding.top);
  LineData* line = mFreeList;
  while (nsnull != line) {
    NS_ASSERTION((0 == line->ChildCount()) && (nsnull == line->mFirstChild),
                 "bad free line");
    LineData* next = line->mNext;
    delete line;
    line = next;
  }
}
// Get the available reflow space for the current y coordinate. The
// available space is relative to our coordinate system (0,0) is our
// upper left corner.
void
nsBlockReflowState::GetAvailableSpace()
{
  nsISpaceManager* sm = mSpaceManager;
#ifdef NS_DEBUG
  // Verify that the caller setup the coordinate system properly
  nscoord wx, wy;
  sm->GetTranslation(wx, wy);
  NS_ASSERTION((wx == mSpaceManagerX) && (wy == mSpaceManagerY),
               "bad coord system");
#endif
  // Fill in band data for the specific Y coordinate. Because the
  // space manager is pre-translated into our content-area (so that
  // nested blocks/inlines will line up properly), we have to remove
  // the Y translation to find the band coordinates relative to our
  // inner (content area) upper left corner (0,0).
  sm->GetBandData(mY - mBorderPadding.top, mContentArea, mCurrentBand);
  // Compute the bounding rect of the available space, i.e. space
  // between any left and right floaters.
  mCurrentBand.ComputeAvailSpaceRect();
  NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
     ("nsBlockReflowState::GetAvailableSpace: band={%d,%d,%d,%d} count=%d",
      mCurrentBand.availSpace.x, mCurrentBand.availSpace.y,
      mCurrentBand.availSpace.width, mCurrentBand.availSpace.height,
      mCurrentBand.count));
}
//----------------------------------------------------------------------
nsIAtom* nsBlockFrame::gFloaterAtom;
nsIAtom* nsBlockFrame::gBulletAtom;
nsresult
NS_NewBlockFrame(nsIContent* aContent, nsIFrame* aParentFrame,
                 nsIFrame*& aNewFrame, PRUint32 aFlags)
{
  nsBlockFrame* it = new nsBlockFrame(aContent, aParentFrame);
  if (nsnull == it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  aNewFrame = it;
  it->SetFlags(aFlags);
  return NS_OK;
}
nsBlockFrame::nsBlockFrame(nsIContent* aContent, nsIFrame* aParent)
  : nsBlockFrameSuper(aContent, aParent)
{
  // XXX for now these are a memory leak
  if (nsnull == gFloaterAtom) {
    gFloaterAtom = NS_NewAtom("Floater-list");
  }
  if (nsnull == gBulletAtom) {
    gBulletAtom = NS_NewAtom("Bullet-list");
  }
}
nsBlockFrame::~nsBlockFrame()
{
  NS_IF_RELEASE(mFirstLineStyle);
  NS_IF_RELEASE(mFirstLetterStyle);
  nsTextRun::DeleteTextRuns(mTextRuns);
}
NS_IMETHODIMP
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
  if (NULL == aInstancePtr) {
    return NS_ERROR_NULL_POINTER;
  }
  if (aIID.Equals(kBlockFrameCID)) {
    *aInstancePtr = (void*) this;
    return NS_OK;
  }
  return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr);
}
NS_IMETHODIMP
nsBlockFrame::ReResolveStyleContext(nsIPresContext* aPresContext,
                                    nsIStyleContext* aParentContext)
{
  nsIStyleContext* oldContext = mStyleContext;
  nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext);
  if (NS_OK != rv) {
    return rv;
  }
  if (oldContext != mStyleContext) {
    // Re-resolve the :first-line pseudo style context
    if (nsnull == mPrevInFlow) {
      nsIStyleContext* newFirstLineStyle =
        aPresContext->ProbePseudoStyleContextFor(mContent,
                                                 nsHTMLAtoms::firstLinePseudo,
                                                 mStyleContext);
      if (newFirstLineStyle != mFirstLineStyle) {
        NS_IF_RELEASE(mFirstLineStyle);
        mFirstLineStyle = newFirstLineStyle;
      }
      else {
        NS_IF_RELEASE(newFirstLineStyle);
      }
      // Re-resolve the :first-letter pseudo style context
      nsIStyleContext* newFirstLetterStyle =
        aPresContext->ProbePseudoStyleContextFor(mContent,
                                                 nsHTMLAtoms::firstLetterPseudo,
                                                 (nsnull != mFirstLineStyle
                                                  ? mFirstLineStyle
                                                  : mStyleContext));
      if (newFirstLetterStyle != mFirstLetterStyle) {
        NS_IF_RELEASE(mFirstLetterStyle);
        mFirstLetterStyle = newFirstLetterStyle;
      }
      else {
        NS_IF_RELEASE(newFirstLetterStyle);
      }
    }
    // Update the child frames on each line
    LineData* line = mLines;
    while (nsnull != line) {
      nsIFrame* child = line->mFirstChild;
      PRInt32 n = line->mChildCount;
      while ((--n >= 0) && NS_SUCCEEDED(rv)) {
        if (line == mLines) {
          rv = child->ReResolveStyleContext(aPresContext,
                                            (nsnull != mFirstLineStyle
                                             ? mFirstLineStyle
                                             : mStyleContext));
        }
        else {
          rv = child->ReResolveStyleContext(aPresContext, mStyleContext);
        }
        child->GetNextSibling(child);
      }
      line = line->mNext;
    }
    // Update the child frames on each overflow line too
    line = mOverflowLines;
    while (nsnull != line) {
      nsIFrame* child = line->mFirstChild;
      PRInt32 n = line->mChildCount;
      while ((--n >= 0) && NS_SUCCEEDED(rv)) {
        rv = child->ReResolveStyleContext(aPresContext, mStyleContext);
        child->GetNextSibling(child);
      }
      line = line->mNext;
    }
  }
  return rv;
}
NS_IMETHODIMP
nsBlockFrame::SetInitialChildList(nsIPresContext& aPresContext,
                                  nsIAtom*        aListName,
                                  nsIFrame*       aChildList)
{
  nsresult rv = AppendNewFrames(aPresContext, aChildList);
  if (NS_OK != rv) {
    return rv;
  }
  // Create list bullet if this is a list-item. Note that this is done
  // here so that RenumberLists will work (it needs the bullets to
  // store the bullet numbers).
  const nsStyleDisplay* styleDisplay;
  GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) styleDisplay);
  if ((nsnull == mPrevInFlow) &&
      (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) &&
      (nsnull == mBullet)) {
    // Create bullet frame
    mBullet = new BulletFrame(mContent, this);
    if (nsnull == mBullet) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    // Resolve style for the bullet frame
    nsIStyleContext* kidSC;
    kidSC = aPresContext.ResolvePseudoStyleContextFor(mContent, 
                                                      nsHTMLAtoms::bulletPseudo, 
                                                      mStyleContext);
    mBullet->SetStyleContext(&aPresContext, kidSC);
    NS_RELEASE(kidSC);
    // If the list bullet frame should be positioned inside then add
    // it to the flow now.
    const nsStyleList* styleList;
    GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList);
    if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) {
      InsertNewFrame(aPresContext, this, mBullet, nsnull);
    }
  }
  // Lookup up the two pseudo style contexts
  if (nsnull == mPrevInFlow) {
    mFirstLineStyle = aPresContext.
      ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLinePseudo,
                                 mStyleContext);
    mFirstLetterStyle = aPresContext.
      ProbePseudoStyleContextFor(mContent, nsHTMLAtoms::firstLetterPseudo,
                                 (nsnull != mFirstLineStyle
                                  ? mFirstLineStyle
                                  : mStyleContext));
#ifdef NOISY_FIRST_LETTER
    if (nsnull != mFirstLetterStyle) {
      ListTag(stdout);
      printf(": first-letter style found\n");
    }
#endif
  }
  return NS_OK;
}
NS_IMETHODIMP
nsBlockFrame::DeleteFrame(nsIPresContext& aPresContext)
{
  // When we have a bullet frame and it's not in our child list then
  // we need to delete it ourselves (this is the common case for
  // list-item's that have outside bullets).
  if ((nsnull != mBullet) &&
      ((nsnull == mLines) || (mBullet != mLines->mFirstChild))) {
    mBullet->DeleteFrame(aPresContext);
    mBullet = nsnull;
  }
  DeleteLineList(aPresContext, mLines);
  DeleteLineList(aPresContext, mOverflowLines);
  DeleteFrameList(aPresContext, &mFloaters);
  return nsBlockFrameSuper::DeleteFrame(aPresContext);
}
NS_IMETHODIMP
nsBlockFrame::IsSplittable(nsSplittableType& aIsSplittable) const
{
  aIsSplittable = NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
  return NS_OK;
}
NS_IMETHODIMP
nsBlockFrame::CreateContinuingFrame(nsIPresContext&  aCX,
                                       nsIFrame*        aParent,
                                       nsIStyleContext* aStyleContext,
                                       nsIFrame*&       aContinuingFrame)
{
  nsBlockFrame* cf = new nsBlockFrame(mContent, aParent);
  if (nsnull == cf) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  PrepareContinuingFrame(aCX, aParent, aStyleContext, cf);
  aContinuingFrame = cf;
  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
     ("nsBlockFrame::CreateContinuingFrame: newFrame=%p", cf));
  return NS_OK;
}
NS_IMETHODIMP
nsBlockFrame::ListTag(FILE* out) const
{
  fprintf(out, "Block<");
  if (nsnull != mContent) {
    nsIAtom* atom;
    mContent->GetTag(atom);
    if (nsnull != atom) {
      nsAutoString tmp;
      atom->ToString(tmp);
      fputs(tmp, out);
    }
  }
  fprintf(out, ">(%d)@%p", ContentIndexInContainer(this), this);
  return NS_OK;
}
NS_METHOD
nsBlockFrame::List(FILE* out, PRInt32 aIndent, nsIListFilter *aFilter) const
{
  PRInt32 i;
  // if a filter is present, only output this frame if the filter says
  // we should
  nsAutoString tagString;
  if (nsnull != mContent) {
    nsIAtom* tag;
    mContent->GetTag(tag);
    if (tag != nsnull)  {
      tag->ToString(tagString);
      NS_RELEASE(tag);
    }
  }
  PRBool outputMe = (nsnull == aFilter) || aFilter->OutputTag(&tagString);
  if (outputMe) {
    // Indent
    for (i = aIndent; --i >= 0; ) fputs("  ", out);
    // Output the tag
    ListTag(out);
    nsIView* view;
    GetView(view);
    if (nsnull != view) {
      fprintf(out, " [view=%p]", view);
    }
    // Output the flow linkage
    if (nsnull != mPrevInFlow) {
      fprintf(out, "prev-in-flow=%p ", mPrevInFlow);
    }
    if (nsnull != mNextInFlow) {
      fprintf(out, "next-in-flow=%p ", mNextInFlow);
    }
    // Output the rect and state
    out << mRect;
    if (0 != mState) {
      fprintf(out, " [state=%08x]", mState);
    }
    fputs("<\n", out);
    aIndent++;
  }
  // Output bullet first
  if (nsnull != mBullet) {
    if (outputMe) {
      for (i = aIndent; --i >= 0; ) fputs("  ", out);
      fprintf(out, "bullet <\n");
    }
    mBullet->List(out, aIndent+1, aFilter);
    if (outputMe) {
      for (i = aIndent; --i >= 0; ) fputs("  ", out);
      fputs(">\n", out);
    }
  }
  // Output the lines
  if (nsnull != mLines) {
    LineData* line = mLines;
    while (nsnull != line) {
      line->List(out, aIndent, aFilter, outputMe);
      line = line->mNext;
    }
  }
  // Output floaters next
  if (nsnull != mFloaters) {
    if (outputMe) {
      for (i = aIndent; --i >= 0; ) fputs("  ", out);
      fprintf(out, "all-floaters <\n");
    }
    nsIFrame* floater = mFloaters;
    while (nsnull != floater) {
      floater->List(out, aIndent+1, aFilter);
      floater->GetNextSibling(floater);
    }
    if (outputMe) {
      for (i = aIndent; --i >= 0; ) fputs("  ", out);
      fputs(">\n", out);
    }
  }
  // Output the text-runs
  if (outputMe) {
    if (nsnull != mTextRuns) {
      for (i = aIndent; --i >= 0; ) fputs("  ", out);
      fputs("text-runs <\n", out);
      ListTextRuns(out, aIndent + 1, mTextRuns);
      for (i = aIndent; --i >= 0; ) fputs("  ", out);
      fputs(">\n", out);
    }
    aIndent--;
    for (i = aIndent; --i >= 0; ) fputs("  ", out);
    fputs(">\n", out);
  }
  return NS_OK;
}
/////////////////////////////////////////////////////////////////////////////
// Child frame enumeration
NS_IMETHODIMP
nsBlockFrame::FirstChild(nsIAtom* aListName, nsIFrame*& aFirstChild) const
{
  if (nsnull == aListName) {
    aFirstChild = (nsnull != mLines) ? mLines->mFirstChild : nsnull;
    return NS_OK;
  }
  else if (aListName == gFloaterAtom) {
    aFirstChild = nsnull;/* XXX temporary */
    return NS_OK;
  }
  else if (aListName == gBulletAtom) {
    aFirstChild = mBullet;
    return NS_OK;
  }
  aFirstChild = nsnull;
  return NS_ERROR_INVALID_ARG;
}
NS_IMETHODIMP
nsBlockFrame::GetAdditionalChildListName(PRInt32   aIndex,
                                         nsIAtom*& aListName) const
{
  if (aIndex < 0) {
    return NS_ERROR_INVALID_ARG;
  }
  nsIAtom* atom = nsnull;
  switch (aIndex) {
  case 0:
    atom = gFloaterAtom;
    NS_ADDREF(atom);
    break;
  case 1:
    atom = gBulletAtom;
    NS_ADDREF(atom);
    break;
  }
  aListName = atom;
  return NS_OK;
}
//////////////////////////////////////////////////////////////////////
// Reflow methods
void
nsBlockFrame::TakeRunInFrames(nsBlockFrame* aRunInFrame)
{
  // Simply steal the run-in-frame's line list and make it our
  // own. XXX Very similar to the logic in DrainOverflowLines...
  LineData* line = aRunInFrame->mLines;
  // Make all the frames on the mOverflowLines list mine
  nsIFrame* lastFrame = nsnull;
  nsIFrame* frame = line->mFirstChild;
  while (nsnull != frame) {
    nsIFrame* geometricParent;
    nsIFrame* contentParent;
    frame->GetGeometricParent(geometricParent);
    frame->GetContentParent(contentParent);
    if (contentParent == geometricParent) {
      frame->SetContentParent(this);
    }
    frame->SetGeometricParent(this);
    lastFrame = frame;
    frame->GetNextSibling(frame);
  }
  // Join the line lists
  if (nsnull == mLines) {
    mLines = line;
  }
  else {
    // Join the sibling lists together
    lastFrame->SetNextSibling(mLines->mFirstChild);
    // Place overflow lines at the front of our line list
    LineData* lastLine = LastLine(line);
    lastLine->mNext = mLines;
    mLines = line;
  }
  aRunInFrame->mLines = nsnull;
}
NS_IMETHODIMP
nsBlockFrame::Reflow(nsIPresContext&          aPresContext,
                     nsHTMLReflowMetrics&     aMetrics,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus)
{
  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                 ("enter nsBlockFrame::Reflow: maxSize=%d,%d reason=%d",
                  aReflowState.maxSize.width,
                  aReflowState.maxSize.height,
                  aReflowState.reason));
  // If this is the initial reflow, generate any synthetic content
  // that needs generating.
  if (eReflowReason_Initial == aReflowState.reason) {
    NS_ASSERTION(0 != (NS_FRAME_FIRST_REFLOW & mState), "bad mState");
  }
  else {
    NS_ASSERTION(0 == (NS_FRAME_FIRST_REFLOW & mState), "bad mState");
  }
  // Replace parent provided reflow state with our own significantly
  // more extensive version.
  nsBlockReflowState state(aPresContext, aReflowState, aMetrics);
  if (NS_BODY_NO_AUTO_MARGINS & mFlags) {
    // Pretend that there is a really big bottom margin preceeding the
    // first line so that it's margin is never applied.
  }
  if (NS_BODY_THE_BODY & mFlags) {
    state.mIsMarginRoot = PR_TRUE;
  }
  // Prepare inline-reflow engine
  nsInlineReflow inlineReflow(state.mLineLayout, state, this, PR_TRUE);
  state.mInlineReflow = &inlineReflow;
  state.mLineLayout.PushInline(&inlineReflow);
  nsresult rv = NS_OK;
  if (eReflowReason_Initial == state.reason) {
    if (nsnull != aReflowState.mRunInFrame) {
#ifdef NOISY_RUNIN
      ListTag(stdout);
      printf(": run-in from: ");
      aReflowState.mRunInFrame->ListTag(stdout);
      printf("\n");
#endif
      // Take frames away from the run-in frame
      TakeRunInFrames(aReflowState.mRunInFrame);
    }
    if (!DrainOverflowLines()) {
      RenumberLists(state);
      rv = InitialReflow(state);
    }
    else {
      RenumberLists(state);
      rv = ResizeReflow(state);
    }
    mState &= ~NS_FRAME_FIRST_REFLOW;
  }
  else if (eReflowReason_Incremental == state.reason) {
#if XXX
    // We can have an overflow here if our parent doesn't bother to
    // continue us
    DrainOverflowLines();
#endif
    nsIFrame* target;
    state.reflowCommand->GetTarget(target);
    if (this == target) {
      RenumberLists(state);
      nsIReflowCommand::ReflowType type;
      state.reflowCommand->GetType(type);
      switch (type) {
      case nsIReflowCommand::FrameAppended:
        rv = FrameAppendedReflow(state);
        break;
      case nsIReflowCommand::FrameInserted:
        rv = FrameInsertedReflow(state);
        break;
      case nsIReflowCommand::FrameRemoved:
        rv = FrameRemovedReflow(state);
        break;
      case nsIReflowCommand::StyleChanged:
        rv = StyleChangedReflow(state);
        break;
      default:
        // Map any other incremental operations into full reflows
        rv = ResizeReflow(state);
        break;
      }
    }
    else {
      // Get next frame in reflow command chain
      state.reflowCommand->GetNext(state.mNextRCFrame);
      // Now do the reflow
      rv = ChildIncrementalReflow(state);
    }
  }
  else if (eReflowReason_Resize == state.reason) {
    DrainOverflowLines();
    rv = ResizeReflow(state);
  }
  // Compute our final size
  ComputeFinalSize(state, aMetrics);
  BuildFloaterList();
#ifdef NS_DEBUG
  if (GetVerifyTreeEnable()) {
    VerifyChildCount(mLines);
    VerifyLines(mLines);
  }
#endif
  aStatus = rv;
  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                 ("exit nsBlockFrame::Reflow: size=%d,%d rv=%x",
                  aMetrics.width, aMetrics.height, rv));
  return NS_OK;
}
void
nsBlockFrame::BuildFloaterList()
{
  nsIFrame* head = nsnull;
  nsIFrame* current = nsnull;
  LineData* line = mLines;
  while (nsnull != line) {
    if (nsnull != line->mFloaters) {
      nsVoidArray& array = *line->mFloaters;
      PRInt32 i, n = array.Count();
      for (i = 0; i < n; i++) {
        nsPlaceholderFrame* ph = (nsPlaceholderFrame*) array[i];
        nsIFrame* floater = ph->GetAnchoredItem();
        if (nsnull == head) {
          current = head = floater;
        }
        else {
          current->SetNextSibling(floater);
          current = floater;
        }
      }
    }
    line = line->mNext;
  }
  mFloaters = head;
}
void
nsBlockFrame::RenumberLists(nsBlockReflowState& aState)
{
  // Setup initial list ordinal value
  PRInt32 ordinal = 1;
  nsIHTMLContent* hc;
  if (mContent && (NS_OK == mContent->QueryInterface(kIHTMLContentIID, (void**) &hc))) {
    nsHTMLValue value;
    if (NS_CONTENT_ATTR_HAS_VALUE ==
        hc->GetAttribute(nsHTMLAtoms::start, value)) {
      if (eHTMLUnit_Integer == value.GetUnit()) {
        ordinal = value.GetIntValue();
        if (ordinal <= 0) {
          ordinal = 1;
        }
      }
    }
    NS_RELEASE(hc);
  }
  aState.mNextListOrdinal = ordinal;
  // Get to first-in-flow
  nsBlockFrame* block = this;
  while (nsnull != block->mPrevInFlow) {
    block = (nsBlockFrame*) block->mPrevInFlow;
  }
  // For each flow-block...
  while (nsnull != block) {
    // For each frame in the flow-block...
    nsIFrame* frame = block->mLines ? block->mLines->mFirstChild : nsnull;
    while (nsnull != frame) {
      // If the frame is a list-item and the frame implements our
      // block frame API then get it's bullet and set the list item
      // ordinal.
      const nsStyleDisplay* display;
      frame->GetStyleData(eStyleStruct_Display,
                          (const nsStyleStruct*&) display);
      if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
        // Make certain that the frame isa block-frame in case
        // something foriegn has crept in.
        nsBlockFrame* listItem;
        if (NS_OK == frame->QueryInterface(kBlockFrameCID,
                                           (void**) &listItem)) {
          if (nsnull != listItem->mBullet) {
            listItem->mBullet->SetListItemOrdinal(aState);
          }
        }
      }
      frame->GetNextSibling(frame);
    }
    block = (nsBlockFrame*) block->mNextInFlow;
  }
}
void
nsBlockFrame::ComputeFinalSize(nsBlockReflowState&  aState,
                               nsHTMLReflowMetrics& aMetrics)
{
  // Compute final width
  if (eHTMLFrameConstraint_FixedContent == aState.widthConstraint) {
    // Use style defined width
    aMetrics.width = aState.mBorderPadding.left +
      aState.minWidth + aState.mBorderPadding.right;
  }
  else {
    nscoord computedWidth = aState.mKidXMost + aState.mBorderPadding.right;
    PRBool compact = PR_FALSE;
    if (NS_STYLE_DISPLAY_COMPACT == aState.mStyleDisplay->mDisplay) {
      // If we are display: compact AND we have no lines or we have
      // exactly one line and that line is not a block line AND that
      // line doesn't end in a BR of any sort THEN we remain a compact
      // frame.
      if ((nsnull == mLines) ||
          ((nsnull == mLines->mNext) && !mLines->IsBlock() &&
           (NS_STYLE_CLEAR_NONE == mLines->mBreakType) &&
           (computedWidth <= aState.mCompactMarginWidth))) {
        compact = PR_TRUE;
      }
    }
    // There are two options here. We either shrink wrap around our
    // contents or we fluff out to the maximum available width. Note:
    // We always shrink wrap when given an unconstrained width.
    if ((0 == (NS_BODY_SHRINK_WRAP & mFlags)) &&
        !aState.mUnconstrainedWidth &&
        !compact) {
      // Fluff out to the max width if we aren't already that wide
      if (computedWidth < aState.maxSize.width) {
        computedWidth = aState.maxSize.width;
      }
    }
    aMetrics.width = computedWidth;
  }
  // Compute final height
  if (eHTMLFrameConstraint_FixedContent == aState.heightConstraint) {
    // Use style defined height
    aMetrics.height = aState.mBorderPadding.top +
      aState.minHeight + aState.mBorderPadding.bottom;
  }
  else {
    // Shrink wrap our height around our contents.
    if (aState.mIsMarginRoot) {
      // When we are a margin root make sure that our last childs
      // bottom margin is fully applied.
      if ((NS_BODY_NO_AUTO_MARGINS & mFlags) &&
          (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & aState.mPrevMarginFlags)) {
        // Do not apply last auto margin when the last childs margin
        // is auto and we are configured to ignore them.
      }
      else {
        aState.mY += aState.mPrevBottomMargin;
      }
    }
    aState.mY += aState.mBorderPadding.bottom;
    aMetrics.height = aState.mY;
  }
  // Return top and bottom margin information
  if (aState.mIsMarginRoot) {
    aMetrics.mCarriedOutTopMargin = 0;
    aMetrics.mCarriedOutBottomMargin = 0;
    aMetrics.mCarriedOutMarginFlags = 0;
  }
  else {
    aMetrics.mCarriedOutTopMargin = aState.mCollapsedTopMargin;
    aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
    aMetrics.mCarriedOutMarginFlags = aState.mCarriedOutMarginFlags;
    if (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & aState.mPrevMarginFlags) {
      aMetrics.mCarriedOutMarginFlags |= NS_CARRIED_BOTTOM_MARGIN_IS_AUTO;
    }
  }
  // Special check for zero sized content: If our content is zero
  // sized then we collapse into nothingness.
  if ((eHTMLFrameConstraint_Unconstrained == aState.widthConstraint) &&
      (eHTMLFrameConstraint_Unconstrained == aState.heightConstraint) &&
      ((0 == aState.mKidXMost - aState.mBorderPadding.left) &&
       (0 == aState.mY - aState.mBorderPadding.top))) {
    aMetrics.width = 0;
    aMetrics.height = 0;
  }
  aMetrics.ascent = aMetrics.height;
  aMetrics.descent = 0;
  // XXX this needs ALOT OF REWORKING
  if (aState.mComputeMaxElementSize) {
    nscoord maxWidth, maxHeight;
    if (aState.mNoWrap) {
      maxWidth = 0;
      maxHeight = 0;
      LineData* line = mLines;
      while (nsnull != line) {
        nscoord xm = line->mBounds.XMost();
        if (xm > maxWidth) {
          maxWidth = xm;
        }
        line = line->mNext;
      }
      // XXX winging it!
      maxHeight = aState.mMaxElementSize.height +
        aState.mBorderPadding.top + aState.mBorderPadding.bottom;
    }
    else {
      maxWidth = aState.mMaxElementSize.width +
        aState.mBorderPadding.left + aState.mBorderPadding.right;
      maxHeight = aState.mMaxElementSize.height +
        aState.mBorderPadding.top + aState.mBorderPadding.bottom;
    }
    // Factor in any left and right floaters as well
    LineData* line = mLines;
    PRInt32 maxLeft = 0, maxRight = 0;
    while (nsnull != line) {
      if (nsnull != line->mFloaters) {
        nsRect r;
        nsMargin floaterMargin;
        PRInt32 leftSum = 0, rightSum = 0;
        PRInt32 n = line->mFloaters->Count();
        for (PRInt32 i = 0; i < n; i++) {
          nsPlaceholderFrame* placeholder = (nsPlaceholderFrame*)
            line->mFloaters->ElementAt(i);
          nsIFrame* floater = placeholder->GetAnchoredItem();
          floater->GetRect(r);
          const nsStyleDisplay* floaterDisplay;
          const nsStyleSpacing* floaterSpacing;
          floater->GetStyleData(eStyleStruct_Display,
                                (const nsStyleStruct*&)floaterDisplay);
          floater->GetStyleData(eStyleStruct_Spacing,
                                (const nsStyleStruct*&)floaterSpacing);
          floaterSpacing->CalcMarginFor(floater, floaterMargin);
          nscoord width = r.width + floaterMargin.left + floaterMargin.right;
          NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) ||
                       (NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats),
                       "invalid float type");
          if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
            leftSum += width;
          }
          else {
            rightSum += width;
          }
        }
        if (leftSum > maxLeft) maxLeft = leftSum;
        if (rightSum > maxRight) maxRight = rightSum;
      }
      line = line->mNext;
    }
    // XXX this part is an approximation
    if (aState.mNoWrap) {
      maxWidth += maxLeft + maxRight;
    }
    else {
      if (maxLeft > maxWidth) {
        maxWidth = maxLeft;
      }
      if (maxRight > maxWidth) {
        maxWidth = maxRight;
      }
    }
    // Store away the final value
    aMetrics.maxElementSize->width = maxWidth;
    aMetrics.maxElementSize->height = maxHeight;
#ifdef NOISY_MAX_ELEMENT_SIZE
    ListTag(stdout);
    printf(": max-element-size:%d,%d desired:%d,%d\n",
           maxWidth, maxHeight, aMetrics.width, aMetrics.height);
#endif
  }
  // Compute the combined area of our children
  // XXX take into account the overflow->clip property!
  nscoord x0 = 0, y0 = 0, x1 = aMetrics.width, y1 = aMetrics.height;
  LineData* line = mLines;
  while (nsnull != line) {
    // Compute min and max x/y values for the reflowed frame's
    // combined areas
    nscoord x = line->mCombinedArea.x;
    nscoord y = line->mCombinedArea.y;
    nscoord xmost = x + line->mCombinedArea.width;
    nscoord ymost = y + line->mCombinedArea.height;
    if (x < x0) x0 = x;
    if (xmost > x1) x1 = xmost;
    if (y < y0) y0 = y;
    if (ymost > y1) y1 = ymost;
    // If the line has floaters, factor those in as well
    nsVoidArray* floaters = line->mFloaters;
    if (nsnull != floaters) {
      PRInt32 i, n = floaters->Count();
      for (i = 0; i < n; i++) {
        nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
        nsIFrame* frame = ph->GetAnchoredItem();
        // XXX This is wrong! The floater may have a combined area
        // that exceeds its bounding box!
        nsRect r;
        frame->GetRect(r);
        if (r.x < x0) x0 = r.x;
        if (r.XMost() > x1) x1 = r.XMost();
        if (r.y < y0) y0 = r.y;
        if (r.YMost() > y1) y1 = r.YMost();
      }
    }
    line = line->mNext;
  }
  if (nsnull != mBullet) {
    nsRect r;
    mBullet->GetRect(r);
    if (r.x < x0) x0 = r.x;
    if (r.XMost() > x1) x1 = r.XMost();
    if (r.y < y0) y0 = r.y;
    if (r.YMost() > y1) y1 = r.YMost();
  }
  aMetrics.mCombinedArea.x = x0;
  aMetrics.mCombinedArea.y = y0;
  aMetrics.mCombinedArea.width = x1 - x0;
  aMetrics.mCombinedArea.height = y1 - y0;
  // If the combined area of our children exceeds our bounding box
  // then set the NS_FRAME_OUTSIDE_CHILDREN flag, otherwise clear it.
  if ((aMetrics.mCombinedArea.x < 0) ||
      (aMetrics.mCombinedArea.y < 0) ||
      (aMetrics.mCombinedArea.XMost() > aMetrics.width) ||
      (aMetrics.mCombinedArea.YMost() > aMetrics.height)) {
    mState |= NS_FRAME_OUTSIDE_CHILDREN;
  }
  else {
    mState &= ~NS_FRAME_OUTSIDE_CHILDREN;
  }
}
nsresult
nsBlockFrame::AppendNewFrames(nsIPresContext& aPresContext,
                              nsIFrame* aNewFrame)
{
  // Get our last line and then get its last child
  nsIFrame* lastFrame;
  LineData* lastLine = LastLine(mLines);
  if (nsnull != lastLine) {
    lastFrame = lastLine->LastChild();
  } else {
    lastFrame = nsnull;
  }
  // Add the new frames to the sibling list; wrap any frames that
  // require wrapping
  if (nsnull != lastFrame) {
    lastFrame->SetNextSibling(aNewFrame);
  }
  nsresult rv;
  // Make sure that new inlines go onto the end of the lastLine when
  // the lastLine is mapping inline frames.
  PRInt32 pendingInlines = 0;
  if (nsnull != lastLine) {
    if (!lastLine->IsBlock()) {
      pendingInlines = 1;
    }
  }
  // Now create some lines for the new frames
  nsIFrame* prevFrame = lastFrame;
  for (nsIFrame* frame = aNewFrame; nsnull != frame; frame->GetNextSibling(frame)) {
    // See if the child is a block or non-block
    const nsStyleDisplay* kidDisplay;
    rv = frame->GetStyleData(eStyleStruct_Display,
                             (const nsStyleStruct*&) kidDisplay);
    if (NS_OK != rv) {
      return rv;
    }
    const nsStylePosition* kidPosition;
    rv = frame->GetStyleData(eStyleStruct_Position,
                             (const nsStyleStruct*&) kidPosition);
    if (NS_OK != rv) {
      return rv;
    }
    PRBool isBlock =
      nsLineLayout::TreatFrameAsBlock(kidDisplay, kidPosition);
    // See if we need to move the frame outside of the flow, and insert a
    // placeholder frame in its place
    nsIFrame* placeholder;
    if (MoveFrameOutOfFlow(aPresContext, frame, kidDisplay, kidPosition,
                           placeholder)) {
      // Reset the previous frame's next sibling pointer
      if (nsnull != prevFrame) {
        prevFrame->SetNextSibling(placeholder);
      }
      // The placeholder frame is always inline
      frame = placeholder;
      isBlock = PR_FALSE;
    }
    else {
      // Wrap the frame in a view if necessary
      nsIStyleContext* kidSC;
      frame->GetStyleContext(kidSC);
      rv = CreateViewForFrame(aPresContext, frame, kidSC, PR_FALSE);
      NS_RELEASE(kidSC);
      if (NS_OK != rv) {
        return rv;
      }
    }
    // If the child is an inline then add it to the lastLine (if it's
    // an inline line, otherwise make a new line). If the child is a
    // block then make a new line and put the child in that line.
    if (isBlock) {
      // If the previous line has pending inline data to be reflowed,
      // do so now.
      if (0 != pendingInlines) {
        // Set this to true in case we don't end up reflowing all of the
        // frames on the line (because they end up being pushed).
        lastLine->MarkDirty();
        pendingInlines = 0;
      }
      // Create a line for the block
      LineData* line = new LineData(frame, 1, LINE_IS_BLOCK);
      if (nsnull == line) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      if (nsnull == lastLine) {
        mLines = line;
      }
      else {
        lastLine->mNext = line;
      }
      lastLine = line;
    }
    else {
      // Queue up the inlines for reflow later on
      if (0 == pendingInlines) {
        LineData* line = new LineData(frame, 0, 0);
        if (nsnull == line) {
          return NS_ERROR_OUT_OF_MEMORY;
        }
        if (nsnull == lastLine) {
          mLines = line;
        }
        else {
          lastLine->mNext = line;
        }
        lastLine = line;
      }
      lastLine->mChildCount++;
      pendingInlines++;
    }
    // Remember the previous frame
    prevFrame = frame;
  }
  if (0 != pendingInlines) {
    // Set this to true in case we don't end up reflowing all of the
    // frames on the line (because they end up being pushed).
    lastLine->MarkDirty();
  }
  return NS_OK;
}
nsresult
nsBlockFrame::InitialReflow(nsBlockReflowState& aState)
{
  // Generate text-run information
  nsresult rv = FindTextRuns(aState);
  if (NS_OK != rv) {
    return rv;
  }
  // Reflow everything
  aState.GetAvailableSpace();
  return ResizeReflow(aState);
}
void
nsBlockFrame::RecoverLineMargins(nsBlockReflowState& aState,
                                 LineData* aLine,
                                 nscoord& aTopMarginResult,
                                 nscoord& aBottomMarginResult)
{
  nscoord childsCarriedOutTopMargin = aLine->mCarriedOutTopMargin;
  nscoord childsCarriedOutBottomMargin = aLine->mCarriedOutBottomMargin;
  nscoord childsTopMargin = 0;
  nscoord childsBottomMargin = 0;
  if (aLine->IsBlock()) {
    // Recover the block frames computed bottom margin value
    nsIFrame* frame = aLine->mFirstChild;
    if (nsnull != frame) {
      const nsStyleSpacing* spacing;
      frame->GetStyleData(eStyleStruct_Spacing,
                          (const nsStyleStruct*&) spacing);
      if (nsnull != spacing) {
        nsMargin margin;
        nsInlineReflow::CalculateBlockMarginsFor(aState.mPresContext, frame,
                                                 spacing, margin);
        childsTopMargin = margin.top;
        childsBottomMargin = margin.bottom;
      }
    }
  }
  // Collapse the carried-out-margins with the childs margins
  aBottomMarginResult =
    nsInlineReflow::MaxMargin(childsCarriedOutBottomMargin, childsBottomMargin);
  aTopMarginResult =
    nsInlineReflow::MaxMargin(childsCarriedOutTopMargin, childsTopMargin);
}
nsresult
nsBlockFrame::FrameAppendedReflow(nsBlockReflowState& aState)
{
  nsresult  rv = NS_OK;
  // Get the first of the newly appended frames
  nsIFrame* firstAppendedFrame;
  aState.reflowCommand->GetChildFrame(firstAppendedFrame);
  // Add the new frames to the child list, and create new lines. Each
  // impacted line will be marked dirty
  AppendNewFrames(aState.mPresContext, firstAppendedFrame);
  RenumberLists(aState);
  // Generate text-run information
  rv = FindTextRuns(aState);
  if (NS_OK != rv) {
    return rv;
  }
  // Recover our reflow state
  LineData* firstDirtyLine = mLines;
  LineData* lastCleanLine = nsnull;
  PRBool haveRunIn = PR_FALSE;
  while (nsnull != firstDirtyLine) {
    if (firstDirtyLine->IsDirty()) {
      break;
    }
    // Recover run-in state
    if (firstDirtyLine->IsBlock()) {
      nsIFrame* frame = firstDirtyLine->mFirstChild;
      const nsStyleDisplay* display;
      frame->GetStyleData(eStyleStruct_Display,
                          (const nsStyleStruct*&) display);
      if ((NS_STYLE_DISPLAY_RUN_IN == display->mDisplay) ||
          (NS_STYLE_DISPLAY_COMPACT == display->mDisplay)) {
        haveRunIn = PR_TRUE;
        break;
      }
    }
    // Recover xmost
    nscoord xmost = firstDirtyLine->mBounds.XMost();
    if (xmost > aState.mKidXMost) {
      aState.mKidXMost = xmost;
    }
    // Recover the mPrevBottomMargin and the mCollapsedTopMargin values
    nscoord topMargin, bottomMargin;
    RecoverLineMargins(aState, firstDirtyLine, topMargin, bottomMargin);
    if (0 == firstDirtyLine->mBounds.height) {
      // For zero height lines, collapse the lines top and bottom
      // margins together to produce the effective bottomMargin value.
      bottomMargin = nsInlineReflow::MaxMargin(topMargin, bottomMargin);
      bottomMargin = nsInlineReflow::MaxMargin(aState.mPrevBottomMargin,
                                               bottomMargin);
    }
    else {
      aState.mPrevMarginFlags = firstDirtyLine->GetMarginFlags();
    }
    aState.mPrevBottomMargin = bottomMargin;
    // Advance Y to be below the line.
    aState.mY = firstDirtyLine->mBounds.YMost();
    // Advance to the next line
    lastCleanLine = firstDirtyLine;
    firstDirtyLine = firstDirtyLine->mNext;
  }
  if (haveRunIn) {
    // XXX For now, reflow the world when we contain a frame that is a
    // run-in/compact frame. (lazy code)
    lastCleanLine = nsnull;
    firstDirtyLine = mLines;
    aState.mY = aState.mBorderPadding.top;
    aState.mPrevMarginFlags = 0;
    aState.mPrevBottomMargin = 0;
  }
  if (nsnull != lastCleanLine) {
    // Unplace any floaters on the lastCleanLine and every line that
    // follows it (which shouldn't be much given that this is
    // frame-appended reflow)
    LineData* line = lastCleanLine;
    while (nsnull != line) {
      line->UnplaceFloaters(aState.mSpaceManager);
      line = line->mNext;
    }
    // Place any floaters the last clean line has
    if (nsnull != lastCleanLine->mFloaters) {
      aState.mCurrentLine = lastCleanLine;
      aState.PlaceFloaters(lastCleanLine->mFloaters, PR_TRUE);
    }
    // Apply any clear-after semantics the line might have
//XXX    nscoord y0, dy;
    switch (lastCleanLine->mBreakType) {
    case NS_STYLE_CLEAR_LEFT:
    case NS_STYLE_CLEAR_RIGHT:
    case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
      NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                     ("line %p: ClearFloaters: y=%d, break-type=%d\n",
                      lastCleanLine, aState.mY, lastCleanLine->mBreakType));
//XXX      y0 = aState.mY;
      aState.ClearFloaters(lastCleanLine->mBreakType);
#if 0
      dy = aState.mY - y0;
      if (dy > aState.mPrevBottomMargin) {
        aState.mPrevBottomMargin = dy;
        aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN;
      }
#endif
      NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                     ("  cleared floaters, now y=%d\n", aState.mY));
      break;
    }
  }
  aState.GetAvailableSpace();
  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
     ("nsBlockReflowState::FrameAppendedReflow: y=%d firstDirtyLine=%p",
      aState.mY, firstDirtyLine));
  // Reflow lines from there forward
  aState.mPrevLine = lastCleanLine;
  return ReflowLinesAt(aState, firstDirtyLine);
}
// XXX keep the text-run data in the first-in-flow of the block
nsresult
nsBlockFrame::FindTextRuns(nsBlockReflowState& aState)
{
  // Destroy old run information first
  nsTextRun::DeleteTextRuns(mTextRuns);
  mTextRuns = nsnull;
  aState.mLineLayout.ResetTextRuns();
  // Ask each child that implements nsIInlineReflow to find its text runs
  LineData* line = mLines;
  while (nsnull != line) {
    if (!line->IsBlock()) {
      nsIFrame* frame = line->mFirstChild;
      PRInt32 n = line->ChildCount();
      while (--n >= 0) {
        nsIHTMLReflow* hr;
        if (NS_OK == frame->QueryInterface(kIHTMLReflowIID, (void**)&hr)) {
          nsresult rv = hr->FindTextRuns(aState.mLineLayout);
          if (NS_OK != rv) {
            return rv;
          }
        }
        else {
          // A frame that doesn't implement nsIHTMLReflow isn't text
          // therefore it will end an open text run.
          aState.mLineLayout.EndTextRun();
        }
        frame->GetNextSibling(frame);
      }
    }
    else {
      // A frame that doesn't implement nsIInlineReflow isn't text
      // therefore it will end an open text run.
      aState.mLineLayout.EndTextRun();
    }
    line = line->mNext;
  }
  aState.mLineLayout.EndTextRun();
  // Now take the text-runs away from the line layout engine.
  mTextRuns = aState.mLineLayout.TakeTextRuns();
  return NS_OK;
}
// XXX rewrite this
nsresult
nsBlockFrame::FrameInsertedReflow(nsBlockReflowState& aState)
{
  // Get the inserted frame
  nsIFrame* newFrame;
  aState.reflowCommand->GetChildFrame(newFrame);
  // Get the previous sibling frame
  nsIFrame* prevSibling;
  aState.reflowCommand->GetPrevSiblingFrame(prevSibling);
  // Insert the frame. This marks the line dirty...
  InsertNewFrame(aState.mPresContext, this, newFrame, prevSibling);
#if XXX
  LineData* line = mLines;
  while (nsnull != line->mNext) {
    if (line->IsDirty()) {
      break;
    }
    line = line->mNext;
  }
  NS_ASSERTION(nsnull != line, "bad inserted reflow");
  //XXX return ReflowDirtyLines(aState, line);
#endif
  // XXX Correct implementation: reflow the dirty lines only; all
  // other lines can be moved; recover state before first dirty line.
  // XXX temporary
  aState.mSpaceManager->ClearRegions();
  aState.GetAvailableSpace();
  aState.mPrevLine = nsnull;
  return ReflowLinesAt(aState, mLines);
}
// XXX rewrite this
nsresult
nsBlockFrame::FrameRemovedReflow(nsBlockReflowState& aState)
{
  if (nsnull == mLines) {
    return NS_OK;
  } 
  // Get the deleted frame
  nsIFrame* deletedFrame;
  aState.reflowCommand->GetChildFrame(deletedFrame);
  // Find the previous sibling frame
  nsIFrame* prevSibling = nsnull;
  for (nsIFrame* f = mLines->mFirstChild; f != deletedFrame;
       f->GetNextSibling(f)) {
    if (nsnull == f) {
      // We didn't find the deleted frame in our child list
      NS_WARNING("Can't find deleted frame");
      return NS_OK;
    }
    prevSibling = f;
  }
  // Find the line that contains deletedFrame; we also find the pointer to
  // the line.
  nsBlockFrame* flow = this;
  LineData** linep = &flow->mLines;
  LineData* line = flow->mLines;
  while (nsnull != line) {
    if (line->Contains(deletedFrame)) {
      break;
    }
    linep = &line->mNext;
    line = line->mNext;
  }
  // Remove frame and its continuations
  while (nsnull != deletedFrame) {
    while ((nsnull != line) && (nsnull != deletedFrame)) {
#ifdef NS_DEBUG
      nsIFrame* parent;
      deletedFrame->GetGeometricParent(parent);
      NS_ASSERTION(flow == parent, "messed up delete code");
#endif
      NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
         ("nsBlockFrame::ContentDeleted: deadFrame=%p", deletedFrame));
      // Remove deletedFrame from the line
      if (line->mFirstChild == deletedFrame) {
        nsIFrame* nextFrame;
        deletedFrame->GetNextSibling(nextFrame);
        line->mFirstChild = nextFrame;
      }
      // Take deletedFrame out of the sibling list
      if (nsnull != prevSibling) {
        nsIFrame* nextFrame;
        deletedFrame->GetNextSibling(nextFrame);
        prevSibling->SetNextSibling(nextFrame);
      }
      // Destroy frame; capture its next-in-flow first in case we need
      // to destroy that too.
      nsIFrame* nextInFlow;
      deletedFrame->GetNextInFlow(nextInFlow);
      if (nsnull != nextInFlow) {
        deletedFrame->BreakFromNextFlow();
      }
      deletedFrame->DeleteFrame(aState.mPresContext);
      deletedFrame = nextInFlow;
      // If line is empty, remove it now
      LineData* next = line->mNext;
      if (0 == --line->mChildCount) {
        *linep = next;
        line->mNext = nsnull;
        delete line;
      }
      else {
        linep = &line->mNext;
      }
      line = next;
    }
    // Advance to next flow block if the frame has more continuations
    if (nsnull != deletedFrame) {
      flow = (nsBlockFrame*) flow->mNextInFlow;
      NS_ASSERTION(nsnull != flow, "whoops, continuation without a parent");
      line = flow->mLines;
      prevSibling = nsnull;
    }
  }
  // XXX Correct implementation: reflow the dirty lines only; all
  // other lines can be moved; recover state before first dirty line.
  // XXX temporary
  aState.mSpaceManager->ClearRegions();
  aState.GetAvailableSpace();
  aState.mPrevLine = nsnull;
  return ReflowLinesAt(aState, mLines);
}
// XXX Todo: some incremental reflows are passing through this block
// and into a child block; those cannot impact our text-runs. In that
// case skip the FindTextRuns work.
// XXX easy optimizations: find the line that contains the next child
// in the reflow-command path and mark it dirty and only reflow it;
// recover state before it, slide lines down after it.
nsresult
nsBlockFrame::ChildIncrementalReflow(nsBlockReflowState& aState)
{
#if 0
  // Generate text-run information; this will also "fluff out" any
  // inline children's frame tree.
  nsresult rv = FindTextRuns(aState);
  if (NS_OK != rv) {
    return rv;
  }
#endif
  // XXX temporary
  aState.mSpaceManager->ClearRegions();
  aState.GetAvailableSpace();
  aState.mPrevLine = nsnull;
  return ReflowLinesAt(aState, mLines);
}
nsresult
nsBlockFrame::StyleChangedReflow(nsBlockReflowState& aState)
{
  // XXX temporary
  aState.mSpaceManager->ClearRegions();
  aState.GetAvailableSpace();
  aState.mPrevLine = nsnull;
  return ReflowLinesAt(aState, mLines);
}
nsresult
nsBlockFrame::ResizeReflow(nsBlockReflowState& aState)
{
  // Mark everything dirty
  LineData* line = mLines;
  while (nsnull != line) {
    line->MarkDirty();
    line = line->mNext;
  }
  // Reflow all of our lines
  aState.GetAvailableSpace();
  aState.mPrevLine = nsnull;
  return ReflowLinesAt(aState, mLines);
}
nsresult
nsBlockFrame::ReflowLinesAt(nsBlockReflowState& aState, LineData* aLine)
{
  // Inform line layout of where the text runs are
  aState.mLineLayout.SetReflowTextRuns(mTextRuns);
  // Reflow the lines that are already ours
  while (nsnull != aLine) {
    nsReflowStatus rs;
    if (!ReflowLine(aState, aLine, rs)) {
      if (NS_IS_REFLOW_ERROR(rs)) {
        return nsresult(rs);
      }
      return NS_FRAME_NOT_COMPLETE;
    }
    aState.mLineLayout.NextLine();
    aState.mPrevLine = aLine;
    aLine = aLine->mNext;
  }
  // Pull data from a next-in-flow if we can
  while (nsnull != aState.mNextInFlow) {
    // Grab first line from our next-in-flow
    aLine = aState.mNextInFlow->mLines;
    if (nsnull == aLine) {
      aState.mNextInFlow = (nsBlockFrame*) aState.mNextInFlow->mNextInFlow;
      continue;
    }
    // XXX See if the line is not dirty; if it's not maybe we can
    // avoid the pullup if it can't fit?
    aState.mNextInFlow->mLines = aLine->mNext;
    aLine->mNext = nsnull;
    if (0 == aLine->ChildCount()) {
      // The line is empty. Try the next one.
      NS_ASSERTION(nsnull == aLine->mFirstChild, "bad empty line");
      aLine->mNext = aState.mFreeList;
      aState.mFreeList = aLine;
      continue;
    }
    // Make the children in the line ours.
    nsIFrame* frame = aLine->mFirstChild;
    nsIFrame* lastFrame = nsnull;
    PRInt32 n = aLine->ChildCount();
    while (--n >= 0) {
      nsIFrame* geometricParent;
      nsIFrame* contentParent;
      frame->GetGeometricParent(geometricParent);
      frame->GetContentParent(contentParent);
      if (contentParent == geometricParent) {
        frame->SetContentParent(this);
      }
      frame->SetGeometricParent(this);
      lastFrame = frame;
      frame->GetNextSibling(frame);
    }
    lastFrame->SetNextSibling(nsnull);
    // Add line to our line list
    if (nsnull == aState.mPrevLine) {
      NS_ASSERTION(nsnull == mLines, "bad aState.mPrevLine");
      mLines = aLine;
    }
    else {
      NS_ASSERTION(nsnull == aState.mPrevLine->mNext, "bad aState.mPrevLine");
      aState.mPrevLine->mNext = aLine;
      aState.mPrevChild->SetNextSibling(aLine->mFirstChild);
    }
#ifdef NS_DEBUG
    if (GetVerifyTreeEnable()) {
      VerifyChildCount(mLines);
    }
#endif
    // Now reflow it and any lines that it makes during it's reflow.
    while (nsnull != aLine) {
      nsReflowStatus rs;
      if (!ReflowLine(aState, aLine, rs)) {
        if (NS_IS_REFLOW_ERROR(rs)) {
          return nsresult(rs);
        }
        return NS_FRAME_NOT_COMPLETE;
      }
      aState.mLineLayout.NextLine();
      aState.mPrevLine = aLine;
      aLine = aLine->mNext;
    }
  }
  return NS_FRAME_COMPLETE;
}
/**
 * Reflow a line. The line will either contain a single block frame
 * or contain 1 or more inline frames.
 */
PRBool
nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
                         LineData* aLine,
                         nsReflowStatus& aReflowResult)
{
  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
     ("nsBlockFrame::ReflowLine: line=%p", aLine));
  // Setup the line-layout for the new line
  aState.mLineLayout.Reset();
  aState.mCurrentLine = aLine;
  aState.mInlineReflowPrepared = PR_FALSE;
  // If the line already has floaters on it from last time, remove
  // them from the spacemanager now.
  if (nsnull != aLine->mFloaters) {
    if (eReflowReason_Resize != aState.reason) {
      aLine->UnplaceFloaters(aState.mSpaceManager);
    }
    delete aLine->mFloaters;
    aLine->mFloaters = nsnull;
  }
  aLine->ClearDirty();
  aLine->SetNeedDidReflow();
  // Reflow mapped frames in the line
  nsBlockFrame* nextInFlow;
  PRBool keepGoing = PR_FALSE;
  PRInt32 n = aLine->ChildCount();
  if (0 != n) {
    nsIFrame* frame = aLine->mFirstChild;
#ifdef NS_DEBUG
    const nsStyleDisplay* display;
    frame->GetStyleData(eStyleStruct_Display,
                        (const nsStyleStruct*&) display);
    const nsStylePosition* position;
    frame->GetStyleData(eStyleStruct_Position,
                        (const nsStyleStruct*&) position);
    PRBool isBlock = nsLineLayout::TreatFrameAsBlock(display, position);
    NS_ASSERTION(isBlock == aLine->IsBlock(), "bad line isBlock");
#endif 
   if (aLine->IsBlock()) {
      keepGoing = ReflowBlockFrame(aState, aLine, frame, aReflowResult);
      return keepGoing;
    }
    else {
      while (--n >= 0) {
        PRBool addedToLine;
        keepGoing = ReflowInlineFrame(aState, aLine, frame, aReflowResult,
                                      addedToLine);
        if (!keepGoing) {
          // It is possible that one or more of next lines are empty
          // (because of DeleteNextInFlowsFor). If so, delete them now
          // in case we are finished.
          LineData* nextLine = aLine->mNext;
          while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
            // Discard empty lines immediately. Empty lines can happen
            // here because of DeleteNextInFlowsFor not being able to
            // delete lines.
            aLine->mNext = nextLine->mNext;
            NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
            nextLine->mNext = aState.mFreeList;
            aState.mFreeList = nextLine;
            nextLine = aLine->mNext;
          }
          goto done;
        }
        frame->GetNextSibling(frame);
        if (addedToLine) {
          NS_ASSERTION(nsnull != frame, "added to line but no new frame");
          n++;
        }
      }
    }
  }
  // Pull frames from the next line until we can't
  while (nsnull != aLine->mNext) {
    PRBool addedToLine;
    keepGoing = PullFrame(aState, aLine, &aLine->mNext,
                          PR_FALSE, aReflowResult, addedToLine);
    if (!keepGoing) {
      goto done;
    }
  }
  // Pull frames from the next-in-flow(s) until we can't
  nextInFlow = aState.mNextInFlow;
  while (nsnull != nextInFlow) {
    LineData* line = nextInFlow->mLines;
    if (nsnull == line) {
      nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
      aState.mNextInFlow = nextInFlow;
      continue;
    }
    PRBool addedToLine;
    keepGoing = PullFrame(aState, aLine, &nextInFlow->mLines,
                          PR_TRUE, aReflowResult, addedToLine);
    if (!keepGoing) {
      goto done;
    }
  }
  keepGoing = PR_TRUE;
done:;
  if (!aLine->IsBlock()) {
    return PlaceLine(aState, aLine, aReflowResult);
  }
  return keepGoing;
}
// block's vs. inlines's:
// 1. break-before/break-after (is handled by nsInlineReflow)
// 2. pull-up: can't pull a block onto a non-empty line
// 3. blocks require vertical margin handling
// 4. left/right margins have to be figured differently for blocks
// Assume that container is aware of block/inline differences of the
// child frame and handles them through different pathways OR that the
// nsInlineReflow does and reports back the different results
// Here is how we format B0[ I0[ I1[ text0 B1 text1] ] ]
// B0 reflows I0
//  I0 formats I1
//   I1 reflows text0
//    I1 hits B1 and notices it can't be placed
//    I1 pushes B1 to overflow list
//    I1 returns not-complete
//  I0 creates I1'
//  I0 returns not-complete
// B0 creates I0'
// B0 flushes out line
// B0 creates new line
// B0 puts I0' on new line
// B0 reflows I0'
//  I0' reflows I1'
//   I1' reflows B1
//   I1' returns not-complete
//  I0' creates I1''
//  I0' returns not-complete
// B0 creates I0''
// B0 flushes out line
// B0 creates new line
// B0 puts I0'' on new line
// B0 reflows I0''
//  I0'' reflows I1''
//   I1'' reflows text1
//   I1'' returns complete
//  I0'' returns complete
// B0 flushes out line
// B0 returns complete
// Here is how we format B0[ I0[ text0 I1[ B1 text1] ] ]
// This is harder; I1 doesn't have text0 before B1 to know that we
// have to stop reflowing I1 before reflowing B1; and worse yet, we
// only have to stop if I1 begins on the same line that contains text0
// (it might not depending on the size of text0 and the size given to
// I0 to reflow into).
// To solve this we need to know when we hit B1 whether or not a break
// is required. To answer this question we need to be able to walk
// backwards up the nsReflowState's and discover where B1 will
// actually end up. Can't use the X coordinate because of
// border/padding.
// Since B0 will define this what we do is put a counter into the
// nsLineLayout structure. As the reflow recurses down the tree we
// will bump the counter whenever a non-inline frame is seen and
// placed (one that has a non-zero area too). When I1 goes to place B1
// it will see that the count is 1 (because of text0) and know to
// return a "break-before" status (instead of a not-complete; we
// return not-complete if we have placed ANY children and break-before
// when we have placed NO children).
// XXX factor this some more so that nsInlineFrame can use it too.
// The post-reflow-success code that computes the final margin
// values and carries them forward, plus the code that factors in
// the max-element size.
// XXX inline frames need this too when they wrap up blocks, right??
// Otherwise blocks in inlines won't interact with floaters properly.
void
nsBlockFrame::PrepareInlineReflow(nsBlockReflowState& aState,
                                  nsIFrame* aFrame,
                                  PRBool aIsBlock)
{
  // Setup initial coordinate system for reflowing the frame into
  nscoord x, availWidth, availHeight;
  if (aIsBlock) {
    // XXX Child needs to apply OUR border-padding and IT's left
    // margin and right margin! How should this be done?
    x = aState.mBorderPadding.left;
    if (aState.mUnconstrainedWidth) {
      availWidth = NS_UNCONSTRAINEDSIZE;
    }
    else {
      availWidth = aState.mContentArea.width;
    }
    if (aState.mUnconstrainedHeight) {
      availHeight = NS_UNCONSTRAINEDSIZE;
    }
    else {
      /* XXX get the height right! */
      availHeight = aState.mContentArea.height - aState.mY;
    }
  }
  else {
    // If the child doesn't handle run-around then we give it the band
    // adjusted data. The band aligned data doesn't have our
    // border/padding applied to it so add that in.
    x = aState.mCurrentBand.availSpace.x + aState.mBorderPadding.left;
    availWidth = aState.mCurrentBand.availSpace.width;
    if (aState.mUnconstrainedHeight) {
      availHeight = NS_UNCONSTRAINEDSIZE;
    }
    else {
      /* XXX get the height right! */
      availHeight = aState.mCurrentBand.availSpace.height;
    }
  }
  aState.mInlineReflow->Init(x, aState.mY, availWidth, availHeight);
  aState.mInlineReflowPrepared = PR_TRUE;
  // Setup the first-letter-style-ok flag
  nsLineLayout& lineLayout = aState.mLineLayout;
  if (mFirstLetterStyle && (0 == lineLayout.GetLineNumber())) {
    lineLayout.SetFirstLetterStyleOK(PR_TRUE);
  }
  else {
    lineLayout.SetFirstLetterStyleOK(PR_FALSE);
  }
}
// XXX switch to two versions: inline vs. block?
PRUintn
nsBlockFrame::CalculateMargins(nsBlockReflowState& aState,
                               LineData* aLine,
                               PRBool aInlineContext,
                               nscoord& aTopMarginResult,
                               nscoord& aBottomMarginResult)
{
  PRUintn result = 0;
  nsInlineReflow& ir = *aState.mInlineReflow;
  // First get the childs top and bottom margins. For inline
  // situations the child frame(s) margins were applied during line
  // layout, therefore we consider them zero here.
  nscoord childsTopMargin, childsBottomMargin;
  PRUintn marginFlags;
  if (aInlineContext) {
    childsTopMargin = 0;
    childsBottomMargin = 0;
    marginFlags = ir.GetCarriedOutMarginFlags();
  }
  else {
    childsTopMargin = ir.GetTopMargin();
    childsBottomMargin = ir.GetBottomMargin();
    marginFlags = ir.GetMarginFlags();
  }
  // Get the carried margin values. Note that even in an inline
  // situation there may be a carried margin (e.g. an inline frame
  // contains a block frame and the block frame has top/bottom
  // margins). While CSS doesn't specify what this means, we do to
  // improve compatability with poorly formed HTML (and commonly used
  // HTML).
  nscoord childsCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
  nscoord childsCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
  if (childsCarriedOutTopMargin || childsCarriedOutBottomMargin) {
    result |= HAVE_CARRIED_MARGIN;
  }
  // Compute the collapsed top margin value (this is a generational
  // margin collapse not a sibling margin collapse).
  nscoord collapsedTopMargin = 
    nsInlineReflow::MaxMargin(childsCarriedOutTopMargin, childsTopMargin);
  // Now perform sibling to sibling margin collapsing.
  collapsedTopMargin = nsInlineReflow::MaxMargin(aState.mPrevBottomMargin,
                                                 collapsedTopMargin);
  PRBool topMarginIsAuto = PR_FALSE;
  if (NS_CARRIED_TOP_MARGIN_IS_AUTO & marginFlags) {
    if (aInlineContext) {
      topMarginIsAuto = collapsedTopMargin == childsCarriedOutTopMargin;
    }
    else {
      topMarginIsAuto = collapsedTopMargin == childsTopMargin;
    }
  }
  // Compute the collapsed bottom margin value (this is a generational
  // margin collapse not a sibling margin collapse).
  nscoord collapsedBottomMargin =
    nsInlineReflow::MaxMargin(childsCarriedOutBottomMargin,
                              childsBottomMargin);
  if (NS_CARRIED_BOTTOM_MARGIN_IS_AUTO & marginFlags) {
    if (aInlineContext) {
      if (collapsedBottomMargin == childsCarriedOutBottomMargin) {
        result |= NS_CARRIED_BOTTOM_MARGIN_IS_AUTO;
      }
    }
    else if (collapsedBottomMargin == childsBottomMargin) {
      result |= NS_CARRIED_BOTTOM_MARGIN_IS_AUTO;
    }
  }
  // Now that we have the collapsed margin values, address any special
  // situations that limit or change how the margins are applied.
  if (0 == aLine->mBounds.height) {
    // When a line is empty, we collapse its top and bottom margins to
    // a single bottom margin value.
    // XXX This is not obviously a part of the css2 box model.
    collapsedBottomMargin =
      nsInlineReflow::MaxMargin(collapsedTopMargin, collapsedBottomMargin);
    collapsedTopMargin = 0;
  }
  else {
    PRBool isTopLine = (aState.mY == aState.mBorderPadding.top);
    if (isTopLine) {
      if (!aState.mIsMarginRoot) {
        // We are not a root for margin collapsing and this is our first
        // non-empty-line (with a block child).
        if (topMarginIsAuto) {
          // Propogate auto-margin flag outward
          aState.mCarriedOutMarginFlags |= NS_CARRIED_TOP_MARGIN_IS_AUTO;
          result |= NS_CARRIED_TOP_MARGIN_IS_AUTO;
        }
        // Keep the collapsed margin value around to pass out to our
        // parent. We don't apply its top margin (our parent will) so
        // zero it out.
        aState.mCollapsedTopMargin = collapsedTopMargin;
        collapsedTopMargin = 0;
      }
      else if (topMarginIsAuto && (NS_BODY_NO_AUTO_MARGINS & mFlags)) {
        // Our top margin is an auto margin and we are supposed to
        // ignore them. Change it to zero.
        collapsedTopMargin = 0;
      }
    }
  }
  aTopMarginResult = collapsedTopMargin;
  aBottomMarginResult = collapsedBottomMargin;
  return result;
}
void
nsBlockFrame::SlideFrames(nsIPresContext& aPresContext,
                          nsISpaceManager* aSpaceManager,
                          LineData* aLine, nscoord aDY)
{
  // Adjust the Y coordinate of the frames in the line
  nsIFrame* kid = aLine->mFirstChild;
  PRInt32 n = aLine->ChildCount();
  while (--n >= 0) {
    nsRect r;
    kid->GetRect(r);
    r.y += aDY;
    kid->SetRect(r);
    // If the child has an floaters that impact the space manager,
    // slide them now
    nsIHTMLReflow* ihr;
    if (NS_OK == kid->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) {
      ihr->MoveInSpaceManager(aPresContext, aSpaceManager, 0, aDY);
    }
    kid->GetNextSibling(kid);
  }
  // Slide down our floaters too
  nsVoidArray* floaters = aLine->mFloaters;
  if (nsnull != floaters) {
    PRInt32 i;
    n = floaters->Count();
    for (i = 0; i < n; i++) {
      nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
      kid = ph->GetAnchoredItem();
      nsRect r;
      kid->GetRect(r);
      r.y += aDY;
      kid->SetRect(r);
    }
  }
  // Slide line box too
  aLine->mBounds.y += aDY;
}
NS_IMETHODIMP
nsBlockFrame::MoveInSpaceManager(nsIPresContext& aPresContext,
                                 nsISpaceManager* aSpaceManager,
                                 nscoord aDeltaX, nscoord aDeltaY)
{
  LineData* line = mLines;
  while (nsnull != line) {
    PRInt32 i, n;
    nsIFrame* kid;
    // Move the floaters in the spacemanager
    nsVoidArray* floaters = line->mFloaters;
    if (nsnull != floaters) {
      n = floaters->Count();
      for (i = 0; i < n; i++) {
        nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
        kid = ph->GetAnchoredItem();
        aSpaceManager->OffsetRegion(kid, aDeltaX, aDeltaY);
      }
    }
    // Tell kids about the move too
    n = line->ChildCount();
    kid = line->mFirstChild;
    while (--n >= 0) {
      nsIHTMLReflow* ihr;
      if (NS_OK == kid->QueryInterface(kIHTMLReflowIID, (void**)&ihr)) {
        ihr->MoveInSpaceManager(aPresContext, aSpaceManager, aDeltaX, aDeltaY);
      }
      kid->GetNextSibling(kid);
    }
    
    line = line->mNext;
  }
  return NS_OK;
}
nsBlockFrame*
nsBlockFrame::FindFollowingBlockFrame(nsIFrame* aFrame)
{
  nsBlockFrame* followingBlockFrame = nsnull;
  nsIFrame* frame = aFrame;
  for (;;) {
    nsIFrame* nextFrame;
    frame->GetNextSibling(nextFrame);
    if (nsnull != nextFrame) {
      const nsStyleDisplay* display;
      nsresult rv = nextFrame->GetStyleData(eStyleStruct_Display,
                                            (const nsStyleStruct*&) display);
      if (NS_SUCCEEDED(rv) && (nsnull != display)) {
        if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
#ifdef NOISY_RUNIN
          ListTag(stdout);
          printf(": frame: ");
          aFrame->ListTag(stdout);
          printf(" followed by: ");
          nextFrame->ListTag(stdout);
          printf("\n");
#endif
          followingBlockFrame = (nsBlockFrame*) nextFrame;
          break;
        }
        else if (NS_STYLE_DISPLAY_INLINE == display->mDisplay) {
          // If it's a text-frame and it's just whitespace and we are
          // in a normal whitespace situation THEN skip it and keep
          // going...
          // XXX probably should really check this ...
        }
      }
      else
        break;
      frame = nextFrame;
    }
    else
      break;
  }
  return followingBlockFrame;
}
PRBool
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
                               LineData* aLine,
                               nsIFrame* aFrame,
                               nsReflowStatus& aReflowResult)
{
  NS_PRECONDITION(0 == aState.mLineLayout.GetPlacedFrames(),
                  "non-empty line with a block");
  nsInlineReflow& ir = *aState.mInlineReflow;
  // Prepare the inline reflow engine
  nsBlockFrame* runInToFrame;
  nsBlockFrame* compactWithFrame;
  nscoord compactMarginWidth = 0;
  PRBool isCompactFrame = PR_FALSE;
  PRBool asBlock = PR_TRUE;
  const nsStyleDisplay* display;
  nsresult rv = aFrame->GetStyleData(eStyleStruct_Display,
                                     (const nsStyleStruct*&) display);
  if ((NS_OK == rv) && (nsnull != display)) {
    switch (display->mDisplay) {
    case NS_STYLE_DISPLAY_TABLE:
      asBlock = PR_FALSE;
      break;
    case NS_STYLE_DISPLAY_RUN_IN:
#ifdef NOISY_RUNIN
      ListTag(stdout);
      printf(": trying to see if ");
      aFrame->ListTag(stdout);
      printf(" is a run-in candidate\n");
#endif
      runInToFrame = FindFollowingBlockFrame(aFrame);
      if (nsnull != runInToFrame) {
// XXX run-in frame should be pushed to the next-in-flow too if the
// run-in-to frame is pushed.
        aReflowResult = NS_FRAME_COMPLETE;
        nsRect r(0, aState.mY, 0, 0);
        aLine->mBounds = r;
        aLine->mCombinedArea = r;
        aLine->mCarriedOutTopMargin = 0;
        aLine->mCarriedOutBottomMargin = 0;
        aLine->SetMarginFlags(0);
        aLine->ClearOutsideChildren();
        aLine->mBreakType = NS_STYLE_CLEAR_NONE;
//XXX        aFrame->WillReflow(aState.mPresContext);
        aFrame->SetRect(r);
        aState.mPrevChild = aFrame;
        aState.mRunInToFrame = runInToFrame;
        aState.mRunInFrame = (nsBlockFrame*) aFrame;
        return PR_TRUE;
      }
      break;
    case NS_STYLE_DISPLAY_COMPACT:
      compactWithFrame = FindFollowingBlockFrame(aFrame);
      if (nsnull != compactWithFrame) {
        const nsStyleSpacing* spacing;
        nsMargin margin;
        rv = compactWithFrame->GetStyleData(eStyleStruct_Spacing,
                                            (const nsStyleStruct*&) spacing);
        if (NS_SUCCEEDED(rv) && (nsnull != spacing)) {
          // XXX % margins
          spacing->CalcMarginFor(compactWithFrame, margin);
          compactMarginWidth = margin.left;
        }
        isCompactFrame = PR_TRUE;
      }
      break;
    }
    // clear floaters if the clear style is not none
    if (NS_STYLE_CLEAR_NONE != display->mBreakType) {
      aState.ClearFloaters(display->mBreakType);
    }
  }
  PrepareInlineReflow(aState, aFrame, asBlock);
  ir.SetIsFirstChild((aLine == mLines) &&
                     (aFrame == aLine->mFirstChild));
  ir.SetCompactMarginWidth(compactMarginWidth);
  if (aFrame == aState.mRunInToFrame) {
    ir.SetRunInFrame(aState.mRunInFrame);
  }
  // Reflow the block frame
  nsReflowStatus reflowStatus = ir.ReflowFrame(aFrame);
  if (NS_IS_REFLOW_ERROR(reflowStatus)) {
    aReflowResult = reflowStatus;
    return PR_FALSE;
  }
  aReflowResult = reflowStatus;
  // XXX We need to check the *type* of break and if it's a column/page
  // break apply and cause the block to be split (assuming we are
  // laying out in a column).
#if XXX
  if (NS_INLINE_IS_BREAK(reflowStatus)) {
    // XXX For now we ignore it
  }
#endif
  // Align the frame
  nscoord maxAscent, maxDescent;
  ir.VerticalAlignFrames(aLine->mBounds, maxAscent, maxDescent);
  ir.HorizontalAlignFrames(aLine->mBounds);
  ir.RelativePositionFrames(aLine->mCombinedArea);
  // Calculate margins
  nscoord topMargin, bottomMargin;
  PRUintn marginFlags = CalculateMargins(aState, aLine, PR_FALSE,
                                         topMargin, bottomMargin);
  // Try to place the frame
  nscoord newY = aLine->mBounds.YMost() + topMargin;
  if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
    // The frame doesn't fit inside our available space. Push the
    // line to the next-in-flow and return our incomplete status to
    // our parent.
    PushLines(aState);
    aReflowResult = NS_FRAME_NOT_COMPLETE;
    return PR_FALSE;
  }
  if (isCompactFrame) {
    // For compact frames, we don't adjust the Y coordinate at all IF
    // the compact frame ended up fitting in the margin space
    // allocated for it.
    nsRect r;
    aFrame->GetRect(r);
    if (r.width <= compactMarginWidth) {
      // XXX margins will be wrong
      // XXX ltr/rtl for horizontal placement within the margin area
      // XXX vertical alignment with the compactWith frame's *first line*
      newY = aState.mY;
    }
  }
  else if ((nsnull != mBullet) && ShouldPlaceBullet(aLine)) {
    const nsStyleFont* font;
    rv = aFrame->GetStyleData(eStyleStruct_Font, (const nsStyleStruct*&) font);
    if (NS_SUCCEEDED(rv) && (nsnull != font)) {
      nsIRenderingContext& rc = *aState.rendContext;
      rc.SetFont(font->mFont);
      nscoord firstLineAscent = 0;
      nsIFontMetrics* fm;
      rv = rc.GetFontMetrics(fm);
      if (NS_SUCCEEDED(rv) && (nsnull != fm)) {
        fm->GetMaxAscent(firstLineAscent);
        NS_RELEASE(fm);
      }
      PlaceBullet(aState, firstLineAscent, topMargin);
    }
  }
  aState.mY = newY;
  // Apply collapsed top-margin value
  if (0 != topMargin) {
    SlideFrames(aState.mPresContext, aState.mSpaceManager, aLine, topMargin);
  }
  // Record bottom margin value for sibling to sibling compression or
  // for returning as our carried out bottom margin. Adjust running
  // margin value when either we have carried margins from the line or
  // we have a non-zero height line.
  if ((HAVE_CARRIED_MARGIN & marginFlags) || (0 != aLine->mBounds.height)) {
    aState.mPrevBottomMargin = bottomMargin;
    aState.mPrevMarginFlags = marginFlags;
  }
  aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
  aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
  aLine->SetMarginFlags(marginFlags);
  aLine->ClearOutsideChildren();
  nsFrameState state;
  aFrame->GetFrameState(state);
  if (NS_FRAME_OUTSIDE_CHILDREN & state) {
    aLine->SetOutsideChildren();
  }
  nscoord xmost = aLine->mBounds.XMost();
  if (xmost > aState.mKidXMost) {
    aState.mKidXMost = xmost;
  }
  // Update max-element-size
  if (aState.mComputeMaxElementSize) {
    const nsSize& kidMaxElementSize = ir.GetMaxElementSize();
    if (kidMaxElementSize.width > aState.mMaxElementSize.width) {
      aState.mMaxElementSize.width = kidMaxElementSize.width;
    }
    if (kidMaxElementSize.height > aState.mMaxElementSize.height) {
      aState.mMaxElementSize.height = kidMaxElementSize.height;
    }
  }
  aState.mPrevChild = aFrame;
  // Refresh our available space in case a floater was placed by our
  // child.
  // XXX expensive!
  // XXX unnecessary because the child block will contain the floater!
  aState.GetAvailableSpace();
  aLine->mBreakType = NS_STYLE_CLEAR_NONE;
  if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus)) {
    // Some of the block fit. We need to have the block frame
    // continued, so we make sure that it has a next-in-flow now.
    nsIFrame* nextInFlow;
    nsresult rv;
    rv = nsHTMLContainerFrame::CreateNextInFlow(aState.mPresContext,
                                                this,
                                                aFrame,
                                                nextInFlow);
    if (NS_OK != rv) {
      aReflowResult = rv;
      return PR_FALSE;
    }
    if (nsnull != nextInFlow) {
      // We made a next-in-flow for the block child frame. Create a
      // line to map the block childs next-in-flow.
      LineData* line = new LineData(nextInFlow, 1, LINE_IS_BLOCK);
      if (nsnull == line) {
        aReflowResult = NS_ERROR_OUT_OF_MEMORY;
        return PR_FALSE;
      }
      line->mNext = aLine->mNext;
      aLine->mNext = line;
    }
    // Advance mPrevLine because we are keeping aLine (since some of
    // the child block frame fit). Then push any remaining lines to
    // our next-in-flow
    aState.mPrevLine = aLine;
    if (nsnull != aLine->mNext) {
      PushLines(aState);
    }
    aReflowResult = NS_INLINE_LINE_BREAK_AFTER(reflowStatus);
    return PR_FALSE;
  }
  return PR_TRUE;
}
/**
 * Reflow an inline frame. Returns PR_FALSE if the frame won't fit
 * on the line and the line should be flushed.
 */
PRBool
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
                                LineData* aLine,
                                nsIFrame* aFrame,
                                nsReflowStatus& aReflowResult,
                                PRBool& aAddedToLine)
{
  nsresult rv;
  nsIFrame* nextInFlow;
  aAddedToLine = PR_FALSE;
  if (!aState.mInlineReflowPrepared) {
    PrepareInlineReflow(aState, aFrame, PR_FALSE);
  }
  // When reflowing a frame that is on the first-line, check and see
  // if a special style context should be placed in the context chain.
  PRBool reflowingFirstLetter = PR_FALSE;
  if ((nsnull == mPrevInFlow) &&
      (0 == aState.mLineLayout.GetLineNumber())) {
    if (nsnull != mFirstLineStyle) {
      // Update the child frames style to inherit from the first-line
      // style.
      // XXX add code to check first and only do it if it needs doing!
#ifdef REALLY_NOISY_FIRST_LINE
      DumpStyleGeneaology(aFrame, "");
#endif
#ifdef NOISY_FIRST_LINE
      aFrame->ListTag(stdout);
      printf(": adding in first-line style\n");
#endif
      aFrame->ReResolveStyleContext(&aState.mPresContext, mFirstLineStyle);
#ifdef REALLY_NOISY_FIRST_LINE
      DumpStyleGeneaology(aFrame, "  ");
#endif
    }
    if ((nsnull != mFirstLetterStyle) &&
        aState.mLineLayout.GetFirstLetterStyleOK()) {
      // XXX For now, we only allow text objects to have :first-letter
      // style.
      nsIContent* c;
      nsresult rv = aFrame->GetContent(c);
      if (NS_SUCCEEDED(rv) && (nsnull != c)) {
        nsITextContent* tc;
        nsresult rv = c->QueryInterface(kITextContentIID, (void**)&tc);
        if (NS_SUCCEEDED(rv)) {
          // XXX add code to check first and only do it if it needs doing!
          // Update the child frames style to inherit from the
          // first-letter style.
          aFrame->ReResolveStyleContext(&aState.mPresContext,
                                        mFirstLetterStyle);
          reflowingFirstLetter = PR_TRUE;
          NS_RELEASE(tc);
        }
        NS_RELEASE(c);
      }
    }
  }
  PRBool isFirstChild = (aLine == mLines) &&
    (aFrame == aLine->mFirstChild);
  aState.mInlineReflow->SetIsFirstChild(isFirstChild);
  aReflowResult = aState.mInlineReflow->ReflowFrame(aFrame);
  if (NS_IS_REFLOW_ERROR(aReflowResult)) {
    return PR_FALSE;
  }
  if (!NS_INLINE_IS_BREAK(aReflowResult)) {
    aState.mPrevChild = aFrame;
    if (NS_FRAME_IS_COMPLETE(aReflowResult)) {
      return PR_TRUE;
    }
    // Create continuation frame (if necessary); add it to the end of
    // the current line so that it can be pushed to the next line
    // properly.
    rv = nsHTMLContainerFrame::CreateNextInFlow(aState.mPresContext,
                                                this, aFrame, nextInFlow);
    if (NS_OK != rv) {
      aReflowResult = rv;
      return PR_FALSE;
    }
    if (nsnull != nextInFlow) {
      // Add new child to the line
      aLine->mChildCount++;
    }
    // XXX Special hackery to keep going if we just split because of a
    // :first-letter situation.
    if (reflowingFirstLetter) {
      if (nsnull != nextInFlow) {
        aAddedToLine = PR_TRUE;
        nextInFlow->ReResolveStyleContext(&aState.mPresContext,
                                          mFirstLineStyle
                                          ? mFirstLineStyle
                                          : mStyleContext);
      }
      if (aState.mInlineReflow->GetAvailWidth() > 0) {
        return PR_TRUE;
      }
    }
    // Split line after the frame we just reflowed
    aFrame->GetNextSibling(aFrame);
  }
  else {
    if (NS_INLINE_IS_BREAK_AFTER(aReflowResult)) {
      aState.mPrevChild = aFrame;
      if (NS_FRAME_IS_NOT_COMPLETE(aReflowResult)) {
        // Create continuation frame (if necessary); add it to the end of
        // the current line so that it can be pushed to the next line
        // properly.
        rv = nsHTMLContainerFrame::CreateNextInFlow(aState.mPresContext,
                                                    this, aFrame,
                                                    nextInFlow);
        if (NS_OK != rv) {
          aReflowResult = rv;
          return PR_FALSE;
        }
        if (nsnull != nextInFlow) {
          // Add new child to the line
          aLine->mChildCount++;
        }
      }
      aFrame->GetNextSibling(aFrame);
    }
  }
  // Split line since we aren't going to keep going
  rv = SplitLine(aState, aLine, aFrame);
  if (NS_IS_REFLOW_ERROR(rv)) {
    aReflowResult = rv;
  }
  return PR_FALSE;
}
void
nsBlockFrame::RestoreStyleFor(nsIPresContext& aPresContext, nsIFrame* aFrame)
{
#ifdef REALLY_NOISY_FIRST_LINE
  DumpStyleGeneaology(aFrame, "");
#endif
  nsIStyleContext* kidSC;
  aFrame->GetStyleContext(kidSC);
  if (nsnull != kidSC) {
    nsIStyleContext* kidParentSC;
    kidParentSC = kidSC->GetParent();
    if ((kidParentSC == mFirstLineStyle) ||
        (kidParentSC == mFirstLetterStyle)) {
#ifdef NOISY_FIRST_LINE
      printf("  ");
      aFrame->ListTag(stdout);
      printf(": removing first-line style\n");
#endif
      aFrame->ReResolveStyleContext(&aPresContext, mStyleContext);
#ifdef REALLY_NOISY_FIRST_LINE
      DumpStyleGeneaology(aFrame, "  ");
#endif
    }
    NS_IF_RELEASE(kidParentSC);
    NS_RELEASE(kidSC);
  }
}
// XXX alloc lines using free-list in aState
// XXX refactor this since the split NEVER has to deal with blocks
nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState,
                        LineData* aLine,
                        nsIFrame* aFrame)
{
  PRInt32 pushCount = aLine->ChildCount() -
    aState.mInlineReflow->GetCurrentFrameNum();
  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                 ("nsBlockFrame::SplitLine: pushing %d frames",
                  pushCount));
  if (0 != pushCount) {
    NS_ASSERTION(nsnull != aFrame, "whoops");
    LineData* to = aLine->mNext;
    if (nsnull != to) {
      // Only push into the next line if it's empty; otherwise we can
      // end up pushing a frame which is continued into the same frame
      // as it's continuation. This causes all sorts of bad side
      // effects so we don't allow it.
      if (0 != to->ChildCount()) {
        LineData* insertedLine = new LineData(aFrame, pushCount, 0);
        aLine->mNext = insertedLine;
        insertedLine->mNext = to;
        to = insertedLine;
      } else {
        to->mFirstChild = aFrame;
        to->mChildCount += pushCount;
      }
    } else {
      to = new LineData(aFrame, pushCount, 0);
      aLine->mNext = to;
    }
    if (nsnull == to) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    aLine->mChildCount -= pushCount;
#ifdef NS_DEBUG
    if (GetVerifyTreeEnable()) {
      aLine->Verify();
    }
#endif
    NS_ASSERTION(0 != aLine->ChildCount(), "bad push");
    // Let inline reflow know that some frames are no longer part of
    // its state.
    aState.mInlineReflow->ChangeFrameCount(aLine->ChildCount());
    // Repair style contexts when frames move to the next line and
    // this is the first line.
    if (((nsnull != mFirstLineStyle) || (nsnull != mFirstLetterStyle)) &&
        (0 == aState.mLineLayout.GetLineNumber())) {
      // Take the pseudo-style out of the style context chain
      nsIFrame* kid = to->mFirstChild;
      PRInt32 n = pushCount;
      while (--n >= 0) {
        RestoreStyleFor(aState.mPresContext, kid);
        kid->GetNextSibling(kid);
      }
    }
  }
  return NS_OK;
}
PRBool
nsBlockFrame::PullFrame(nsBlockReflowState& aState,
                        LineData* aLine,
                        LineData** aFromList,
                        PRBool aUpdateGeometricParent,
                        nsReflowStatus& aReflowResult,
                        PRBool& aAddedToLine)
{
  LineData* fromLine = *aFromList;
  NS_ASSERTION(nsnull != fromLine, "bad line to pull from");
  if (0 == fromLine->ChildCount()) {
    // Discard empty lines immediately. Empty lines can happen here
    // because of DeleteChildsNextInFlow not being able to delete
    // lines.
    *aFromList = fromLine->mNext;
    NS_ASSERTION(nsnull == fromLine->mFirstChild, "bad empty line");
    fromLine->mNext = aState.mFreeList;
    aState.mFreeList = fromLine;
    return PR_TRUE;
  }
  // If our line is not empty and the child in aFromLine is a block
  // then we cannot pull up the frame into this line.
  if ((0 != aLine->ChildCount()) && fromLine->IsBlock()) {
    aReflowResult = NS_INLINE_LINE_BREAK_BEFORE();
    return PR_FALSE;
  }
  // Take frame from fromLine
  nsIFrame* frame = fromLine->mFirstChild;
  if (0 == aLine->mChildCount++) {
    aLine->mFirstChild = frame;
    aLine->SetIsBlock(fromLine->IsBlock());
#ifdef NS_DEBUG
    const nsStyleDisplay* display;
    frame->GetStyleData(eStyleStruct_Display,
                        (const nsStyleStruct*&) display);
    const nsStylePosition* position;
    frame->GetStyleData(eStyleStruct_Position,
                        (const nsStyleStruct*&) position);
    PRBool isBlock = nsLineLayout::TreatFrameAsBlock(display, position);
    NS_ASSERTION(isBlock == aLine->IsBlock(), "bad line isBlock");
#endif
  }
  if (0 != --fromLine->mChildCount) {
    frame->GetNextSibling(fromLine->mFirstChild);
  }
  else {
    // Free up the fromLine now that it's empty
    *aFromList = fromLine->mNext;
    fromLine->mFirstChild = nsnull;
    fromLine->mNext = aState.mFreeList;
    aState.mFreeList = fromLine;
  }
  // Change geometric parents
  if (aUpdateGeometricParent) {
    nsIFrame* geometricParent;
    nsIFrame* contentParent;
    frame->GetGeometricParent(geometricParent);
    frame->GetContentParent(contentParent);
    if (contentParent == geometricParent) {
      frame->SetContentParent(this);
    }
    frame->SetGeometricParent(this);
    // The frame is being pulled from a next-in-flow; therefore we
    // need to add it to our sibling list.
    if (nsnull != aState.mPrevChild) {
      aState.mPrevChild->SetNextSibling(frame);
    }
    frame->SetNextSibling(nsnull);
  }
  // Reflow the frame
  if (aLine->IsBlock()) {
    aAddedToLine = PR_FALSE;
    return ReflowBlockFrame(aState, aLine, frame, aReflowResult);
  }
  else {
    return ReflowInlineFrame(aState, aLine, frame, aReflowResult,
                             aAddedToLine);
  }
}
PRBool
nsBlockFrame::IsLastLine(nsBlockReflowState& aState,
                         LineData* aLine,
                         nsReflowStatus aReflowStatus)
{
  // If the frame is not-complete then there must be another line
  // following...
  if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
    // If any subsequent line has a frame on it then this is not the
    // last line.
    LineData* next = aLine->mNext;
    while (nsnull != next) {
      if (0 != next->ChildCount()) {
        return PR_FALSE;
      }
      next = next->mNext;
    }
    // If there are next-in-flows and they have any non-empty lines
    // then this is not the last line (because the pullup code will
    // pullup frames from the next-in-flows after this line is
    // placed). Even if the pullup code doesn't pullup, we don't want
    // to signal the last line except in our last-in-flow.
    nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
    while (nsnull != mNextInFlow) {
      LineData* line = nextInFlow->mLines;
      while (nsnull != line) {
        if (0 != next->ChildCount()) {
          return PR_FALSE;
        }
        line = line->mNext;
      }
      nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
    }
    // This is the last line
    return PR_TRUE;
  }
  return PR_FALSE;
}
// XXX This is identical to the back end of the block reflow code, not
// counting the continuation of block frames part. Factor this!
PRBool
nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
                        LineData* aLine,
                        nsReflowStatus aReflowStatus)
{
  PRBool isLastLine = PR_FALSE;
  if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) {
    // For justification we need to know if this line is the last
    // line. If it is, then justification is disabled.
    isLastLine = IsLastLine(aState, aLine, aReflowStatus);
  }
  // Align the children. This also determines the actual height and
  // width of the line.
  nsInlineReflow& ir = *aState.mInlineReflow;
  nscoord maxAscent, maxDescent;
  ir.VerticalAlignFrames(aLine->mBounds, maxAscent, maxDescent);
  ir.HorizontalAlignFrames(aLine->mBounds, isLastLine);
  ir.RelativePositionFrames(aLine->mCombinedArea);
  // Calculate the bottom margin for the line.
  nscoord lineBottomMargin = 0;
  if (0 == aLine->mBounds.height) {
    nsIFrame* brFrame = aState.mLineLayout.GetBRFrame();
    if (nsnull != brFrame) {
      // If a line ends in a BR, and the line is empty of height, then
      // we make sure that the line ends up with some height
      // anyway. Note that the height looks like vertical margin so
      // that it can compress with other block margins.
      nsIStyleContext* brSC;
      nsIPresContext& px = aState.mPresContext;
      nsresult rv = brFrame->GetStyleContext(brSC);
      if ((NS_OK == rv) && (nsnull != brSC)) {
        const nsStyleFont* font = (const nsStyleFont*)
          brSC->GetStyleData(eStyleStruct_Font);
        nsIFontMetrics* fm = px.GetMetricsFor(font->mFont);
        if (nsnull != fm) {
          fm->GetHeight(lineBottomMargin);
          NS_RELEASE(fm);
        }
        NS_RELEASE(brSC);
      }
    }
  }
  else {
    aState.mRunInToFrame = nsnull;
    aState.mRunInFrame = nsnull;
  }
  // Calculate the lines top and bottom margin values. The margin will
  // come from an embedded block frame, not from inline frames.
  nscoord topMargin, bottomMargin;
  PRUintn marginFlags = CalculateMargins(aState, aLine, PR_TRUE,
                                         topMargin, bottomMargin);
  // See if the line fit. If it doesn't we need to push it. Our first
  // line will always fit.
  // XXX This is a good place to check and see if we have
  // below-current-line floaters, and if we do make sure that they fit
  // too.
  // XXX don't forget to factor in the top/bottom margin when sharing
  // this with the block code
  nscoord newY = aLine->mBounds.YMost() + topMargin + lineBottomMargin;
  NS_FRAME_TRACE(NS_FRAME_TRACE_CHILD_REFLOW,
     ("nsBlockFrame::PlaceLine: newY=%d limit=%d lineHeight=%d",
      newY, aState.mBottomEdge, aLine->mBounds.height));
  if ((mLines != aLine) && (newY > aState.mBottomEdge)) {
    // Push this line and all of it's children and anything else that
    // follows to our next-in-flow
    PushLines(aState);
    return PR_FALSE;
  }
  // Compute LINE_OUTSIDE_CHILDREN state for this line. The bit is set
  // if any child frame has outside children.
  aLine->ClearOutsideChildren();
  nsIFrame* kid = aLine->mFirstChild;
  PRInt32 n = aLine->ChildCount();
  while (--n >= 0) {
    nsFrameState state;
    kid->GetFrameState(state);
    if (NS_FRAME_OUTSIDE_CHILDREN & state) {
      aLine->SetOutsideChildren();
      break;
    }
    kid->GetNextSibling(kid);
  }
  // Apply collapsed top-margin value
  // XXX I bet the bullet placement just got broken by this code
  if (0 != topMargin) {
    SlideFrames(aState.mPresContext, aState.mSpaceManager, aLine, topMargin);
  }
  // Adjust running margin value when either we have carried margins
  // from the line or we have a non-zero height line.
  if ((HAVE_CARRIED_MARGIN & marginFlags) || (0 != aLine->mBounds.height)) {
    aState.mPrevBottomMargin = bottomMargin;
    aState.mPrevMarginFlags = marginFlags;
  }
  aLine->mCarriedOutTopMargin = ir.GetCarriedOutTopMargin();
  aLine->mCarriedOutBottomMargin = ir.GetCarriedOutBottomMargin();
  aLine->SetMarginFlags(marginFlags);
  // Now that we know the line is staying put, put in the outside
  // bullet if we have one.
  if ((nsnull == mPrevInFlow) && (nsnull != mBullet) &&
      ShouldPlaceBullet(aLine)) {
    PlaceBullet(aState, maxAscent, topMargin);
  }
  // Update max-element-size
  if (aState.mComputeMaxElementSize) {
    const nsSize& kidMaxElementSize = ir.GetMaxElementSize();
    if (kidMaxElementSize.width > aState.mMaxElementSize.width) {
      aState.mMaxElementSize.width = kidMaxElementSize.width;
    }
    if (kidMaxElementSize.height > aState.mMaxElementSize.height) {
      aState.mMaxElementSize.height = kidMaxElementSize.height;
    }
  }
  nscoord xmost = aLine->mBounds.XMost();
  if (xmost > aState.mKidXMost) {
    aState.mKidXMost = xmost;
  }
  aState.mY = newY;
  // Any below current line floaters to place?
  if (0 != aState.mPendingFloaters.Count()) {
    aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE);
    aState.mPendingFloaters.Clear();
    // XXX Factor in the height of the floaters as well when considering
    // whether the line fits.
    // The default policy is that if there isn't room for the floaters then
    // both the line and the floaters are pushed to the next-in-flow...
  }
  // Based on the last child we reflowed reflow status, we may need to
  // clear past any floaters.
  aLine->mBreakType = NS_STYLE_CLEAR_NONE;
  if (NS_INLINE_IS_BREAK_AFTER(aReflowStatus)) {
    PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(aReflowStatus);
    aLine->mBreakType = breakType;
    // Apply break to the line
    switch (breakType) {
    default:
      break;
    case NS_STYLE_CLEAR_LEFT:
    case NS_STYLE_CLEAR_RIGHT:
    case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
      NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                     ("nsBlockFrame::PlaceLine: clearing floaters=%d",
                      breakType));
#if XXX
      {
        nscoord y0 = aState.mY;
        aState.ClearFloaters(breakType);
        nscoord dy = aState.mY - y0;
        if (dy > aLine->mCarriedOutBottomMargin) {
          aLine->mCarriedOutBottomMargin = dy;
        }
      }
#else
      aState.ClearFloaters(breakType);
#if 0
      y0 = aState.mY;
      aState.ClearFloaters(lastCleanLine->mBreakType);
      dy = aState.mY - y0;
      if (dy > aState.mPrevBottomMargin) {
        aState.mPrevBottomMargin = dy;
        aState.mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN;
      }
#endif
#endif
      break;
    }
    // XXX page breaks, etc, need to be passed upwards too!
  }
  // Update available space after placing line in case below current
  // line floaters were placed or in case we just used up the space in
  // the current band and are ready to move into a new band.
  aState.GetAvailableSpace();
  return PR_TRUE;
}
PRBool
nsBlockFrame::ShouldPlaceBullet(LineData* aLine)
{
  PRBool ok = PR_FALSE;
  const nsStyleList* list;
  GetStyleData(eStyleStruct_List, (const nsStyleStruct*&)list);
  if (NS_STYLE_LIST_STYLE_POSITION_OUTSIDE == list->mListStylePosition) {
    LineData* line = mLines;
    while (nsnull != line) {
      if (line->mBounds.height > 0) {
        if (aLine == line) {
          ok = PR_TRUE;
          break;
        }
      }
      if (aLine == line) {
        break;
      }
      line = line->mNext;
    }
  }
  return ok;
}
void
nsBlockFrame::PlaceBullet(nsBlockReflowState& aState,
                          nscoord aMaxAscent,
                          nscoord aTopMargin)
{
  // Reflow the bullet now
  nsSize availSize;
  availSize.width = NS_UNCONSTRAINEDSIZE;
  availSize.height = NS_UNCONSTRAINEDSIZE;
  nsHTMLReflowState reflowState(aState.mPresContext, mBullet, aState,
                                availSize, &aState.mLineLayout);
  nsHTMLReflowMetrics metrics(nsnull);
  nsIHTMLReflow* htmlReflow;
  nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
  if (NS_SUCCEEDED(rv)) {
    nsReflowStatus  status;
    htmlReflow->WillReflow(aState.mPresContext);
    htmlReflow->Reflow(aState.mPresContext, metrics, reflowState, status);
    htmlReflow->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED);
  }
  // Place the bullet now; use its right margin to distance it
  // from the rest of the frames in the line
  nsMargin margin;
  const nsStyleSpacing* spacing;
  mBullet->GetStyleData(eStyleStruct_Spacing,
                        (const nsStyleStruct*&) spacing);
  spacing->CalcMarginFor(mBullet, margin);
  nscoord x = aState.mBorderPadding.left - margin.right -
    metrics.width;
  // XXX This calculation may be wrong, especially if
  // vertical-alignment occurs on the line!
  nscoord y = aState.mBorderPadding.top + aMaxAscent -
    metrics.ascent + aTopMargin;
  mBullet->SetRect(nsRect(x, y, metrics.width, metrics.height));
}
static nsresult
FindFloatersIn(nsIFrame* aFrame, nsVoidArray*& aArray)
{
  const nsStyleDisplay* display;
  aFrame->GetStyleData(eStyleStruct_Display,
                       (const nsStyleStruct*&) display);
  if (NS_STYLE_FLOAT_NONE != display->mFloats) {
    if (nsnull == aArray) {
      aArray = new nsVoidArray();
      if (nsnull == aArray) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
    }
    aArray->AppendElement(aFrame);
  }
  if (NS_STYLE_DISPLAY_INLINE == display->mDisplay) {
    nsIFrame* kid;
    aFrame->FirstChild(nsnull, kid);
    while (nsnull != kid) {
      nsresult rv = FindFloatersIn(kid, aArray);
      if (NS_OK != rv) {
        return rv;
      }
      kid->GetNextSibling(kid);
    }
  }
  return NS_OK;
}
void
nsBlockFrame::FindFloaters(LineData* aLine)
{
  nsVoidArray* floaters = aLine->mFloaters;
  if (nsnull != floaters) {
    // Empty floater array before proceeding
    floaters->Clear();
  }
  nsIFrame* frame = aLine->mFirstChild;
  PRInt32 n = aLine->ChildCount();
  while (--n >= 0) {
    FindFloatersIn(frame, floaters);
    frame->GetNextSibling(frame);
  }
  aLine->mFloaters = floaters;
  // Get rid of floater array if we don't need it
  if (nsnull != floaters) {
    if (0 == floaters->Count()) {
      delete floaters;
      aLine->mFloaters = nsnull;
    }
  }
}
void
nsBlockFrame::PushLines(nsBlockReflowState& aState)
{
  NS_ASSERTION(nsnull != aState.mPrevLine, "bad push");
  LineData* lastLine = aState.mPrevLine;
  LineData* nextLine = lastLine->mNext;
  lastLine->mNext = nsnull;
  mOverflowLines = nextLine;
  // Break frame sibling list
  nsIFrame* lastFrame = lastLine->LastChild();
  lastFrame->SetNextSibling(nsnull);
  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
     ("nsBlockFrame::PushLines: line=%p prevInFlow=%p nextInFlow=%p",
      mOverflowLines, mPrevInFlow, mNextInFlow));
#ifdef NS_DEBUG
  if (GetVerifyTreeEnable()) {
    VerifyChildCount(mLines);
    VerifyChildCount(mOverflowLines, PR_TRUE);
  }
#endif
}
PRBool
nsBlockFrame::DrainOverflowLines()
{
  PRBool drained = PR_FALSE;
  // First grab the prev-in-flows overflow lines
  nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
  if (nsnull != prevBlock) {
    LineData* line = prevBlock->mOverflowLines;
    if (nsnull != line) {
      drained = PR_TRUE;
      NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
         ("nsBlockFrame::DrainOverflowLines: line=%p prevInFlow=%p",
          line, prevBlock));
      prevBlock->mOverflowLines = nsnull;
      // Make all the frames on the mOverflowLines list mine
      nsIFrame* lastFrame = nsnull;
      nsIFrame* frame = line->mFirstChild;
      while (nsnull != frame) {
        nsIFrame* geometricParent;
        nsIFrame* contentParent;
        frame->GetGeometricParent(geometricParent);
        frame->GetContentParent(contentParent);
        if (contentParent == geometricParent) {
          frame->SetContentParent(this);
        }
        frame->SetGeometricParent(this);
        lastFrame = frame;
        frame->GetNextSibling(frame);
      }
      // Join the line lists
      if (nsnull == mLines) {
        mLines = line;
      }
      else {
        // Join the sibling lists together
        lastFrame->SetNextSibling(mLines->mFirstChild);
        // Place overflow lines at the front of our line list
        LineData* lastLine = LastLine(line);
        lastLine->mNext = mLines;
        mLines = line;
      }
    }
  }
  // Now grab our own overflow lines
  if (nsnull != mOverflowLines) {
    // This can happen when we reflow and not everything fits and then
    // we are told to reflow again before a next-in-flow is created
    // and reflows.
    NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
       ("nsBlockFrame::DrainOverflowLines: from me, line=%p",
        mOverflowLines));
    LineData* lastLine = LastLine(mLines);
    if (nsnull == lastLine) {
      mLines = mOverflowLines;
    }
    else {
      lastLine->mNext = mOverflowLines;
      nsIFrame* lastFrame = lastLine->LastChild();
      lastFrame->SetNextSibling(mOverflowLines->mFirstChild);
      // Update our last-content-index now that we have a new last child
      lastLine = LastLine(mOverflowLines);
    }
    mOverflowLines = nsnull;
    drained = PR_TRUE;
  }
#ifdef NS_DEBUG
  if (GetVerifyTreeEnable()) {
    VerifyChildCount(mLines, PR_TRUE);
  }
#endif
  return drained;
}
nsresult
nsBlockFrame::InsertNewFrame(nsIPresContext& aPresContext,
                             nsBlockFrame*   aParentFrame,
                             nsIFrame*       aNewFrame,
                             nsIFrame*       aPrevSibling)
{
  const nsStyleDisplay* display;
  aNewFrame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display);
  const nsStylePosition* position;
  aNewFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&) position);
  PRUint16 newFrameIsBlock = nsLineLayout::TreatFrameAsBlock(display, position)
                               ? LINE_IS_BLOCK : 0;
  // See if we need to move the frame outside of the flow, and insert a
  // placeholder frame in its place
  nsIFrame* placeholder;
  if (MoveFrameOutOfFlow(aPresContext, aNewFrame, display, position, placeholder)) {
    // Add the placeholder frame to the flow
    aNewFrame = placeholder;
    newFrameIsBlock = PR_FALSE;  // placeholder frame is always inline
  }
  else {
    // Wrap the frame in a view if necessary
    nsIStyleContext* kidSC;
    aNewFrame->GetStyleContext(kidSC);
    nsresult rv = CreateViewForFrame(aPresContext, aNewFrame, kidSC, PR_FALSE);    
    NS_RELEASE(kidSC);
    if (NS_OK != rv) {
      return rv;
    }
  }
  // Insert/append the frame into flows line list at the right spot
  LineData* newLine;
  LineData* line = aParentFrame->mLines;
  if (nsnull == aPrevSibling) {
    // Insert new frame into the sibling list
    aNewFrame->SetNextSibling(line->mFirstChild);
    if (line->IsBlock() || newFrameIsBlock) {
      // Create a new line
      newLine = new LineData(aNewFrame, 1, newFrameIsBlock);
      if (nsnull == newLine) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      newLine->mNext = aParentFrame->mLines;
      aParentFrame->mLines = newLine;
    } else {
      // Insert frame at the front of the line
      line->mFirstChild = aNewFrame;
      line->mChildCount++;
      line->MarkDirty();
    }
  }
  else {
    // Find line containing the previous sibling to the new frame
    line = FindLineContaining(line, aPrevSibling);
    NS_ASSERTION(nsnull != line, "no line contains the previous sibling");
    if (nsnull != line) {
      if (line->IsBlock()) {
        // Create a new line just after line
        newLine = new LineData(aNewFrame, 1, newFrameIsBlock);
        if (nsnull == newLine) {
          return NS_ERROR_OUT_OF_MEMORY;
        }
        newLine->mNext = line->mNext;
        line->mNext = newLine;
      }
      else if (newFrameIsBlock) {
        // Split line in two, if necessary. We can't allow a block to
        // end up in an inline line.
        if (line->IsLastChild(aPrevSibling)) {
          // The new frame goes after prevSibling and prevSibling is
          // the last frame on the line. Therefore we don't need to
          // split the line, just create a new line.
          newLine = new LineData(aNewFrame, 1, newFrameIsBlock);
          if (nsnull == newLine) {
            return NS_ERROR_OUT_OF_MEMORY;
          }
          newLine->mNext = line->mNext;
          line->mNext = newLine;
        }
        else {
          // The new frame goes after prevSibling and prevSibling is
          // somewhere in the line, but not at the end. Split the line
          // just after prevSibling.
          PRInt32 i, n = line->ChildCount();
          nsIFrame* frame = line->mFirstChild;
          for (i = 0; i < n; i++) {
            if (frame == aPrevSibling) {
              nsIFrame* nextSibling;
              aPrevSibling->GetNextSibling(nextSibling);
              // Create new line to hold the remaining frames
              NS_ASSERTION(n - i - 1 > 0, "bad line count");
              newLine = new LineData(nextSibling, n - i - 1, 0);
              if (nsnull == newLine) {
                return NS_ERROR_OUT_OF_MEMORY;
              }
              newLine->mNext = line->mNext;
              line->mNext = newLine;
              line->MarkDirty();
              line->mChildCount = i + 1;
              break;
            }
            frame->GetNextSibling(frame);
          }
          // Now create a new line to hold the block
          newLine = new LineData(aNewFrame, 1, newFrameIsBlock);
          if (nsnull == newLine) {
            return NS_ERROR_OUT_OF_MEMORY;
          }
          newLine->mNext = line->mNext;
          line->mNext = newLine;
        }
      }
      else {
        // Insert frame into the line.
        //XXX NS_ASSERTION(line->GetLastContentIsComplete(), "bad line LCIC");
        line->mChildCount++;
        line->MarkDirty();
      }
    }
    // Insert new frame into the sibling list; note: this must be done
    // after the above logic because the above logic depends on the
    // sibling list being in the "before insertion" state.
    nsIFrame* nextSibling;
    aPrevSibling->GetNextSibling(nextSibling);
    aNewFrame->SetNextSibling(nextSibling);
    aPrevSibling->SetNextSibling(aNewFrame);
  }
  return NS_OK;
}
PRBool
nsBlockFrame::DeleteChildsNextInFlow(nsIPresContext& aPresContext,
                                     nsIFrame* aChild)
{
  NS_PRECONDITION(IsChild(aChild), "bad geometric parent");
  nsIFrame* nextInFlow;
  nsBlockFrame* parent;
   
  aChild->GetNextInFlow(nextInFlow);
  NS_PRECONDITION(nsnull != nextInFlow, "null next-in-flow");
  nextInFlow->GetGeometricParent((nsIFrame*&)parent);
  // If the next-in-flow has a next-in-flow then delete it, too (and
  // delete it first).
  nsIFrame* nextNextInFlow;
  nextInFlow->GetNextInFlow(nextNextInFlow);
  if (nsnull != nextNextInFlow) {
    parent->DeleteChildsNextInFlow(aPresContext, nextInFlow);
  }
#ifdef NS_DEBUG
  PRInt32   childCount;
  nsIFrame* firstChild;
  nextInFlow->FirstChild(nsnull, firstChild);
  childCount = LengthOf(firstChild);
  NS_ASSERTION((0 == childCount) && (nsnull == firstChild),
               "deleting !empty next-in-flow");
#endif
  // Disconnect the next-in-flow from the flow list
  nextInFlow->BreakFromPrevFlow();
  // Remove nextInFlow from the parents line list. Also remove it from
  // the sibling list.
  if (RemoveChild(parent->mLines, nextInFlow)) {
    NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
       ("nsBlockFrame::DeleteNextInFlowsFor: frame=%p (from mLines)",
        nextInFlow));
    goto done;
  }
  // If we get here then we didn't find the child on the line list. If
  // it's not there then it has to be on the overflow lines list.
  if (nsnull != mOverflowLines) {
    if (RemoveChild(parent->mOverflowLines, nextInFlow)) {
      NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
         ("nsBlockFrame::DeleteNextInFlowsFor: frame=%p (from overflow)",
          nextInFlow));
      goto done;
    }
  }
  NS_NOTREACHED("can't find next-in-flow in overflow list");
 done:;
  // If the parent is us then we will finish reflowing and update the
  // content offsets of our parents when we are a pseudo-frame; if the
  // parent is not us then it's a next-in-flow which means it will get
  // reflowed by our parent and fix its content offsets. So there.
  // Delete the next-in-flow frame and adjust its parents child count
  nextInFlow->DeleteFrame(aPresContext);
#ifdef NS_DEBUG
  aChild->GetNextInFlow(nextInFlow);
  NS_POSTCONDITION(nsnull == nextInFlow, "non null next-in-flow");
#endif
  return PR_TRUE;
}
PRBool
nsBlockFrame::RemoveChild(LineData* aLines, nsIFrame* aChild)
{
  LineData* line = aLines;
  nsIFrame* prevChild = nsnull;
  while (nsnull != line) {
    nsIFrame* child = line->mFirstChild;
    PRInt32 n = line->ChildCount();
    while (--n >= 0) {
      nsIFrame* nextChild;
      child->GetNextSibling(nextChild);
      if (child == aChild) {
        NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
           ("nsBlockFrame::RemoveChild: line=%p frame=%p",
            line, aChild));
        // Continuations HAVE to be at the start of a line
        NS_ASSERTION(child == line->mFirstChild, "bad continuation");
        line->mFirstChild = nextChild;
        if (0 == --line->mChildCount) {
          line->mFirstChild = nsnull;
        }
        if (nsnull != prevChild) {
          // When nextInFlow and it's continuation are in the same
          // container then we remove the nextInFlow from the sibling
          // list.
          prevChild->SetNextSibling(nextChild);
        }
        return PR_TRUE;
      }
      prevChild = child;
      child = nextChild;
    }
    line = line->mNext;
  }
  return PR_FALSE;
}
////////////////////////////////////////////////////////////////////////
// Floater support
void
nsBlockFrame::ReflowFloater(nsIPresContext& aPresContext,
                            nsBlockReflowState& aState,
                            nsIFrame* aFloaterFrame)
{
  // Prepare the reflow state for the floater frame. Note that
  // initially it's maxSize will be 0,0 until we compute it.
  nsSize kidAvailSize(0, 0);
  nsHTMLReflowState reflowState(aPresContext, aFloaterFrame, aState,
                                kidAvailSize, eReflowReason_Initial);
  // If either dimension is constrained then get the border and
  // padding values in advance.
  nsMargin bp(0, 0, 0, 0);
  if (reflowState.HaveFixedContentWidth() ||
      reflowState.HaveFixedContentHeight()) {
    const nsStyleSpacing* spacing;
    if (NS_OK == aFloaterFrame->GetStyleData(eStyleStruct_Spacing,
                                             (const nsStyleStruct*&)spacing)) {
      spacing->CalcBorderPaddingFor(aFloaterFrame, bp);
    }
  }
  // Compute the available width for the floater
  if (reflowState.HaveFixedContentWidth()) {
    // When the floater has a contrained width, give it just enough
    // space for its styled width plus its borders and paddings.
    kidAvailSize.width = reflowState.minWidth + bp.left + bp.right;
  }
  else {
    // If we are floating something and we don't know the width then
    // find a maximum width for it to reflow into. Walk upwards until
    // we find something with an unconstrained width.
    const nsHTMLReflowState* rsp = &aState;
    kidAvailSize.width = 0;
    while (nsnull != rsp) {
      if (eHTMLFrameConstraint_FixedContent == rsp->widthConstraint) {
        kidAvailSize.width = rsp->minWidth;
        break;
      }
      else if (NS_UNCONSTRAINEDSIZE != rsp->widthConstraint) {
        kidAvailSize.width = rsp->maxSize.width;
        if (kidAvailSize.width > 0) {
          break;
        }
      }
      // XXX This cast is unfortunate!
      rsp = (const nsHTMLReflowState*) rsp->parentReflowState;
    }
  }
  // Compute the available height for the floater
  if (reflowState.HaveFixedContentHeight()) {
    kidAvailSize.height = reflowState.minHeight + bp.top + bp.bottom;
  }
  else {
    kidAvailSize.height = NS_UNCONSTRAINEDSIZE;
  }
  reflowState.maxSize = kidAvailSize;
  // Resize reflow the anchored item into the available space
  nsIHTMLReflow*  floaterReflow;
  if (NS_OK == aFloaterFrame->QueryInterface(kIHTMLReflowIID,
                                             (void**)&floaterReflow)) {
    nsHTMLReflowMetrics desiredSize(nsnull);
    nsReflowStatus  status;
    floaterReflow->WillReflow(aPresContext);
    floaterReflow->Reflow(aPresContext, desiredSize, reflowState, status);
    aFloaterFrame->SizeTo(desiredSize.width, desiredSize.height);
  }
}
void
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
{
  nsIFrame* floater = aPlaceholder->GetAnchoredItem();
  floater->SetGeometricParent(mBlock);
  mBlock->ReflowFloater(mPresContext, *this, floater);
  AddFloater(aPlaceholder);
}
// This is called by the line layout's AddFloater method when a
// place-holder frame is reflowed in a line. If the floater is a
// left-most child (it's x coordinate is at the line's left margin)
// then the floater is place immediately, otherwise the floater
// placement is deferred until the line has been reflowed.
void
nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder)
{
  // Update the current line's floater array
  NS_ASSERTION(nsnull != mCurrentLine, "null ptr");
  if (nsnull == mCurrentLine->mFloaters) {
    mCurrentLine->mFloaters = new nsVoidArray();
  }
  mCurrentLine->mFloaters->AppendElement(aPlaceholder);
  // Now place the floater immediately if possible. Otherwise stash it
  // away in mPendingFloaters and place it later.
  if (0 == mLineLayout.GetPlacedFrames()) {
    NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
       ("nsBlockReflowState::AddFloater: IsLeftMostChild, placeHolder=%p",
        aPlaceholder));
    // Flush out pending bottom margin before placing floater
    if (0 != mPrevBottomMargin) {
      mY += mPrevBottomMargin;
      mPrevMarginFlags |= ALREADY_APPLIED_BOTTOM_MARGIN;
      mPrevBottomMargin = 0;
    }
    // Because we are in the middle of reflowing a placeholder frame
    // within a line (and possibly nested in an inline frame or two
    // that's a child of our block) we need to restore the space
    // manager's translation to the space that the block resides in
    // before placing the floater.
    PRBool isLeftFloater;
    nscoord ox, oy;
    mSpaceManager->GetTranslation(ox, oy);
    nscoord dx = ox - mSpaceManagerX;
    nscoord dy = oy - mSpaceManagerY;
    mSpaceManager->Translate(-dx, -dy);
    PlaceFloater(aPlaceholder, isLeftFloater);
    // Pass on updated available space to the current inline reflow engine
    GetAvailableSpace();
    mLineLayout.UpdateInlines(mCurrentBand.availSpace.x + mBorderPadding.left,
                              mY,
                              mCurrentBand.availSpace.width,
                              mCurrentBand.availSpace.height,
                              isLeftFloater);
    // Restore coordinate system
    mSpaceManager->Translate(dx, dy);
  }
  else {
    // This floater will be placed after the line is done (it is a
    // below current line floater).
    NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
       ("nsBlockReflowState::AddFloater: pending, placeHolder=%p",
        aPlaceholder));
    mPendingFloaters.AppendElement(aPlaceholder);
  }
}
PRBool
nsBlockReflowState::IsLeftMostChild(nsIFrame* aFrame)
{
  for (;;) {
    nsIFrame* parent;
    aFrame->GetGeometricParent(parent);
    if (parent == mBlock) {
      nsIFrame* child = mCurrentLine->mFirstChild;
      PRInt32 n = mCurrentLine->ChildCount();
      while ((nsnull != child) && (aFrame != child) && (--n >= 0)) {
        nsSize  size;
        // Is the child zero-sized?
        child->GetSize(size);
        if (size.width > 0) {
          // We found a non-zero sized child frame that precedes aFrame
          return PR_FALSE;
        }
        child->GetNextSibling(child);
      }
      break;
    }
    else {
      // See if there are any non-zero sized child frames that precede
      // aFrame in the child list
      nsIFrame* child;
      parent->FirstChild(nsnull, child);
      while ((nsnull != child) && (aFrame != child)) {
        nsSize  size;
        // Is the child zero-sized?
        child->GetSize(size);
        if (size.width > 0) {
          // We found a non-zero sized child frame that precedes aFrame
          return PR_FALSE;
        }
        child->GetNextSibling(child);
      }
    }
  
    // aFrame is the left-most non-zero sized frame in its geometric parent.
    // Walk up one level and check that its parent is left-most as well
    aFrame = parent;
  }
  return PR_TRUE;
}
void
nsBlockReflowState::PlaceFloater(nsPlaceholderFrame* aPlaceholder,
                                 PRBool& aIsLeftFloater)
{
  nsIFrame* floater = aPlaceholder->GetAnchoredItem();
  // Reflow the floater if it's targetted for a reflow
  if (nsnull != reflowCommand) {
    nsIFrame* target;
    reflowCommand->GetTarget(target);
    if (floater == target) {
      mBlock->ReflowFloater(mPresContext, *this, floater);
    }
  }
  // Get the type of floater
  const nsStyleDisplay* floaterDisplay;
  const nsStyleSpacing* floaterSpacing;
  floater->GetStyleData(eStyleStruct_Display,
                        (const nsStyleStruct*&)floaterDisplay);
  floater->GetStyleData(eStyleStruct_Spacing,
                        (const nsStyleStruct*&)floaterSpacing);
  // See if the floater should clear any preceeding floaters...
  if (NS_STYLE_CLEAR_NONE != floaterDisplay->mBreakType) {
    ClearFloaters(floaterDisplay->mBreakType);
  }
  else {
    // Get the band of available space
    GetAvailableSpace();
  }
  // Get the floaters bounding box and margin information
  nsRect region;
  floater->GetRect(region);
  nsMargin floaterMargin;
  floaterSpacing->CalcMarginFor(floater, floaterMargin);
  // Adjust the floater size by its margin. That's the area that will
  // impact the space manager.
  region.width += floaterMargin.left + floaterMargin.right;
  region.height += floaterMargin.top + floaterMargin.bottom;
  // Find a place to place the floater. The CSS2 spec doesn't want
  // floaters overlapping each other or sticking out of the containing
  // block (CSS2 spec section 9.5.1, see the rule list).
  NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) ||
               (NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats),
               "invalid float type");
  // While there is not enough room for the floater, clear past
  // other floaters until there is room (or the band is not impacted
  // by a floater).
  while ((mCurrentBand.availSpace.width < region.width) &&
         (mCurrentBand.availSpace.width < mContentArea.width)) {
    // The CSS2 spec says that floaters should be placed as high as
    // possible. We accomodate this easily by noting that if the band
    // is not the full width of the content area then it must have
    // been impacted by a floater. And we know that the height of the
    // band will be the height of the shortest floater, therefore we
    // adjust mY by that distance and keep trying until we have enough
    // space for this floater.
    mY += mCurrentBand.availSpace.height;
    GetAvailableSpace();
  }
  // Assign an x and y coordinate to the floater. Note that the x,y
  // coordinates are computed relative to the translation in the
  // spacemanager which means that the impacted region will be
  // inside the border/padding area.
  if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
    aIsLeftFloater = PR_TRUE;
    region.x = mCurrentBand.availSpace.x;
  }
  else {
    aIsLeftFloater = PR_FALSE;
    region.x = mCurrentBand.availSpace.XMost() - region.width;
  }
  region.y = mY - mBorderPadding.top;
  if (region.y < 0) {
    // CSS2 spec, 9.5.1 rule [4]: A floating box's outer top may not
    // be higher than the top of its containing block.
    // XXX It's not clear if it means the higher than the outer edge
    // or the border edge or the inner edge?
    region.y = 0;
  }
  // Place the floater in the space manager
  mSpaceManager->AddRectRegion(floater, region);
  // Set the origin of the floater frame, in frame coordinates. These
  // coordinates are not relative to the spacemanager
  // translation, therefore we have to factor in our border/padding.
  floater->MoveTo(mBorderPadding.left + floaterMargin.left + region.x,
                  mBorderPadding.top + floaterMargin.top + region.y);
}
/**
 * Place below-current-line floaters.
 */
void
nsBlockReflowState::PlaceFloaters(nsVoidArray* aFloaters, PRBool aAllOfThem)
{
  NS_PRECONDITION(aFloaters->Count() > 0, "no floaters");
  PRInt32 numFloaters = aFloaters->Count();
  for (PRInt32 i = 0; i < numFloaters; i++) {
    nsPlaceholderFrame* placeholderFrame = (nsPlaceholderFrame*)
      aFloaters->ElementAt(i);
    if (!aAllOfThem && IsLeftMostChild(placeholderFrame)) {
      // Left-most children are placed during the line's reflow
      continue;
    }
    PRBool isLeftFloater;
    PlaceFloater(placeholderFrame, isLeftFloater);
  }
  // Update available spcae now that the floaters have been placed
  GetAvailableSpace();
}
void
nsBlockReflowState::ClearFloaters(PRUint8 aBreakType)
{
  // Update band information based on current mY before clearing
  GetAvailableSpace();
  for (;;) {
    PRBool haveFloater = PR_FALSE;
    // Find the Y coordinate to clear to. Note that the band trapezoid
    // coordinates are relative to the our spacemanager translation
    // (which means the band coordinates are inside the border+padding
    // area of this block frame).
    NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
       ("nsBlockReflowState::ClearFloaters: mY=%d trapCount=%d",
        mY, mCurrentBand.count));
    nscoord clearYMost = mY - mBorderPadding.top;
    nsRect tmp;
    PRInt32 i;
    for (i = 0; i < mCurrentBand.count; i++) {
      const nsStyleDisplay* display;
      nsBandTrapezoid* trapezoid = &mCurrentBand.data[i];
      NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
         ("nsBlockReflowState::ClearFloaters: trap=%d state=%d",
          i, trapezoid->state));
      if (nsBandTrapezoid::Available != trapezoid->state) {
        haveFloater = PR_TRUE;
        if (nsBandTrapezoid::OccupiedMultiple == trapezoid->state) {
          PRInt32 fn, numFrames = trapezoid->frames->Count();
          NS_ASSERTION(numFrames > 0, "bad trapezoid frame list");
          for (fn = 0; fn < numFrames; fn++) {
            nsIFrame* frame = (nsIFrame*) trapezoid->frames->ElementAt(fn);
            frame->GetStyleData(eStyleStruct_Display,
                                (const nsStyleStruct*&)display);
            NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
               ("nsBlockReflowState::ClearFloaters: frame[%d]=%p floats=%d",
                fn, frame, display->mFloats));
            switch (display->mFloats) {
            case NS_STYLE_FLOAT_LEFT:
              if ((NS_STYLE_CLEAR_LEFT == aBreakType) ||
                  (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType)) {
                trapezoid->GetRect(tmp);
                nscoord ym = tmp.YMost();
                if (ym > clearYMost) {
                  clearYMost = ym;
                }
              }
              break;
            case NS_STYLE_FLOAT_RIGHT:
              if ((NS_STYLE_CLEAR_RIGHT == aBreakType) ||
                  (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType)) {
                trapezoid->GetRect(tmp);
                nscoord ym = tmp.YMost();
                if (ym > clearYMost) {
                  clearYMost = ym;
                  NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
                  ("nsBlockReflowState::ClearFloaters: right clearYMost=%d",
                   clearYMost));
                }
              }
              break;
            }
          }
        }
        else {
          trapezoid->frame->GetStyleData(eStyleStruct_Display,
                                         (const nsStyleStruct*&)display);
          NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
             ("nsBlockReflowState::ClearFloaters: frame=%p floats=%d",
              trapezoid->frame, display->mFloats));
          switch (display->mFloats) {
          case NS_STYLE_FLOAT_LEFT:
            if ((NS_STYLE_CLEAR_LEFT == aBreakType) ||
                (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType)) {
              trapezoid->GetRect(tmp);
              nscoord ym = tmp.YMost();
              if (ym > clearYMost) {
                clearYMost = ym;
                NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
                  ("nsBlockReflowState::ClearFloaters: left clearYMost=%d",
                   clearYMost));
              }
            }
            break;
          case NS_STYLE_FLOAT_RIGHT:
            if ((NS_STYLE_CLEAR_RIGHT == aBreakType) ||
                (NS_STYLE_CLEAR_LEFT_AND_RIGHT == aBreakType)) {
              trapezoid->GetRect(tmp);
              nscoord ym = tmp.YMost();
              if (ym > clearYMost) {
                clearYMost = ym;
                NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
                  ("nsBlockReflowState::ClearFloaters: right clearYMost=%d",
                   clearYMost));
              }
            }
            break;
          }
        }
      }
    }
    // Nothing to clear
    if (!haveFloater || (clearYMost == mY - mBorderPadding.top)) {
      break;
    }
    NS_FRAME_LOG(NS_FRAME_TRACE_CHILD_REFLOW,
       ("nsBlockReflowState::ClearFloaters: mY=%d clearYMost=%d",
        mY, clearYMost));
    mY = mBorderPadding.top + clearYMost + 1;
    // Get a new band
    GetAvailableSpace();
  }
}
//////////////////////////////////////////////////////////////////////
// Painting, event handling
PRIntn
nsBlockFrame::GetSkipSides() const
{
  PRIntn skip = 0;
  if (nsnull != mPrevInFlow) {
    skip |= 1 << NS_SIDE_TOP;
  }
  if (nsnull != mNextInFlow) {
    skip |= 1 << NS_SIDE_BOTTOM;
  }
  return skip;
}
NS_IMETHODIMP
nsBlockFrame::Paint(nsIPresContext&      aPresContext,
                    nsIRenderingContext& aRenderingContext,
                    const nsRect&        aDirtyRect)
{
  const nsStyleDisplay* disp = (const nsStyleDisplay*)
    mStyleContext->GetStyleData(eStyleStruct_Display);
  if (disp->mVisible) {
    PRIntn skipSides = GetSkipSides();
    const nsStyleColor* color = (const nsStyleColor*)
      mStyleContext->GetStyleData(eStyleStruct_Color);
    const nsStyleSpacing* spacing = (const nsStyleSpacing*)
      mStyleContext->GetStyleData(eStyleStruct_Spacing);
    // Paint background and border
    nsRect rect(0, 0, mRect.width, mRect.height);
    nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
                                    aDirtyRect, rect, *color, 0, 0);
    nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
                                aDirtyRect, rect, *spacing, skipSides);
    // If overflow is hidden then set the clip rect so that children
    // don't leak out of us
    if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
      PRBool clipState;
      aRenderingContext.PushState();
      aRenderingContext.SetClipRect(nsRect(0, 0, mRect.width, mRect.height),
                                    nsClipCombine_kIntersect, clipState);
    }
    PaintFloaters(aPresContext, aRenderingContext, aDirtyRect);
    PaintChildren(aPresContext, aRenderingContext, aDirtyRect);
    if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
      PRBool clipState;
      aRenderingContext.PopState(clipState);
    }
  }
  return NS_OK;
}
void
nsBlockFrame::PaintFloaters(nsIPresContext& aPresContext,
                            nsIRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect)
{
  for (LineData* line = mLines; nsnull != line; line = line->mNext) {
    nsVoidArray* floaters = line->mFloaters;
    if (nsnull == floaters) {
      continue;
    }
    PRInt32 i, n = floaters->Count();
    for (i = 0; i < n; i++) {
      nsPlaceholderFrame* ph = (nsPlaceholderFrame*) floaters->ElementAt(i);
      PaintChild(aPresContext, aRenderingContext, aDirtyRect,
                 ph->GetAnchoredItem());
    }
  }
}
void
nsBlockFrame::PaintChildren(nsIPresContext& aPresContext,
                            nsIRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect)
{
  if (nsnull != mBullet) {
    // Paint outside bullets manually
    const nsStyleList* list = (const nsStyleList*)
    mStyleContext->GetStyleData(eStyleStruct_List);
    if (NS_STYLE_LIST_STYLE_POSITION_OUTSIDE == list->mListStylePosition) {
      PaintChild(aPresContext, aRenderingContext, aDirtyRect, mBullet);
    }
  }
  for (LineData* line = mLines; nsnull != line; line = line->mNext) {
    // If the line has outside children or if the line intersects the
    // dirty rect then paint the children in the line.
    if (line->OutsideChildren() ||
        !((line->mBounds.YMost() <= aDirtyRect.y) ||
          (line->mBounds.y >= aDirtyRect.YMost()))) {
      nsIFrame* kid = line->mFirstChild;
      PRInt32 n = line->ChildCount();
      while (--n >= 0) {
        PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid);
        kid->GetNextSibling(kid);
      }
    }
  }
}
//////////////////////////////////////////////////////////////////////
// Debugging
#ifdef NS_DEBUG
static PRBool
InLineList(LineData* aLines, nsIFrame* aFrame)
{
  while (nsnull != aLines) {
    nsIFrame* frame = aLines->mFirstChild;
    PRInt32 n = aLines->ChildCount();
    while (--n >= 0) {
      if (frame == aFrame) {
        return PR_TRUE;
      }
      frame->GetNextSibling(frame);
    }
    aLines = aLines->mNext;
  }
  return PR_FALSE;
}
static PRBool
InSiblingList(LineData* aLine, nsIFrame* aFrame)
{
  if (nsnull != aLine) {
    nsIFrame* frame = aLine->mFirstChild;
    while (nsnull != frame) {
      if (frame == aFrame) {
        return PR_TRUE;
      }
      frame->GetNextSibling(frame);
    }
  }
  return PR_FALSE;
}
PRBool
nsBlockFrame::IsChild(nsIFrame* aFrame)
{
  nsIFrame* parent;
  aFrame->GetGeometricParent(parent);
  if (parent != (nsIFrame*)this) {
    return PR_FALSE;
  }
  if (InLineList(mLines, aFrame) && InSiblingList(mLines, aFrame)) {
    return PR_TRUE;
  }
  if (InLineList(mOverflowLines, aFrame) &&
      InSiblingList(mOverflowLines, aFrame)) {
    return PR_TRUE;
  }
  return PR_FALSE;
}
#endif
#define VERIFY_ASSERT(_expr, _msg)      \
  if (!(_expr)) {                       \
    DumpTree();                         \
  }                                     \
  NS_ASSERTION(_expr, _msg)
NS_IMETHODIMP
nsBlockFrame::VerifyTree() const
{
  // XXX rewrite this
  return NS_OK;
}
#ifdef DO_SELECTION
nsIFrame * nsBlockFrame::FindHitFrame(nsBlockFrame * aBlockFrame, 
                                         const nscoord aX,
                                         const nscoord aY,
                                         const nsPoint & aPoint)
{
  nsPoint mousePoint(aPoint.x-aX, aPoint.y-aY);
  nsIFrame * contentFrame = nsnull;
  LineData * line         = aBlockFrame->mLines;
  if (nsnull != line) {
    // First find the line that contains the aIndex
    while (nsnull != line && contentFrame == nsnull) {
      nsIFrame* frame = line->mFirstChild;
      PRInt32 n = line->ChildCount();
      while (--n >= 0) {
        nsRect bounds;
        frame->GetRect(bounds);
        if (bounds.Contains(mousePoint)) {
           nsBlockFrame * blockFrame;
          if (NS_OK == frame->QueryInterface(kBlockFrameCID, (void**)&blockFrame)) {
            frame = FindHitFrame(blockFrame, bounds.x, bounds.y, aPoint);
            //NS_RELEASE(blockFrame);
            return frame;
          } else {
            return frame;
          }
        }
        frame->GetNextSibling(frame);
      }
      line = line->mNext;
    }
  }
  return aBlockFrame;
}
NS_IMETHODIMP
nsBlockFrame::HandleEvent(nsIPresContext& aPresContext,
                          nsGUIEvent* aEvent,
                          nsEventStatus& aEventStatus)
{
  if (0) {
    nsHTMLContainerFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
  }
  //return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
    aEventStatus = nsEventStatus_eIgnore;
  
  //if (nsnull != mContent && (aEvent->message != NS_MOUSE_LEFT_BUTTON_UP ||
  //    (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP && !mDoingSelection))) {
  if (nsnull != mContent) {
    mContent->HandleDOMEvent(aPresContext, (nsEvent*)aEvent, nsnull, DOM_EVENT_INIT, aEventStatus);
  }
  if (DisplaySelection(aPresContext) == PR_FALSE) {
    if (aEvent->message != NS_MOUSE_LEFT_BUTTON_DOWN) {
      return NS_OK;
    }
  }
  if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) {
    int x = 0;
  }
  //nsRect bounds;
  //GetRect(bounds);
  //nsIFrame * contentFrame = FindHitFrame(this, bounds.x, bounds.y, aEvent->point);
  nsIFrame * contentFrame = FindHitFrame(this, 0,0, aEvent->point);
  if (contentFrame == nsnull) {
    return NS_OK;
  }
  if(nsEventStatus_eConsumeNoDefault != aEventStatus) {
    if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) {
    } else if (aEvent->message == NS_MOUSE_MOVE && mDoingSelection ||
               aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
      // no-op
    } else {
      return NS_OK;
    }
    if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
      if (mDoingSelection) {
        contentFrame->HandleRelease(aPresContext, aEvent, aEventStatus);
      }
    } else if (aEvent->message == NS_MOUSE_MOVE) {
      mDidDrag = PR_TRUE;
      contentFrame->HandleDrag(aPresContext, aEvent, aEventStatus);
    } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) {
      contentFrame->HandlePress(aPresContext, aEvent, aEventStatus);
    }
  }
  return NS_OK;
}
nsIFrame * gNearByFrame = nsnull;
NS_METHOD nsBlockFrame::HandleDrag(nsIPresContext& aPresContext, 
                                      nsGUIEvent*     aEvent,
                                      nsEventStatus&  aEventStatus)
{
  if (DisplaySelection(aPresContext) == PR_FALSE)
  {
    aEventStatus = nsEventStatus_eIgnore;
    return NS_OK;
  }
  // Keep old start and end
  //nsIContent * startContent = mSelectionRange->GetStartContent(); // ref counted
  //nsIContent * endContent   = mSelectionRange->GetEndContent();   // ref counted
  mDidDrag = PR_TRUE;
  nsIFrame * contentFrame = nsnull;
  LineData* line = mLines;
  if (nsnull != line) {
    // First find the line that contains the aIndex
    while (nsnull != line && contentFrame == nsnull) {
      nsIFrame* frame = line->mFirstChild;
      PRInt32 n = line->ChildCount();
      while (--n >= 0) {
        nsRect bounds;
        frame->GetRect(bounds);
        if (aEvent->point.y >= bounds.y && aEvent->point.y < bounds.y+bounds.height) {
          contentFrame = frame;
          if (frame != gNearByFrame) {
            if (gNearByFrame != nsnull) {
              int x = 0;
            }
            aEvent->point.x = bounds.x+bounds.width-50;
            gNearByFrame = frame;
            return contentFrame->HandleDrag(aPresContext, aEvent, aEventStatus);
          } else {
            return NS_OK;
          }
          //break;
        }
        frame->GetNextSibling(frame);
      }
      line = line->mNext;
    }
  }
  return NS_OK;
}
#endif