/* -*- 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.1 (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.
 *
 * Contributor(s): 
 *   Pierre Phaneuf <pp@ludusdesign.com>
 */
#include "nsCOMPtr.h"
#include "nsBlockFrame.h"
#include "nsBlockReflowContext.h"
#include "nsBlockBandData.h"
#include "nsBulletFrame.h"
#include "nsLineBox.h"
#include "nsInlineFrame.h"
#include "nsLineLayout.h"
#include "nsPlaceholderFrame.h"
#include "nsStyleConsts.h"
#include "nsHTMLIIDs.h"
#include "nsCSSRendering.h"
#include "nsIFrameManager.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"
#include "nsLayoutAtoms.h"
#include "nsITextContent.h"
#include "nsStyleChangeList.h"
#include "nsISizeOfHandler.h"
#include "nsIFocusTracker.h"
#include "nsIFrameSelection.h"
#include "nsSpaceManager.h"
#include "prenv.h"
#include "plstr.h"

// XXX HTML:P's that are empty yet have style indicating they should
// clear floaters - we need to ignore the clear behavior.

#ifdef DEBUG

static PRBool gLamePaintMetrics;
static PRBool gLameReflowMetrics;
static PRBool gNoisy;
static PRBool gNoisyDamageRepair;
static PRBool gNoisyMaxElementSize;
static PRBool gNoisyReflow;
static PRBool gReallyNoisyReflow;
static PRBool gNoisySpaceManager;
static PRBool gVerifyLines;
static PRBool gDisableResizeOpt;
static PRBool gListTextRuns;

struct BlockDebugFlags {
  const char* name;
  PRBool* on;
};

static BlockDebugFlags gFlags[] = {
  { "reflow", &gNoisyReflow },
  { "really-noisy-reflow", &gReallyNoisyReflow },
  { "max-element-size", &gNoisyMaxElementSize },
  { "space-manager", &gNoisySpaceManager },
  { "verify-lines", &gVerifyLines },
  { "damage-repair", &gNoisyDamageRepair },
  { "lame-paint-metrics", &gLamePaintMetrics },
  { "lame-reflow-metrics", &gLameReflowMetrics },
  { "disable-resize-opt", &gDisableResizeOpt },
  { "list-text-runs", &gListTextRuns },
};
#define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))

static void
ShowDebugFlags()
{
  printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
  BlockDebugFlags* bdf = gFlags;
  BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
  for (; bdf < end; bdf++) {
    printf("  %s\n", bdf->name);
  }
  printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma seperated list of flag\n");
  printf("names (no whitespace)\n");
}

static void
InitDebugFlags()
{
  static PRBool firstTime = PR_TRUE;
  if (firstTime) {
    firstTime = PR_FALSE;
    char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
    if (flags) {
      PRBool error = PR_FALSE;
      for (;;) {
        char* cm = PL_strchr(flags, ',');
        if (cm) *cm = '\0';

        PRBool found = PR_FALSE;
        BlockDebugFlags* bdf = gFlags;
        BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
        for (; bdf < end; bdf++) {
          if (PL_strcasecmp(bdf->name, flags) == 0) {
            *(bdf->on) = PR_TRUE;
            printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
            gNoisy = PR_TRUE;
            found = PR_TRUE;
            break;
          }
        }
        if (!found) {
          error = PR_TRUE;
        }

        if (!cm) break;
        *cm = ',';
        flags = cm + 1;
      }
      if (error) {
        ShowDebugFlags();
      }
    }
  }
}

#undef NOISY_FIRST_LINE
#undef REALLY_NOISY_FIRST_LINE
#undef NOISY_FIRST_LETTER
#undef NOISY_MAX_ELEMENT_SIZE
#undef NOISY_FLOATER_CLEARING
#undef NOISY_FINAL_SIZE
#undef NOISY_REMOVE_FRAME
#undef NOISY_COMBINED_AREA
#undef NOISY_VERTICAL_MARGINS
#undef NOISY_REFLOW_REASON
#undef REFLOW_STATUS_COVERAGE
#undef NOISY_SPACEMANAGER

#endif

//----------------------------------------------------------------------

// Debugging support code

#ifdef DEBUG
static PRInt32 gNoiseIndent;
static const char* kReflowCommandType[] = {
  "ContentChanged",
  "StyleChanged",
  "PullupReflow",
  "PushReflow",
  "CheckPullupReflow",
  "ReflowDirty",
  "UserDefined",
};
#endif

#ifdef REALLY_NOISY_FIRST_LINE
static void
DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
{
  fputs(gap, stdout);
  nsFrame::ListTag(stdout, aFrame);
  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

#ifdef REFLOW_STATUS_COVERAGE
static void
RecordReflowStatus(PRBool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
{
  static PRUint32 record[2];

  // 0: child-is-block
  // 1: child-is-inline
  PRIntn index = 0;
  if (!aChildIsBlock) index |= 1;

  // Compute new status
  PRUint32 newS = record[index];
  if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
    if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
      newS |= 1;
    }
    else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
      newS |= 2;
    }
    else {
      newS |= 4;
    }
  }
  else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
    newS |= 8;
  }
  else {
    newS |= 16;
  }

  // Log updates to the status that yield different values
  if (record[index] != newS) {
    record[index] = newS;
    printf("record(%d): %02x %02x\n", index, record[0], record[1]);
  }
}
#endif

//----------------------------------------------------------------------

inline void CombineRects(const nsRect& r1, nsRect& r2)
{
  nscoord xa = r2.x;
  nscoord ya = r2.y;
  nscoord xb = xa + r2.width;
  nscoord yb = ya + r2.height;
  nscoord x = r1.x;
  nscoord y = r1.y;
  nscoord xmost = x + r1.width;
  nscoord ymost = y + r1.height;
  if (x < xa) {
    xa = x;
  }
  if (xmost > xb) {
    xb = xmost;
  }
  if (y < ya) {
    ya = y;
  }
  if (ymost > yb) {
    yb = ymost;
  }
  r2.x = xa;
  r2.y = ya;
  r2.width = xb - xa;
  r2.height = yb - ya;
}

//----------------------------------------------------------------------

class nsBlockReflowState {
public:
  nsBlockReflowState(const nsHTMLReflowState& aReflowState,
                     nsIPresContext* aPresContext,
                     nsBlockFrame* aFrame,
                     const nsHTMLReflowMetrics& aMetrics,
                     PRBool aBlockMarginRoot);

  ~nsBlockReflowState();

  /**
   * Update our state when aLine is skipped over during incremental
   * reflow.
   */
  void RecoverStateFrom(nsLineBox* aLine, PRBool aPrevLineWasClean);

  /**
   * 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 GetAvailableSpace() {
#ifdef DEBUG
    // Verify that the caller setup the coordinate system properly
    nscoord wx, wy;
    mSpaceManager->GetTranslation(wx, wy);
    NS_ASSERTION((wx == mSpaceManagerX) && (wy == mSpaceManagerY),
                 "bad coord system");
#endif
    mBand.GetAvailableSpace(mY - BorderPadding().top, mAvailSpaceRect);

#ifdef DEBUG
    if (gNoisyReflow) {
      nsFrame::IndentBy(stdout, gNoiseIndent);
      printf("GetAvailableSpace: band=%d,%d,%d,%d count=%d\n",
             mAvailSpaceRect.x, mAvailSpaceRect.y,
             mAvailSpaceRect.width, mAvailSpaceRect.height,
             mBand.GetTrapezoidCount());
    }
#endif
  }

  void GetAvailableSpace(nscoord aY) {
#ifdef DEBUG
    // Verify that the caller setup the coordinate system properly
    nscoord wx, wy;
    mSpaceManager->GetTranslation(wx, wy);
    NS_ASSERTION((wx == mSpaceManagerX) && (wy == mSpaceManagerY),
                 "bad coord system");
#endif
    mBand.GetAvailableSpace(aY - BorderPadding().top, mAvailSpaceRect);

#ifdef DEBUG
    if (gNoisyReflow) {
      nsFrame::IndentBy(stdout, gNoiseIndent);
      printf("GetAvailableSpace: band=%d,%d,%d,%d count=%d\n",
             mAvailSpaceRect.x, mAvailSpaceRect.y,
             mAvailSpaceRect.width, mAvailSpaceRect.height,
             mBand.GetTrapezoidCount());
    }
#endif
  }

  void InitFloater(nsLineLayout& aLineLayout,
                   nsPlaceholderFrame* aPlaceholderFrame);

  void AddFloater(nsLineLayout& aLineLayout,
                  nsPlaceholderFrame* aPlaceholderFrame,
                  PRBool aInitialReflow);

  PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats);

  void PlaceFloater(nsFloaterCache* aFloaterCache,
                    PRBool* aIsLeftFloater);

  void PlaceBelowCurrentLineFloaters(nsFloaterCacheList& aFloaters);

  void ClearFloaters(nscoord aY, PRUint8 aBreakType);

  PRBool ClearPastFloaters(PRUint8 aBreakType);

  PRBool IsLeftMostChild(nsIPresContext* aPresContext, nsIFrame* aFrame);

  PRBool IsAdjacentWithTop() const {
    return mY == mReflowState.mComputedBorderPadding.top;
  }

  const nsMargin& BorderPadding() const {
    return mReflowState.mComputedBorderPadding;
  }

  void UpdateMaxElementSize(const nsSize& aMaxElementSize) {
#ifdef NOISY_MAX_ELEMENT_SIZE
    nsSize oldSize = mMaxElementSize;
#endif
    if (aMaxElementSize.width > mMaxElementSize.width) {
      mMaxElementSize.width = aMaxElementSize.width;
    }
    if (aMaxElementSize.height > mMaxElementSize.height) {
      mMaxElementSize.height = aMaxElementSize.height;
    }
#ifdef NOISY_MAX_ELEMENT_SIZE
    if ((mMaxElementSize.width != oldSize.width) ||
        (mMaxElementSize.height != oldSize.height)) {
      nsFrame::IndentBy(stdout, mBlock->GetDepth());
      if (NS_UNCONSTRAINEDSIZE == mReflowState.availableWidth) {
        printf("PASS1 ");
      }
      nsFrame::ListTag(stdout, mBlock);
      printf(": old max-element-size=%d,%d new=%d,%d\n",
             oldSize.width, oldSize.height,
             mMaxElementSize.width, mMaxElementSize.height);
    }
#endif
  }

  void UpdateMaximumWidth(nscoord aMaximumWidth) {
    if (aMaximumWidth > mMaximumWidth) {
      mMaximumWidth = aMaximumWidth;
    }
  }

  void RecoverVerticalMargins(nsLineBox* aLine,
                              PRBool aApplyTopMargin,
                              nscoord* aTopMarginResult,
                              nscoord* aBottomMarginResult);

  void ComputeBlockAvailSpace(nsIFrame* aFrame,
                              nsSplittableType aSplitType,
                              const nsStyleDisplay* aDisplay,
                              nsRect& aResult);

  void RecoverStateFrom(nsLineBox* aLine,
                        PRBool aApplyTopMargin,
                        nscoord aDeltaY,
                        nsRect* aDamageRect);

  void AdvanceToNextLine() {
    mLineNumber++;
  }

  PRBool IsImpactedByFloater() {
#ifdef REALLY_NOISY_REFLOW
    printf("nsBlockReflowState::IsImpactedByFloater %p returned %d\n", 
           this, mBand.GetFloaterCount());
#endif
    return mBand.GetFloaterCount();
  }

  nsLineBox* NewLineBox(nsIFrame* aFrame, PRInt32 aCount, PRBool aIsBlock);

  void FreeLineBox(nsLineBox* aLine);

  void StoreMaxElementSize(nsIFrame* aFloater, const nsSize& aSize) {
    mBand.StoreMaxElementSize(mPresContext, aFloater, aSize);
  }

  //----------------------------------------

  // This state is the "global" state computed once for the reflow of
  // the block.

  // The block frame that is using this object
  nsBlockFrame* mBlock;

  nsIPresContext* mPresContext;

  const nsHTMLReflowState& mReflowState;

  nsISpaceManager* mSpaceManager;

  // The coordinates within the spacemanager where the block is being
  // placed <b>after</b> taking into account the blocks border and
  // padding. This, therefore, represents the inner "content area" (in
  // spacemanager coordinates) where child frames will be placed,
  // including child blocks and floaters.
  nscoord mSpaceManagerX, mSpaceManagerY;

  // XXX get rid of this
  nsReflowStatus mReflowStatus;

  nscoord mBottomEdge;

  PRPackedBool mUnconstrainedWidth;
  PRPackedBool mUnconstrainedHeight;
  PRPackedBool mShrinkWrapWidth;
  PRPackedBool mNeedResizeReflow;
  PRPackedBool mIsInlineIncrReflow;

  // The content area to reflow child frames within. The x/y
  // coordinates are known to be mBorderPadding.left and
  // mBorderPadding.top. The width/height may be NS_UNCONSTRAINEDSIZE
  // if the container reflowing this frame has given the frame an
  // unconstrained area.
  nsSize mContentArea;

  // Our wrapping behavior
  PRPackedBool mNoWrap;

  // Is this frame a root for top/bottom margin collapsing?
  PRPackedBool mIsTopMarginRoot, mIsBottomMarginRoot;

  // See ShouldApplyTopMargin
  PRPackedBool mApplyTopMargin;

  //----------------------------------------

  // This state is "running" state updated by the reflow of each line
  // in the block. This same state is "recovered" when a line is not
  // dirty and is passed over during incremental reflow.

  // The current line being reflowed
  nsLineBox* mCurrentLine;

  // The previous line just reflowed
  nsLineBox* mPrevLine;

  // The current Y coordinate in the block
  nscoord mY;

  // The available space within the current band.
  nsRect mAvailSpaceRect;

  // The maximum x-most of each line
  nscoord mKidXMost;

  // The combined area of all floaters placed so far
  nsRect mFloaterCombinedArea;

  // For unconstained-width reflow, we keep the right floaters
  // combined area stored seperately.
  PRBool mHaveRightFloaters;
  nsRect mRightFloaterCombinedArea;

  nsFloaterCacheFreeList mFloaterCacheFreeList;

  // Previous child. This is used when pulling up a frame to update
  // the sibling list.
  nsIFrame* mPrevChild;

  // The next immediate child frame that is the target of an
  // incremental reflow command. Once that child has been reflowed we
  // null this slot out.
  nsIFrame* mNextRCFrame;

  // The previous child frames collapsed bottom margin value.
  nscoord mPrevBottomMargin;

  // The current next-in-flow for the block. When lines are pulled
  // from a next-in-flow, this is used to know which next-in-flow to
  // pull from. When a next-in-flow is emptied of lines, we advance
  // this to the next next-in-flow.
  nsBlockFrame* mNextInFlow;

  // The current band data for the current Y coordinate
  nsBlockBandData mBand;

  //----------------------------------------

  // Temporary line-reflow state. This state is used during the reflow
  // of a given line, but doesn't have meaning before or after.

  // The list of floaters that are "current-line" floaters. These are
  // added to the line after the line has been reflowed, to keep the
  // list fiddling from being N^2.
  nsFloaterCacheFreeList mCurrentLineFloaters;

  // The list of floaters which are "below current-line"
  // floaters. These are reflowed/placed after the line is reflowed
  // and placed. Again, this is done to keep the list fiddling from
  // being N^2.
  nsFloaterCacheFreeList mBelowCurrentLineFloaters;

  PRPackedBool mComputeMaxElementSize;
  PRPackedBool mComputeMaximumWidth;

  nsSize mMaxElementSize;
  nscoord mMaximumWidth;

  nscoord mMinLineHeight;

  PRInt32 mLineNumber;
};

// XXX This is vile. Make it go away
void
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
{
  mBlockRS->InitFloater(*this, aFrame);
}
void
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
{
  mBlockRS->AddFloater(*this, aFrame, PR_FALSE);
}

//----------------------------------------------------------------------

nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
                                       nsIPresContext* aPresContext,
                                       nsBlockFrame* aFrame,
                                       const nsHTMLReflowMetrics& aMetrics,
                                       PRBool aBlockMarginRoot)
  : mBlock(aFrame),
    mPresContext(aPresContext),
    mReflowState(aReflowState),
    mNeedResizeReflow(PR_FALSE),
    mIsInlineIncrReflow(PR_FALSE),
    mIsTopMarginRoot(PR_FALSE),
    mIsBottomMarginRoot(PR_FALSE),
    mApplyTopMargin(PR_FALSE),
    mNextRCFrame(nsnull),
    mPrevBottomMargin(0),
    mLineNumber(0)
{
  const nsMargin& borderPadding = BorderPadding();

  if (aBlockMarginRoot) {
    mIsTopMarginRoot = PR_TRUE;
    mIsBottomMarginRoot = PR_TRUE;
  }
  if (0 != aReflowState.mComputedBorderPadding.top) {
    mIsTopMarginRoot = PR_TRUE;
  }
  if (0 != aReflowState.mComputedBorderPadding.bottom) {
    mIsBottomMarginRoot = PR_TRUE;
  }
  if (mIsTopMarginRoot) {
    mApplyTopMargin = PR_TRUE;
  }
  
  mSpaceManager = aReflowState.mSpaceManager;

  NS_ASSERTION( nsnull != mSpaceManager, "SpaceManager should be set in nsBlockReflowState" );
  if( nsnull != mSpaceManager ) {
    // Translate into our content area and then save the 
    // coordinate system origin for later.
    mSpaceManager->Translate(borderPadding.left, borderPadding.top);
    mSpaceManager->GetTranslation(mSpaceManagerX, mSpaceManagerY);
  }

  mReflowStatus = NS_FRAME_COMPLETE;

  mPresContext = aPresContext;
  mBlock->GetNextInFlow((nsIFrame**)&mNextInFlow);
  mKidXMost = 0;

  // Compute content area width (the content area is inside the border
  // and padding)
  mUnconstrainedWidth = PR_FALSE;
  mShrinkWrapWidth = PR_FALSE;
  if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedWidth) {
    mContentArea.width = aReflowState.mComputedWidth;
  }
  else {
    if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) {
      mContentArea.width = NS_UNCONSTRAINEDSIZE;
      mUnconstrainedWidth = PR_TRUE;
    }
    else if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) {
      // Choose a width based on the content (shrink wrap width) up
      // to the maximum width
      mContentArea.width = aReflowState.mComputedMaxWidth;
      mShrinkWrapWidth = PR_TRUE;
    }
    else {
      nscoord lr = borderPadding.left + borderPadding.right;
      mContentArea.width = aReflowState.availableWidth - lr;
    }
  }
  mHaveRightFloaters = PR_FALSE;

  // Compute content area height. Unlike the width, if we have a
  // specified style height we ignore it since extra content is
  // managed by the "overflow" property. When we don't have a
  // specified style height then we may end up limiting our height if
  // the availableHeight is constrained (this situation occurs when we
  // are paginated).
  mUnconstrainedHeight = PR_FALSE;
  if (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) {
    // We are in a paginated situation. The bottom edge is just inside
    // the bottom border and padding. The content area height doesn't
    // include either border or padding edge.
    mBottomEdge = aReflowState.availableHeight - borderPadding.bottom;
    mContentArea.height = mBottomEdge - borderPadding.top;
  }
  else {
    // When we are not in a paginated situation then we always use
    // an constrained height.
    mUnconstrainedHeight = PR_TRUE;
    mContentArea.height = mBottomEdge = NS_UNCONSTRAINEDSIZE;
  }

  mY = borderPadding.top;
  mBand.Init(mSpaceManager, mContentArea);

  mPrevChild = nsnull;
  mCurrentLine = nsnull;
  mPrevLine = nsnull;

  const nsStyleText* styleText;
  mBlock->GetStyleData(eStyleStruct_Text,
                       (const nsStyleStruct*&) styleText);
  switch (styleText->mWhiteSpace) {
  case NS_STYLE_WHITESPACE_PRE:
  case NS_STYLE_WHITESPACE_NOWRAP:
    mNoWrap = PR_TRUE;
    break;
  default:
    mNoWrap = PR_FALSE;
    break;
  }

  mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;
  mMaxElementSize.SizeTo(0, 0);
  mComputeMaximumWidth = NS_REFLOW_CALC_MAX_WIDTH == (aMetrics.mFlags & NS_REFLOW_CALC_MAX_WIDTH);
  mMaximumWidth = 0;

  mMinLineHeight = nsHTMLReflowState::CalcLineHeight(mPresContext,
                                                     aReflowState.rendContext,
                                                     aReflowState.frame);
}

nsBlockReflowState::~nsBlockReflowState()
{
  // Restore the coordinate system
  const nsMargin& borderPadding = BorderPadding();
  mSpaceManager->Translate(-borderPadding.left, -borderPadding.top);
}

nsLineBox*
nsBlockReflowState::NewLineBox(nsIFrame* aFrame,
                               PRInt32 aCount,
                               PRBool aIsBlock)
{
  nsCOMPtr<nsIPresShell> shell;
  mPresContext->GetShell(getter_AddRefs(shell));

  return NS_NewLineBox(shell, aFrame, aCount, aIsBlock);
}

void
nsBlockReflowState::FreeLineBox(nsLineBox* aLine)
{
  if (aLine) {
    nsCOMPtr<nsIPresShell> presShell;
    mPresContext->GetShell(getter_AddRefs(presShell));
    
    aLine->Destroy(presShell);
  }
}

// Compute the amount of available space for reflowing a block frame
// at the current Y coordinate. This method assumes that
// GetAvailableSpace has already been called.
void
nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
                                           nsSplittableType aSplitType,
                                           const nsStyleDisplay* aDisplay,
                                           nsRect& aResult)
{
  aResult.y = mY;
  aResult.height = mUnconstrainedHeight
    ? NS_UNCONSTRAINEDSIZE
    : mBottomEdge - mY;

  const nsMargin& borderPadding = BorderPadding();

  if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
    if (mBand.GetFloaterCount()) {
      // Use the float-edge property to determine how the child block
      // will interact with the floater.
      const nsStyleSpacing* spacing;
      aFrame->GetStyleData(eStyleStruct_Spacing,
                           (const nsStyleStruct*&) spacing);
      switch (spacing->mFloatEdge) {
        default:
        case NS_STYLE_FLOAT_EDGE_CONTENT:  // content and only content does runaround of floaters
          // The child block will flow around the floater. Therefore
          // give it all of the available space.
          aResult.x = borderPadding.left;
          aResult.width = mUnconstrainedWidth
            ? NS_UNCONSTRAINEDSIZE
            : mContentArea.width;
           break;
        case NS_STYLE_FLOAT_EDGE_BORDER: 
        case NS_STYLE_FLOAT_EDGE_PADDING:
          {
            // The child block's border should be placed adjacent to,
            // but not overlap the floater(s).
            nsMargin m(0, 0, 0, 0);
            spacing->GetMargin(m); // XXX percentage margins
            if (NS_STYLE_FLOAT_EDGE_PADDING == spacing->mFloatEdge) {
              // Add in border too
              nsMargin b;
              spacing->GetBorder(b);
              m += b;
            }

            // determine left edge
            if (mBand.GetLeftFloaterCount()) {
              aResult.x = mAvailSpaceRect.x + borderPadding.left - m.left;
            }
            else {
              aResult.x = borderPadding.left;
            }

            // determine width
            if (mUnconstrainedWidth) {
              aResult.width = NS_UNCONSTRAINEDSIZE;
            }
            else {
              if (mBand.GetRightFloaterCount()) {
                if (mBand.GetLeftFloaterCount()) {
                  aResult.width = mAvailSpaceRect.width + m.left + m.right;
                }
                else {
                  aResult.width = mAvailSpaceRect.width + m.right;
                }
              }
              else {
                aResult.width = mAvailSpaceRect.width + m.left;
              }
            }
          }
          break;

        case NS_STYLE_FLOAT_EDGE_MARGIN:
          {
            // The child block's margins should be placed adjacent to,
            // but not overlap the floater.
            aResult.x = mAvailSpaceRect.x + borderPadding.left;
            aResult.width = mAvailSpaceRect.width;
          }
          break;
      }
    }
    else {
      // Since there are no floaters present the float-edge property
      // doesn't matter therefore give the block element all of the
      // available space since it will flow around the floater itself.
      aResult.x = borderPadding.left;
      aResult.width = mUnconstrainedWidth
        ? NS_UNCONSTRAINEDSIZE
        : mContentArea.width;
    }
  }
  else {
    // The frame is clueless about the space manager and therefore we
    // only give it free space. An example is a table frame - the
    // tables do not flow around floaters.
    aResult.x = mAvailSpaceRect.x + borderPadding.left;
    aResult.width = mAvailSpaceRect.width;
  }
#ifdef REALLY_NOISY_REFLOW
  printf("  CBAS: result %d %d %d %d\n", aResult.x, aResult.y, aResult.width, aResult.height);
#endif
}

PRBool
nsBlockReflowState::ClearPastFloaters(PRUint8 aBreakType)
{
  nscoord saveY, deltaY;

  PRBool applyTopMargin = PR_FALSE;
  switch (aBreakType) {
  case NS_STYLE_CLEAR_LEFT:
  case NS_STYLE_CLEAR_RIGHT:
  case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
    // Apply the previous margin before clearing
    saveY = mY + mPrevBottomMargin;
    ClearFloaters(saveY, aBreakType);
#ifdef NOISY_FLOATER_CLEARING
    nsFrame::ListTag(stdout, mBlock);
    printf(": ClearPastFloaters: mPrevBottomMargin=%d saveY=%d oldY=%d newY=%d deltaY=%d\n",
           mPrevBottomMargin, saveY, saveY - mPrevBottomMargin, mY,
           mY - saveY);
#endif

    // Determine how far we just moved. If we didn't move then there
    // was nothing to clear to don't mess with the normal margin
    // collapsing behavior. In either case we need to restore the Y
    // coordinate to what it was before the clear.
    deltaY = mY - saveY;
    if (0 != deltaY) {
      // Pretend that the distance we just moved is a previous
      // blocks bottom margin. Note that GetAvailableSpace has been
      // done so that the available space calculations will be done
      // after clearing the appropriate floaters.
      //
      // What we are doing here is applying CSS2 section 9.5.2's
      // rules for clearing - "The top margin of the generated box
      // is increased enough that the top border edge is below the
      // bottom outer edge of the floating boxes..."
      //
      // What this will do is cause the top-margin of the block
      // frame we are about to reflow to be collapsed with that
      // distance.
      mPrevBottomMargin = deltaY;
      mY = saveY;

      // Force margin to be applied in this circumstance
      applyTopMargin = PR_TRUE;
    }
    else {
      // Put aState.mY back to its original value since no clearing
      // happened. That way the previous blocks bottom margin will
      // be applied properly.
      mY = saveY - mPrevBottomMargin;
    }
    break;
  }
  return applyTopMargin;
}

// Recover the collapsed vertical margin values for aLine. Note that
// the values are not collapsed with aState.mPrevBottomMargin, nor are
// they collapsed with each other when the line height is zero.
void
nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine,
                                           PRBool aApplyTopMargin,
                                           nscoord* aTopMarginResult,
                                           nscoord* aBottomMarginResult)
{
  if (aLine->IsBlock()) {
    // Update band data
    GetAvailableSpace();

    // Setup reflow state to compute the block childs top and bottom
    // margins
    nsIFrame* frame = aLine->mFirstChild;
    nsRect availSpaceRect;
    const nsStyleDisplay* display;
    frame->GetStyleData(eStyleStruct_Display,
                        (const nsStyleStruct*&) display);
    nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
    frame->IsSplittable(splitType);
    ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect);
    nsSize availSpace(availSpaceRect.width, availSpaceRect.height);
    nsHTMLReflowState reflowState(mPresContext, mReflowState,
                                  frame, availSpace);

    // Compute collapsed top margin
    nscoord topMargin = 0;
    if (aApplyTopMargin) {
      topMargin =
        nsBlockReflowContext::ComputeCollapsedTopMargin(mPresContext,
                                                        reflowState);
    }

    // Compute collapsed bottom margin
    nscoord bottomMargin = reflowState.mComputedMargin.bottom;
    bottomMargin =
      nsBlockReflowContext::MaxMargin(bottomMargin,
                                      aLine->GetCarriedOutBottomMargin());
    *aTopMarginResult = topMargin;
    *aBottomMarginResult = bottomMargin;
  }
  else {
    // XXX_ib
    *aTopMarginResult = 0;
    *aBottomMarginResult = 0;
  }
}

void
nsBlockReflowState::RecoverStateFrom(nsLineBox* aLine,
                                     PRBool aApplyTopMargin,
                                     nscoord aDeltaY,
                                     nsRect* aDamageRect)
{
  // Make the line being recovered the current line
  mCurrentLine = aLine;

  // Update aState.mPrevChild as if we had reflowed all of the frames
  // in this line.
  mPrevChild = aLine->LastChild();

  // Recover mKidXMost and mMaxElementSize
  nscoord xmost = aLine->mBounds.XMost();
  if (xmost > mKidXMost) {
#ifdef DEBUG
    if (CRAZY_WIDTH(xmost)) {
      nsFrame::ListTag(stdout, mBlock);
      printf(": WARNING: xmost:%d\n", xmost);
    }
#endif
    mKidXMost = xmost;
  }
  if (mComputeMaxElementSize) {
    UpdateMaxElementSize(nsSize(aLine->mMaxElementWidth, aLine->mBounds.height));
  }

  // If computing the maximum width, then update mMaximumWidth
  if (mComputeMaximumWidth) {
    UpdateMaximumWidth(aLine->mMaximumWidth);
  }

  // The line may have clear before semantics.
  if (aLine->IsBlock() && aLine->HasBreak()) {
    // Clear past floaters before the block if the clear style is not none
    aApplyTopMargin = ClearPastFloaters(aLine->GetBreakType());
#ifdef NOISY_VERTICAL_MARGINS
    nsFrame::ListTag(stdout, mBlock);
    printf(": RecoverStateFrom: y=%d child ", mY);
    nsFrame::ListTag(stdout, aLine->mFirstChild);
    printf(" has clear of %d => %s, mPrevBottomMargin=%d\n", aLine->mBreakType,
           aApplyTopMargin ? "applyTopMargin" : "nope", mPrevBottomMargin);
#endif
  }

  // Recover mPrevBottomMargin and calculate the line's new Y
  // coordinate (newLineY)
  nscoord newLineY = mY;
  nsRect lineCombinedArea;
  aLine->GetCombinedArea(&lineCombinedArea);
  if (aLine->IsBlock()) {
    if ((0 == aLine->mBounds.height) && (0 == lineCombinedArea.height)) {
      if (nsBlockReflowContext::IsHTMLParagraph(aLine->mFirstChild)) {
        // Empty HTML paragraphs disappear entirely - their margins go
        // to zero. Therefore we leave mPrevBottomMargin alone.
      }
      else {
        // The line's top and bottom margin values need to be collapsed
        // with the mPrevBottomMargin to determine a new
        // mPrevBottomMargin value.
        nscoord topMargin, bottomMargin;
        RecoverVerticalMargins(aLine, aApplyTopMargin,
                               &topMargin, &bottomMargin);
        nscoord m = nsBlockReflowContext::MaxMargin(bottomMargin,
                                                    mPrevBottomMargin);
        m = nsBlockReflowContext::MaxMargin(m, topMargin);
        mPrevBottomMargin = m;
      }
    }
    else {
      // Recover the top and bottom margins for this line
      nscoord topMargin, bottomMargin;
      RecoverVerticalMargins(aLine, aApplyTopMargin,
                             &topMargin, &bottomMargin);

      // Compute the collapsed top margin value
      nscoord collapsedTopMargin =
        nsBlockReflowContext::MaxMargin(topMargin, mPrevBottomMargin);

      // The lineY is just below the collapsed top margin value. The
      // mPrevBottomMargin gets set to the bottom margin value for the
      // line.
      newLineY += collapsedTopMargin;
      mPrevBottomMargin = bottomMargin;
    }
  }
  else if (0 == aLine->GetHeight()) {
    // For empty inline lines we leave the previous bottom margin
    // alone so that it's collpased with the next line.
  }
  else {
    // For non-empty inline lines the previous margin is applied
    // before the line. Therefore apply it now and zero it out.
    newLineY += mPrevBottomMargin;
    mPrevBottomMargin = 0;
  }

  // Save away the old combined area for later
  nsRect oldCombinedArea = lineCombinedArea;

  // Slide the frames in the line by the computed delta. This also
  // updates the lines Y coordinate and the combined area's Y
  // coordinate.
  nscoord finalDeltaY = newLineY - aLine->mBounds.y;
  mBlock->SlideLine(*this, aLine, finalDeltaY);

  // Place floaters for this line into the space manager
  if (aLine->HasFloaters()) {
    // Undo border/padding translation since the nsFloaterCache's
    // coordinates are relative to the frame not relative to the
    // border/padding.
    const nsMargin& bp = BorderPadding();
    mSpaceManager->Translate(-bp.left, -bp.top);

    // Place the floaters into the space-manager again. Also slide
    // them, just like the regular frames on the line.
    nsRect r;
    nsFloaterCache* fc = aLine->GetFirstFloater();
    while (fc) {
      fc->mRegion.y += finalDeltaY;
      fc->mCombinedArea.y += finalDeltaY;
      nsIFrame* floater = fc->mPlaceholder->GetOutOfFlowFrame();
      floater->GetRect(r);
      floater->MoveTo(mPresContext, r.x, r.y + finalDeltaY);
#ifdef DEBUG
      if (gNoisyReflow || gNoisySpaceManager) {
        nscoord tx, ty;
        mSpaceManager->GetTranslation(tx, ty);
        nsFrame::IndentBy(stdout, gNoiseIndent);
        printf("RecoverState: txy=%d,%d (%d,%d) ",
               tx, ty, mSpaceManagerX, mSpaceManagerY);
        nsFrame::ListTag(stdout, floater);
        printf(" r.y=%d finalDeltaY=%d (sum=%d) region={%d,%d,%d,%d}\n",
               r.y, finalDeltaY, r.y + finalDeltaY,
               fc->mRegion.x, fc->mRegion.y,
               fc->mRegion.width, fc->mRegion.height);
      }
#endif
      mSpaceManager->AddRectRegion(floater, fc->mRegion);
      fc = fc->Next();
    }
#ifdef DEBUG
    if (gNoisyReflow || gNoisySpaceManager) {
      mSpaceManager->List(stdout);
    }
#endif
    // And then put the translation back again
    mSpaceManager->Translate(bp.left, bp.top);
  }

  // Recover mY
  mY = aLine->mBounds.YMost();

  // Compute the damage area
  if (aDamageRect) {
    if (0 == finalDeltaY) {
      aDamageRect->Empty();
    } else {
      aLine->GetCombinedArea(&lineCombinedArea);
      aDamageRect->UnionRect(oldCombinedArea, lineCombinedArea);
    }
  }

  // It's possible that the line has clear after semantics
  if (!aLine->IsBlock() && aLine->HasBreak()) {
    PRUint8 breakType = aLine->GetBreakType();
    switch (breakType) {
    case NS_STYLE_CLEAR_LEFT:
    case NS_STYLE_CLEAR_RIGHT:
    case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
      ClearFloaters(mY, breakType);
      break;
    }
  }
}

//----------------------------------------------------------------------

const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID;

nsresult
NS_NewBlockFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aFlags)
{
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
  if (nsnull == aNewFrame) {
    return NS_ERROR_NULL_POINTER;
  }
  nsBlockFrame* it = new (aPresShell) nsBlockFrame;
  if (nsnull == it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  it->SetFlags(aFlags);
  *aNewFrame = it;
  return NS_OK;
}

nsBlockFrame::nsBlockFrame()
{
#ifdef DEBUG
  InitDebugFlags();
#endif
}

nsBlockFrame::~nsBlockFrame()
{
  nsTextRun::DeleteTextRuns(mTextRuns);
}

NS_IMETHODIMP
nsBlockFrame::Destroy(nsIPresContext* aPresContext)
{
  // Outside bullets are not in our child-list so check for them here
  // and delete them when present.
  if (HaveOutsideBullet()) {
    mBullet->Destroy(aPresContext);
    mBullet = nsnull;
  }

  mFloaters.DestroyFrames(aPresContext);

  nsLineBox::DeleteLineList(aPresContext, mLines);

  return nsBlockFrameSuper::Destroy(aPresContext);
}

NS_IMETHODIMP
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
  if (NULL == aInstancePtr) {
    return NS_ERROR_NULL_POINTER;
  }
  if (aIID.Equals(kBlockFrameCID)) {
    nsBlockFrame* tmp = this;
    *aInstancePtr = (void*) tmp;
    return NS_OK;
  }
  if (aIID.Equals(NS_GET_IID(nsILineIterator))) {
    nsLineIterator* it = new nsLineIterator;
    if (!it) {
      *aInstancePtr = nsnull;
      return NS_ERROR_OUT_OF_MEMORY;
    }
    const nsStyleDisplay* display;
    GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display);
    nsresult rv = it->Init(mLines,
                           display->mDirection == NS_STYLE_DIRECTION_RTL);
    if (NS_FAILED(rv)) {
      delete it;
      return rv;
    }
    return it->QueryInterface(aIID, aInstancePtr);
  }
  return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr);
}

NS_IMETHODIMP
nsBlockFrame::IsSplittable(nsSplittableType& aIsSplittable) const
{
  aIsSplittable = NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
  return NS_OK;
}

#ifdef DEBUG
static void
ListTextRuns(FILE* out, PRInt32 aIndent, nsTextRun* aRuns)
{
  while (nsnull != aRuns) {
    aRuns->List(out, aIndent);
    aRuns = aRuns->GetNext();
  }
}

NS_METHOD
nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const
{
  IndentBy(out, aIndent);
  ListTag(out);
  nsIView* view;
  GetView(aPresContext, &view);
  if (nsnull != view) {
    fprintf(out, " [view=%p]", view);
  }
  if (nsnull != mNextSibling) {
    fprintf(out, " next=%p", mNextSibling);
  }

  // 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
  fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
  if (0 != mState) {
    fprintf(out, " [state=%08x]", mState);
  }
  PRInt32 numInlineLines = 0;
  PRInt32 numBlockLines = 0;
  if (nsnull != mLines) {
    nsLineBox* line = mLines;
    while (nsnull != line) {
      if (line->IsBlock()) {
        numBlockLines++;
      }
      else {
        numInlineLines++;
      }
      line = line->mNext;
    }
  }
  fprintf(out, " sc=%p(i=%d,b=%d)<\n", mStyleContext, numInlineLines, numBlockLines);
  aIndent++;

  // Output the lines
  if (nsnull != mLines) {
    nsLineBox* line = mLines;
    while (nsnull != line) {
      line->List(aPresContext, out, aIndent);
      line = line->mNext;
    }
  }

  nsIAtom* listName = nsnull;
  PRInt32 listIndex = 0;
  for (;;) {
    nsIFrame* kid;
    GetAdditionalChildListName(listIndex++, &listName);
    if (nsnull == listName) {
      break;
    }
    FirstChild(aPresContext, listName, &kid);
    if (nsnull != kid) {
      IndentBy(out, aIndent);
      nsAutoString tmp;
      if (nsnull != listName) {
        listName->ToString(tmp);
        fputs(tmp, out);
      }
      fputs("<\n", out);
      while (nsnull != kid) {
        nsIFrameDebug*  frameDebug;

        if (NS_SUCCEEDED(kid->QueryInterface(NS_GET_IID(nsIFrameDebug), (void**)&frameDebug))) {
          frameDebug->List(aPresContext, out, aIndent + 1);
        }
        kid->GetNextSibling(&kid);
      }
      IndentBy(out, aIndent);
      fputs(">\n", out);
    }
    NS_IF_RELEASE(listName);
  }

  // Output the text-runs
  if (gListTextRuns && mTextRuns) {
    IndentBy(out, aIndent);
    fputs("text-runs <\n", out);

    ListTextRuns(out, aIndent + 1, mTextRuns);

    IndentBy(out, aIndent);
    fputs(">\n", out);
  }

  aIndent--;
  IndentBy(out, aIndent);
  fputs(">\n", out);

  return NS_OK;
}

NS_IMETHODIMP
nsBlockFrame::GetFrameName(nsString& aResult) const
{
  return MakeFrameName("Block", aResult);
}
#endif

NS_IMETHODIMP
nsBlockFrame::GetFrameType(nsIAtom** aType) const
{
  NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer");
  *aType = nsLayoutAtoms::blockFrame; 
  NS_ADDREF(*aType);
  return NS_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Child frame enumeration

NS_IMETHODIMP
nsBlockFrame::FirstChild(nsIPresContext* aPresContext,
                         nsIAtom*        aListName,
                         nsIFrame**      aFirstChild) const
{
  NS_PRECONDITION(nsnull != aFirstChild, "null OUT parameter pointer");
  if (nsnull == aListName) {
    *aFirstChild = (nsnull != mLines) ? mLines->mFirstChild : nsnull;
    return NS_OK;
  }
  else if (aListName == nsLayoutAtoms::overflowList) {
    nsLineBox*  overflowLines = GetOverflowLines(aPresContext, PR_FALSE);
    *aFirstChild = overflowLines ? overflowLines->mFirstChild : nsnull;
    return NS_OK;
  }
  else if (aListName == nsLayoutAtoms::floaterList) {
    *aFirstChild = mFloaters.FirstChild();
    return NS_OK;
  }
  else if (aListName == nsLayoutAtoms::bulletList) {
    if (HaveOutsideBullet()) {
      *aFirstChild = mBullet;
    }
    else {
      *aFirstChild = nsnull;
    }
    return NS_OK;
  }
  *aFirstChild = nsnull;
  return NS_ERROR_INVALID_ARG;
}

NS_IMETHODIMP
nsBlockFrame::GetAdditionalChildListName(PRInt32   aIndex,
                                         nsIAtom** aListName) const
{
  NS_PRECONDITION(nsnull != aListName, "null OUT parameter pointer");
  if (aIndex < 0) {
    return NS_ERROR_INVALID_ARG;
  }
  *aListName = nsnull;
  switch (aIndex) {
  case NS_BLOCK_FRAME_FLOATER_LIST_INDEX:
    *aListName = nsLayoutAtoms::floaterList;
    NS_ADDREF(*aListName);
    break;
  case NS_BLOCK_FRAME_BULLET_LIST_INDEX:
    *aListName = nsLayoutAtoms::bulletList;
    NS_ADDREF(*aListName);
    break;
  }
  return NS_OK;
}

NS_IMETHODIMP
nsBlockFrame::IsPercentageBase(PRBool& aBase) const
{
  aBase = PR_TRUE;
  return NS_OK;
}

//////////////////////////////////////////////////////////////////////
// Frame structure methods

//////////////////////////////////////////////////////////////////////
// Reflow methods

NS_IMETHODIMP
nsBlockFrame::Reflow(nsIPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aMetrics,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus)
{
#ifdef DEBUG
  if (gNoisyReflow) {
    IndentBy(stdout, gNoiseIndent);
    ListTag(stdout);
    printf(": begin reflow type %d availSize=%d,%d computedSize=%d,%d\n",
           aReflowState.reason, aReflowState.availableWidth, aReflowState.availableHeight,
           aReflowState.mComputedWidth, aReflowState.mComputedHeight);
  }
  if (gNoisy) {
    gNoiseIndent++;
  }
  PRTime start;
  PRInt32 ctc;
  if (gLameReflowMetrics) {
    start = PR_Now();
    ctc = nsLineBox::GetCtorCount();
  }
#endif

  if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
#ifdef DEBUG_kipp
    {
      extern char* nsPresShell_ReflowStackPointerTop;
      char marker;
      char* newsp = (char*) &marker;
      printf("XXX: frame tree is too deep; approx stack size = %d\n",
             nsPresShell_ReflowStackPointerTop - newsp);
    }
#endif
    aStatus = NS_FRAME_COMPLETE;
    return NS_OK;
  }

  // Should we create a space manager?
  nsCOMPtr<nsISpaceManager> spaceManager;
  nsISpaceManager*          oldSpaceManager = aReflowState.mSpaceManager;
  if (NS_BLOCK_SPACE_MGR & mState) {
    nsSpaceManager* rawPtr = new nsSpaceManager(this);
    if (!rawPtr) {
      return NS_ERROR_OUT_OF_MEMORY;
    }

    spaceManager = do_QueryInterface(rawPtr);

    // Set the space manager in the existing reflow state
    nsHTMLReflowState&  reflowState = (nsHTMLReflowState&)aReflowState;
    reflowState.mSpaceManager = spaceManager.get();
#ifdef NOISY_SPACEMANAGER
    printf("constructed new space manager %p\n", reflowState.mSpaceManager);
#endif
  }

  nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics,
                           NS_BLOCK_MARGIN_ROOT & mState);

  if (eReflowReason_Resize != aReflowState.reason) {
    RenumberLists(aPresContext);
    ComputeTextRuns(aPresContext);
  }

  nsresult rv = NS_OK;
  PRBool isStyleChange = PR_FALSE;
  state.mIsInlineIncrReflow = PR_FALSE;
  nsIFrame* target;
  switch (aReflowState.reason) {
  case eReflowReason_Initial:
#ifdef NOISY_REFLOW_REASON
    ListTag(stdout);
    printf(": reflow=initial\n");
#endif
    DrainOverflowLines(aPresContext);
    rv = PrepareInitialReflow(state);
    mState &= ~NS_FRAME_FIRST_REFLOW;
    break;  

  case eReflowReason_Dirty:    
    break;

  case eReflowReason_Incremental:  // should call GetNext() ?
    aReflowState.reflowCommand->GetTarget(target);
    if (this == target) {
      nsIReflowCommand::ReflowType type;
      aReflowState.reflowCommand->GetType(type);
#ifdef NOISY_REFLOW_REASON
      ListTag(stdout);
      printf(": reflow=incremental type=%d\n", type);
#endif
      switch (type) {
      case nsIReflowCommand::StyleChanged:
        rv = PrepareStyleChangedReflow(state);
        isStyleChange = PR_TRUE;
        break;
      case nsIReflowCommand::ReflowDirty:
        break;
      default:
        // Map any other incremental operations into full reflows
        rv = PrepareResizeReflow(state);
        break;
      }
    }
    else {
      // Get next frame in reflow command chain
      aReflowState.reflowCommand->GetNext(state.mNextRCFrame);
#ifdef NOISY_REFLOW_REASON
      ListTag(stdout);
      printf(": reflow=incremental");
      if (state.mNextRCFrame) {
        printf(" next=");
        nsFrame::ListTag(stdout, state.mNextRCFrame);
      }
      printf("\n");
#endif

#ifdef OLD_BLOCK_INCREMENTAL_REFLOW
      // the old way...
      // in the normal case, this just marks the line dirty
      // but, it causes an incremental reflow targeted at a frame
      // inside a continued span to get dropped on the floor.  see bug 25510
      // This will be yanked as soon as we have high confidence in the enabled code below
      rv = PrepareChildIncrementalReflow(state); 
#else
      // this code does a correct job of propogating incremental reflows (bug 25510)
      // and has the potential to be very efficient.  we should be able to 
      // terminate reflow after the incremental reflow if we can detect that
      // nothing significant has changed.  
      PRBool isFloater; 
      nsLineBox* prevLine; 
      nsLineBox* line = FindLineFor(state.mNextRCFrame, &prevLine, &isFloater); 
      if (line && (PR_FALSE==line->IsBlock())) 
      { 
        if (!isFloater) // punt if isFloater!  
        {
          // reflow the line containing the target of the incr. reflow
          // first mark the line dirty and set up the state object
          rv = PrepareChildIncrementalReflow(state);
          state.mIsInlineIncrReflow = PR_TRUE;
          state.mPrevLine = prevLine;
          state.mCurrentLine = line;
          state.mNextRCFrame = state.mNextRCFrame;
          // let ReflowDirtyLines do all the work
          rv = ReflowDirtyLines(state);
          if (NS_FAILED(rv)) {
            NS_ASSERTION(0, "Reflow failed\n");
            return rv;
          }
          // compute the final size
          ComputeFinalSize(aReflowState, state, aMetrics);

          // finally, mark this block frame as having a dirty child and return
          // XXX: we should be able to optimize this so we only call ReflowDirtyChild
          //      if it's absolutely necessary:  something on the line changed size.
          nsCOMPtr<nsIPresShell> shell;
          aPresContext->GetShell(getter_AddRefs(shell));
          rv = ReflowDirtyChild(shell, state.mNextRCFrame); 
          //XXX: it's possible we need to do some work regarding incremental painting
          //     here, see code below "ReflowDirtyLines() after this switch statement.
          //     It might be right to factor the tail end of this method into a new method
          //     and call that here before calling ReflowDirtyChild().
          return rv;
        } 
      }
      rv = PrepareChildIncrementalReflow(state);
#endif
    }
    break;

  case eReflowReason_StyleChange:
    rv = PrepareStyleChangedReflow(state);
    break;

  case eReflowReason_Resize:
  default:
#ifdef NOISY_REFLOW_REASON
    ListTag(stdout);
    printf(": reflow=resize (%d)\n", aReflowState.reason);
#endif
    DrainOverflowLines(aPresContext);
    rv = PrepareResizeReflow(state);
    break;
  }

  if (NS_FAILED(rv)) {
    NS_ASSERTION(0, "setting up reflow failed.\n");
    return rv;
  }

  // Now reflow...
  rv = ReflowDirtyLines(state);
  if (NS_FAILED(rv)) {
    NS_ASSERTION(0, "reflow dirty lines failed.\n");
    return rv;
  }
  aStatus = state.mReflowStatus;
  if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
    if (NS_STYLE_OVERFLOW_HIDDEN == aReflowState.mStyleDisplay->mOverflow) {
      aStatus = NS_FRAME_COMPLETE;
    }
    else {
#ifdef DEBUG_kipp
      ListTag(stdout); printf(": block is not complete\n");
#endif
    }
  }

  // XXX_pref get rid of this!
  BuildFloaterList();
  
  // Compute our final size
  ComputeFinalSize(aReflowState, state, aMetrics);

  if (NS_BLOCK_WRAP_SIZE & mState) {
    // When the area frame is supposed to wrap around all in-flow
    // children, make sure its big enough to include those that stick
    // outside the box.
    if (NS_FRAME_OUTSIDE_CHILDREN & mState) {
      nscoord xMost = aMetrics.mOverflowArea.XMost();
      if (xMost > aMetrics.width) {
#ifdef NOISY_FINAL_SIZE
        ListTag(stdout);
        printf(": changing desired width from %d to %d\n", aMetrics.width, xMost);
#endif
        aMetrics.width = xMost;
      }
      nscoord yMost = aMetrics.mOverflowArea.YMost();
      if (yMost > aMetrics.height) {
#ifdef NOISY_FINAL_SIZE
        ListTag(stdout);
        printf(": changing desired height from %d to %d\n", aMetrics.height, yMost);
#endif
        aMetrics.height = yMost;
      }
    }
  }

  // see if verifyReflow is enabled, and if so store off the space manager pointer
#ifdef DEBUG
  PRInt32 verifyReflowFlags = nsIPresShell::GetVerifyReflowFlags();
  if (VERIFY_REFLOW_INCLUDE_SPACE_MANAGER & verifyReflowFlags)
  {
    // this is a leak of the space manager, but it's only in debug if verify reflow is enabled, so not a big deal
    nsCOMPtr<nsIPresShell> shell;
    aPresContext->GetShell(getter_AddRefs(shell));
    if (shell) {
      nsCOMPtr<nsIFrameManager>  frameManager;
      shell->GetFrameManager(getter_AddRefs(frameManager));  
      if (frameManager) {
        nsHTMLReflowState&  reflowState = (nsHTMLReflowState&)aReflowState;
        NS_ADDREF(reflowState.mSpaceManager);
        rv = frameManager->SetFrameProperty(this, nsLayoutAtoms::spaceManagerProperty,
                                            reflowState.mSpaceManager, nsnull /* should be nsSpaceManagerDestroyer*/);
      }
    }
  }
#endif

  // If we set the space manager, then restore the old space manager now that we're
  // going out of scope
  if (NS_BLOCK_SPACE_MGR & mState) {
    nsHTMLReflowState&  reflowState = (nsHTMLReflowState&)aReflowState;
#ifdef NOISY_SPACEMANAGER
    printf("restoring old space manager %p\n", oldSpaceManager);
#endif
    reflowState.mSpaceManager = oldSpaceManager;
  }

#ifdef NOISY_SPACEMANAGER
  nsHTMLReflowState&  reflowState = (nsHTMLReflowState&)aReflowState;
  if (reflowState.mSpaceManager) {
    ListTag(stdout);
    printf(": space-manager %p after reflow\n", reflowState.mSpaceManager);
    reflowState.mSpaceManager->List(stdout);
  }
#endif
  
  // If this is an incremental reflow and we changed size, then make sure our
  // border is repainted if necessary
  if (eReflowReason_Incremental == aReflowState.reason ||
      eReflowReason_Dirty == aReflowState.reason) {
    if (isStyleChange) {
      // Lots of things could have changed so damage our entire
      // bounds
      Invalidate(aPresContext, nsRect(0, 0, mRect.width, mRect.height));

    } else {
      nsMargin  border = aReflowState.mComputedBorderPadding -
                         aReflowState.mComputedPadding;
  
      // See if our width changed
      if ((aMetrics.width != mRect.width) && (border.right > 0)) {
        nsRect  damageRect;
  
        if (aMetrics.width < mRect.width) {
          // Our new width is smaller, so we need to make sure that
          // we paint our border in its new position
          damageRect.x = aMetrics.width - border.right;
          damageRect.width = border.right;
          damageRect.y = 0;
          damageRect.height = aMetrics.height;
  
        } else {
          // Our new width is larger, so we need to erase our border in its
          // old position
          damageRect.x = mRect.width - border.right;
          damageRect.width = border.right;
          damageRect.y = 0;
          damageRect.height = mRect.height;
        }
        Invalidate(aPresContext, damageRect);
      }
  
      // See if our height changed
      if ((aMetrics.height != mRect.height) && (border.bottom > 0)) {
        nsRect  damageRect;
        
        if (aMetrics.height < mRect.height) {
          // Our new height is smaller, so we need to make sure that
          // we paint our border in its new position
          damageRect.x = 0;
          damageRect.width = aMetrics.width;
          damageRect.y = aMetrics.height - border.bottom;
          damageRect.height = border.bottom;
  
        } else {
          // Our new height is larger, so we need to erase our border in its
          // old position
          damageRect.x = 0;
          damageRect.width = mRect.width;
          damageRect.y = mRect.height - border.bottom;
          damageRect.height = border.bottom;
        }
        Invalidate(aPresContext, damageRect);
      }
    }
  }

#ifdef DEBUG
  if (gNoisy) {
    gNoiseIndent--;
  }
  if (gNoisyReflow) {
    IndentBy(stdout, gNoiseIndent);
    ListTag(stdout);
    printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
           aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
           aMetrics.width, aMetrics.height,
           aMetrics.mCarriedOutBottomMargin);
    if (mState & NS_FRAME_OUTSIDE_CHILDREN) {
      printf(" combinedArea={%d,%d,%d,%d}",
             aMetrics.mOverflowArea.x,
             aMetrics.mOverflowArea.y,
             aMetrics.mOverflowArea.width,
             aMetrics.mOverflowArea.height);
    }
    if (aMetrics.maxElementSize) {
      printf(" maxElementSize=%d,%d",
             aMetrics.maxElementSize->width,
             aMetrics.maxElementSize->height);
    }
    printf("\n");
  }

  if (gLameReflowMetrics) {
    PRTime end = PR_Now();

    PRInt32 ectc = nsLineBox::GetCtorCount();
    PRInt32 numLines = nsLineBox::ListLength(mLines);
    if (!numLines) numLines = 1;
    PRTime delta, perLineDelta, lines;
    LL_I2L(lines, numLines);
    LL_SUB(delta, end, start);
    LL_DIV(perLineDelta, delta, lines);

    ListTag(stdout);
    char buf[400];
    PR_snprintf(buf, sizeof(buf),
                ": %lld elapsed (%lld per line) (%d lines; %d new lines)",
                delta, perLineDelta, numLines, ectc - ctc);
    printf("%s\n", buf);
  }
#endif
  return rv;
}

static PRBool
HaveAutoWidth(const nsHTMLReflowState& aReflowState)
{
  const nsHTMLReflowState* rs = &aReflowState;
  if (NS_UNCONSTRAINEDSIZE == rs->mComputedWidth) {
    return PR_TRUE;
  }
  const nsStylePosition* pos = rs->mStylePosition;

  for (;;) {
    if (!pos) {
      return PR_TRUE;
    }
    nsStyleUnit widthUnit = pos->mWidth.GetUnit();
    if (eStyleUnit_Auto == widthUnit) {
      return PR_TRUE;
    }
    if (eStyleUnit_Inherit != widthUnit) {
      break;
    }
    const nsHTMLReflowState* prs = (const nsHTMLReflowState*)
      rs->parentReflowState;
    if (!prs) {
      return PR_TRUE;
    }
    rs = prs;
    pos = prs->mStylePosition;
  }

  return PR_FALSE;
}


static PRBool
IsPercentageUnitSides(const nsStyleSides* aSides)
{
  return eStyleUnit_Percent == aSides->GetLeftUnit()
      || eStyleUnit_Percent == aSides->GetRightUnit()
      || eStyleUnit_Percent == aSides->GetTopUnit()
      || eStyleUnit_Percent == aSides->GetBottomUnit();
}

static PRBool
IsPercentageAwareChild(const nsIFrame* aFrame)
{
  const nsStyleSpacing* space;
  nsresult rv = aFrame->GetStyleData(eStyleStruct_Spacing,(const nsStyleStruct*&) space);
  if (NS_FAILED(rv)) {
    return PR_TRUE; // just to be on the safe side
  }

  if (IsPercentageUnitSides(&space->mMargin)
    || IsPercentageUnitSides(&space->mPadding)
    || IsPercentageUnitSides(&space->mBorderRadius)) {
    return PR_TRUE;
  }

  const nsStylePosition* pos;
  rv = aFrame->GetStyleData(eStyleStruct_Position,(const nsStyleStruct*&) pos);
  if (NS_FAILED(rv)) {
    return PR_TRUE; // just to be on the safe side
  }

  if (eStyleUnit_Percent == pos->mWidth.GetUnit()
    || eStyleUnit_Percent == pos->mMaxWidth.GetUnit()
    || eStyleUnit_Percent == pos->mMinWidth.GetUnit()
    || eStyleUnit_Percent == pos->mHeight.GetUnit()
    || eStyleUnit_Percent == pos->mMinHeight.GetUnit()
    || eStyleUnit_Percent == pos->mMaxHeight.GetUnit()
    || IsPercentageUnitSides(&pos->mOffset)) { // XXX need more here!!!
    return PR_TRUE;
  }

  return PR_FALSE;
}
        
void
nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
                               nsBlockReflowState& aState,
                               nsHTMLReflowMetrics& aMetrics)
{
  const nsMargin& borderPadding = aState.BorderPadding();
#ifdef NOISY_FINAL_SIZE
  ListTag(stdout);
  printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n",
         aState.mY, aState.mIsBottomMarginRoot ? "yes" : "no",
         aState.mPrevBottomMargin,
         borderPadding.top, borderPadding.bottom);
#endif

  // Special check for zero sized content: If our content is zero
  // sized then we collapse into nothingness.
  //
  // Consensus after discussion with a few CSS folks is that html's
  // notion of collapsing <P>'s should take precedence over non
  // auto-sided block elements. Therefore we don't honor the width,
  // height, border or padding attributes (the parent has to not apply
  // a margin for us also).
  //
  // Note that this is <b>only</b> done for html paragraphs. Its not
  // appropriate to apply it to other containers, especially XML
  // content!
  PRBool isHTMLParagraph = 0 != (mState & NS_BLOCK_IS_HTML_PARAGRAPH);
  if (isHTMLParagraph &&
      (aReflowState.mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_BLOCK) &&
      (((0 == aState.mKidXMost) ||
        (0 == aState.mKidXMost - borderPadding.left)) &&
       (0 == aState.mY - borderPadding.top))) {
    // Zero out most everything
    aMetrics.width = 0;
    aMetrics.height = 0;
    aMetrics.ascent = 0;
    aMetrics.descent = 0;
    aMetrics.mCarriedOutBottomMargin = 0;

    // Note: Don't zero out the max-element-sizes: they will be zero
    // if this is truly empty, otherwise they won't because of a
    // floater.
    if (nsnull != aMetrics.maxElementSize) {
      aMetrics.maxElementSize->width = aState.mMaxElementSize.width;
      aMetrics.maxElementSize->height = aState.mMaxElementSize.height;
    }
  }
  else {
    // Compute final width
    nscoord maxWidth = 0, maxHeight = 0;
    nscoord minWidth = aState.mKidXMost + borderPadding.right;
    if (!HaveAutoWidth(aReflowState)) {
      // Use style defined width
      aMetrics.width = borderPadding.left + aReflowState.mComputedWidth +
        borderPadding.right;
      // XXX quote css1 section here
      if ((0 == aReflowState.mComputedWidth) && (aMetrics.width < minWidth)) {
        aMetrics.width = minWidth;
      }

      // When style defines the width use it for the max-element-size
      // because we can't shrink any smaller.
      maxWidth = aMetrics.width;
    }
    else {
      nscoord computedWidth = minWidth;
      PRBool compact = PR_FALSE;
#if 0
      if (NS_STYLE_DISPLAY_COMPACT == aReflowState.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->GetBreakType())
             /*XXX && (computedWidth <= aState.mCompactMarginWidth) */
              )) {
          compact = PR_TRUE;
        }
      }
#endif

      // There are two options here. We either shrink wrap around our
      // contents or we fluff out to the maximum block width. Note:
      // We always shrink wrap when given an unconstrained width.
      if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) &&
          !aState.mUnconstrainedWidth && !aState.mShrinkWrapWidth &&
          !compact) {
        // Set our width to the max width if we aren't already that
        // wide. Note that the max-width has nothing to do with our
        // contents (CSS2 section XXX)
        computedWidth = borderPadding.left + aState.mContentArea.width +
          borderPadding.right;
      }

      // See if we should compute our max element size
      if (aState.mComputeMaxElementSize) {
        // Adjust the computedWidth
        if (aState.mNoWrap) {
          // When no-wrap is true the max-element-size.width is the
          // width of the widest line plus the right border. Note that
          // aState.mKidXMost already has the left border factored in
          maxWidth = aState.mKidXMost + borderPadding.right;
        }
        else {
          // Add in border and padding dimensions to already computed
          // max-element-size values.
          maxWidth = aState.mMaxElementSize.width +
            borderPadding.left + borderPadding.right;
        }
        if (computedWidth < maxWidth) {
          computedWidth = maxWidth;
        }
      }

      // Apply min/max values
      if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) {
        nscoord computedMaxWidth = aReflowState.mComputedMaxWidth +
          borderPadding.left + borderPadding.right;
        if (computedWidth > computedMaxWidth) {
          computedWidth = computedMaxWidth;
        }
      }
      if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) {
        nscoord computedMinWidth = aReflowState.mComputedMinWidth +
          borderPadding.left + borderPadding.right;
        if (computedWidth < computedMinWidth) {
          computedWidth = computedMinWidth;
        }
      }
      aMetrics.width = computedWidth;

      // If we're shrink wrapping, then now that we know our final width we
      // need to do horizontal alignment of the inline lines and make sure
      // blocks are correctly sized and positioned. Any lines that need
      // final adjustment will have been marked as dirty
      if (aState.mShrinkWrapWidth && aState.mNeedResizeReflow) {
        // If the parent reflow state is also shrink wrap width, then
        // we don't need to do this, because it will reflow us after it
        // calculates the final width
        PRBool  parentIsShrinkWrapWidth = PR_FALSE;
        if (aReflowState.parentReflowState) {
          if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) {
            parentIsShrinkWrapWidth = PR_TRUE;
          }
        }

        if (!parentIsShrinkWrapWidth) {
          nsHTMLReflowState reflowState(aReflowState);
  
          reflowState.mComputedWidth = aMetrics.width - borderPadding.left -
                                       borderPadding.right;
          reflowState.reason = eReflowReason_Resize;
          reflowState.mSpaceManager->ClearRegions();
  
          nscoord oldDesiredWidth = aMetrics.width;
          nsBlockReflowState state(reflowState, aState.mPresContext, this, aMetrics,
                                   NS_BLOCK_MARGIN_ROOT & mState);
          ReflowDirtyLines(state);
          aState.mY = state.mY;
          NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width");
        }
      }
    }

    if (aState.mShrinkWrapWidth) {
      PRBool  parentIsShrinkWrapWidth = PR_FALSE;
      if (aReflowState.parentReflowState) {
        if (NS_SHRINKWRAPWIDTH == aReflowState.parentReflowState->mComputedWidth) {
          parentIsShrinkWrapWidth = PR_TRUE;
        }
      }
    }

    // Compute final height
    if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) {
      // Use style defined height
      aMetrics.height = borderPadding.top + aReflowState.mComputedHeight +
        borderPadding.bottom;

      // When style defines the height use it for the max-element-size
      // because we can't shrink any smaller.
      maxHeight = aMetrics.height;

      // Don't carry out a bottom margin when our height is fixed
      // unless the bottom of the last line adjoins the bottom of our
      // content area.
      if (!aState.mIsBottomMarginRoot) {
        if (aState.mY + aState.mPrevBottomMargin != aMetrics.height) {
          aState.mPrevBottomMargin = 0;
        }
      }
    }
    else {
      nscoord autoHeight = aState.mY;

      // Shrink wrap our height around our contents.
      if (aState.mIsBottomMarginRoot) {
        // When we are a bottom-margin root make sure that our last
        // childs bottom margin is fully applied.
        // XXX check for a fit
        autoHeight += aState.mPrevBottomMargin;
      }
      autoHeight += borderPadding.bottom;

      // Apply min/max values
      if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxHeight) {
        nscoord computedMaxHeight = aReflowState.mComputedMaxHeight +
          borderPadding.top + borderPadding.bottom;
        if (autoHeight > computedMaxHeight) {
          autoHeight = computedMaxHeight;
        }
      }
      if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) {
        nscoord computedMinHeight = aReflowState.mComputedMinHeight +
          borderPadding.top + borderPadding.bottom;
        if (autoHeight < computedMinHeight) {
          autoHeight = computedMinHeight;
        }
      }
      aMetrics.height = autoHeight;

      if (aState.mComputeMaxElementSize) {
        maxHeight = aState.mMaxElementSize.height +
          borderPadding.top + borderPadding.bottom;
      }
    }

    aMetrics.ascent = aMetrics.height;
    aMetrics.descent = 0;
    if (aState.mComputeMaxElementSize) {
      // Store away the final value
      aMetrics.maxElementSize->width = maxWidth;
      aMetrics.maxElementSize->height = maxHeight;
    }

    // Return bottom margin information
    aMetrics.mCarriedOutBottomMargin =
      aState.mIsBottomMarginRoot ? 0 : aState.mPrevBottomMargin;

#ifdef DEBUG
    if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
      ListTag(stdout);
      printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height);
    }
    if (aState.mComputeMaxElementSize &&
        ((maxWidth > aMetrics.width) || (maxHeight > aMetrics.height))) {
      ListTag(stdout);
      printf(": WARNING: max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n",
             maxWidth, maxHeight, aMetrics.width, aMetrics.height,
             aState.mReflowState.availableWidth,
             aState.mReflowState.availableHeight);
    }
#endif
#ifdef NOISY_MAX_ELEMENT_SIZE
    if (aState.mComputeMaxElementSize) {
      IndentBy(stdout, GetDepth());
      if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) {
        printf("PASS1 ");
      }
      ListTag(stdout);
      printf(": max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n",
             maxWidth, maxHeight, aMetrics.width, aMetrics.height,
             aState.mReflowState.availableWidth,
             aState.mReflowState.availableHeight);
    }
#endif
  }

  // If we're requested to update our maximum width, then compute it
  if (aState.mComputeMaximumWidth) {
    // We need to add in for the right border/padding
    aMetrics.mMaximumWidth = aState.mMaximumWidth + borderPadding.right;
  }

  // Compute the combined area of our children
// XXX_perf: This can be done incrementally
  nscoord xa = 0, ya = 0, xb = aMetrics.width, yb = aMetrics.height;
  if (NS_STYLE_OVERFLOW_HIDDEN != aReflowState.mStyleDisplay->mOverflow) {
    nsLineBox* line = mLines;
    while (nsnull != line) {
      // Compute min and max x/y values for the reflowed frame's
      // combined areas
      nsRect lineCombinedArea;
      line->GetCombinedArea(&lineCombinedArea);
      nscoord x = lineCombinedArea.x;
      nscoord y = lineCombinedArea.y;
      nscoord xmost = x + lineCombinedArea.width;
      nscoord ymost = y + lineCombinedArea.height;
      if (x < xa) {
        xa = x;
      }
      if (xmost > xb) {
        xb = xmost;
      }
      if (y < ya) {
        ya = y;
      }
      if (ymost > yb) {
        yb = ymost;
      }
      line = line->mNext;
    }

    // Factor the bullet in; normally the bullet will be factored into
    // the line-box's combined area. However, if the line is a block
    // line then it won't; if there are no lines, it won't. So just
    // factor it in anyway (it can't hurt if it was already done).
    if (mBullet) {
      nsRect r;
      mBullet->GetRect(r);
      if (r.x < xa) xa = r.x;
      if (r.y < ya) ya = r.y;
      nscoord xmost = r.XMost();
      if (xmost > xb) xb = xmost;
      nscoord ymost = r.YMost();
      if (ymost > yb) yb = ymost;
    }
  }
#ifdef NOISY_COMBINED_AREA
  ListTag(stdout);
  printf(": ca=%d,%d,%d,%d\n", xa, ya, xb-xa, yb-ya);
#endif

  // If the combined area of our children exceeds our bounding box
  // then set the NS_FRAME_OUTSIDE_CHILDREN flag, otherwise clear it.
  aMetrics.mOverflowArea.x = xa;
  aMetrics.mOverflowArea.y = ya;
  aMetrics.mOverflowArea.width = xb - xa;
  aMetrics.mOverflowArea.height = yb - ya;
  if ((aMetrics.mOverflowArea.x < 0) ||
      (aMetrics.mOverflowArea.y < 0) ||
      (aMetrics.mOverflowArea.XMost() > aMetrics.width) ||
      (aMetrics.mOverflowArea.YMost() > aMetrics.height)) {
    mState |= NS_FRAME_OUTSIDE_CHILDREN;
  }
  else {
    mState &= ~NS_FRAME_OUTSIDE_CHILDREN;
  }
}

nsresult
nsBlockFrame::PrepareInitialReflow(nsBlockReflowState& aState)
{
  PrepareResizeReflow(aState);
  return NS_OK;
}

nsresult
nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
{
  // Determine the line being impacted
  PRBool isFloater;
  nsLineBox* prevLine;
  nsLineBox* line = FindLineFor(aState.mNextRCFrame, &prevLine, &isFloater);
  if (nsnull == line) {
    // This can't happen, but just in case it does...
    return PrepareResizeReflow(aState);
  }

  // XXX: temporary: If the child frame is a floater then punt
  if (isFloater) {
    return PrepareResizeReflow(aState);
  }

  // Figure out which line to mark dirty.
  MarkLineDirty(line, prevLine);

  return NS_OK;
}

nsresult
nsBlockFrame::MarkLineDirty(nsLineBox* aLine, nsLineBox* aPrevLine)
{
  // Mark aLine dirty
  aLine->MarkDirty();
#ifdef DEBUG
  if (gNoisyReflow) {
    IndentBy(stdout, gNoiseIndent);
    ListTag(stdout);
    printf(": mark line %p dirty\n", aLine);
  }
#endif

  // Mark previous line dirty if its an inline line so that it can
  // maybe pullup something from the line just affected.
  // XXX We don't need to do this if aPrevLine ends in a break-after...
  if (aPrevLine && !aPrevLine->IsBlock()) {
    aPrevLine->MarkDirty();
#ifdef DEBUG
    if (gNoisyReflow) {
      IndentBy(stdout, gNoiseIndent);
      ListTag(stdout);
      printf(": mark prev-line %p dirty\n", aPrevLine);
    }
#endif
  }

  return NS_OK;
}

nsresult
nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState)
{
  if (nsnull == mBullet) {
    // Don't bother if there is no bullet
    return NS_OK;
  }
  const nsStyleList* styleList;
  GetStyleData(eStyleStruct_List, (const nsStyleStruct*&) styleList);
  if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) {
    if (HaveOutsideBullet()) {
      // We now have an inside bullet, but used to have an outside
      // bullet.  Adjust the frame line list
      nsLineBox* line = aState.NewLineBox(mBullet, 1, PR_FALSE);
      if (!line) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      line->mNext = mLines;
      mLines = line;
    }
    mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
  }
  else {
    if (!HaveOutsideBullet()) {
      // We now have an outside bullet, but used to have an inside
      // bullet. Take the bullet frame out of the first lines frame
      // list.
      if ((nsnull != mLines) && (mBullet == mLines->mFirstChild)) {
        nsIFrame* next;
        mBullet->GetNextSibling(&next);
        mBullet->SetNextSibling(nsnull);
        PRInt32 count = mLines->GetChildCount() - 1;
        NS_ASSERTION(count >= 0, "empty line w/o bullet");
        mLines->SetChildCount(count);
        if (0 == count) {
          nsLineBox* nextLine = mLines->mNext;
          aState.FreeLineBox(mLines);
          mLines = nextLine;
          if (nsnull != nextLine) {
            nextLine->MarkDirty();
          }
        }
        else {
          mLines->mFirstChild = next;
          mLines->MarkDirty();
        }
      }
    }
    mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
  }
#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
  return NS_OK;
}

nsresult
nsBlockFrame::PrepareStyleChangedReflow(nsBlockReflowState& aState)
{
  nsresult rv = UpdateBulletPosition(aState);

  // Mark everything dirty
  nsLineBox* line = mLines;
  while (nsnull != line) {
    line->MarkDirty();
    line = line->mNext;
  }
  return rv;
}

nsresult
nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
{
  // See if we can try and avoid marking all the lines as dirty
  PRBool  tryAndSkipLines = PR_FALSE;

  // See if this is this a constrained resize reflow
  if ((aState.mReflowState.reason == eReflowReason_Resize) &&
      (NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableWidth)) {

    // If the text is left-aligned, then we try and avoid reflowing the lines
    const nsStyleText* styleText = (const nsStyleText*)
      mStyleContext->GetStyleData(eStyleStruct_Text);

    if ((NS_STYLE_TEXT_ALIGN_LEFT == styleText->mTextAlign) ||
        ((NS_STYLE_TEXT_ALIGN_DEFAULT == styleText->mTextAlign) &&
         (NS_STYLE_DIRECTION_LTR == aState.mReflowState.mStyleDisplay->mDirection))) {
      tryAndSkipLines = PR_TRUE;
    }
  }

#ifdef DEBUG
  if (gDisableResizeOpt) {
    tryAndSkipLines = PR_FALSE;
  }
  if (gNoisyReflow) {
    if (!tryAndSkipLines) {
      const nsStyleText* mStyleText = (const nsStyleText*)
        mStyleContext->GetStyleData(eStyleStruct_Text);
      IndentBy(stdout, gNoiseIndent);
      ListTag(stdout);
      printf(": marking all lines dirty: reason=%d availWidth=%d textAlign=%d\n",
             aState.mReflowState.reason,
             aState.mReflowState.availableWidth,
             mStyleText->mTextAlign);
    }
  }
#endif

  nsLineBox* line = mLines;
  if (tryAndSkipLines) {
    // The line's bounds are relative to the border edge of the frame
    nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left;
     
    if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) {
      newAvailWidth += aState.mReflowState.mComputedMaxWidth;
    } else {
      newAvailWidth += aState.mReflowState.mComputedWidth;
    }

#ifdef DEBUG
    if (gNoisyReflow) {
      IndentBy(stdout, gNoiseIndent);
      ListTag(stdout);
      printf(": trying to avoid marking all lines dirty\n");
    }
#endif
    
    PRBool notWrapping = aState.mNoWrap;
    while (nsnull != line) {

      if (line->IsBlock()) {
        // We have to let child blocks make their own decisions.
        line->MarkDirty();
      }
      else {
        // We can avoid reflowing *some* inline lines in some cases.
#ifdef REALLY_NOISY_REFLOW
      printf("PrepareResizeReflow thinks line %p is %simpacted by floaters\n", 
        line, line->IsImpactedByFloater() ? "" : "not ");
#endif


        if (notWrapping) {
          // When no-wrap is set then the only line-breaking that
          // occurs for inline lines is triggered by BR elements or by
          // newlines. Therefore, we don't need to reflow the line.
        }
        else if ((line->mNext && !line->HasBreak()) ||
                 line->HasFloaters() || line->IsImpactedByFloater() ||
                 line->HasPercentageChild() ||
                 (line->mBounds.XMost() > newAvailWidth)) {
          // When an inline line has:
          //
          //  - a next line and it doesn't end in a break, or
          //  - floaters, or
          //  - is impacted by a floater, or
          //  - is wider than the new available space
          //
          // Then we must reflow it.
          line->MarkDirty();
        }

#ifdef DEBUG
        if (gNoisyReflow && !line->IsDirty() && !notWrapping) {
          IndentBy(stdout, gNoiseIndent + 1);
          printf("skipped: line=%p next=%p %s %s %s%s%s breakType=%d xmost=%d\n",
                 line, line->mNext,
                 line->IsBlock() ? "block" : "inline",
                 aState.mNoWrap ? "no-wrap" : "wrapping",
                 line->HasBreak() ? "has-break " : "",
                 line->HasFloaters() ? "has-floaters " : "",
                 line->IsImpactedByFloater() ? "impacted " : "",
                 line->GetBreakType(),
                 line->mBounds.XMost());
        }
#endif
      }
      line = line->mNext;
    }
  }
  else {
    // Mark everything dirty
    while (nsnull != line) {
      line->MarkDirty();
      line = line->mNext;
    }
  }
  return NS_OK;
}

//----------------------------------------

nsLineBox*
nsBlockFrame::FindLineFor(nsIFrame* aFrame,
                          nsLineBox** aPrevLineResult,
                          PRBool* aIsFloaterResult)
{
  nsLineBox* prevLine = nsnull;
  nsLineBox* line = mLines;
  PRBool isFloater = PR_FALSE;
  while (nsnull != line) {
    if (line->Contains(aFrame)) {
      break;
    }
    if (line->HasFloaters()) {
      nsFloaterCache* fc = line->GetFirstFloater();
      while (fc) {
        if (aFrame == fc->mPlaceholder->GetOutOfFlowFrame()) {
          isFloater = PR_TRUE;
          goto done;
        }
        fc = fc->Next();
      }
    }
    prevLine = line;
    line = line->mNext;
  }

 done:
  *aIsFloaterResult = isFloater;
  *aPrevLineResult = prevLine;
  return line;
}

void
nsBlockFrame::RecoverStateFrom(nsBlockReflowState& aState,
                               nsLineBox* aLine,
                               nscoord aDeltaY,
                               nsRect* aDamageRect)
{
  PRBool applyTopMargin = PR_FALSE;
  if (aLine->IsBlock()) {
    nsIFrame* framePrevInFlow;
    aLine->mFirstChild->GetPrevInFlow(&framePrevInFlow);
    if (nsnull == framePrevInFlow) {
      applyTopMargin = ShouldApplyTopMargin(aState, aLine);
    }
  }

  aState.RecoverStateFrom(aLine, applyTopMargin, aDeltaY, aDamageRect);
}

/**
 * Propogate reflow "damage" from the just reflowed line (aLine) to
 * any subsequent lines that were affected. The only thing that causes
 * damage is a change to the impact that floaters make.
 */
void
nsBlockFrame::PropogateReflowDamage(nsBlockReflowState& aState,
                                    nsLineBox* aLine,
                                    const nsRect& aOldCombinedArea,
                                    nscoord aDeltaY)
{
  // See if the line has a relevant combined area, and if it does if
  // the combined area has changed.
  nsRect lineCombinedArea;
  aLine->GetCombinedArea(&lineCombinedArea);
  if (lineCombinedArea != aLine->mBounds) {
    if (lineCombinedArea != aOldCombinedArea) {
      // The line's combined-area changed. Therefore we need to damage
      // the lines below that were previously (or are now) impacted by
      // the change. It's possible that a floater shrunk or grew so
      // use the larger of the impacted area.
      nscoord newYMost = lineCombinedArea.YMost();
      nscoord oldYMost = aOldCombinedArea.YMost();
      nscoord impactYB = newYMost < oldYMost ? oldYMost : newYMost;
      nscoord impactYA = lineCombinedArea.y;

      // Loop over each subsequent line and mark them dirty if they
      // intersect the impacted area. Note: we cannot stop after the
      // first non-intersecting line because lines might be
      // overlapping because of negative margins.
      nsLineBox* next = aLine->mNext;
      while (nsnull != next) {
        nscoord lineYA = next->mBounds.y + aDeltaY;
        nscoord lineYB = lineYA + next->mBounds.height;
        if ((lineYB >= impactYA) && (lineYA < impactYB)) {
          next->MarkDirty();
        }
        next = next->mNext;
      }
    }
    else {
      // The line's combined area didn't change from last
      // time. Therefore just sliding subsequent lines will work.
      return;
    }
  }

  if (aDeltaY) {
    nsLineBox* next = aLine->mNext;
    while (nsnull != next) {
      if (!next->IsDirty()) {
        // Cases we need to find:
        //
        // 1. the line was impacted by a floater and now isn't
        // 2. the line wasn't impacted by a floater and now is
        //
        //XXXPerf: An optimization: if the line was and is completely
        //impacted by a floater and the floater hasn't changed size,
        //then we don't need to mark the line dirty.
        aState.GetAvailableSpace(next->mBounds.y + aDeltaY);
        PRBool wasImpactedByFloater = next->IsImpactedByFloater();
        PRBool isImpactedByFloater = aState.IsImpactedByFloater() ? PR_TRUE : PR_FALSE;
#ifdef REALLY_NOISY_REFLOW
        printf("nsBlockFrame::PropogateReflowDamage %p was = %d, is=%d\n", 
           this, wasImpactedByFloater, isImpactedByFloater);
#endif
        if (wasImpactedByFloater != isImpactedByFloater) {
          next->MarkDirty();
        }
        else if (isImpactedByFloater) {
          //XXX: Maybe the floater itself changed size?
          if (next->IsBlock()) {
            //XXXPerf
            // Case:
            // It's possible that more/less of the line is impacted by
            // the floater than last time. So reflow.
            next->MarkDirty();
          }
        }
      }
      next = next->mNext;
    }
  }
}

static PRBool
WrappedLinesAreDirty(nsLineBox* aLine)
{
  if (aLine->IsInline()) {
    while (aLine->IsLineWrapped()) {
      aLine = aLine->mNext;
      if (!aLine) {
        break;
      }

      NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line");
      if (aLine->IsDirty()) {
        // we found a continuing line that is dirty
        return PR_TRUE;
      }
    }
  }

  return PR_FALSE;
}

/**
 * Reflow the dirty lines
 */
nsresult
nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
{
  nsresult rv = NS_OK;
  PRBool keepGoing = PR_TRUE;

#ifdef DEBUG
  if (gNoisyReflow) {
    if (aState.mReflowState.reason == eReflowReason_Incremental) {
      nsIReflowCommand::ReflowType type;
      aState.mReflowState.reflowCommand->GetType(type);
      IndentBy(stdout, gNoiseIndent);
      ListTag(stdout);
      printf(": incrementally reflowing dirty lines: type=%s(%d) isInline=%s",
             kReflowCommandType[type], type,
             aState.mIsInlineIncrReflow ? "true" : "false");
    }
    else {
      IndentBy(stdout, gNoiseIndent);
      ListTag(stdout);
      printf(": reflowing dirty lines");
    }
    printf(" computedWidth=%d\n", aState.mReflowState.mComputedWidth);
    gNoiseIndent++;
  }
#endif

  // Check whether this is an incremental reflow
  PRBool  incrementalReflow = aState.mReflowState.reason ==
                              eReflowReason_Incremental ||
                              aState.mReflowState.reason ==
                              eReflowReason_Dirty;
  
  nscoord deltaY = 0;

  // Reflow the lines that are already ours
  aState.mPrevLine = nsnull;
  nsLineBox* line = mLines;
  if (aState.mIsInlineIncrReflow && aState.mNextRCFrame)
  {
    const nsLineBox* incrTargetLine = aState.mCurrentLine;
    aState.mCurrentLine = line;
    aState.mPrevLine = nsnull;
    while (line && (line != incrTargetLine))
    {
      nsRect  damageRect;
      RecoverStateFrom(aState, line, deltaY, incrementalReflow ?
                       &damageRect : 0);
      if (incrementalReflow && !damageRect.IsEmpty()) {
        Invalidate(aState.mPresContext, damageRect);
      }
      aState.mPrevLine = line;
      line = line->mNext;
      aState.AdvanceToNextLine();
    }
  }
  
  while (nsnull != line) {
#ifdef DEBUG
    if (gNoisyReflow) {
      nsRect lca;
      line->GetCombinedArea(&lca);
      IndentBy(stdout, gNoiseIndent);
      printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n",
             line, aState.mY, line->IsDirty() ? "yes" : "no",
             line->mBounds.x, line->mBounds.y,
             line->mBounds.width, line->mBounds.height,
             lca.x, lca.y, lca.width, lca.height,
             deltaY, aState.mPrevBottomMargin);
      gNoiseIndent++;
    }
#endif

    // If we're supposed to update our maximum width, then we'll also need to
    // reflow this line if it's line wrapped and any of the continuing lines
    // are dirty
    if (line->IsDirty() || (aState.mComputeMaximumWidth && ::WrappedLinesAreDirty(line))) {
      // Compute the dirty lines "before" YMost, after factoring in
      // the running deltaY value - the running value is implicit in
      // aState.mY.
      nscoord oldHeight = line->mBounds.height;
      nsRect oldCombinedArea;
      line->GetCombinedArea(&oldCombinedArea);

      // Reflow the dirty line. If it's an incremental reflow, then have
      // it invalidate the dirty area
      rv = ReflowLine(aState, line, &keepGoing, incrementalReflow);
      if (NS_FAILED(rv)) {
        return rv;
      }
      if (!keepGoing) {
        if (0 == line->GetChildCount()) {
          DeleteLine(aState, line);
        }
        break;
      }
      nscoord newHeight = line->mBounds.height;
      deltaY += newHeight - oldHeight;

      // If the next line is clean then check and see if reflowing the
      // current line "damaged" the next line. Damage occurs when the
      // current line contains floaters that intrude upon the
      // subsequent lines.
      nsLineBox* next = line->mNext;
      if ((nsnull != next) && !next->IsDirty()) {
        PropogateReflowDamage(aState, line, oldCombinedArea, deltaY);
      }
    }
    else {
      // XXX what if the slid line doesn't fit because we are in a
      // vertically constrained situation?
      // Recover state as if we reflowed this line
      nsRect  damageRect;
      RecoverStateFrom(aState, line, deltaY, incrementalReflow ?
                       &damageRect : 0);
      if (incrementalReflow && !damageRect.IsEmpty()) {
        Invalidate(aState.mPresContext, damageRect);
      }
    }

#ifdef DEBUG
    if (gNoisyReflow) {
      gNoiseIndent--;
      nsRect lca;
      line->GetCombinedArea(&lca);
      IndentBy(stdout, gNoiseIndent);
      printf("line=%p mY=%d newBounds={%d,%d,%d,%d} newCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n",
             line, aState.mY,
             line->mBounds.x, line->mBounds.y,
             line->mBounds.width, line->mBounds.height,
             lca.x, lca.y, lca.width, lca.height,
             deltaY, aState.mPrevBottomMargin);
    }
#endif

    // If this is an inline frame then its time to stop
    aState.mPrevLine = line;
    line = line->mNext;
    aState.AdvanceToNextLine();
  }

  // Pull data from a next-in-flow if we can
  while (keepGoing && (nsnull != aState.mNextInFlow)) {
    // Grab first line from our next-in-flow
    line = aState.mNextInFlow->mLines;
    if (nsnull == line) {
      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 = line->mNext;
    line->mNext = nsnull;
    if (0 == line->GetChildCount()) {
      // The line is empty. Try the next one.
      NS_ASSERTION(nsnull == line->mFirstChild, "bad empty line");
      aState.FreeLineBox(line);
      continue;
    }

    // XXX move to a subroutine: run-in, overflow, pullframe and this do this
    // Make the children in the line ours.
    nsIFrame* frame = line->mFirstChild;
    nsIFrame* lastFrame = nsnull;
    PRInt32 n = line->GetChildCount();
    while (--n >= 0) {
      frame->SetParent(this);
      // When pushing and pulling frames we need to check for whether any
      // views need to be reparented
      nsHTMLContainerFrame::ReparentFrameView(aState.mPresContext, frame, mNextInFlow, 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 = line;
    }
    else {
      NS_ASSERTION(nsnull == aState.mPrevLine->mNext, "bad aState.mPrevLine");
      aState.mPrevLine->mNext = line;
      aState.mPrevChild->SetNextSibling(line->mFirstChild);
    }

    // Now reflow it and any lines that it makes during it's reflow
    // (we have to loop here because reflowing the line may case a new
    // line to be created; see SplitLine's callers for examples of
    // when this happens).
    while (nsnull != line) {
      rv = ReflowLine(aState, line, &keepGoing, incrementalReflow ?
                      PR_TRUE : PR_FALSE);
      if (NS_FAILED(rv)) {
        return rv;
      }
      if (!keepGoing) {
        if (0 == line->GetChildCount()) {
          DeleteLine(aState, line);
        }
        break;
      }

      // If this is an inline frame then its time to stop
      aState.mPrevLine = line;
      line = line->mNext;
      aState.AdvanceToNextLine();
    }
  }

  // Handle an odd-ball case: a list-item with no lines
  if (mBullet && HaveOutsideBullet() && !mLines) {
    nsHTMLReflowMetrics metrics(nsnull);
    ReflowBullet(aState, metrics);

    // There are no lines so we have to fake up some y motion so that
    // we end up with *some* height.
    aState.mY += metrics.height;
  }

#ifdef DEBUG
  if (gNoisyReflow) {
    gNoiseIndent--;
    IndentBy(stdout, gNoiseIndent);
    ListTag(stdout);
    printf(": done reflowing dirty lines (status=%x)\n",
           aState.mReflowStatus);
  }
#endif

  return rv;
}

void
nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
                         nsLineBox* aLine)
{
  NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line");
  if (0 == aLine->GetChildCount()) {
    if (nsnull == aState.mPrevLine) {
      NS_ASSERTION(aLine == mLines, "huh");
      mLines = nsnull;
    }
    else {
      NS_ASSERTION(aState.mPrevLine->mNext == aLine, "bad prev-line");
      aState.mPrevLine->mNext = aLine->mNext;
    }
    aState.FreeLineBox(aLine);
  }
}

/**
 * Reflow a line. The line will either contain a single block frame
 * or contain 1 or more inline frames. aKeepReflowGoing indicates
 * whether or not the caller should continue to reflow more lines.
 */
nsresult
nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
                         nsLineBox* aLine,
                         PRBool* aKeepReflowGoing,
                         PRBool aDamageDirtyArea)
{
  nsresult rv = NS_OK;

  NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line");

  // Setup the line-layout for the new line
  aState.mCurrentLine = aLine;
  aLine->ClearDirty();

  // Now that we know what kind of line we have, reflow it
  nsRect oldCombinedArea;
  aLine->GetCombinedArea(&oldCombinedArea);

  if (aLine->IsBlock()) {
    rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
    
    // We expect blocks to damage any area inside their bounds that is
    // dirty; however, if the frame changes size or position then we
    // need to do some repainting
    if (aDamageDirtyArea) {
      nsRect lineCombinedArea;
      aLine->GetCombinedArea(&lineCombinedArea);
      if ((oldCombinedArea.x != lineCombinedArea.x) ||
          (oldCombinedArea.y != lineCombinedArea.y)) {
        // The block has moved, and so do be safe we need to repaint
        // XXX We need to improve on this...
        nsRect  dirtyRect;
        dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea);
        Invalidate(aState.mPresContext, dirtyRect);

      } else {
        if (oldCombinedArea.width != lineCombinedArea.width) {
          nsRect  dirtyRect;

          // Just damage the vertical strip that was either added or went
          // away
          dirtyRect.x = PR_MIN(oldCombinedArea.XMost(),
                               lineCombinedArea.XMost());
          dirtyRect.y = lineCombinedArea.y;
          dirtyRect.width = PR_MAX(oldCombinedArea.XMost(),
                                   lineCombinedArea.XMost()) -
                            dirtyRect.x;
          dirtyRect.height = PR_MAX(oldCombinedArea.height,
                                    lineCombinedArea.height);
          Invalidate(aState.mPresContext, dirtyRect);
        }
        if (oldCombinedArea.height != lineCombinedArea.height) {
          nsRect  dirtyRect;
          
          // Just damage the horizontal strip that was either added or went
          // away
          dirtyRect.x = lineCombinedArea.x;
          dirtyRect.y = PR_MIN(oldCombinedArea.YMost(),
                               lineCombinedArea.YMost());
          dirtyRect.width = PR_MAX(oldCombinedArea.width,
                                   lineCombinedArea.width);
          dirtyRect.height = PR_MAX(oldCombinedArea.YMost(),
                                    lineCombinedArea.YMost()) -
                             dirtyRect.y;
          Invalidate(aState.mPresContext, dirtyRect);
        }
      }
    }
  }
  else {
    aLine->SetLineWrapped(PR_FALSE);

    // If we're supposed to update the maximum width, then we'll need to reflow
    // the line with an unconstrained width (which will give us the new maximum
    // width), then we'll reflow it again with the constrained width.
    // We only do this if this is a beginning line, i.e., don't do this for
    // lines associated with content that line wrapped (see ReflowDirtyLines()
    // for details).
    // XXX This approach doesn't work when floaters are involved in which case
    // we'll either need to recover the floater state that applies to the
    // unconstrained reflow or keep it around in a separate space manager...
    PRBool  isBeginningLine = !aState.mPrevLine || !aState.mPrevLine->IsLineWrapped();
    if (aState.mComputeMaximumWidth && isBeginningLine) {
      nscoord oldY = aState.mY;
      nscoord oldPrevBottomMargin = aState.mPrevBottomMargin;
      PRBool  oldUnconstrainedWidth = aState.mUnconstrainedWidth;

      // First reflow the line with an unconstrained width. When doing this
      // we need to set the block reflow state's "mUnconstrainedWidth" variable
      // to PR_TRUE so if we encounter a placeholder and then reflow its
      // associated floater we don't end up resetting the line's right edge and
      // have it think the width is unconstrained...
      aState.mUnconstrainedWidth = PR_TRUE;
      ReflowInlineFrames(aState, aLine, aKeepReflowGoing, PR_TRUE);
      aState.mY = oldY;
      aState.mPrevBottomMargin = oldPrevBottomMargin;
      aState.mUnconstrainedWidth = oldUnconstrainedWidth;

      // Update the line's maximum width
      aLine->mMaximumWidth = aLine->mBounds.XMost();
      aState.UpdateMaximumWidth(aLine->mMaximumWidth);

      // Remove any floaters associated with the line from the space
      // manager
      aLine->RemoveFloatersFromSpaceManager(aState.mSpaceManager);

      // Now reflow the line again this time without having it compute
      // the maximum width or max-element-size.
      // Note: we need to reset both member variables, because the inline
      // code examines mComputeMaxElementSize and if there is a placeholder
      // on this line the code to reflow the floater looks at both...
      nscoord oldComputeMaxElementSize = aState.mComputeMaxElementSize;
      nscoord oldComputeMaximumWidth = aState.mComputeMaximumWidth;
        
      aState.mComputeMaxElementSize = PR_FALSE;
      aState.mComputeMaximumWidth = PR_FALSE;
      rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
      aState.mComputeMaxElementSize = oldComputeMaxElementSize;
      aState.mComputeMaximumWidth = oldComputeMaximumWidth;

    } else {
      rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
    }

    // We don't really know what changed in the line, so use the union
    // of the old and new combined areas
    if (aDamageDirtyArea) {
      nsRect combinedArea;
      aLine->GetCombinedArea(&combinedArea);

      nsRect dirtyRect;
      dirtyRect.UnionRect(oldCombinedArea, combinedArea);
      Invalidate(aState.mPresContext, dirtyRect);
    }
  }

  return rv;
}

/**
 * Pull frame from the next available location (one of our lines or
 * one of our next-in-flows lines).
 */
nsresult
nsBlockFrame::PullFrame(nsBlockReflowState& aState,
                        nsLineBox* aLine,
                        nsIFrame*& aFrameResult)
{
  nsresult rv = NS_OK;
  PRBool stopPulling;
  aFrameResult = nsnull;

  // First check our remaining lines
  while (nsnull != aLine->mNext) {
    rv = PullFrame(aState, aLine, &aLine->mNext, PR_FALSE,
                   aFrameResult, stopPulling);
    if (NS_FAILED(rv) || stopPulling) {
      return rv;
    }
  }

  // Pull frames from the next-in-flow(s) until we can't
  nsBlockFrame* nextInFlow = aState.mNextInFlow;
  while (nsnull != nextInFlow) {
    nsLineBox* line = nextInFlow->mLines;
    if (nsnull == line) {
      nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
      aState.mNextInFlow = nextInFlow;
      continue;
    }
    rv = PullFrame(aState, aLine, &nextInFlow->mLines, PR_TRUE,
                   aFrameResult, stopPulling);
    if (NS_FAILED(rv) || stopPulling) {
      return rv;
    }
  }
  return rv;
}

/**
 * Try to pull a frame out of a line pointed at by aFromList. If a
 * frame is pulled then aPulled will be set to PR_TRUE. In addition,
 * if aUpdateGeometricParent is set then the pulled frames geometric
 * parent will be updated (e.g. when pulling from a next-in-flows line
 * list).
 *
 * Note: pulling a frame from a line that is a place-holder frame
 * doesn't automatically remove the corresponding floater from the
 * line's floater array. This happens indirectly: either the line gets
 * emptied (and destroyed) or the line gets reflowed (because we mark
 * it dirty) and the code at the top of ReflowLine empties the
 * array. So eventually, it will be removed, just not right away.
 */
nsresult
nsBlockFrame::PullFrame(nsBlockReflowState& aState,
                        nsLineBox* aLine,
                        nsLineBox** aFromList,
                        PRBool aUpdateGeometricParent,
                        nsIFrame*& aFrameResult,
                        PRBool& aStopPulling)
{
  nsLineBox* fromLine = *aFromList;
  NS_ABORT_IF_FALSE(fromLine, "bad line to pull from");
  NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line");
  NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line");

  if (fromLine->IsBlock()) {
    // If our line is not empty and the child in aFromLine is a block
    // then we cannot pull up the frame into this line. In this case
    // we stop pulling.
    aStopPulling = PR_TRUE;
    aFrameResult = nsnull;
  }
  else {
    // Take frame from fromLine
    nsIFrame* frame = fromLine->mFirstChild;
    aLine->SetChildCount(aLine->GetChildCount() + 1);

    PRInt32 fromLineChildCount = fromLine->GetChildCount();
    if (0 != --fromLineChildCount) {
      // Mark line dirty now that we pulled a child
      fromLine->SetChildCount(fromLineChildCount);
      fromLine->MarkDirty();
      frame->GetNextSibling(&fromLine->mFirstChild);
    }
    else {
      // Free up the fromLine now that it's empty
      *aFromList = fromLine->mNext;
      aState.FreeLineBox(fromLine);
    }

    // Change geometric parents
    if (aUpdateGeometricParent) {
      // Before we set the new parent frame get the current parent
      nsIFrame* oldParentFrame;
      frame->GetParent(&oldParentFrame);
      frame->SetParent(this);

      // When pushing and pulling frames we need to check for whether any
      // views need to be reparented
      NS_ASSERTION(oldParentFrame != this, "unexpected parent frame");
      nsHTMLContainerFrame::ReparentFrameView(aState.mPresContext, frame, oldParentFrame, 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);
    }

    // Stop pulling because we found a frame to pull
    aStopPulling = PR_TRUE;
    aFrameResult = frame;
#ifdef DEBUG
    VerifyLines(PR_TRUE);
#endif
  }
  return NS_OK;
}

static void
PlaceFrameView(nsIPresContext* aPresContext,
               nsIFrame*       aFrame)
{
  nsIView*  view;
  aFrame->GetView(aPresContext, &view);
  if (view) {
    nsContainerFrame::SyncFrameViewAfterReflow(aPresContext, aFrame, view, nsnull);

  } else {
    nsContainerFrame::PositionChildViews(aPresContext, aFrame);
  }
}

void
nsBlockFrame::SlideLine(nsBlockReflowState& aState,
                        nsLineBox* aLine, nscoord aDY)
{
  // Adjust line state
  aLine->SlideBy(aDY);

  // Adjust the frames in the line
  nsIFrame* kid = aLine->mFirstChild;
  if (!kid) {
    return;
  }

  if (aLine->IsBlock()) {
    nsRect r;
    kid->GetRect(r);
    if (aDY) {
      r.y += aDY;
      kid->SetRect(aState.mPresContext, r);
    }

    // Make sure the frame's view and any child views are updated
    ::PlaceFrameView(aState.mPresContext, kid);

    // If the child has any floaters that impact the space-manager,
    // place them now so that they are present in the space-manager
    // again (they were removed by the space-manager's frame when
    // the reflow began).
    nsBlockFrame* bf;
    nsresult rv = kid->QueryInterface(kBlockFrameCID, (void**) &bf);
    if (NS_SUCCEEDED(rv)) {
      // Translate spacemanager to the child blocks upper-left corner
      // so that when it places its floaters (which are relative to
      // it) the right coordinates are used. Note that we have already
      // been translated by our border+padding so factor that in to
      // get the right translation.
      const nsMargin& bp = aState.BorderPadding();
      nscoord dx = r.x - bp.left;
      nscoord dy = r.y - bp.top;
      aState.mSpaceManager->Translate(dx, dy);
      bf->UpdateSpaceManager(aState.mPresContext, aState.mSpaceManager);
      aState.mSpaceManager->Translate(-dx, -dy);
    }
  }
  else {
    // Adjust the Y coordinate of the frames in the line.
    // Note: we need to re-position views even if aDY is 0, because
    // one of our parent frames may have moved and so the view's position
    // relative to its parent may have changed
    nsRect r;
    PRInt32 n = aLine->GetChildCount();
    while (--n >= 0) {
      if (aDY) {
        kid->GetRect(r);
        r.y += aDY;
        kid->SetRect(aState.mPresContext, r);
      }
      // Make sure the frame's view and any child views are updated
      ::PlaceFrameView(aState.mPresContext, kid);
      kid->GetNextSibling(&kid);
    }
  }
}

nsresult
nsBlockFrame::UpdateSpaceManager(nsIPresContext* aPresContext,
                                 nsISpaceManager* aSpaceManager)
{
  nsLineBox* line = mLines;
  while (nsnull != line) {
    // Place the floaters in the spacemanager
    if (line->HasFloaters()) {
      nsFloaterCache* fc = line->GetFirstFloater();
      while (fc) {
        nsIFrame* floater = fc->mPlaceholder->GetOutOfFlowFrame();
        aSpaceManager->AddRectRegion(floater, fc->mRegion);
#ifdef NOISY_SPACEMANAGER
        nscoord tx, ty;
        aSpaceManager->GetTranslation(tx, ty);
        nsFrame::ListTag(stdout, this);
        printf(": UpdateSpaceManager: AddRectRegion: txy=%d,%d {%d,%d,%d,%d}\n",
               tx, ty,
               fc->mRegion.x, fc->mRegion.y,
               fc->mRegion.width, fc->mRegion.height);
#endif
        fc = fc->Next();
      }
    }

    // Tell kids about the move too
    if (line->mFirstChild && line->IsBlock()) {
      // If the child has any floaters that impact the space-manager,
      // place them now so that they are present in the space-manager
      // again (they were removed by the space-manager's frame when
      // the reflow began).
      nsBlockFrame* bf;
      nsresult rv = line->mFirstChild->QueryInterface(kBlockFrameCID,
                                                      (void**) &bf);
      if (NS_SUCCEEDED(rv)) {
        nsPoint origin;
        bf->GetOrigin(origin);

        // Translate spacemanager to the child blocks upper-left
        // corner so that when it places its floaters (which are
        // relative to it) the right coordinates are used.
        aSpaceManager->Translate(origin.x, origin.y);
        bf->UpdateSpaceManager(aPresContext, aSpaceManager);
        aSpaceManager->Translate(-origin.x, -origin.y);
      }
    }
    
    line = line->mNext;
  }

  return NS_OK;
}

NS_IMETHODIMP 
nsBlockFrame::AttributeChanged(nsIPresContext* aPresContext,
                               nsIContent*     aChild,
                               PRInt32         aNameSpaceID,
                               nsIAtom*        aAttribute,
                               PRInt32         aHint)
{
  nsresult rv = nsBlockFrameSuper::AttributeChanged(aPresContext, aChild,
                                                    aNameSpaceID, aAttribute, aHint);

  if (NS_OK != rv) {
    return rv;
  }
  if (nsHTMLAtoms::start == aAttribute) {
    // XXX Not sure if this is necessary anymore
    RenumberLists(aPresContext);

    nsCOMPtr<nsIPresShell> shell;
    aPresContext->GetShell(getter_AddRefs(shell));
    
    nsIReflowCommand* reflowCmd;
    rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
                                 nsIReflowCommand::ContentChanged,
                                 nsnull,
                                 aAttribute);
    if (NS_SUCCEEDED(rv)) {
      shell->AppendReflowCommand(reflowCmd);
      NS_RELEASE(reflowCmd);
    }
  }
  else if (nsHTMLAtoms::value == aAttribute) {
    const nsStyleDisplay* styleDisplay;
    GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) styleDisplay);
    if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) {
      nsIFrame* nextAncestor = mParent;
      nsBlockFrame* blockParent = nsnull;
      
      // Search for the closest ancestor that's a block frame. We
      // make the assumption that all related list items share a
      // common block parent.
      while (nextAncestor != nsnull) {
        if (NS_OK == nextAncestor->QueryInterface(kBlockFrameCID, 
                                                  (void**)&blockParent)) {
          break;
        }
        nextAncestor->GetParent(&nextAncestor);
      }

      // Tell the enclosing block frame to renumber list items within
      // itself
      if (nsnull != blockParent) {
        // XXX Not sure if this is necessary anymore
        blockParent->RenumberLists(aPresContext);

        nsCOMPtr<nsIPresShell> shell;
        aPresContext->GetShell(getter_AddRefs(shell));
        
        nsIReflowCommand* reflowCmd;
        rv = NS_NewHTMLReflowCommand(&reflowCmd, blockParent,
                                     nsIReflowCommand::ContentChanged,
                                     nsnull,
                                     aAttribute);
        if (NS_SUCCEEDED(rv)) {
          shell->AppendReflowCommand(reflowCmd);
          NS_RELEASE(reflowCmd);
        }
      }
    }
  }

  return rv;
}

nsBlockFrame*
nsBlockFrame::FindFollowingBlockFrame(nsIFrame* aFrame)
{
  nsBlockFrame* followingBlockFrame = nsnull;
  nsIFrame* frame = aFrame;
  for (;;) {
    nsIFrame* nextFrame;
    frame->GetNextSibling(&nextFrame);
    if (nsnull != nextFrame) {
      const nsStyleDisplay* display;
      nextFrame->GetStyleData(eStyleStruct_Display,
                              (const nsStyleStruct*&) display);
      if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
        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 WRITE ME!
      }
      frame = nextFrame;
    }
    else
      break;
  }
  return followingBlockFrame;
}

PRBool
nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
                                   nsLineBox* aLine)
{
  if (aState.mApplyTopMargin) {
    // Apply short-circuit check to avoid searching the line list
    return PR_TRUE;
  }

  if (!aState.IsAdjacentWithTop()) {
    // If we aren't at the top Y coordinate then something of non-zero
    // height must have been placed. Therefore the childs top-margin
    // applies.
    aState.mApplyTopMargin = PR_TRUE;
    return PR_TRUE;
  }

  // Determine if this line is "essentially" the first line
  nsLineBox* line = mLines;
  while (line != aLine) {
    if (line->IsBlock()) {
      // A line which preceeds aLine contains a block; therefore the
      // top margin applies.
      aState.mApplyTopMargin = PR_TRUE;
      return PR_TRUE;
    }
    else if (line->HasFloaters()) {
      // A line which preceeds aLine is not empty therefore the top
      // margin applies.
      aState.mApplyTopMargin = PR_TRUE;
      return PR_TRUE;
    }
    line = line->mNext;
  }

  // The line being reflowed is "essentially" the first line in the
  // block. Therefore its top-margin will be collapsed by the
  // generational collapsing logic with its parent (us).
  return PR_FALSE;
}

nsIFrame*
nsBlockFrame::GetTopBlockChild()
{
  nsIFrame* firstChild = mLines ? mLines->mFirstChild : nsnull;
  if (firstChild) {
    if (mLines->IsBlock()) {
      // Winner
      return firstChild;
    }

    // If the first line is not a block line then the second line must
    // be a block line otherwise the top child can't be a block.
    nsLineBox* next = mLines->mNext;
    if ((nsnull == next) || !next->IsBlock()) {
      // There is no line after the first line or its not a block so
      // don't bother trying to skip over the first line.
      return nsnull;
    }

    // The only time we can skip over the first line and pretend its
    // not there is if the line contains only compressed
    // whitespace. If white-space is significant to this frame then we
    // can't skip over the line.
    const nsStyleText* styleText;
    GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText);
    if ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
        (NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)) {
      // Whitespace is significant
      return nsnull;
    }

    // See if each frame is a text frame that contains nothing but
    // whitespace.
    PRInt32 n = mLines->GetChildCount();
    while (--n >= 0) {
      nsIContent* content;
      nsresult rv = firstChild->GetContent(&content);
      if (NS_FAILED(rv) || (nsnull == content)) {
        return nsnull;
      }
      nsITextContent* tc;
      rv = content->QueryInterface(kITextContentIID, (void**) &tc);
      NS_RELEASE(content);
      if (NS_FAILED(rv) || (nsnull == tc)) {
        return nsnull;
      }
      PRBool isws = PR_FALSE;
      tc->IsOnlyWhitespace(&isws);
      NS_RELEASE(tc);
      if (!isws) {
        return nsnull;
      }
      firstChild->GetNextSibling(&firstChild);
    }

    // If we make it to this point then every frame on the first line
    // was compressible white-space. Since we already know that the
    // second line contains a block, that block is the
    // top-block-child.
    return next->mFirstChild;
  }

  return nsnull;
}

nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
                               nsLineBox* aLine,
                               PRBool* aKeepReflowGoing)
{
  NS_PRECONDITION(*aKeepReflowGoing, "bad caller");

  nsresult rv = NS_OK;

  nsIFrame* frame = aLine->mFirstChild;

  // Prepare the block reflow engine
  const nsStyleDisplay* display;
  frame->GetStyleData(eStyleStruct_Display,
                      (const nsStyleStruct*&) display);
  nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState,
                           aState.mComputeMaxElementSize,
                           aState.mComputeMaximumWidth);
  brc.SetNextRCFrame(aState.mNextRCFrame);

  // See if we should apply the top margin. If the block frame being
  // reflowed is a continuation (non-null prev-in-flow) then we don't
  // apply its top margin because its not significant. Otherwise, dig
  // deeper.
  PRBool applyTopMargin = PR_FALSE;
  nsIFrame* framePrevInFlow;
  frame->GetPrevInFlow(&framePrevInFlow);
  if (nsnull == framePrevInFlow) {
    applyTopMargin = ShouldApplyTopMargin(aState, aLine);
  }

  // Clear past floaters before the block if the clear style is not none
  PRUint8 breakType = display->mBreakType;
  aLine->SetBreakType(breakType);
  if (NS_STYLE_CLEAR_NONE != breakType) {
    PRBool alsoApplyTopMargin = aState.ClearPastFloaters(breakType);
    if (alsoApplyTopMargin) {
      applyTopMargin = PR_TRUE;
    }
#ifdef NOISY_VERTICAL_MARGINS
    ListTag(stdout);
    printf(": y=%d child ", aState.mY);
    ListTag(stdout, frame);
    printf(" has clear of %d => %s, mPrevBottomMargin=%d\n",
           breakType,
           applyTopMargin ? "applyTopMargin" : "nope",
           aState.mPrevBottomMargin);
#endif
  }

  nscoord topMargin = 0;
  if (applyTopMargin) {
    // Precompute the blocks top margin value so that we can get the
    // correct available space (there might be a floater that's
    // already been placed below the aState.mPrevBottomMargin

    // Setup a reflowState to get the style computed margin-top value

    // The availSpace here is irrelevant to our needs - all we want
    // out if this setup is the margin-top value which doesn't depend
    // on the childs available space.
    nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
    nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
                                  frame, availSpace);

    // Now compute the collapsed margin-top value
    topMargin =
      nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext,
                                                      reflowState);

    // And collapse it with the previous bottom margin to get the final value
    topMargin =
      nsBlockReflowContext::MaxMargin(topMargin, aState.mPrevBottomMargin);

    // Temporarily advance the running Y value so that the
    // GetAvailableSpace method will return the right available
    // space. This undone as soon as the margin is computed.
    aState.mY += topMargin;
  }

  // Compute the available space for the block
  aState.GetAvailableSpace();
#ifdef REALLY_NOISY_REFLOW
  printf("setting line %p isImpacted to %s\n", aLine, aState.IsImpactedByFloater()?"true":"false");
#endif
  PRBool isImpacted = aState.IsImpactedByFloater() ? PR_TRUE : PR_FALSE;
  aLine->SetLineIsImpactedByFloater(isImpacted);
  nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
  frame->IsSplittable(splitType);
  nsRect availSpace;
  aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);

  // Now put the Y coordinate back and flow the block letting the
  // block reflow context compute the same top margin value we just
  // computed (sigh).
  if (topMargin) {
    aState.mY -= topMargin;
    availSpace.y -= topMargin;
    if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
      availSpace.height += topMargin;
    }
  }

  // Reflow the block into the available space
  nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
  nsMargin computedOffsets;
  rv = brc.ReflowBlock(frame, availSpace,
                       applyTopMargin, aState.mPrevBottomMargin,
                       aState.IsAdjacentWithTop(),
                       computedOffsets, frameReflowStatus);
  if (frame == aState.mNextRCFrame) {
    // NULL out mNextRCFrame so if we reflow it again we don't think it's still
    // an incremental reflow
    aState.mNextRCFrame = nsnull;
  }
  if (NS_FAILED(rv)) {
    return rv;
  }
  aState.mPrevChild = frame;

#if defined(REFLOW_STATUS_COVERAGE)
  RecordReflowStatus(PR_TRUE, frameReflowStatus);
#endif

  if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
    // None of the child block fits.
    PushLines(aState);
    *aKeepReflowGoing = PR_FALSE;
    aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
  }
  else {
    // Note: line-break-after a block is a nop

    // Try to place the child block
    PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
    nscoord collapsedBottomMargin;
    nsRect combinedArea(0,0,0,0);
    *aKeepReflowGoing = brc.PlaceBlock(isAdjacentWithTop, computedOffsets,
                                       &collapsedBottomMargin,
                                       aLine->mBounds, combinedArea);

    if (aState.mShrinkWrapWidth) {
      // Mark the line as block so once we known the final shrink wrap width
      // we can reflow the block to the correct size
      // XXX We don't always need to do this...
      aLine->MarkDirty();
      aState.mNeedResizeReflow = PR_TRUE;
    }
    if (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth) {
      // Add the right margin to the line's bounnds. That way it will be taken into
      // account when we compute our shrink wrap size
      nscoord marginRight = brc.GetMargin().right;
      if (marginRight != NS_UNCONSTRAINEDSIZE) {
        aLine->mBounds.width += marginRight;
      }
    }
    aLine->SetCombinedArea(combinedArea);
    if (*aKeepReflowGoing) {
      // Some of the child block fit

      // Advance to new Y position
      nscoord newY = aLine->mBounds.YMost();
      aState.mY = newY;
      aLine->SetCarriedOutBottomMargin(brc.GetCarriedOutBottomMargin());

      // Continue the block frame now if it didn't completely fit in
      // the available space.
      if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
        PRBool madeContinuation;
        rv = CreateContinuationFor(aState, aLine, frame, madeContinuation);
        if (NS_FAILED(rv)) {
          return rv;
        }

        // Push continuation to a new line, but only if we actually
        // made one.
        if (madeContinuation) {
          frame->GetNextSibling(&frame);
          nsLineBox* line = aState.NewLineBox(frame, 1, PR_TRUE);
          if (nsnull == line) {
            return NS_ERROR_OUT_OF_MEMORY;
          }
          line->mNext = aLine->mNext;
          aLine->mNext = line;

          // Do not count the continuation child on the line it used
          // to be on
          aLine->SetChildCount(aLine->GetChildCount() - 1);
       }

        // Advance to next line since some of the block fit. That way
        // only the following lines will be pushed.
        aState.mPrevLine = aLine;
        PushLines(aState);
        aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
        *aKeepReflowGoing = PR_FALSE;

        // The bottom margin for a block is only applied on the last
        // flow block. Since we just continued the child block frame,
        // we know that line->mFirstChild is not the last flow block
        // therefore zero out the running margin value.
#ifdef NOISY_VERTICAL_MARGINS
        ListTag(stdout);
        printf(": reflow incomplete, frame=");
        nsFrame::ListTag(stdout, frame);
        printf(" prevBottomMargin=%d, setting to zero\n",
               aState.mPrevBottomMargin);
#endif
        aState.mPrevBottomMargin = 0;
      }
      else {
#ifdef NOISY_VERTICAL_MARGINS
        ListTag(stdout);
        printf(": reflow complete for ");
        nsFrame::ListTag(stdout, frame);
        printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
               aState.mPrevBottomMargin, collapsedBottomMargin);
#endif
        if (collapsedBottomMargin >= 0) {
          aState.mPrevBottomMargin = collapsedBottomMargin;
        }
        else {
          // Leave margin alone: it was a collapsed paragraph that
          // must not interfere with the running margin calculations
          // (in other words it should act like an empty line of
          // whitespace).
        }
      }
#ifdef NOISY_VERTICAL_MARGINS
      ListTag(stdout);
      printf(": frame=");
      nsFrame::ListTag(stdout, frame);
      printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
             aLine->GetCarriedOutBottomMargin(), collapsedBottomMargin,
             aState.mPrevBottomMargin);
#endif

      // Post-process the "line"
      nsSize maxElementSize(0, 0);
      if (aState.mComputeMaxElementSize) {
        maxElementSize = brc.GetMaxElementSize();
        if (aState.IsImpactedByFloater() &&
            (NS_FRAME_SPLITTABLE_NON_RECTANGULAR != splitType)) {
          // Add in floater impacts to the lines max-element-size, but
          // only if the block element isn't one of us (otherwise the
          // floater impacts will be counted twice).
          ComputeLineMaxElementSize(aState, aLine, &maxElementSize);
        }
      }
      // If we asked the block to update its maximum width, then record the
      // updated value in the line, and update the current maximum width
      if (aState.mComputeMaximumWidth) {
        aLine->mMaximumWidth = brc.GetMaximumWidth();
        aState.UpdateMaximumWidth(aLine->mMaximumWidth);

      }
      PostPlaceLine(aState, aLine, maxElementSize);

      // Place the "marker" (bullet) frame.
      //
      // According to the CSS2 spec, section 12.6.1, the "marker" box
      // participates in the height calculation of the list-item box's
      // first line box.
      //
      // There are exactly two places a bullet can be placed: near the
      // first or second line. Its only placed on the second line in a
      // rare case: an empty first line followed by a second line that
      // contains a block (example: <LI>\n<P>... ). This is where
      // the second case can happen.
      if (HaveOutsideBullet() &&
          ((aLine == mLines) ||
           ((0 == mLines->mBounds.height) && (aLine == mLines->mNext)))) {
        // Reflow the bullet
        nsHTMLReflowMetrics metrics(nsnull);
        ReflowBullet(aState, metrics);

        // For bullets that are placed next to a child block, there will
        // be no correct ascent value. Therefore, make one up...
        nscoord ascent = 0;
        const nsStyleFont* font;
        frame->GetStyleData(eStyleStruct_Font,
                            (const nsStyleStruct*&) font);
        nsIRenderingContext& rc = *aState.mReflowState.rendContext;
        rc.SetFont(font->mFont);
        nsIFontMetrics* fm;
        rv = rc.GetFontMetrics(fm);
        if (NS_SUCCEEDED(rv) && (nsnull != fm)) {
          fm->GetMaxAscent(ascent);
          NS_RELEASE(fm);
        }
        rv = NS_OK;

        // Tall bullets won't look particularly nice here...
        nsRect bbox;
        mBullet->GetRect(bbox);
        nscoord bulletTopMargin = applyTopMargin ? collapsedBottomMargin : 0;
        bbox.y = aState.BorderPadding().top + ascent -
          metrics.ascent + bulletTopMargin;
        mBullet->SetRect(aState.mPresContext, bbox);
      }
    }
    else {
      // None of the block fits. Determine the correct reflow status.
      if (aLine == mLines) {
        // If it's our very first line then we need to be pushed to
        // our parents next-in-flow. Therefore, return break-before
        // status for our reflow status.
        aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
      }
      else {
        // Push the line that didn't fit and any lines that follow it
        // to our next-in-flow.
        PushLines(aState);
        aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
      }
    }
  }
#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
  return rv;
}

#define LINE_REFLOW_OK   0
#define LINE_REFLOW_STOP 1
#define LINE_REFLOW_REDO 2

nsresult
nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
                                 nsLineBox* aLine,
                                 PRBool* aKeepReflowGoing,
                                 PRBool aUpdateMaximumWidth)
{
  nsresult rv = NS_OK;
  *aKeepReflowGoing = PR_TRUE;

#ifdef DEBUG
  PRInt32 spins = 0;
#endif
  PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
  while (LINE_REFLOW_REDO == lineReflowStatus) {
    // Prevent overflowing limited thread stacks by creating
    // nsLineLayout from the heap when the frame tree depth gets
    // large.
    if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
      rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
                                      &lineReflowStatus,
                                      aUpdateMaximumWidth);
    }
    else {
      rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
                                    &lineReflowStatus,
                                    aUpdateMaximumWidth);
    }
    if (NS_FAILED(rv)) {
      break;
    }
#ifdef DEBUG
    spins++;
    if (1000 == spins) {
      ListTag(stdout);
      printf(": yikes! spinning on a line over 1000 times!\n");
      NS_ABORT();
    }
#endif
  }
  return rv;
}

nsresult
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
                                         nsLineBox* aLine,
                                         PRBool* aKeepReflowGoing,
                                         PRUint8* aLineReflowStatus,
                                         PRBool aUpdateMaximumWidth)
{
  nsLineLayout* ll = new nsLineLayout(aState.mPresContext,
                                      aState.mReflowState.mSpaceManager,
                                      &aState.mReflowState,
                                      aState.mComputeMaxElementSize);
  if (!ll) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
  ll->SetReflowTextRuns(mTextRuns);
  nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
                                     aLineReflowStatus, aUpdateMaximumWidth);
  ll->EndLineReflow();
  delete ll;
  return rv;
}

nsresult
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
                                       nsLineBox* aLine,
                                       PRBool* aKeepReflowGoing,
                                       PRUint8* aLineReflowStatus,
                                       PRBool aUpdateMaximumWidth)
{
  nsLineLayout lineLayout(aState.mPresContext,
                          aState.mReflowState.mSpaceManager,
                          &aState.mReflowState,
                          aState.mComputeMaxElementSize);
  lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
  lineLayout.SetReflowTextRuns(mTextRuns);
  nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine,
                                     aKeepReflowGoing, aLineReflowStatus,
                                     aUpdateMaximumWidth);
  lineLayout.EndLineReflow();
  return rv;
}

nsresult
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
                                   nsLineLayout& aLineLayout,
                                   nsLineBox* aLine,
                                   PRBool* aKeepReflowGoing,
                                   PRUint8* aLineReflowStatus,
                                   PRBool aUpdateMaximumWidth)
{
  // Forget all of the floaters on the line
  aLine->FreeFloaters(aState.mFloaterCacheFreeList);
  aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0);
  aState.mRightFloaterCombinedArea.SetRect(0, 0, 0, 0);

  // Setup initial coordinate system for reflowing the inline frames
  // into. Apply a previous block frame's bottom margin first.
  aState.mY += aState.mPrevBottomMargin;
  aState.GetAvailableSpace();
  PRBool impactedByFloaters = aState.IsImpactedByFloater() ? PR_TRUE : PR_FALSE;
  aLine->SetLineIsImpactedByFloater(impactedByFloaters);
#ifdef REALLY_NOISY_REFLOW
  printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
         this, impactedByFloaters);
#endif

  const nsMargin& borderPadding = aState.BorderPadding();
  nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
  nscoord availWidth = aState.mAvailSpaceRect.width;
  nscoord availHeight;
  if (aState.mUnconstrainedHeight) {
    availHeight = NS_UNCONSTRAINEDSIZE;
  }
  else {
    /* XXX get the height right! */
    availHeight = aState.mAvailSpaceRect.height;
  }
  if (aUpdateMaximumWidth) {
    availWidth = NS_UNCONSTRAINEDSIZE;
  }
  aLineLayout.BeginLineReflow(x, aState.mY,
                              availWidth, availHeight,
                              impactedByFloaters,
                              PR_FALSE /*XXX isTopOfPage*/);

  // XXX Unfortunately we need to know this before reflowing the first
  // inline frame in the line. FIX ME.
  if ((0 == aLineLayout.GetLineNumber()) &&
      (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
    aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
  }

  // Reflow the frames that are already on the line first
  nsresult rv = NS_OK;
  PRUint8 lineReflowStatus = LINE_REFLOW_OK;
  PRInt32 i;
  nsIFrame* frame = aLine->mFirstChild;
  aLine->SetHasPercentageChild(PR_FALSE); // To be set by ReflowInlineFrame below
  for (i = 0; i < aLine->GetChildCount(); i++) {      
    rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
                           &lineReflowStatus);
    if (NS_FAILED(rv)) {
      return rv;
    }
    if (LINE_REFLOW_OK != lineReflowStatus) {
      // It is possible that one or more of next lines are empty
      // (because of DeleteChildsNextInFlow). If so, delete them now
      // in case we are finished.
      nsLineBox* nextLine = aLine->mNext;
      while ((nsnull != nextLine) && (0 == nextLine->GetChildCount())) {
        // XXX Is this still necessary now that DeleteChildsNextInFlow
        // uses DoRemoveFrame?
        aLine->mNext = nextLine->mNext;
        NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
        aState.FreeLineBox(nextLine);
        nextLine = aLine->mNext;
      }
      break;
    }
    frame->GetNextSibling(&frame);
  }

  // Pull frames and reflow them until we can't
  while (LINE_REFLOW_OK == lineReflowStatus) {
    rv = PullFrame(aState, aLine, frame);
    if (NS_FAILED(rv)) {
      return rv;
    }
    if (nsnull == frame) {
      break;
    }
    while (LINE_REFLOW_OK == lineReflowStatus) {
      PRInt32 oldCount = aLine->GetChildCount();
      rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
                             &lineReflowStatus);
      if (NS_FAILED(rv)) {
        return rv;
      }
      if (aLine->GetChildCount() != oldCount) {
        // We just created a continuation for aFrame AND its going
        // to end up on this line (e.g. :first-letter
        // situation). Therefore we have to loop here before trying
        // to pull another frame.
        frame->GetNextSibling(&frame);
      }
      else {
        break;
      }
    }
  }
  if (LINE_REFLOW_REDO == lineReflowStatus) {
    // This happens only when we have a line that is impacted by
    // floaters and the first element in the line doesn't fit with
    // the floaters.
    //
    // What we do is to advance past the first floater we find and
    // then reflow the line all over again.
    NS_ASSERTION(aState.IsImpactedByFloater(),
                 "redo line on totally empty line");
    NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
                 "unconstrained height on totally empty line");

    aState.mY += aState.mAvailSpaceRect.height;
    // XXX: a small optimization can be done here when paginating:
    // if the new Y coordinate is past the end of the block then
    // push the line and return now instead of later on after we are
    // past the floater.
  }
  else {
    // If we are propogating out a break-before status then there is
    // no point in placing the line.
    if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
      rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth);
    }
  }
  *aLineReflowStatus = lineReflowStatus;

  return rv;
}

/**
 * Reflow an inline frame. The reflow status is mapped from the frames
 * reflow status to the lines reflow status (not to our reflow status).
 * The line reflow status is simple: PR_TRUE means keep placing frames
 * on the line; PR_FALSE means don't (the line is done). If the line
 * has some sort of breaking affect then aLine's break-type will be set
 * to something other than NS_STYLE_CLEAR_NONE.
 */
nsresult
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
                                nsLineLayout& aLineLayout,
                                nsLineBox* aLine,
                                nsIFrame* aFrame,
                                PRUint8* aLineReflowStatus)
{
  *aLineReflowStatus = LINE_REFLOW_OK;

  // If it's currently ok to be reflowing in first-letter style then
  // we must be about to reflow a frame that has first-letter style.
  PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
#ifdef NOISY_FIRST_LETTER
  ListTag(stdout);
  printf(": reflowing ");
  nsFrame::ListTag(stdout, aFrame);
  printf(" reflowingFirstLetter=%s\n", reflowingFirstLetter ? "on" : "off");
#endif

  // Remember if we have a percentage aware child on this line
  if (IsPercentageAwareChild(aFrame)) {
    aLine->SetHasPercentageChild(PR_TRUE);
  }

  // Reflow the inline frame
  nsReflowStatus frameReflowStatus;
  PRBool         pushedFrame;
  nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame,
                                        frameReflowStatus, nsnull, pushedFrame);
  if (aFrame == aState.mNextRCFrame) {
    // NULL out mNextRCFrame so if we reflow it again we don't think it's still
    // an incremental reflow
    aState.mNextRCFrame = nsnull;
  }
  if (NS_FAILED(rv)) {
    return rv;
  }
#ifdef REALLY_NOISY_REFLOW_CHILD
  nsFrame::ListTag(stdout, aFrame);
  printf(": status=%x\n", frameReflowStatus);
#endif

#if defined(REFLOW_STATUS_COVERAGE)
  RecordReflowStatus(PR_FALSE, frameReflowStatus);
#endif

  // Send post-reflow notification
  aState.mPrevChild = aFrame;

  // Process the child frames reflow status. There are 5 cases:
  // complete, not-complete, break-before, break-after-complete,
  // break-after-not-complete. There are two situations: we are a
  // block or we are an inline. This makes a total of 10 cases
  // (fortunately, there is some overlap).
  aLine->SetBreakType(NS_STYLE_CLEAR_NONE);
  if (NS_INLINE_IS_BREAK(frameReflowStatus)) {
    // Always abort the line reflow (because a line break is the
    // minimal amount of break we do).
    *aLineReflowStatus = LINE_REFLOW_STOP;

    // XXX what should aLine's break-type be set to in all these cases?
    PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
    NS_ASSERTION(breakType != NS_STYLE_CLEAR_NONE, "bad break type");
    NS_ASSERTION(NS_STYLE_CLEAR_PAGE != breakType, "no page breaks yet");

    if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
      // Break-before cases.
      if (aFrame == aLine->mFirstChild) {
        // If we break before the first frame on the line then we must
        // be trying to place content where theres no room (e.g. on a
        // line with wide floaters). Inform the caller to reflow the
        // line after skipping past a floater.
        *aLineReflowStatus = LINE_REFLOW_REDO;
      }
      else {
        // It's not the first child on this line so go ahead and split
        // the line. We will see the frame again on the next-line.
        rv = SplitLine(aState, aLineLayout, aLine, aFrame);
        if (NS_FAILED(rv)) {
          return rv;
        }

        // If we're splitting the line because the frame didn't fit and it
        // was pushed, then mark the line as having word wrapped. We need to
        // know that if we're shrink wrapping our width
        if (pushedFrame) {
          aLine->SetLineWrapped(PR_TRUE);
        }
      }
    }
    else {
      // Break-after cases
      if (breakType == NS_STYLE_CLEAR_LINE) {
        if (!aLineLayout.GetLineEndsInBR()) {
          breakType = NS_STYLE_CLEAR_NONE;
        }
      }
      aLine->SetBreakType(breakType);
      if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
        // Create a continuation for the incomplete frame. Note that the
        // frame may already have a continuation.
        PRBool madeContinuation;
        rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
        if (NS_FAILED(rv)) {
          return rv;
        }
        // Remember that the line has wrapped
        aLine->SetLineWrapped(PR_TRUE);
      }

      // Split line, but after the frame just reflowed
      nsIFrame* nextFrame;
      aFrame->GetNextSibling(&nextFrame);
      rv = SplitLine(aState, aLineLayout, aLine, nextFrame);
      if (NS_FAILED(rv)) {
        return rv;
      }

      // Mark next line dirty in case SplitLine didn't end up
      // pushing any frames.
      nsLineBox* next = aLine->mNext;
      if ((nsnull != next) && !next->IsBlock()) {
        next->MarkDirty();
      }
    }
  }
  else if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
    // Frame is not-complete, no special breaking status

    // Create a continuation for the incomplete frame. Note that the
    // frame may already have a continuation.
    PRBool madeContinuation;
    rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
    if (NS_FAILED(rv)) {
      return rv;
    }

    // Remember that the line has wrapped
    aLine->SetLineWrapped(PR_TRUE);
    
    // If we are reflowing the first letter frame then don't split the
    // line and don't stop the line reflow...
    PRBool splitLine = !reflowingFirstLetter;
    if (reflowingFirstLetter) {
      nsCOMPtr<nsIAtom> frameType;
      aFrame->GetFrameType(getter_AddRefs(frameType));
      if ((nsLayoutAtoms::inlineFrame == frameType.get()) ||
          (nsLayoutAtoms::lineFrame == frameType.get())) {
        splitLine = PR_TRUE;
      }
    }

    if (splitLine) {
      // Split line after the current frame
      *aLineReflowStatus = LINE_REFLOW_STOP;
      aFrame->GetNextSibling(&aFrame);
      rv = SplitLine(aState, aLineLayout, aLine, aFrame);
      if (NS_FAILED(rv)) {
        return rv;
      }

      // Mark next line dirty in case SplitLine didn't end up
      // pushing any frames.
      nsLineBox* next = aLine->mNext;
      if ((nsnull != next) && !next->IsBlock()) {
        next->MarkDirty();
      }
    }
  }

  return NS_OK;
}

/**
 * Create a continuation, if necessary, for aFrame. Place it on the
 * same line that aFrame is on. Set aMadeNewFrame to PR_TRUE if a
 * new frame is created.
 */
nsresult
nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
                                    nsLineBox* aLine,
                                    nsIFrame* aFrame,
                                    PRBool& aMadeNewFrame)
{
  aMadeNewFrame = PR_FALSE;
  nsresult rv;
  nsIFrame* nextInFlow;
  rv = CreateNextInFlow(aState.mPresContext, this, aFrame, nextInFlow);
  if (NS_FAILED(rv)) {
    return rv;
  }
  if (nsnull != nextInFlow) {
    aMadeNewFrame = PR_TRUE;
    aLine->SetChildCount(aLine->GetChildCount() + 1);
  }
#ifdef DEBUG
  VerifyLines(PR_FALSE);
#endif
  return rv;
}

nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState,
                        nsLineLayout& aLineLayout,
                        nsLineBox* aLine,
                        nsIFrame* aFrame)
{
  NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");

  PRInt32 pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
  NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count"); 

#ifdef DEBUG
  if (gNoisyReflow) {
    nsFrame::IndentBy(stdout, gNoiseIndent);
    printf("split line: from line=%p pushCount=%d aFrame=", aLine, pushCount);
    if (aFrame) {
      nsFrame::ListTag(stdout, aFrame);
    }
    else {
      printf("(null)");
    }
    printf("\n");
    if (gReallyNoisyReflow) {
      aLine->List(aState.mPresContext, stdout, gNoiseIndent+1);
    }
  }
#endif

  if (0 != pushCount) {
    NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push");
    NS_ABORT_IF_FALSE(nsnull != aFrame, "whoops");

    // Put frames being split out into their own line
    nsLineBox* newLine = aState.NewLineBox(aFrame, pushCount, PR_FALSE);
    if (!newLine) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    newLine->mNext = aLine->mNext;
    aLine->mNext = newLine;
    aLine->SetChildCount(aLine->GetChildCount() - pushCount);
#ifdef DEBUG
    if (gReallyNoisyReflow) {
      newLine->List(aState.mPresContext, stdout, gNoiseIndent+1);
    }
#endif

    // Let line layout know that some frames are no longer part of its
    // state.
    aLineLayout.SplitLineTo(aLine->GetChildCount());
#ifdef DEBUG
    VerifyLines(PR_TRUE);
#endif
  }
  return NS_OK;
}

PRBool
nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine)
{
  nsLineBox* next = aLine->mNext;
  while (nsnull != next) {
    // There is another line
    if (0 != next->GetChildCount()) {
      // If the next line is a block line then we must not justify
      // this line because it means that this line is the last in a
      // group of inline lines.
      return !next->IsBlock();
    }

    // The next line is empty, try the next one
    next = next->mNext;
  }

  // XXX Not sure about this part
  // Try our next-in-flows lines to answer the question
  nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
  while (nsnull != nextInFlow) {
    nsLineBox* line = nextInFlow->mLines;
    while (nsnull != line) {
      if (0 != line->GetChildCount()) {
        return !line->IsBlock();
      }
      line = line->mNext;
    }
    nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
  }

  // This is the last line - so don't allow justification
  return PR_FALSE;
}

nsresult
nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
                        nsLineLayout& aLineLayout,
                        nsLineBox* aLine,
                        PRBool* aKeepReflowGoing,
                        PRBool aUpdateMaximumWidth)
{
  nsresult rv = NS_OK;

  // Trim extra white-space from the line before placing the frames
  PRBool trimmed = aLineLayout.TrimTrailingWhiteSpace();
  aLine->SetTrimmed(trimmed);

  // Vertically align the frames on this line.
  //
  // According to the CSS2 spec, section 12.6.1, the "marker" box
  // participates in the height calculation of the list-item box's
  // first line box.
  //
  // There are exactly two places a bullet can be placed: near the
  // first or second line. Its only placed on the second line in a
  // rare case: an empty first line followed by a second line that
  // contains a block (example: <LI>\n<P>... ).
  //
  // For this code, only the first case is possible because this
  // method is used for placing a line of inline frames. If the rare
  // case is happening then the worst that will happen is that the
  // bullet frame will be reflowed twice.
  PRBool addedBullet = PR_FALSE;
  if (HaveOutsideBullet() && (aLine == mLines) &&
      (!aLineLayout.IsZeroHeight() || !aLine->mNext)) {
    nsHTMLReflowMetrics metrics(nsnull);
    ReflowBullet(aState, metrics);
    aLineLayout.AddBulletFrame(mBullet, metrics);
    addedBullet = PR_TRUE;
  }
  nsSize maxElementSize;
  aLineLayout.VerticalAlignFrames(aLine, maxElementSize);
  // See if we're shrink wrapping the width
  if (aState.mShrinkWrapWidth) {
    // When determining the line's width we also need to include any
    // right floaters that impact us. This represents the shrink wrap
    // width of the line
    if (aState.IsImpactedByFloater() && !aLine->IsLineWrapped()) {
      NS_ASSERTION(aState.mContentArea.width >= aState.mAvailSpaceRect.XMost(), "bad state");
      aLine->mBounds.width += aState.mContentArea.width - aState.mAvailSpaceRect.XMost();
    }
  }
#ifdef DEBUG
	{
		static nscoord lastHeight = 0;
	  if (CRAZY_HEIGHT(aLine->mBounds.y)) {
	  	lastHeight = aLine->mBounds.y;
	  	if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) {
		    nsFrame::ListTag(stdout);
		    printf(": line=%p y=%d line.bounds.height=%d\n",
		           aLine, aLine->mBounds.y, aLine->mBounds.height);
	    }
	  }
	  else {
	  	lastHeight = 0;
	  }
  }
#endif

  // Only block frames horizontally align their children because
  // inline frames "shrink-wrap" around their children (therefore
  // there is no extra horizontal space).
#if XXX_fix_me
  PRBool allowJustify = PR_TRUE;
  if (NS_STYLE_TEXT_ALIGN_JUSTIFY == aState.mStyleText->mTextAlign) {
    allowJustify = ShouldJustifyLine(aState, aLine);
  }
#else
  PRBool allowJustify = PR_FALSE;
#endif

  PRBool successful;
  nsRect combinedArea;
  successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify,
                                                 aState.mShrinkWrapWidth);
  if (!successful) {
    // Mark the line dirty and then later once we've determined the width
    // we can do the horizontal alignment
    aLine->MarkDirty();
    aState.mNeedResizeReflow = PR_TRUE;
  }
  aLineLayout.RelativePositionFrames(combinedArea);
  aLine->SetCombinedArea(combinedArea);
  if (addedBullet) {
    aLineLayout.RemoveBulletFrame(mBullet);
  }

  // Inline lines do not have margins themselves; however they are
  // impacted by prior block margins. If this line ends up having some
  // height then we zero out the previous bottom margin value that was
  // already applied to the line's starting Y coordinate. Otherwise we
  // leave it be so that the previous blocks bottom margin can be
  // collapsed with a block that follows.
  nscoord newY;
  if (aLine->mBounds.height > 0) {
    // This line has some height. Therefore the application of the
    // previous-bottom-margin should stick.
    aState.mPrevBottomMargin = 0;
    newY = aLine->mBounds.YMost();
  }
  else {
    // Don't let the previous-bottom-margin value affect the newY
    // coordinate (it was applied in ReflowInlineFrames speculatively)
    // since the line is empty.
    nscoord dy = -aState.mPrevBottomMargin;
    newY = aState.mY + dy;
    aLine->SlideBy(dy);
  }

  // See if the line fit. If it doesn't we need to push it. Our first
  // line will always fit.
  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);

    // Stop reflow and whack the reflow status if reflow hasn't
    // already been stopped.
    if (*aKeepReflowGoing) {
      NS_ASSERTION(NS_FRAME_COMPLETE == aState.mReflowStatus, 
                   "lost reflow status");
      aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
      *aKeepReflowGoing = PR_FALSE;
    }
    return rv;
  }

  aState.mY = newY;
  if (aState.mComputeMaxElementSize) {
#ifdef NOISY_MAX_ELEMENT_SIZE
    IndentBy(stdout, GetDepth());
    if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) {
      printf("PASS1 ");
    }
    ListTag(stdout);
    printf(": line.floaters=%s band.floaterCount=%d\n",
           //aLine->mFloaters.NotEmpty() ? "yes" : "no",
           aState.mHaveRightFloaters ? "(have right floaters)" : "",
           aState.mBand.GetFloaterCount());
#endif
    if (0 != aState.mBand.GetFloaterCount()) {
      // Add in floater impacts to the lines max-element-size
      ComputeLineMaxElementSize(aState, aLine, &maxElementSize);
    }
  }
  
  // If we're reflowing the line just to incrementally update the
  // maximum width, then don't post-place the line. It's doing work we
  // don't need, and it will update things like aState.mKidXMost that
  // we don't want updated...
  if (aUpdateMaximumWidth) {
    // However, we do need to update the max-element-size if requested
    if (aState.mComputeMaxElementSize) {
      aState.UpdateMaxElementSize(maxElementSize);
      // We also cache the max element width in the line. This is needed for
      // incremental reflow
      aLine->mMaxElementWidth = maxElementSize.width;
    }

  } else {
    PostPlaceLine(aState, aLine, maxElementSize);
  }

  // Add the already placed current-line floaters to the line
  aLine->AppendFloaters(aState.mCurrentLineFloaters);

  // Any below current line floaters to place?
  if (aState.mBelowCurrentLineFloaters.NotEmpty()) {
    // Reflow the below-current-line floaters, then add them to the
    // lines floater list.
    aState.PlaceBelowCurrentLineFloaters(aState.mBelowCurrentLineFloaters);
    aLine->AppendFloaters(aState.mBelowCurrentLineFloaters);
  }

  // When a line has floaters, factor them into the combined-area
  // computations.
  if (aLine->HasFloaters()) {
    // Combine the floater combined area (stored in aState) and the
    // value computed by the line layout code.
    nsRect lineCombinedArea;
    aLine->GetCombinedArea(&lineCombinedArea);
#ifdef NOISY_COMBINED_AREA
    ListTag(stdout);
    printf(": lineCA=%d,%d,%d,%d floaterCA=%d,%d,%d,%d\n",
           lineCombinedArea.x, lineCombinedArea.y,
           lineCombinedArea.width, lineCombinedArea.height,
           aState.mFloaterCombinedArea.x, aState.mFloaterCombinedArea.y,
           aState.mFloaterCombinedArea.width,
           aState.mFloaterCombinedArea.height);
#endif
    CombineRects(aState.mFloaterCombinedArea, lineCombinedArea);

    if (aState.mHaveRightFloaters &&
        (aState.mUnconstrainedWidth || aState.mShrinkWrapWidth)) {
      // We are reflowing in an unconstrained situation or shrink wrapping and
      // have some right floaters. They were placed at the infinite right edge
      // which will cause the combined area to be unusable.
      //
      // To solve this issue, we pretend that the right floaters ended up just
      // past the end of the line. Note that the right floater combined area
      // we computed as we were going will have as its X coordinate the left
      // most edge of all the right floaters. Therefore, to accomplish our goal
      // all we do is set that X value to the lines XMost value.
#ifdef NOISY_COMBINED_AREA
      printf("  ==> rightFloaterCA=%d,%d,%d,%d lineXMost=%d\n",
             aState.mRightFloaterCombinedArea.x,
             aState.mRightFloaterCombinedArea.y,
             aState.mRightFloaterCombinedArea.width,
             aState.mRightFloaterCombinedArea.height,
             aLine->mBounds.XMost());
#endif
      aState.mRightFloaterCombinedArea.x = aLine->mBounds.XMost();
      CombineRects(aState.mRightFloaterCombinedArea, lineCombinedArea);

      if (aState.mShrinkWrapWidth) {
        // Mark the line dirty so we come back and re-place the floater once
        // the shrink wrap width is determined
        aLine->MarkDirty();
        aState.mNeedResizeReflow = PR_TRUE;
      }
    }
    aLine->SetCombinedArea(lineCombinedArea);
#ifdef NOISY_COMBINED_AREA
    printf("  ==> final lineCA=%d,%d,%d,%d\n",
           lineCombinedArea.x, lineCombinedArea.y,
           lineCombinedArea.width, lineCombinedArea.height);
#endif
    aState.mHaveRightFloaters = PR_FALSE;
  }

  // Apply break-after clearing if necessary
  PRUint8 breakType = aLine->GetBreakType();
  switch (breakType) {
  case NS_STYLE_CLEAR_LEFT:
  case NS_STYLE_CLEAR_RIGHT:
  case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
    aState.ClearFloaters(aState.mY, breakType);
    break;
  }

  return rv;
}

// Compute the line's max-element-size by adding into the raw value
// computed by reflowing the contents of the line (aMaxElementSize)
// the impact of floaters on this line or the preceeding lines.
void
nsBlockFrame::ComputeLineMaxElementSize(nsBlockReflowState& aState,
                                        nsLineBox* aLine,
                                        nsSize* aMaxElementSize)
{
  nscoord maxWidth, maxHeight;
  aState.mBand.GetMaxElementSize(aState.mPresContext, &maxWidth, &maxHeight);
#ifdef NOISY_MAX_ELEMENT_SIZE
  IndentBy(stdout, GetDepth());
  if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) {
    printf("PASS1 ");
  }
  ListTag(stdout);
  printf(": maxFloaterSize=%d,%d\n", maxWidth, maxHeight);
#endif

  // If the floaters are wider than the content, then use the maximum
  // floater width as the maximum width.
  //
  // It used to be the case that we would always place some content
  // next to a floater, regardless of the amount of available space
  // after subtracing off the floaters sizes. This can lead to content
  // overlapping floaters, so we no longer do this (and pass CSS2's
  // conformance tests). This is not how navigator 4-1 used to do
  // things.
  if (maxWidth > aMaxElementSize->width) {
    aMaxElementSize->width = maxWidth;
  }

  // Only update the max-element-size's height value if the floater is
  // part of the current line.
  if (aLine->HasFloaters()) {
    // If the maximum-height of the tallest floater is larger than the
    // maximum-height of the content then update the max-element-size
    // height
    if (maxHeight > aMaxElementSize->height) {
      aMaxElementSize->height = maxHeight;
    }
  }
}

void
nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState,
                            nsLineBox* aLine,
                            const nsSize& aMaxElementSize)
{
  // If it's inline elements, then make sure the views are correctly
  // positioned and sized
  if (aLine->IsInline()) {
    nsIFrame* frame = aLine->mFirstChild;
    for (PRInt32 i = 0; i < aLine->GetChildCount(); i++) {
      ::PlaceFrameView(aState.mPresContext, frame);
      frame->GetNextSibling(&frame);
    }
  }

  // Update max-element-size
  if (aState.mComputeMaxElementSize) {
    aState.UpdateMaxElementSize(aMaxElementSize);
    // We also cache the max element width in the line. This is needed for
    // incremental reflow
    aLine->mMaxElementWidth = aMaxElementSize.width;
  }

  // If this is an unconstrained reflow, then cache the line width in the
  // line. We'll need this during incremental reflow if we're asked to
  // calculate the maximum width
  if (aState.mUnconstrainedWidth) {
    aLine->mMaximumWidth = aLine->mBounds.XMost();
  }

  // Update xmost
  nscoord xmost = aLine->mBounds.XMost();
#ifdef DEBUG
  if (CRAZY_WIDTH(xmost)) {
    ListTag(stdout);
    printf(": line=%p xmost=%d\n", aLine, xmost);
  }
#endif
  // If we're shrink wrapping our width and the line was wrapped,
  // then make sure we take up all of the available width
  if (aState.mShrinkWrapWidth && aLine->IsLineWrapped()) {
    aState.mKidXMost = aState.BorderPadding().left + aState.mContentArea.width;

  }
  else if (xmost > aState.mKidXMost) {
    aState.mKidXMost = xmost;
  }
}

void
nsBlockFrame::PushLines(nsBlockReflowState& aState)
{
  NS_ASSERTION(nsnull != aState.mPrevLine, "bad push");

  nsLineBox* lastLine = aState.mPrevLine;
  nsLineBox* nextLine = lastLine->mNext;

  if (nextLine) {
    lastLine->mNext = nsnull;
    SetOverflowLines(aState.mPresContext, nextLine);
  
    // Mark all the overflow lines dirty so that they get reflowed when
    // they are pulled up by our next-in-flow.
    while (nsnull != nextLine) {
      nextLine->MarkDirty();
      nextLine = nextLine->mNext;
    }
  }

  // Break frame sibling list
  nsIFrame* lastFrame = lastLine->LastChild();
  lastFrame->SetNextSibling(nsnull);

#ifdef DEBUG
  VerifyOverflowSituation(aState.mPresContext);
#endif
}

PRBool
nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext)
{
#ifdef DEBUG
  VerifyOverflowSituation(aPresContext);
#endif
  PRBool drained = PR_FALSE;
  nsLineBox* overflowLines;

  // First grab the prev-in-flows overflow lines
  nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
  if (nsnull != prevBlock) {
    overflowLines = prevBlock->GetOverflowLines(aPresContext, PR_TRUE);
    if (nsnull != overflowLines) {
      drained = PR_TRUE;

      // Make all the frames on the overflow line list mine
      nsIFrame* lastFrame = nsnull;
      nsIFrame* frame = overflowLines->mFirstChild;
      while (nsnull != frame) {
        frame->SetParent(this);

        // When pushing and pulling frames we need to check for whether any
        // views need to be reparented
        nsHTMLContainerFrame::ReparentFrameView(aPresContext, frame, prevBlock, this);

        // Get the next frame
        lastFrame = frame;
        frame->GetNextSibling(&frame);
      }

      // Join the line lists
      if (nsnull == mLines) {
        mLines = overflowLines;
      }
      else {
        // Join the sibling lists together
        lastFrame->SetNextSibling(mLines->mFirstChild);

        // Place overflow lines at the front of our line list
        nsLineBox* lastLine = nsLineBox::LastLine(overflowLines);
        lastLine->mNext = mLines;
        mLines = overflowLines;
      }
    }
  }

  // Now grab our own overflow lines
  overflowLines = GetOverflowLines(aPresContext, PR_TRUE);
  if (overflowLines) {
    // 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.
    nsLineBox* lastLine = nsLineBox::LastLine(mLines);
    if (nsnull == lastLine) {  // if we had no lines before the drain operation
      mLines = overflowLines;  // set our mLines to the overflow
    }
    else {                     // otherwise, append the overflow to the mLines list
      lastLine->mNext = overflowLines;
      nsIFrame* lastFrame = lastLine->LastChild();
      lastFrame->SetNextSibling(overflowLines->mFirstChild);
    }
    drained = PR_TRUE;
  }
  return drained;
}

nsLineBox*
nsBlockFrame::GetOverflowLines(nsIPresContext* aPresContext,
                               PRBool          aRemoveProperty) const
{
  nsCOMPtr<nsIPresShell>     presShell;
  aPresContext->GetShell(getter_AddRefs(presShell));

  if (presShell) {
    nsCOMPtr<nsIFrameManager>  frameManager;
    presShell->GetFrameManager(getter_AddRefs(frameManager));
  
    if (frameManager) {
      PRUint32  options = 0;
      void*     value;
  
      if (aRemoveProperty) {
        options |= NS_IFRAME_MGR_REMOVE_PROP;
      }
      frameManager->GetFrameProperty((nsIFrame*)this, nsLayoutAtoms::overflowLinesProperty,
                                     options, &value);
      return (nsLineBox*)value;
    }
  }

  return nsnull;
}

// Destructor function for the overflowLines frame property
static void
DestroyOverflowLines(nsIPresContext* aPresContext,
                     nsIFrame*       aFrame,
                     nsIAtom*        aPropertyName,
                     void*           aPropertyValue)
{
  if (aPropertyValue) {
    nsLineBox* lines = (nsLineBox*) aPropertyValue;
    nsLineBox::DeleteLineList(aPresContext, lines);
  }
}

nsresult
nsBlockFrame::SetOverflowLines(nsIPresContext* aPresContext,
                               nsLineBox*      aOverflowFrames)
{
  nsCOMPtr<nsIPresShell>     presShell;
  nsresult                   rv = NS_ERROR_FAILURE;

  aPresContext->GetShell(getter_AddRefs(presShell));
  if (presShell) {
    nsCOMPtr<nsIFrameManager>  frameManager;
    presShell->GetFrameManager(getter_AddRefs(frameManager));
  
    if (frameManager) {
      rv = frameManager->SetFrameProperty(this, nsLayoutAtoms::overflowLinesProperty,
                                          aOverflowFrames, DestroyOverflowLines);

      // Verify that we didn't overwrite an existing overflow list
      NS_ASSERTION(rv != NS_IFRAME_MGR_PROP_OVERWRITTEN, "existing overflow list");
    }
  }
  return rv;
}

//////////////////////////////////////////////////////////////////////
// Frame list manipulation routines

nsIFrame*
nsBlockFrame::LastChild()
{
  if (mLines) {
    nsLineBox* line = nsLineBox::LastLine(mLines);
    return line->LastChild();
  }
  return nsnull;
}

NS_IMETHODIMP
nsBlockFrame::AppendFrames(nsIPresContext* aPresContext,
                           nsIPresShell&   aPresShell,
                           nsIAtom*        aListName,
                           nsIFrame*       aFrameList)
{
  if (nsnull == aFrameList) {
    return NS_OK;
  }
  if (nsLayoutAtoms::floaterList == aListName) {
    // XXX we don't *really* care about this right now because we are
    // BuildFloaterList ing still
    mFloaters.AppendFrames(nsnull, aFrameList);
    return NS_OK;
  }
  else if (nsnull != aListName) {
    return NS_ERROR_INVALID_ARG;
  }

  // Find the proper last-child for where the append should go
  nsIFrame* lastKid = nsnull;
  nsLineBox* lastLine = nsLineBox::LastLine(mLines);
  if (lastLine) {
    lastKid = lastLine->LastChild();
  }

  // Add frames after the last child
#ifdef NOISY_REFLOW_REASON
  ListTag(stdout);
  printf(": append ");
  nsFrame::ListTag(stdout, aFrameList);
  if (lastKid) {
    printf(" after ");
    nsFrame::ListTag(stdout, lastKid);
  }
  printf("\n");
#endif
  nsresult rv = AddFrames(aPresContext, aFrameList, lastKid);
  if (NS_SUCCEEDED(rv)) {
    // Ask the parent frame to reflow me.
    ReflowDirtyChild(&aPresShell, nsnull);
  }
  return rv;
}

NS_IMETHODIMP
nsBlockFrame::InsertFrames(nsIPresContext* aPresContext,
                           nsIPresShell&   aPresShell,
                           nsIAtom*        aListName,
                           nsIFrame*       aPrevFrame,
                           nsIFrame*       aFrameList)
{
  if (nsLayoutAtoms::floaterList == aListName) {
    // XXX we don't *really* care about this right now because we are
    // BuildFloaterList'ing still
    mFloaters.AppendFrames(nsnull, aFrameList);
    return NS_OK;
  }
  else if (nsnull != aListName) {
    return NS_ERROR_INVALID_ARG;
  }

#ifdef NOISY_REFLOW_REASON
  ListTag(stdout);
  printf(": insert ");
  nsFrame::ListTag(stdout, aFrameList);
  if (aPrevFrame) {
    printf(" after ");
    nsFrame::ListTag(stdout, aPrevFrame);
  }
  printf("\n");
#endif
  nsresult rv = AddFrames(aPresContext, aFrameList, aPrevFrame);
  if (NS_SUCCEEDED(rv)) {
    // Ask the parent frame to reflow me.
    ReflowDirtyChild(&aPresShell, nsnull);
  }
  return rv;
}

nsresult
nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
                        nsIFrame* aFrameList,
                        nsIFrame* aPrevSibling)
{
  if (nsnull == aFrameList) {
    return NS_OK;
  }

  nsCOMPtr<nsIPresShell> presShell;
  aPresContext->GetShell(getter_AddRefs(presShell));

  // Attempt to find the line that contains the previous sibling
  nsLineBox* prevSibLine = nsnull;
  PRInt32 prevSiblingIndex = -1;
  if (aPrevSibling) {
    // Find the line that contains the previous sibling
    prevSibLine = nsLineBox::FindLineContaining(mLines, aPrevSibling,
                                                &prevSiblingIndex);
    NS_ASSERTION(nsnull != prevSibLine, "prev sibling not in line list");
    if (nsnull == prevSibLine) {
      // Note: defensive code! FindLineContaining must not return
      // null in this case, so if it does...
      aPrevSibling = nsnull;
    }
  }

  // Find the frame following aPrevSibling so that we can join up the
  // two lists of frames.
  nsIFrame* prevSiblingNextFrame = nsnull;
  if (aPrevSibling) {
    aPrevSibling->GetNextSibling(&prevSiblingNextFrame);

    // Split line containing aPrevSibling in two if the insertion
    // point is somewhere in the middle of the line.
    PRInt32 rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
    if (rem) {
      // Split the line in two where the frame(s) are being inserted.
      nsLineBox* line = NS_NewLineBox(presShell, prevSiblingNextFrame, rem, PR_FALSE);
      if (!line) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      line->mNext = prevSibLine->mNext;
      prevSibLine->mNext = line;
      prevSibLine->SetChildCount(prevSibLine->GetChildCount() - rem);
      prevSibLine->MarkDirty();
    }

    // Now (partially) join the sibling lists together
    aPrevSibling->SetNextSibling(aFrameList);
  }
  else if (mLines) {
    prevSiblingNextFrame = mLines->mFirstChild;
  }

  // Walk through the new frames being added and update the line data
  // structures to fit.
  nsIFrame* newFrame = aFrameList;
  while (newFrame) {
    PRBool isBlock = nsLineLayout::TreatFrameAsBlock(newFrame);

    // If the frame is a block frame, or if there is no previous line
    // or if the previous line is a block line then make a new line.
    if (isBlock || !prevSibLine || prevSibLine->IsBlock()) {
      // Create a new line for the frame and add its line to the line
      // list.
      nsLineBox* line = NS_NewLineBox(presShell, newFrame, 1, isBlock);
      if (!line) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
      if (prevSibLine) {
        // Append new line after prevSibLine
        line->mNext = prevSibLine->mNext;
        prevSibLine->mNext = line;
      }
      else {
        // New line is going before the other lines
        line->mNext = mLines;
        mLines = line;
      }
      prevSibLine = line;
    }
    else {
      prevSibLine->SetChildCount(prevSibLine->GetChildCount() + 1);
      prevSibLine->MarkDirty();
    }

    aPrevSibling = newFrame;
    newFrame->GetNextSibling(&newFrame);
  }
  if (prevSiblingNextFrame) {
    // Connect the last new frame to the remainder of the sibling list
    aPrevSibling->SetNextSibling(prevSiblingNextFrame);
  }

#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
  return NS_OK;
}


void
nsBlockFrame::FixParentAndView(nsIPresContext* aPresContext, nsIFrame* aFrame)
{
  while (aFrame) {
    nsIFrame* oldParent;
    aFrame->GetParent(&oldParent);
    aFrame->SetParent(this);
    if (this != oldParent) {
      nsHTMLContainerFrame::ReparentFrameView(aPresContext, aFrame, oldParent, this);
      aPresContext->ReParentStyleContext(aFrame, mStyleContext);
    }
    aFrame->GetNextSibling(&aFrame);
  }
}

NS_IMETHODIMP
nsBlockFrame::RemoveFrame(nsIPresContext* aPresContext,
                          nsIPresShell&   aPresShell,
                          nsIAtom*        aListName,
                          nsIFrame*       aOldFrame)
{
  nsresult rv = NS_OK;

#ifdef NOISY_REFLOW_REASON
    ListTag(stdout);
    printf(": remove ");
    nsFrame::ListTag(stdout, aOldFrame);
    printf("\n");
#endif

  if (nsLayoutAtoms::floaterList == aListName) {
    // Remove floater from the floater list first
    mFloaters.RemoveFrame(aOldFrame);

    // Find which line contains the floater
    nsLineBox* line = mLines;
    while (nsnull != line) {
      if (line->IsInline() && line->RemoveFloater(aOldFrame)) {
        aOldFrame->Destroy(aPresContext);
        goto found_it;
      }
      line = line->mNext;
    }
   found_it:

    // Mark every line at and below the line where the floater was dirty
    while (nsnull != line) {
      line->MarkDirty();
      line = line->mNext;
    }
  }
  else if (nsnull != aListName) {
    rv = NS_ERROR_INVALID_ARG;
  }
  else {
    rv = DoRemoveFrame(aPresContext, aOldFrame);
  }

  if (NS_SUCCEEDED(rv)) {
    // Ask the parent frame to reflow me.
    ReflowDirtyChild(&aPresShell, nsnull);  
  }
  return rv;
}

nsresult
nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
                            nsIFrame* aDeletedFrame)
{
  nsCOMPtr<nsIPresShell> presShell;
  aPresContext->GetShell(getter_AddRefs(presShell));
  
  // Find the line and the previous sibling that contains
  // deletedFrame; we also find the pointer to the line.
  nsBlockFrame* flow = this;
  nsLineBox** linep = &flow->mLines;
  nsLineBox* line = flow->mLines;
  nsLineBox* prevLine = nsnull;
  nsIFrame* prevSibling = nsnull;
  while (nsnull != line) {
    nsIFrame* frame = line->mFirstChild;
    PRInt32 n = line->GetChildCount();
    while (--n >= 0) {
      if (frame == aDeletedFrame) {
        goto found_frame;
      }
      prevSibling = frame;
      frame->GetNextSibling(&frame);
    }
    linep = &line->mNext;
    prevLine = line;
    line = line->mNext;
  }
 found_frame:;
#ifdef NS_DEBUG
  NS_ASSERTION(nsnull != line, "can't find deleted frame in lines");
  if (nsnull != prevSibling) {
    nsIFrame* tmp;
    prevSibling->GetNextSibling(&tmp);
    NS_ASSERTION(tmp == aDeletedFrame, "bad prevSibling");
  }
#endif

  // Remove frame and all of its continuations
  while (nsnull != aDeletedFrame) {
    while ((nsnull != line) && (nsnull != aDeletedFrame)) {
#ifdef NS_DEBUG
      nsIFrame* parent;
      aDeletedFrame->GetParent(&parent);
      NS_ASSERTION(flow == parent, "messed up delete code");
      NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
#endif

      // See if the frame being deleted is the last one on the line
      PRBool isLastFrameOnLine = PR_FALSE;
      if (1 == line->GetChildCount()) {
        isLastFrameOnLine = PR_TRUE;
      }
      else if (line->LastChild() == aDeletedFrame) {
        isLastFrameOnLine = PR_TRUE;
      }

      // Remove aDeletedFrame from the line
      nsIFrame* nextFrame;
      aDeletedFrame->GetNextSibling(&nextFrame);
      if (line->mFirstChild == aDeletedFrame) {
        line->mFirstChild = nextFrame;
      }
      if (prevLine && !prevLine->IsBlock()) {
        // Since we just removed a frame that follows some inline
        // frames, we need to reflow the previous line.
        prevLine->MarkDirty();
      }

      // Take aDeletedFrame out of the sibling list. Note that
      // prevSibling will only be nsnull when we are deleting the very
      // first frame.
      if (nsnull != prevSibling) {
        prevSibling->SetNextSibling(nextFrame);
      }

      // Destroy frame; capture its next-in-flow first in case we need
      // to destroy that too.
      nsIFrame* nextInFlow;
      aDeletedFrame->GetNextInFlow(&nextInFlow);
      nsSplittableType st;
      aDeletedFrame->IsSplittable(st);
      if (NS_FRAME_NOT_SPLITTABLE != st) {
        nsSplittableFrame::RemoveFromFlow(aDeletedFrame);
      }
#ifdef NOISY_REMOVE_FRAME
      printf("DoRemoveFrame: prevLine=%p line=%p frame=",
             prevLine, line);
      nsFrame::ListTag(stdout, aDeletedFrame);
      printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow);
#endif
      aDeletedFrame->Destroy(aPresContext);
      aDeletedFrame = nextInFlow;

      // If line is empty, remove it now
      nsLineBox* next = line->mNext;
      PRInt32 lineChildCount = line->GetChildCount();
      if (0 == --lineChildCount) {
        *linep = next;
        line->mNext = nsnull;
        // Invalidate the space taken up by the line.
        // XXX We need to do this if we're removing a frame as a result of
        // a call to RemoveFrame(), but we may not need to do this in all
        // cases...
        nsRect lineCombinedArea;
        line->GetCombinedArea(&lineCombinedArea);
        Invalidate(aPresContext, lineCombinedArea);
        line->Destroy(presShell);
        line = next;
      }
      else {
        // Make the line that just lost a frame dirty
        line->SetChildCount(lineChildCount);
        line->MarkDirty();

        // If we just removed the last frame on the line then we need
        // to advance to the next line.
        if (isLastFrameOnLine) {
          prevLine = line;
          linep = &line->mNext;
          line = next;
        }
      }

      // See if we should keep looking in the current flow's line list.
      if (nsnull != aDeletedFrame) {
        if (aDeletedFrame != nextFrame) {
          // The deceased frames continuation is not the next frame in
          // the current flow's frame list. Therefore we know that the
          // continuation is in a different parent. So break out of
          // the loop so that we advance to the next parent.
#ifdef NS_DEBUG
          nsIFrame* checkParent;
          aDeletedFrame->GetParent(&checkParent);
          NS_ASSERTION(checkParent != flow, "strange continuation");
#endif
          break;
        }
      }
    }

    // Advance to next flow block if the frame has more continuations
    if (nsnull != aDeletedFrame) {
      flow = (nsBlockFrame*) flow->mNextInFlow;
      NS_ASSERTION(nsnull != flow, "whoops, continuation without a parent");
      prevLine = nsnull;
      line = flow->mLines;
      linep = &flow->mLines;
      prevSibling = nsnull;
    }
  }

#ifdef DEBUG
  VerifyLines(PR_TRUE);
#endif
  return NS_OK;
}

void
nsBlockFrame::DeleteChildsNextInFlow(nsIPresContext* aPresContext,
                                     nsIFrame* aChild)
{
  NS_PRECONDITION(IsChild(aPresContext, aChild), "bad geometric parent");
  nsIFrame* nextInFlow;
  aChild->GetNextInFlow(&nextInFlow);
  NS_PRECONDITION(nsnull != nextInFlow, "null next-in-flow");
  nsBlockFrame* parent;
  nextInFlow->GetParent((nsIFrame**)&parent);
  NS_PRECONDITION(nsnull != parent, "next-in-flow with no parent");
  NS_PRECONDITION(nsnull != parent->mLines, "next-in-flow with weird parent");
//  NS_PRECONDITION(nsnull == parent->mOverflowLines, "parent with overflow");
  parent->DoRemoveFrame(aPresContext, nextInFlow);
}

////////////////////////////////////////////////////////////////////////
// Floater support

nsresult
nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
                            nsPlaceholderFrame* aPlaceholder,
                            nsRect& aCombinedRect,
                            nsMargin& aMarginResult,
                            nsMargin& aComputedOffsetsResult)
{
  // XXX update this just
  aState.GetAvailableSpace();

  // Reflow the floater. Since floaters are continued we given them an
  // unbounded height. Floaters with an auto width are sized to zero
  // according to the css2 spec.
  // XXX We also need to take into account whether we should clear any
  // preceeding floaters...
  // XXX Why do we have to add in our border/padding?
  nsRect availSpace(aState.mAvailSpaceRect.x + aState.BorderPadding().left,
                    aState.mAvailSpaceRect.y + aState.BorderPadding().top,
                    aState.mAvailSpaceRect.width, NS_UNCONSTRAINEDSIZE);
  nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
  PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();

  // Setup block reflow state to reflow the floater
  nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState,
                           aState.mComputeMaxElementSize,
                           aState.mComputeMaximumWidth);
  brc.SetNextRCFrame(aState.mNextRCFrame);

  // Reflow the floater
  nsReflowStatus frameReflowStatus;
  nsresult rv = brc.ReflowBlock(floater, availSpace, PR_TRUE, 0,
                                isAdjacentWithTop,
                                aComputedOffsetsResult, frameReflowStatus);
  if (floater == aState.mNextRCFrame) {
    // NULL out mNextRCFrame so if we reflow it again we don't think it's still
    // an incremental reflow
    aState.mNextRCFrame = nsnull;
  }
  if (NS_FAILED(rv)) {
    return rv;
  }

  // Capture the margin information for the caller
  const nsMargin& m = brc.GetMargin();
  aMarginResult.top = brc.GetTopMargin();
  aMarginResult.right = m.right;
  aMarginResult.bottom = 
    nsBlockReflowContext::MaxMargin(brc.GetCarriedOutBottomMargin(),
                                    m.bottom);
  aMarginResult.left = m.left;

  const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
  aCombinedRect = metrics.mOverflowArea;
  // Set the rect, make sure the view is properly sized and positioned,
  // and tell the frame we're done reflowing it
  floater->SizeTo(aState.mPresContext, metrics.width, metrics.height);
  nsIView*  view;
  floater->GetView(aState.mPresContext, &view);
  if (view) {
    nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, floater, view,
                                               &metrics.mOverflowArea,
                                               NS_FRAME_NO_MOVE_VIEW);
  }
  floater->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED);

  // If we computed it, then stash away the max-element-size for later
  if (aState.mComputeMaxElementSize) {
    aState.StoreMaxElementSize(floater, brc.GetMaxElementSize());
  }

  return NS_OK;
}

void
nsBlockReflowState::InitFloater(nsLineLayout& aLineLayout,
                                nsPlaceholderFrame* aPlaceholder)
{
  // Set the geometric parent of the floater
  nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
  floater->SetParent(mBlock);

  // Then add the floater to the current line and place it when
  // appropriate
  AddFloater(aLineLayout, aPlaceholder, PR_TRUE);
}

// 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(nsLineLayout& aLineLayout,
                               nsPlaceholderFrame* aPlaceholder,
                               PRBool aInitialReflow)
{
  NS_PRECONDITION(nsnull != mCurrentLine, "null ptr");

  // Allocate a nsFloaterCache for the floater
  nsFloaterCache* fc = mFloaterCacheFreeList.Alloc();
  fc->mPlaceholder = aPlaceholder;
  fc->mIsCurrentLineFloater = aLineLayout.CanPlaceFloaterNow();

  // Now place the floater immediately if possible. Otherwise stash it
  // away in mPendingFloaters and place it later.
  if (fc->mIsCurrentLineFloater) {
    // Record this floater in the current-line list
    mCurrentLineFloaters.Append(fc);

    // 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.
    nscoord ox, oy;
    mSpaceManager->GetTranslation(ox, oy);
    nscoord dx = ox - mSpaceManagerX;
    nscoord dy = oy - mSpaceManagerY;
    mSpaceManager->Translate(-dx, -dy);

    // Reflow the floater
    mBlock->ReflowFloater(*this, aPlaceholder, fc->mCombinedArea,
                          fc->mMargins, fc->mOffsets);

    // And then place it
    PRBool isLeftFloater;
    PlaceFloater(fc, &isLeftFloater);

    // Pass on updated available space to the current inline reflow engine
    GetAvailableSpace();
    aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY,
                           mUnconstrainedWidth ? NS_UNCONSTRAINEDSIZE : mAvailSpaceRect.width,
                           mAvailSpaceRect.height,
                           isLeftFloater,
                           aPlaceholder->GetOutOfFlowFrame());

    // 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).
    mBelowCurrentLineFloaters.Append(fc);
  }
}

PRBool
nsBlockReflowState::IsLeftMostChild(nsIPresContext* aPresContext, nsIFrame* aFrame)
{
  for (;;) {
    nsIFrame* parent;
    aFrame->GetParent(&parent);
    if (parent == mBlock) {
      nsIFrame* child = mCurrentLine->mFirstChild;
      PRInt32 n = mCurrentLine->GetChildCount();
      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(aPresContext, 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;
}

PRBool
nsBlockReflowState::CanPlaceFloater(const nsRect& aFloaterRect,
                                    PRUint8 aFloats)
{
  // If the current Y coordinate is not impacted by any floaters
  // then by definition the floater fits.
  PRBool result = PR_TRUE;
  if (0 != mBand.GetFloaterCount()) {
    if (mAvailSpaceRect.width < aFloaterRect.width) {
      // The available width is too narrow (and its been impacted by a
      // prior floater)
      result = PR_FALSE;
    }
    else {
      // At this point we know that there is enough horizontal space for
      // the floater (somewhere). Lets see if there is enough vertical
      // space.
      if (mAvailSpaceRect.height < aFloaterRect.height) {
        // The available height is too short. However, its possible that
        // there is enough open space below which is not impacted by a
        // floater.
        //
        // Compute the X coordinate for the floater based on its float
        // type, assuming its placed on the current line. This is
        // where the floater will be placed horizontally if it can go
        // here.
        nscoord xa;
        if (NS_STYLE_FLOAT_LEFT == aFloats) {
          xa = mAvailSpaceRect.x;
        }
        else {
          xa = mAvailSpaceRect.XMost() - aFloaterRect.width;

          // In case the floater is too big, don't go past the left edge
          if (xa < mAvailSpaceRect.x) {
            xa = mAvailSpaceRect.x;
          }
        }
        nscoord xb = xa + aFloaterRect.width;

        // Calculate the top and bottom y coordinates, again assuming
        // that the floater is placed on the current line.
        const nsMargin& borderPadding = BorderPadding();
        nscoord ya = mY - borderPadding.top;
        if (ya < 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?
          ya = 0;
        }
        nscoord yb = ya + aFloaterRect.height;

        nscoord saveY = mY;
        for (;;) {
          // Get the available space at the new Y coordinate
          mY += mAvailSpaceRect.height;
          GetAvailableSpace();

          if (0 == mBand.GetFloaterCount()) {
            // Winner. This band has no floaters on it, therefore
            // there can be no overlap.
            break;
          }

          // Check and make sure the floater won't intersect any
          // floaters on this band. The floaters starting and ending
          // coordinates must be entirely in the available space.
          if ((xa < mAvailSpaceRect.x) || (xb > mAvailSpaceRect.XMost())) {
            // The floater can't go here.
            result = PR_FALSE;
            break;
          }

          // See if there is now enough height for the floater.
          if (yb < mY + mAvailSpaceRect.height) {
            // Winner. The bottom Y coordinate of the floater is in
            // this band.
            break;
          }
        }

        // Restore Y coordinate and available space information
        // regardless of the outcome.
        mY = saveY;
        GetAvailableSpace();
      }
    }
  }
  return result;
}

void
nsBlockReflowState::PlaceFloater(nsFloaterCache* aFloaterCache,
                                 PRBool* aIsLeftFloater)
{
  // Save away the Y coordinate before placing the floater. We will
  // restore mY at the end after placing the floater. This is
  // necessary because any adjustments to mY during the floater
  // placement are for the floater only, not for any non-floating
  // content.
  nscoord saveY = mY;
  nsIFrame* floater = aFloaterCache->mPlaceholder->GetOutOfFlowFrame();

  // Get the type of floater
  const nsStyleDisplay* floaterDisplay;
  const nsStyleSpacing* floaterSpacing;
  const nsStylePosition* floaterPosition;
  floater->GetStyleData(eStyleStruct_Display,
                        (const nsStyleStruct*&)floaterDisplay);
  floater->GetStyleData(eStyleStruct_Spacing,
                        (const nsStyleStruct*&)floaterSpacing);
  floater->GetStyleData(eStyleStruct_Position,
                        (const nsStyleStruct*&)floaterPosition);

  // See if the floater should clear any preceeding floaters...
  if (NS_STYLE_CLEAR_NONE != floaterDisplay->mBreakType) {
    ClearFloaters(mY, floaterDisplay->mBreakType);
  }
  else {
    // Get the band of available space
    GetAvailableSpace();
  }

  // Get the floaters bounding box and margin information
  nsRect region;
  floater->GetRect(region);

  // Adjust the floater size by its margin. That's the area that will
  // impact the space manager.
  region.width += aFloaterCache->mMargins.left + aFloaterCache->mMargins.right;
  region.height += aFloaterCache->mMargins.top + aFloaterCache->mMargins.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 if possible (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).
  // 
  // Note: The CSS2 spec says that floaters should be placed as high
  // as possible.
  while (!CanPlaceFloater(region, floaterDisplay->mFloats)) {
    mY += mAvailSpaceRect.height;
    GetAvailableSpace();
  }

  // Assign an x and y coordinate to the floater. Note that the x,y
  // coordinates are computed <b>relative to the translation in the
  // spacemanager</b> which means that the impacted region will be
  // <b>inside</b> the border/padding area.
  PRBool okToAddRectRegion = PR_TRUE;
  PRBool isLeftFloater;
  if (NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) {
    isLeftFloater = PR_TRUE;
    region.x = mAvailSpaceRect.x;
  }
  else {
    isLeftFloater = PR_FALSE;
    region.x = mAvailSpaceRect.XMost() - region.width;
  }
  *aIsLeftFloater = isLeftFloater;
  const nsMargin& borderPadding = BorderPadding();
  region.y = mY - borderPadding.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
  if (okToAddRectRegion) {
#ifdef DEBUG
    nsresult rv =
#endif
    mSpaceManager->AddRectRegion(floater, region);
    NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "bad floater placement");
  }

  // Save away the floaters region in the spacemanager, after making
  // it relative to the containing block's frame instead of relative
  // to the spacemanager translation (which is inset by the
  // border+padding).
  aFloaterCache->mRegion.x = region.x + borderPadding.left;
  aFloaterCache->mRegion.y = region.y + borderPadding.top;
  aFloaterCache->mRegion.width = region.width;
  aFloaterCache->mRegion.height = region.height;
#ifdef NOISY_SPACEMANAGER
  nscoord tx, ty;
  mSpaceManager->GetTranslation(tx, ty);
  nsFrame::ListTag(stdout, mBlock);
  printf(": PlaceFloater: AddRectRegion: txy=%d,%d (%d,%d) {%d,%d,%d,%d}\n",
         tx, ty, mSpaceManagerX, mSpaceManagerY,
         aFloaterCache->mRegion.x, aFloaterCache->mRegion.y,
         aFloaterCache->mRegion.width, aFloaterCache->mRegion.height);
#endif

  // Set the origin of the floater frame, in frame coordinates. These
  // coordinates are <b>not</b> relative to the spacemanager
  // translation, therefore we have to factor in our border/padding.
  nscoord x = borderPadding.left + aFloaterCache->mMargins.left + region.x;
  nscoord y = borderPadding.top + aFloaterCache->mMargins.top + region.y;

  // If floater is relatively positioned, factor that in as well
  if (NS_STYLE_POSITION_RELATIVE == floaterPosition->mPosition) {
    x += aFloaterCache->mOffsets.left;
    y += aFloaterCache->mOffsets.top;
  }

  // Position the floater and make sure and views are properly positioned
  nsIView*  view;
  floater->MoveTo(mPresContext, x, y);
  floater->GetView(mPresContext, &view);
  if (view) {
    nsContainerFrame::PositionFrameView(mPresContext, floater, view);
  } else {
    nsContainerFrame::PositionChildViews(mPresContext, floater);
  }

  // Update the floater combined area state
  nsRect combinedArea = aFloaterCache->mCombinedArea;
  combinedArea.x += x;
  combinedArea.y += y;
  if (!isLeftFloater && (mUnconstrainedWidth || mShrinkWrapWidth)) {
    // When we are placing a right floater in an unconstrained situation or
    // when shrink wrapping, we don't apply it to the floater combined area
    // immediately. Otherwise we end up with an infinitely wide combined
    // area. Instead, we save it away in mRightFloaterCombinedArea so that
    // later on when we know the width of a line we can compute a better value.
    if (!mHaveRightFloaters) {
      mRightFloaterCombinedArea = combinedArea;
      mHaveRightFloaters = PR_TRUE;
    }
    else {
      CombineRects(combinedArea, mRightFloaterCombinedArea);
    }
  }
  else {
    CombineRects(combinedArea, mFloaterCombinedArea);
  }

  // Now restore mY
  mY = saveY;

#ifdef DEBUG
  if (gNoisyReflow) {
    nsRect r;
    floater->GetRect(r);
    nsFrame::IndentBy(stdout, gNoiseIndent);
    printf("placed floater: ");
    ((nsFrame*)floater)->ListTag(stdout);
    printf(" %d,%d,%d,%d\n", r.x, r.y, r.width, r.height);
  }
#endif
}

/**
 * Place below-current-line floaters.
 */
void
nsBlockReflowState::PlaceBelowCurrentLineFloaters(nsFloaterCacheList& aList)
{
  nsFloaterCache* fc = aList.Head();
  while (fc) {
    if (!fc->mIsCurrentLineFloater) {
#ifdef DEBUG
      if (gNoisyReflow) {
        nsFrame::IndentBy(stdout, gNoiseIndent);
        printf("placing bcl floater: ");
        nsFrame::ListTag(stdout, fc->mPlaceholder->GetOutOfFlowFrame());
        printf("\n");
      }
#endif
      mBlock->ReflowFloater(*this, fc->mPlaceholder, fc->mCombinedArea,
                            fc->mMargins, fc->mOffsets);

      // Place the floater
      PRBool isLeftFloater;
      PlaceFloater(fc, &isLeftFloater);
    }
    fc = fc->Next();
  }
}

void
nsBlockReflowState::ClearFloaters(nscoord aY, PRUint8 aBreakType)
{
#ifdef DEBUG
  if (gNoisyReflow) {
    nsFrame::IndentBy(stdout, gNoiseIndent);
    printf("clear floaters: in: mY=%d aY=%d(%d)\n",
           mY, aY, aY - BorderPadding().top);
  }
#endif

#ifdef NOISY_FLOATER_CLEARING
  printf("nsBlockReflowState::ClearFloaters: aY=%d breakType=%dn",
         aY, aBreakType);
  mSpaceManager->List(stdout);
#endif
  const nsMargin& bp = BorderPadding();
  nscoord newY = mBand.ClearFloaters(aY - bp.top, aBreakType);
  mY = newY + bp.top;
  GetAvailableSpace();

#ifdef DEBUG
  if (gNoisyReflow) {
    nsFrame::IndentBy(stdout, gNoiseIndent);
    printf("clear floaters: out: mY=%d(%d)\n", mY, mY - bp.top);
  }
#endif
}

//////////////////////////////////////////////////////////////////////
// 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;
}

#ifdef DEBUG
static void ComputeCombinedArea(nsLineBox* aLine,
                                nscoord aWidth, nscoord aHeight,
                                nsRect& aResult)
{
  nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
  while (nsnull != aLine) {
    // Compute min and max x/y values for the reflowed frame's
    // combined areas
    nsRect lineCombinedArea;
    aLine->GetCombinedArea(&lineCombinedArea);
    nscoord x = lineCombinedArea.x;
    nscoord y = lineCombinedArea.y;
    nscoord xmost = x + lineCombinedArea.width;
    nscoord ymost = y + lineCombinedArea.height;
    if (x < xa) {
      xa = x;
    }
    if (xmost > xb) {
      xb = xmost;
    }
    if (y < ya) {
      ya = y;
    }
    if (ymost > yb) {
      yb = ymost;
    }
    aLine = aLine->mNext;
  }

  aResult.x = xa;
  aResult.y = ya;
  aResult.width = xb - xa;
  aResult.height = yb - ya;
}
#endif

NS_IMETHODIMP
nsBlockFrame::Paint(nsIPresContext*      aPresContext,
                    nsIRenderingContext& aRenderingContext,
                    const nsRect&        aDirtyRect,
                    nsFramePaintLayer    aWhichLayer)
{
  if (NS_FRAME_IS_UNFLOWABLE & mState) {
    return NS_OK;
  }

#ifdef DEBUG
  if (gNoisyDamageRepair) {
    if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
      PRInt32 depth = GetDepth();
      nsRect ca;
      ComputeCombinedArea(mLines, mRect.width, mRect.height, ca);
      nsFrame::IndentBy(stdout, depth);
      ListTag(stdout);
      printf(": bounds=%d,%d,%d,%d dirty=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
             mRect.x, mRect.y, mRect.width, mRect.height,
             aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
             ca.x, ca.y, ca.width, ca.height);
    }
  }
#endif  

  const nsStyleDisplay* disp = (const nsStyleDisplay*)
    mStyleContext->GetStyleData(eStyleStruct_Display);

  // 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) {
    aRenderingContext.PushState();
    SetClipRect(aRenderingContext);
  }

  // Only paint the border and background if we're visible
  if (disp->IsVisible() && (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) &&
      (0 != mRect.width) && (0 != mRect.height)) {
    PRIntn skipSides = GetSkipSides();
    const nsStyleColor* color = (const nsStyleColor*)
      mStyleContext->GetStyleData(eStyleStruct_Color);
    const nsStyleSpacing* spacing = (const nsStyleSpacing*)
      mStyleContext->GetStyleData(eStyleStruct_Spacing);

    // Paint background, border and outline
    nsRect rect(0, 0, mRect.width, mRect.height);
    nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
                                    aDirtyRect, rect, *color, *spacing, 0, 0);
    nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
                                aDirtyRect, rect, *spacing, mStyleContext,
                                skipSides);
    nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
                                 aDirtyRect, rect, *spacing, mStyleContext, 0);
  }

  // Child elements have the opportunity to override the visibility
  // property and display even if the parent is hidden
  if (NS_FRAME_PAINT_LAYER_FLOATERS == aWhichLayer) {
    PaintFloaters(aPresContext, aRenderingContext, aDirtyRect);
  }
  PaintChildren(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);

  if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
    PRBool clipState;
    aRenderingContext.PopState(clipState);
  }

#if 0
  if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) && GetShowFrameBorders()) {
    // Render the bands in the spacemanager
    nsISpaceManager* sm = mSpaceManager;

    if (nsnull != sm) {
      nsBlockBandData band;
      band.Init(sm, nsSize(mRect.width, mRect.height));
      nscoord y = 0;
      while (y < mRect.height) {
        nsRect availArea;
        band.GetAvailableSpace(y, availArea);
  
        // Render a box and a diagonal line through the band
        aRenderingContext.SetColor(NS_RGB(0,255,0));
        aRenderingContext.DrawRect(0, availArea.y,
                                   mRect.width, availArea.height);
        aRenderingContext.DrawLine(0, availArea.y,
                                   mRect.width, availArea.YMost());
  
        // Render boxes and opposite diagonal lines around the
        // unavailable parts of the band.
        PRInt32 i;
        for (i = 0; i < band.GetTrapezoidCount(); i++) {
          const nsBandTrapezoid* trapezoid = band.GetTrapezoid(i);
          if (nsBandTrapezoid::Available != trapezoid->mState) {
            nsRect r;
            trapezoid->GetRect(r);
            if (nsBandTrapezoid::OccupiedMultiple == trapezoid->mState) {
              aRenderingContext.SetColor(NS_RGB(0,255,128));
            }
            else {
              aRenderingContext.SetColor(NS_RGB(128,255,0));
            }
            aRenderingContext.DrawRect(r);
            aRenderingContext.DrawLine(r.x, r.YMost(), r.XMost(), r.y);
          }
        }
        y = availArea.YMost();
      }
    }
  }
#endif

  return NS_OK;
}

void
nsBlockFrame::PaintFloaters(nsIPresContext* aPresContext,
                            nsIRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect)
{
  for (nsLineBox* line = mLines; nsnull != line; line = line->mNext) {
    if (!line->HasFloaters()) {
      continue;
    }
    nsFloaterCache* fc = line->GetFirstFloater();
    while (fc) {
      nsIFrame* floater = fc->mPlaceholder->GetOutOfFlowFrame();
      PaintChild(aPresContext, aRenderingContext, aDirtyRect,
                 floater, NS_FRAME_PAINT_LAYER_BACKGROUND);
      PaintChild(aPresContext, aRenderingContext, aDirtyRect,
                 floater, NS_FRAME_PAINT_LAYER_FLOATERS);
      PaintChild(aPresContext, aRenderingContext, aDirtyRect,
                 floater, NS_FRAME_PAINT_LAYER_FOREGROUND);
      fc = fc->Next();
    }
  }
}

void
nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
                            nsIRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect,
                            nsFramePaintLayer aWhichLayer)
{
#ifdef DEBUG
  PRInt32 depth = 0;
  if (gNoisyDamageRepair) {
    if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
      depth = GetDepth();
    }
  }
  PRTime start;
  PRInt32 drawnLines;
  if (gLamePaintMetrics) {
    start = PR_Now();
    drawnLines = 0;
  }
#endif

  for (nsLineBox* line = mLines; nsnull != line; line = line->mNext) {
    // If the line's combined area (which includes child frames that
    // stick outside of the line's bounding box or our bounding box)
    // intersects the dirty rect then paint the line.
    if (line->CombinedAreaIntersects(aDirtyRect)) {
#ifdef DEBUG
      if (gNoisyDamageRepair &&
          (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
        nsRect lineCombinedArea;
        line->GetCombinedArea(&lineCombinedArea);
        nsFrame::IndentBy(stdout, depth+1);
        printf("draw line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
               line, line->mBounds.x, line->mBounds.y,
               line->mBounds.width, line->mBounds.height,
               lineCombinedArea.x, lineCombinedArea.y,
               lineCombinedArea.width, lineCombinedArea.height);
      }
      if (gLamePaintMetrics) {
        drawnLines++;
      }
#endif
      nsIFrame* kid = line->mFirstChild;
      PRInt32 n = line->GetChildCount();
      while (--n >= 0) {
        PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
                   aWhichLayer);
        kid->GetNextSibling(&kid);
      }
    }
#ifdef DEBUG
    else {
      if (gNoisyDamageRepair &&
          (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
        nsRect lineCombinedArea;
        line->GetCombinedArea(&lineCombinedArea);
        nsFrame::IndentBy(stdout, depth+1);
        printf("skip line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
               line, line->mBounds.x, line->mBounds.y,
               line->mBounds.width, line->mBounds.height,
               lineCombinedArea.x, lineCombinedArea.y,
               lineCombinedArea.width, lineCombinedArea.height);
      }
    }
#endif  
  }

  if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
    if ((nsnull != mBullet) && HaveOutsideBullet()) {
      // Paint outside bullets manually
      PaintChild(aPresContext, aRenderingContext, aDirtyRect, mBullet,
                 aWhichLayer);
    }
  }

#ifdef DEBUG
  if (gLamePaintMetrics) {
    PRTime end = PR_Now();

    PRInt32 numLines = nsLineBox::ListLength(mLines);
    if (!numLines) numLines = 1;
    PRTime lines, deltaPerLine, delta;
    LL_I2L(lines, numLines);
    LL_SUB(delta, end, start);
    LL_DIV(deltaPerLine, delta, lines);

    ListTag(stdout);
    char buf[400];
    PR_snprintf(buf, sizeof(buf),
                ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d",
                delta, deltaPerLine,
                numLines, drawnLines, numLines - drawnLines);
    printf("%s\n", buf);
  }
#endif
}


NS_IMETHODIMP
nsBlockFrame::HandleEvent(nsIPresContext* aPresContext, 
                          nsGUIEvent*     aEvent,
                          nsEventStatus*  aEventStatus)
{
  if (aEvent->message == NS_MOUSE_MOVE) {
    nsCOMPtr<nsIPresShell> shell;
    nsresult rv = aPresContext->GetShell(getter_AddRefs(shell));
    if (NS_SUCCEEDED(rv)){
      nsCOMPtr<nsIFrameSelection> frameselection;
      if (NS_SUCCEEDED(shell->GetFrameSelection(getter_AddRefs(frameselection))) && frameselection){
          PRBool mouseDown = PR_FALSE;
          if (NS_FAILED(frameselection->GetMouseDownState(&mouseDown)) || !mouseDown) 
            return NS_OK;//do not handle
      }
    }
  }

  if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN || aEvent->message == NS_MOUSE_MOVE ||
    aEvent->message == NS_MOUSE_LEFT_DOUBLECLICK ) {
    nsresult result;
    nsIFrame *resultFrame = nsnull;//this will be passed the handle event when we 
                                   //can tell who to pass it to
    nsCOMPtr<nsILineIterator> it; 
    nsIFrame *mainframe = this;
    nsCOMPtr<nsIPresShell> shell;
    aPresContext->GetShell(getter_AddRefs(shell));
    if (!shell)
      return NS_OK;
    nsCOMPtr<nsIFocusTracker> tracker;
    result = shell->QueryInterface(NS_GET_IID(nsIFocusTracker),getter_AddRefs(tracker));

    result = mainframe->QueryInterface(NS_GET_IID(nsILineIterator),getter_AddRefs(it));
    nsIView* parentWithView;
    nsPoint origin;

    while(NS_SUCCEEDED(result))
    { //we are starting aloop to allow us to "drill down to the one we want" 
      mainframe->GetOffsetFromView(aPresContext, origin, &parentWithView);

      if (NS_FAILED(result))
        return NS_OK;//do not handle
      PRInt32 countLines;
      result = it->GetNumLines(&countLines);
      if (NS_FAILED(result))
        return NS_OK;//do not handle
      PRInt32 i;
      PRInt32 lineFrameCount;
      nsIFrame *firstFrame;
      nsRect  rect;
      PRInt32 closestLine = 0;
      PRInt32 closestDistance = 999999; //some HUGE number that will always fail first comparison
      //incase we hit another block frame.
      for (i = 0; i< countLines;i++)
      {
        PRUint32 flags;
        result = it->GetLine(i, &firstFrame, &lineFrameCount,rect,&flags);
        if (NS_FAILED(result))
          continue;//do not handle
        rect+=origin;
        rect.width = aEvent->point.x - rect.x+1;//EXTEND RECT TO REACH POINT
        if (rect.Contains(aEvent->point.x, aEvent->point.y))
        {
          closestLine = i;
          break;
        }
        else
        {
          PRInt32 distance = PR_MIN(abs(rect.y - aEvent->point.y),abs((rect.y + rect.height) - aEvent->point.y));
          if (distance < closestDistance)
          {
            closestDistance = distance;
            closestLine = i;
          }
          else if (distance > closestDistance)
            break;//done
        }
      }
      //we will now ask where to go. if we cant find what we want"aka another block frame" 
      //we drill down again
      nsPeekOffsetStruct pos;
      pos.mTracker = tracker;
      pos.mDirection = eDirNext;
      pos.mDesiredX = aEvent->point.x;
      
      result = nsFrame::GetNextPrevLineFromeBlockFrame(aPresContext,
                                          &pos,
                                          mainframe, 
                                          closestLine-1, 
                                          0
                                          );
      
      if (NS_SUCCEEDED(result) && pos.mResultFrame){
        result = pos.mResultFrame->QueryInterface(NS_GET_IID(nsILineIterator),getter_AddRefs(it));//if this fails thats ok
        resultFrame = pos.mResultFrame;
        mainframe = resultFrame;
      }
      else
        break;//time to go nothing was found
    }
    //end while loop. if nssucceeded resutl then keep going that means
    //we have successfully hit another block frame and we should keep going.


    if (resultFrame)
      return resultFrame->HandleEvent(aPresContext, aEvent, aEventStatus);
    else
      return NS_OK; //just stop it
  }
  return NS_OK;
}


NS_IMETHODIMP
nsBlockFrame::GetFrameForPoint(nsIPresContext* aPresContext,
                               const nsPoint& aPoint,
                               nsFramePaintLayer aWhichLayer,
                               nsIFrame** aFrame)
{
  nsresult rv;

  switch (aWhichLayer) {
    case NS_FRAME_PAINT_LAYER_FOREGROUND:
      rv = GetFrameForPointUsing(aPresContext, aPoint, nsnull, NS_FRAME_PAINT_LAYER_FOREGROUND, PR_FALSE, aFrame);
      if (NS_OK == rv) {
        return NS_OK;
      }
      if (nsnull != mBullet) {
        rv = GetFrameForPointUsing(aPresContext, aPoint, nsLayoutAtoms::bulletList, NS_FRAME_PAINT_LAYER_FOREGROUND, PR_FALSE, aFrame);
      }
      return rv;
      break;

    case NS_FRAME_PAINT_LAYER_FLOATERS:
      // we painted our floaters before our children, and thus
      // we should check floaters within children first
      rv = GetFrameForPointUsing(aPresContext, aPoint, nsnull, NS_FRAME_PAINT_LAYER_FLOATERS, PR_FALSE, aFrame);
      if (NS_OK == rv) {
        return NS_OK;
      }
      if (mFloaters.NotEmpty()) {

        rv = GetFrameForPointUsing(aPresContext, aPoint, nsLayoutAtoms::floaterList, NS_FRAME_PAINT_LAYER_FOREGROUND, PR_FALSE, aFrame);
        if (NS_OK == rv) {
          return NS_OK;
        }

        rv = GetFrameForPointUsing(aPresContext, aPoint, nsLayoutAtoms::floaterList, NS_FRAME_PAINT_LAYER_FLOATERS, PR_FALSE, aFrame);
        if (NS_OK == rv) {
          return NS_OK;
        }

        return GetFrameForPointUsing(aPresContext, aPoint, nsLayoutAtoms::floaterList, NS_FRAME_PAINT_LAYER_BACKGROUND, PR_FALSE, aFrame);

      } else {
        return NS_ERROR_FAILURE;
      }
      break;

    case NS_FRAME_PAINT_LAYER_BACKGROUND:
      // we're a block, so PR_TRUE for consider self
      return GetFrameForPointUsing(aPresContext, aPoint, nsnull, NS_FRAME_PAINT_LAYER_BACKGROUND, PR_TRUE, aFrame);
      break;
  }
  // we shouldn't get here
  NS_ASSERTION(PR_FALSE, "aWhichLayer was not understood");
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild)
{  
  // Mark the line containing the child frame dirty.
  if (aChild) {    
    PRBool isFloater;
    nsLineBox* prevLine;
    nsLineBox* line = FindLineFor(aChild, &prevLine, &isFloater);
    
    if (!isFloater) {
      if (line) MarkLineDirty(line, prevLine);
    }
    else {
      line = mLines;
      while (nsnull != line) {
        line->MarkDirty();
        line = line->mNext;
      }
#ifdef DEBUG_nisheeth      
      NS_ASSERTION(0, "nsBlockFrame::ReflowDirtyChild: Marked all lines dirty.");
#endif
    }
  }

  // Either generate a reflow command to reflow the dirty child or 
  // coalesce this reflow request with an existing reflow command    
  if (!(mState & NS_FRAME_HAS_DIRTY_CHILDREN)) {
    // If this is the first dirty child, 
    // post a dirty children reflow command targeted at yourself
    mState |= NS_FRAME_HAS_DIRTY_CHILDREN;

    nsFrame::CreateAndPostReflowCommand(aPresShell, this, 
      nsIReflowCommand::ReflowDirty, nsnull, nsnull, nsnull);
  }
  else {
    if (!(mState & NS_FRAME_IS_DIRTY)) {      
      // Mark yourself as dirty
      mState |= NS_FRAME_IS_DIRTY;

      // Cancel the dirty children reflow command you posted earlier
      nsIReflowCommand::ReflowType type = nsIReflowCommand::ReflowDirty;
      aPresShell->CancelReflowCommand(this, &type);

      // Pass up the reflow request to the parent frame.
      mParent->ReflowDirtyChild(aPresShell, this);
    }
  }
  
  return NS_OK;
}

//////////////////////////////////////////////////////////////////////
// Debugging

#ifdef NS_DEBUG
static PRBool
InLineList(nsLineBox* aLines, nsIFrame* aFrame)
{
  while (nsnull != aLines) {
    nsIFrame* frame = aLines->mFirstChild;
    PRInt32 n = aLines->GetChildCount();
    while (--n >= 0) {
      if (frame == aFrame) {
        return PR_TRUE;
      }
      frame->GetNextSibling(&frame);
    }
    aLines = aLines->mNext;
  }
  return PR_FALSE;
}

static PRBool
InSiblingList(nsLineBox* 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(nsIPresContext* aPresContext, nsIFrame* aFrame)
{
  nsIFrame* parent;
  aFrame->GetParent(&parent);
  if (parent != (nsIFrame*)this) {
    return PR_FALSE;
  }
  if (InLineList(mLines, aFrame) && InSiblingList(mLines, aFrame)) {
    return PR_TRUE;
  }
  nsLineBox* overflowLines = GetOverflowLines(aPresContext, PR_FALSE);
  if (InLineList(overflowLines, aFrame) && InSiblingList(overflowLines, aFrame)) {
    return PR_TRUE;
  }
  return PR_FALSE;
}

NS_IMETHODIMP
nsBlockFrame::VerifyTree() const
{
  // XXX rewrite this
  return NS_OK;
}

NS_IMETHODIMP
nsBlockFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const
{
  if (!aHandler || !aResult) {
    return NS_ERROR_NULL_POINTER;
  }

  PRUint32 sum = sizeof(*this);

  // Add in size of each line object
  nsLineBox* line = mLines;
  while (line) {
    PRUint32  lineBoxSize;
    nsIAtom* atom = line->SizeOf(aHandler, &lineBoxSize);
    aHandler->AddSize(atom, lineBoxSize);
    line = line->mNext;
  }

  // Add in text-run data
  nsTextRun* runs = mTextRuns;
  while (runs) {
    PRUint32 runSize;
    runs->SizeOf(aHandler, &runSize);
    aHandler->AddSize(nsLayoutAtoms::textRun, runSize);
    runs = runs->GetNext();
  }

  *aResult = sum;
  return NS_OK;
}
#endif

//----------------------------------------------------------------------

NS_IMETHODIMP
nsBlockFrame::Init(nsIPresContext*  aPresContext,
                   nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIStyleContext* aContext,
                   nsIFrame*        aPrevInFlow)
{
  if (aPrevInFlow) {
    // Copy over the block/area frame type flags
    nsBlockFrame*  blockFrame = (nsBlockFrame*)aPrevInFlow;

    SetFlags(blockFrame->mState & NS_BLOCK_FLAGS_MASK);
  }

  nsresult rv = nsBlockFrameSuper::Init(aPresContext, aContent, aParent,
                                        aContext, aPrevInFlow);
  if (nsBlockReflowContext::IsHTMLParagraph(this)) {
    mState |= NS_BLOCK_IS_HTML_PARAGRAPH;
  }
  return rv;
}

nsIStyleContext*
nsBlockFrame::GetFirstLetterStyle(nsIPresContext* aPresContext)
{
  nsIStyleContext* fls;
  aPresContext->ProbePseudoStyleContextFor(mContent,
                                           nsHTMLAtoms::firstLetterPseudo,
                                           mStyleContext, PR_FALSE, &fls);
  return fls;
}

NS_IMETHODIMP
nsBlockFrame::SetInitialChildList(nsIPresContext* aPresContext,
                                  nsIAtom*        aListName,
                                  nsIFrame*       aChildList)
{
  nsresult rv = NS_OK;

  if (nsLayoutAtoms::floaterList == aListName) {
    mFloaters.SetFrames(aChildList);
  }
  else {

    // Lookup up the two pseudo style contexts
    if (nsnull == mPrevInFlow) {
      nsIStyleContext* firstLetterStyle = GetFirstLetterStyle(aPresContext);
      if (nsnull != firstLetterStyle) {
        mState |= NS_BLOCK_HAS_FIRST_LETTER_STYLE;
#ifdef NOISY_FIRST_LETTER
        ListTag(stdout);
        printf(": first-letter style found\n");
#endif
        NS_RELEASE(firstLetterStyle);
      }
    }

    rv = AddFrames(aPresContext, aChildList, nsnull);
    if (NS_FAILED(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)) {
      // Resolve style for the bullet frame
      nsIStyleContext* kidSC;
      aPresContext->ResolvePseudoStyleContextFor(mContent, 
                                             nsHTMLAtoms::mozListBulletPseudo,
                                             mStyleContext, PR_FALSE, &kidSC);

      // Create bullet frame
      nsCOMPtr<nsIPresShell> shell;
      aPresContext->GetShell(getter_AddRefs(shell));
      mBullet = new (shell.get()) nsBulletFrame;

      if (nsnull == mBullet) {
        NS_RELEASE(kidSC);
        return NS_ERROR_OUT_OF_MEMORY;
      }
      mBullet->Init(aPresContext, mContent, this, kidSC, nsnull);
      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) {
        AddFrames(aPresContext, mBullet, nsnull);
        mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
      }
      else {
        mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
      }
    }
  }

  return NS_OK;
}

PRBool
nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
{
  const nsStyleContent* styleContent;
  aFrame->GetStyleData(eStyleStruct_Content,
                       (const nsStyleStruct*&) styleContent);
  if (0 != styleContent->CounterResetCount()) {
    // Winner
    return PR_TRUE;
  }
  return PR_FALSE;
}

void
nsBlockFrame::RenumberLists(nsIPresContext* aPresContext)
{
  if (!FrameStartsCounterScope(this)) {
    // If this frame doesn't start a counter scope then we don't need
    // to renumber child list items.
    return;
  }

  // Setup initial list ordinal value
  // XXX Map html's start property to counter-reset style
  PRInt32 ordinal = 1;
  nsIHTMLContent* hc;
  if (mContent && (NS_OK == mContent->QueryInterface(kIHTMLContentIID, (void**) &hc))) {
    nsHTMLValue value;
    if (NS_CONTENT_ATTR_HAS_VALUE ==
        hc->GetHTMLAttribute(nsHTMLAtoms::start, value)) {
      if (eHTMLUnit_Integer == value.GetUnit()) {
        ordinal = value.GetIntValue();
        if (ordinal <= 0) {
          ordinal = 1;
        }
      }
    }
    NS_RELEASE(hc);
  }

  // Get to first-in-flow
  nsBlockFrame* block = (nsBlockFrame*) GetFirstInFlow();
  RenumberListsInBlock(aPresContext, block, &ordinal);
}

PRBool
nsBlockFrame::RenumberListsInBlock(nsIPresContext* aPresContext,
                                   nsBlockFrame* aBlockFrame,
                                   PRInt32* aOrdinal)
{
  PRBool renumberedABullet = PR_FALSE;

  while (nsnull != aBlockFrame) {
    // Examine each line in the block
    nsLineBox* line = aBlockFrame->mLines;
    while (line) {
      nsIFrame* kid = line->mFirstChild;
      PRInt32 n = line->GetChildCount();
      while (--n >= 0) {
        PRBool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal);
        if (kidRenumberedABullet) {
          line->MarkDirty();
          renumberedABullet = PR_TRUE;
        }
        kid->GetNextSibling(&kid);
      }
      line = line->mNext;
    }

    // Advance to the next continuation
    aBlockFrame->GetNextInFlow((nsIFrame**) &aBlockFrame);
  }

  return renumberedABullet;
}

// XXX temporary code: after ib work is done in frame construction
// code this can be removed.
PRBool
nsBlockFrame::RenumberListsIn(nsIPresContext* aPresContext,
                              nsIFrame* aContainerFrame,
                              PRInt32* aOrdinal)
{
  PRBool renumberedABullet = PR_FALSE;

  // For each flow-block...
  while (nsnull != aContainerFrame) {
    // For each frame in the flow-block...
    nsIFrame* kid;
    aContainerFrame->FirstChild(aPresContext, nsnull, &kid);
    while (nsnull != kid) {
      PRBool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal);
      if (kidRenumberedABullet) {
        renumberedABullet = PR_TRUE;
      }
      kid->GetNextSibling(&kid);
    }
    aContainerFrame->GetNextInFlow(&aContainerFrame);
  }
  return renumberedABullet;
}

PRBool
nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext,
                               nsIFrame* aKid,
                               PRInt32* aOrdinal)
{
  PRBool kidRenumberedABullet = PR_FALSE;

  // 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;
  aKid->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 foreign has crept in.
    nsBlockFrame* listItem;
    nsresult rv = aKid->QueryInterface(kBlockFrameCID, (void**)&listItem);
    if (NS_SUCCEEDED(rv)) {
      if (nsnull != listItem->mBullet) {
        PRBool changed;
        *aOrdinal = listItem->mBullet->SetListItemOrdinal(*aOrdinal,
                                                          &changed);
        if (changed) {
          kidRenumberedABullet = PR_TRUE;
        }
      }

      // XXX temporary? if the list-item has child list-items they
      // should be numbered too; especially since the list-item is
      // itself (ASSUMED!) not to be a counter-reseter.
      PRBool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal);
      if (meToo) {
        kidRenumberedABullet = PR_TRUE;
      }
    }
  }
  else if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
    if (FrameStartsCounterScope(aKid)) {
      // Don't bother recursing into a block frame that is a new
      // counter scope. Any list-items in there will be handled by
      // it.
    }
    else {
      // If the display=block element ISA block-frame then go
      // ahead and recurse into it as it might have child
      // list-items.
      nsBlockFrame* kidBlock;
      nsresult rv = aKid->QueryInterface(kBlockFrameCID, (void**) &kidBlock);
      if (NS_SUCCEEDED(rv)) {
        kidRenumberedABullet = RenumberListsInBlock(aPresContext, kidBlock, aOrdinal);
      }
    }
  } else if (NS_STYLE_DISPLAY_INLINE == display->mDisplay) {
    // XXX temporary code: after ib work is done in frame construction
    // code this can be removed.

    // If the display=inline element ISA nsInlineFrame then go
    // ahead and recurse into it as it might have child
    // list-items.
    nsInlineFrame* kidInline;
    nsresult rv = aKid->QueryInterface(nsInlineFrame::kInlineFrameCID,
                                       (void**) &kidInline);
    if (NS_SUCCEEDED(rv)) {
      kidRenumberedABullet = RenumberListsIn(aPresContext, aKid, aOrdinal);
    }
  }
  return kidRenumberedABullet;
}

void
nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
                           nsHTMLReflowMetrics& aMetrics)
{
  // Reflow the bullet now
  nsSize availSize;
  availSize.width = NS_UNCONSTRAINEDSIZE;
  availSize.height = NS_UNCONSTRAINEDSIZE;
  nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
                                mBullet, availSize);
  nsReflowStatus  status;
  mBullet->WillReflow(aState.mPresContext);
  mBullet->Reflow(aState.mPresContext, aMetrics, reflowState, status);

  // Place the bullet now; use its right margin to distance it
  // from the rest of the frames in the line
  nscoord x = - reflowState.mComputedMargin.right - aMetrics.width;

  // Approximate the bullets position; vertical alignment will provide
  // the final vertical location.
  const nsMargin& bp = aState.BorderPadding();
  nscoord y = bp.top;
  mBullet->SetRect(aState.mPresContext, nsRect(x, y, aMetrics.width, aMetrics.height));
  mBullet->DidReflow(aState.mPresContext, NS_FRAME_REFLOW_FINISHED);
}

//XXX get rid of this -- its slow
void
nsBlockFrame::BuildFloaterList()
{
  nsIFrame* head = nsnull;
  nsIFrame* current = nsnull;
  nsLineBox* line = mLines;
  while (nsnull != line) {
    if (line->HasFloaters()) {
      nsFloaterCache* fc = line->GetFirstFloater();
      while (fc) {
        nsIFrame* floater = fc->mPlaceholder->GetOutOfFlowFrame();
        if (nsnull == head) {
          current = head = floater;
        }
        else {
          current->SetNextSibling(floater);
          current = floater;
        }
        fc = fc->Next();
      }
    }
    line = line->mNext;
  }

  // Terminate end of floater list just in case a floater was removed
  if (nsnull != current) {
    current->SetNextSibling(nsnull);
  }
  mFloaters.SetFrames(head);
}

// XXX keep the text-run data in the first-in-flow of the block

// XXX Switch to an interface to pass to child frames -or- do the
// grovelling directly ourselves?
nsresult
nsBlockFrame::ComputeTextRuns(nsIPresContext* aPresContext)
{
  // Destroy old run information first
  nsTextRun::DeleteTextRuns(mTextRuns);
  mTextRuns = nsnull;

  nsLineLayout textRunThingy(aPresContext);

  // Ask each child to find its text runs
  nsLineBox* line = mLines;
  while (nsnull != line) {
    if (!line->IsBlock()) {
      nsIFrame* frame = line->mFirstChild;
      PRInt32 n = line->GetChildCount();
      while (--n >= 0) {
        frame->FindTextRuns(textRunThingy);
        frame->GetNextSibling(&frame);
      }
    }
    else {
      // A block frame isn't text therefore it will end an open text
      // run.
      textRunThingy.EndTextRun();
    }
    line = line->mNext;
  }
  textRunThingy.EndTextRun();

  // Now take the text-runs away from the line layout engine.
  mTextRuns = textRunThingy.TakeTextRuns();
  return NS_OK;
}

#ifdef DEBUG
void
nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
{
  if (!gVerifyLines) {
    return;
  }
  nsLineBox* line = mLines;
  if (!line) {
    return;
  }

  // Add up the counts on each line. Also validate that IsFirstLine is
  // set properly.
  PRInt32 count = 0;
  PRBool seenBlock = PR_FALSE;
  while (nsnull != line) {
    if (aFinalCheckOK) {
      NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line");
      if (line->IsBlock()) {
        seenBlock = PR_TRUE;
      }
      if (line->IsBlock()) {
        NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
      }
    }
    count += line->GetChildCount();
    line = line->mNext;
  }

  // Then count the frames
  PRInt32 frameCount = 0;
  nsIFrame* frame = mLines->mFirstChild;
  while (nsnull != frame) {
    frameCount++;
    frame->GetNextSibling(&frame);
  }
  NS_ASSERTION(count == frameCount, "bad line list");

  // Next: test that each line has right number of frames on it
  line = mLines;
  nsLineBox* prevLine = nsnull;
  while (nsnull != line) {
    count = line->GetChildCount();
    frame = line->mFirstChild;
    while (--count >= 0) {
      frame->GetNextSibling(&frame);
    }
    prevLine = line;
    line = line->mNext;
    if ((nsnull != line) && (0 != line->GetChildCount())) {
      NS_ASSERTION(frame == line->mFirstChild, "bad line list");
    }
  }
}

// Its possible that a frame can have some frames on an overflow
// list. But its never possible for multiple frames to have overflow
// lists. Check that this fact is actually true.
void
nsBlockFrame::VerifyOverflowSituation(nsIPresContext* aPresContext)
{
  nsBlockFrame* flow = (nsBlockFrame*) GetFirstInFlow();
  while (nsnull != flow) {
    nsLineBox* overflowLines = GetOverflowLines(aPresContext, PR_FALSE);
    if (nsnull != overflowLines) {
      NS_ASSERTION(nsnull != overflowLines->mFirstChild,
                   "bad overflow list");
    }
    flow = (nsBlockFrame*) flow->mNextInFlow;
  }
}

PRInt32
nsBlockFrame::GetDepth() const
{
  PRInt32 depth = 0;
  nsIFrame* parent = mParent;
  while (nsnull != parent) {
    parent->GetParent(&parent);
    depth++;
  }
  return depth;
}
#endif
