/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla 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/MPL/
 *
 * 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 the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
#include "nsCOMPtr.h"
#include "nsStyleConsts.h"
#include "nsCSSAnonBoxes.h"
#include "nsFrame.h"
#include "nsIContent.h"
#include "nsHTMLAtoms.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
#include "nsLayoutAtoms.h"
#include "nsIDeviceContext.h"
#include "nsIRenderingContext.h"
#include "nsIFontMetrics.h"
#include "nsBlockFrame.h"
#include "nsLineBox.h"
#include "nsImageFrame.h"
#include "nsIServiceManager.h"
#include "nsIPercentHeightObserver.h"
#include "nsContentUtils.h"
#include "nsLayoutUtils.h"
#ifdef IBMBIDI
#include "nsBidiUtils.h"
#endif

#ifdef NS_DEBUG
#undef NOISY_VERTICAL_ALIGN
#else
#undef NOISY_VERTICAL_ALIGN
#endif

// Prefs-driven control for |text-decoration: blink|
static PRPackedBool sPrefIsLoaded = PR_FALSE;
static PRPackedBool sBlinkIsAllowed = PR_TRUE;

enum eNormalLineHeightControl {
  eUninitialized = -1,
  eNoExternalLeading = 0,   // does not include external leading 
  eIncludeExternalLeading,  // use whatever value font vendor provides
  eCompensateLeading        // compensate leading if leading provided by font vendor is not enough
};

#ifdef FONT_LEADING_APIS_V2
static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized;
#endif

#ifdef DEBUG
const char*
nsHTMLReflowState::ReasonToString(nsReflowReason aReason)
{
  static const char* reasons[] = {
    "initial", "incremental", "resize", "style-change", "dirty"
  };

  return reasons[aReason];
}
#endif

// Initialize a <b>root</b> reflow state with a rendering context to
// use for measuring things.
nsHTMLReflowState::nsHTMLReflowState(nsPresContext*      aPresContext,
                                     nsIFrame*            aFrame,
                                     nsReflowReason       aReason,
                                     nsIRenderingContext* aRenderingContext,
                                     const nsSize&        aAvailableSpace)
  : mReflowDepth(0)
{
  NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");
  parentReflowState = nsnull;
  frame = aFrame;
  reason = aReason;
  path = nsnull;
  availableWidth = aAvailableSpace.width;
  availableHeight = aAvailableSpace.height;
  rendContext = aRenderingContext;
  mSpaceManager = nsnull;
  mLineLayout = nsnull;
  mFlags.mSpecialHeightReflow = PR_FALSE;
  mFlags.mIsTopOfPage = PR_FALSE;
  mFlags.mNextInFlowUntouched = PR_FALSE;
  mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
  mFlags.mHasClearance = PR_FALSE;
  mDiscoveredClearance = nsnull;
  mPercentHeightObserver = nsnull;
  mPercentHeightReflowInitiator = nsnull;
  Init(aPresContext);
#ifdef IBMBIDI
  mFlags.mVisualBidiFormControl = IsBidiFormControl(aPresContext);
  mRightEdge = NS_UNCONSTRAINEDSIZE;
#endif
}

// Initialize a <b>root</b> reflow state for an <b>incremental</b>
// reflow.
nsHTMLReflowState::nsHTMLReflowState(nsPresContext*      aPresContext,
                                     nsIFrame*            aFrame,
                                     nsReflowPath*        aReflowPath,
                                     nsIRenderingContext* aRenderingContext,
                                     const nsSize&        aAvailableSpace)
  : mReflowDepth(0)
{
  NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");  

  reason = eReflowReason_Incremental;
  path = aReflowPath;
  parentReflowState = nsnull;
  frame = aFrame;
  availableWidth = aAvailableSpace.width;
  availableHeight = aAvailableSpace.height;
  rendContext = aRenderingContext;
  mSpaceManager = nsnull;
  mLineLayout = nsnull;
  mFlags.mSpecialHeightReflow = PR_FALSE;
  mFlags.mIsTopOfPage = PR_FALSE;
  mFlags.mNextInFlowUntouched = PR_FALSE;
  mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
  mFlags.mHasClearance = PR_FALSE;
  mDiscoveredClearance = nsnull;
  mPercentHeightObserver = nsnull;
  mPercentHeightReflowInitiator = nsnull;
  Init(aPresContext);
#ifdef IBMBIDI
  mFlags.mVisualBidiFormControl = IsBidiFormControl(aPresContext);
  mRightEdge = NS_UNCONSTRAINEDSIZE;
#endif // IBMBIDI
}

static PRBool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent)
{
  nsIFrame* frameNext = aFrame->GetNextInFlow();
  nsIFrame* parentNext = aParent->GetNextInFlow();
  return frameNext && parentNext && frameNext->GetParent() == parentNext;
}

// Initialize a reflow state for a child frames reflow. Some state
// is copied from the parent reflow state; the remaining state is
// computed.
nsHTMLReflowState::nsHTMLReflowState(nsPresContext*          aPresContext,
                                     const nsHTMLReflowState& aParentReflowState,
                                     nsIFrame*                aFrame,
                                     const nsSize&            aAvailableSpace,
                                     nsReflowReason           aReason,
                                     PRBool                   aInit)
  : mReflowDepth(aParentReflowState.mReflowDepth + 1),
    mFlags(aParentReflowState.mFlags)
{
  parentReflowState = &aParentReflowState;
  frame = aFrame;
  reason = aReason;
  if (reason == eReflowReason_Incremental) {
    // If the child frame isn't along the reflow path, then convert
    // the incremental reflow to a dirty reflow.
    path = aParentReflowState.path->GetSubtreeFor(aFrame);
    if (! path)
      reason = eReflowReason_Dirty;
  }
  else
    path = nsnull;

  availableWidth = aAvailableSpace.width;
  availableHeight = aAvailableSpace.height;

  rendContext = aParentReflowState.rendContext;
  mSpaceManager = aParentReflowState.mSpaceManager;
  mLineLayout = aParentReflowState.mLineLayout;
  mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
  mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
    CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
  mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
  mFlags.mHasClearance = PR_FALSE;
  mDiscoveredClearance = nsnull;
  mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && 
                            aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) 
                           ? aParentReflowState.mPercentHeightObserver : nsnull;
  mPercentHeightReflowInitiator = aParentReflowState.mPercentHeightReflowInitiator;

  if (aInit) {
    Init(aPresContext);
  }

#ifdef IBMBIDI
  mFlags.mVisualBidiFormControl = (aParentReflowState.mFlags.mVisualBidiFormControl) ?
                                  PR_TRUE : IsBidiFormControl(aPresContext);
  mRightEdge = aParentReflowState.mRightEdge;
#endif // IBMBIDI
}

// Same as the previous except that the reason is taken from the
// parent's reflow state.
nsHTMLReflowState::nsHTMLReflowState(nsPresContext*          aPresContext,
                                     const nsHTMLReflowState& aParentReflowState,
                                     nsIFrame*                aFrame,
                                     const nsSize&            aAvailableSpace)
  : mReflowDepth(aParentReflowState.mReflowDepth + 1),
    mFlags(aParentReflowState.mFlags)
{
  parentReflowState = &aParentReflowState;
  frame = aFrame;
  reason = aParentReflowState.reason;
  if (reason == eReflowReason_Incremental) {
    // If the child frame isn't along the reflow path, then convert
    // the incremental reflow to a dirty reflow.
    path = aParentReflowState.path->GetSubtreeFor(aFrame);
    if (! path)
      reason = eReflowReason_Dirty;
  }
  else
    path = nsnull;

  availableWidth = aAvailableSpace.width;
  availableHeight = aAvailableSpace.height;

  rendContext = aParentReflowState.rendContext;
  mSpaceManager = aParentReflowState.mSpaceManager;
  mLineLayout = aParentReflowState.mLineLayout;
  mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
  mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
    CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
  mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
  mFlags.mHasClearance = PR_FALSE;
  mDiscoveredClearance = nsnull;
  mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && 
                            aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) 
                           ? aParentReflowState.mPercentHeightObserver : nsnull;
  mPercentHeightReflowInitiator = aParentReflowState.mPercentHeightReflowInitiator;

  Init(aPresContext);

#ifdef IBMBIDI
  mFlags.mVisualBidiFormControl = (aParentReflowState.mFlags.mVisualBidiFormControl) ?
                                   PR_TRUE : IsBidiFormControl(aPresContext);
  mRightEdge = aParentReflowState.mRightEdge;
#endif // IBMBIDI
}

// Version that species the containing block width and height
nsHTMLReflowState::nsHTMLReflowState(nsPresContext*          aPresContext,
                                     const nsHTMLReflowState& aParentReflowState,
                                     nsIFrame*                aFrame,
                                     const nsSize&            aAvailableSpace,
                                     nscoord                  aContainingBlockWidth,
                                     nscoord                  aContainingBlockHeight,
                                     nsReflowReason           aReason)
  : mReflowDepth(aParentReflowState.mReflowDepth + 1),
    mFlags(aParentReflowState.mFlags)
{
  parentReflowState = &aParentReflowState;
  frame = aFrame;
  reason = aReason;
  if (reason == eReflowReason_Incremental) {
    // If the child frame isn't along the reflow path, then convert
    // the incremental reflow to a dirty reflow.
    path = aParentReflowState.path->GetSubtreeFor(aFrame);
    if (! path)
      reason = eReflowReason_Dirty;
  }
  else
    path = nsnull;

  availableWidth = aAvailableSpace.width;
  availableHeight = aAvailableSpace.height;

  rendContext = aParentReflowState.rendContext;
  mSpaceManager = aParentReflowState.mSpaceManager;
  mLineLayout = aParentReflowState.mLineLayout;
  mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
  mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
    CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
  mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
  mFlags.mHasClearance = PR_FALSE;
  mDiscoveredClearance = nsnull;
  mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver && 
                            aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this)) 
                           ? aParentReflowState.mPercentHeightObserver : nsnull;
  mPercentHeightReflowInitiator = aParentReflowState.mPercentHeightReflowInitiator;

  Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight);

#ifdef IBMBIDI
  mFlags.mVisualBidiFormControl = (aParentReflowState.mFlags.mVisualBidiFormControl) ?
                                   PR_TRUE : IsBidiFormControl(aPresContext);
  mRightEdge = aParentReflowState.mRightEdge;
#endif // IBMBIDI
}

void
nsHTMLReflowState::Init(nsPresContext* aPresContext,
                        nscoord         aContainingBlockWidth,
                        nscoord         aContainingBlockHeight,
                        nsMargin*       aBorder,
                        nsMargin*       aPadding)
{
  mCompactMarginWidth = 0;
#ifdef DEBUG
  mDebugHook = nsnull;
#endif

  mStylePosition = frame->GetStylePosition();
  mStyleDisplay = frame->GetStyleDisplay();
  mStyleVisibility = frame->GetStyleVisibility();
  mStyleBorder = frame->GetStyleBorder();
  mStyleMargin = frame->GetStyleMargin();
  mStylePadding = frame->GetStylePadding();
  mStyleText = frame->GetStyleText();

  InitFrameType();
  InitCBReflowState();
  InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, aBorder, aPadding);
}

void nsHTMLReflowState::InitCBReflowState()
{
  if (!parentReflowState) {
    mCBReflowState = nsnull;
    return;
  }

  if (parentReflowState->frame->IsContainingBlock() ||
      // Absolutely positioned frames should always be kids of the frames that
      // determine their containing block
      (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE)) {
    // a block inside a table cell needs to use the table cell
    if (parentReflowState->parentReflowState &&
        IS_TABLE_CELL(parentReflowState->parentReflowState->frame->GetType())) {
      mCBReflowState = parentReflowState->parentReflowState;
    } else {
      mCBReflowState = parentReflowState;
    }
      
    return;
  }
  
  mCBReflowState = parentReflowState->mCBReflowState;
}

const nsHTMLReflowState*
nsHTMLReflowState::GetPageBoxReflowState(const nsHTMLReflowState* aParentRS)
{
  // XXX write me as soon as we can ask a frame if it's a page frame...
  return nsnull;
}

/* static */
nscoord
nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState)
{
  const nsHTMLReflowState* rs = aReflowState->mCBReflowState;
  if (!rs)
    return 0;
  return rs->mComputedWidth;
}

nscoord
nsHTMLReflowState::AdjustIntrinsicMinContentWidthForStyle(nscoord aWidth) const
{
  nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
  if (eStyleUnit_Percent == widthUnit) {
    aWidth = 0;
  } else if (eStyleUnit_Coord == widthUnit) {
    // Sometimes we can get an unconstrained size here because we're
    // computing the maximum-width. Although it doesn't seem right
    // for max-width computation to change our computed width.
    if (NS_UNCONSTRAINEDSIZE != mComputedWidth) {
      aWidth = mComputedWidth;
    }
  }

  nsStyleUnit maxWidthUnit = mStylePosition->mMaxWidth.GetUnit();
  if (eStyleUnit_Percent == maxWidthUnit) {
    aWidth = 0;
  } else if (eStyleUnit_Coord == maxWidthUnit) {
    NS_ASSERTION(NS_UNCONSTRAINEDSIZE != mComputedMaxWidth,
                 "Should be a computed max-width here");
    aWidth = PR_MIN(aWidth, mComputedMaxWidth);
  }

  nsStyleUnit minWidthUnit = mStylePosition->mMinWidth.GetUnit();
  if (eStyleUnit_Coord == minWidthUnit) { 
    NS_ASSERTION(NS_UNCONSTRAINEDSIZE != mComputedMinWidth,
                 "Should be a computed max-width here");
    aWidth = PR_MAX(aWidth, mComputedMinWidth);
  }
  
  return aWidth;
}

nscoord
nsHTMLReflowState::AdjustIntrinsicContentWidthForStyle(nscoord aWidth) const
{
  nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
  if (eStyleUnit_Coord == widthUnit) {
    // Sometimes we can get an unconstrained size here because we're
    // computing the maximum-width. Although it doesn't seem right
    // for max-width computation to change our computed width.
    if (NS_UNCONSTRAINEDSIZE != mComputedWidth) {
      aWidth = mComputedWidth;
    }
  }

  nsStyleUnit maxWidthUnit = mStylePosition->mMaxWidth.GetUnit();
  if (eStyleUnit_Coord == maxWidthUnit) {
    NS_ASSERTION(NS_UNCONSTRAINEDSIZE != mComputedMaxWidth,
                 "Should be a computed max-width here");
    aWidth = PR_MIN(aWidth, mComputedMaxWidth);
  }

  nsStyleUnit minWidthUnit = mStylePosition->mMinWidth.GetUnit();
  if (eStyleUnit_Coord == minWidthUnit) { 
    NS_ASSERTION(NS_UNCONSTRAINEDSIZE != mComputedMinWidth,
                 "Should be a computed max-width here");
    aWidth = PR_MAX(aWidth, mComputedMinWidth);
  }
  
  return aWidth;
}

/* static */
nsIFrame*
nsHTMLReflowState::GetContainingBlockFor(const nsIFrame* aFrame)
{
  NS_PRECONDITION(aFrame, "Must have frame to work with");
  nsIFrame* container = aFrame->GetParent();
  if (aFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
    // Absolutely positioned frames are just kids of their containing
    // blocks (which may happen to be inlines).
    return container;
  }
  while (container && !container->IsContainingBlock()) {
    container = container->GetParent();
  }
  return container;
}

void
nsHTMLReflowState::InitFrameType()
{
  const nsStyleDisplay *disp = mStyleDisplay;
  nsCSSFrameType frameType;

  // Section 9.7 of the CSS2 spec indicates that absolute position
  // takes precedence over float which takes precedence over display.
  // Make sure the frame was actually moved out of the flow, and don't
  // just assume what the style says
  // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right?
  if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
    if (disp->IsAbsolutelyPositioned()) {
      frameType = NS_CSS_FRAME_TYPE_ABSOLUTE;
    }
    else {
      NS_ASSERTION(NS_STYLE_FLOAT_NONE != disp->mFloats,
                   "unknown out of flow frame type");
      frameType = NS_CSS_FRAME_TYPE_FLOATING;
    }
  }
  else {
    switch (disp->mDisplay) {
    case NS_STYLE_DISPLAY_BLOCK:
    case NS_STYLE_DISPLAY_LIST_ITEM:
    case NS_STYLE_DISPLAY_TABLE:
    case NS_STYLE_DISPLAY_TABLE_CAPTION:
      frameType = NS_CSS_FRAME_TYPE_BLOCK;
      break;

    case NS_STYLE_DISPLAY_INLINE:
    case NS_STYLE_DISPLAY_MARKER:
    case NS_STYLE_DISPLAY_INLINE_TABLE:
    case NS_STYLE_DISPLAY_INLINE_BOX:
    case NS_STYLE_DISPLAY_INLINE_GRID:
    case NS_STYLE_DISPLAY_INLINE_STACK:
      frameType = NS_CSS_FRAME_TYPE_INLINE;
      break;

    case NS_STYLE_DISPLAY_RUN_IN:
    case NS_STYLE_DISPLAY_COMPACT:
      // XXX need to look ahead at the frame's sibling
      frameType = NS_CSS_FRAME_TYPE_BLOCK;
      break;

    case NS_STYLE_DISPLAY_TABLE_CELL:
    case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
    case NS_STYLE_DISPLAY_TABLE_COLUMN:
    case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP:
    case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
    case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
    case NS_STYLE_DISPLAY_TABLE_ROW:
      frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
      break;

    case NS_STYLE_DISPLAY_NONE:
    default:
      frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
      break;
    }
  }

  // See if the frame is replaced
  if (frame->GetStateBits() & NS_FRAME_REPLACED_ELEMENT) {
    frameType = NS_FRAME_REPLACED(frameType);
  }

  mFrameType = frameType;
}

void
nsHTMLReflowState::ComputeRelativeOffsets(const nsHTMLReflowState* cbrs,
                                          nscoord aContainingBlockWidth,
                                          nscoord aContainingBlockHeight)
{
  nsStyleCoord  coord;

  // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right,
  // and 'right' moves the boxes to the left. The computed values are always:
  // left=-right
  PRBool  leftIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit();
  PRBool  rightIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit();

  // Check for percentage based values and an unconstrained containing
  // block width. Treat them like 'auto'
  if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
    if (eStyleUnit_Percent == mStylePosition->mOffset.GetLeftUnit()) {
      leftIsAuto = PR_TRUE;
    }
    if (eStyleUnit_Percent == mStylePosition->mOffset.GetRightUnit()) {
      rightIsAuto = PR_TRUE;
    }
  }

  // If neither 'left' not 'right' are auto, then we're over-constrained and
  // we ignore one of them
  if (!leftIsAuto && !rightIsAuto) {
    const nsStyleVisibility* vis = frame->GetStyleVisibility();
    
    if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
      rightIsAuto = PR_TRUE;
    } else {
      leftIsAuto = PR_TRUE;
    }
  }

  if (leftIsAuto) {
    if (rightIsAuto) {
      // If both are 'auto' (their initial values), the computed values are 0
      mComputedOffsets.left = mComputedOffsets.right = 0;
    } else {
      // 'Right' isn't 'auto' so compute its value
      ComputeHorizontalValue(aContainingBlockWidth,
                             mStylePosition->mOffset.GetRightUnit(),
                             mStylePosition->mOffset.GetRight(coord),
                             mComputedOffsets.right);
      
      // Computed value for 'left' is minus the value of 'right'
      mComputedOffsets.left = -mComputedOffsets.right;
    }

  } else {
    NS_ASSERTION(rightIsAuto, "unexpected specified constraint");
    
    // 'Left' isn't 'auto' so compute its value
    ComputeHorizontalValue(aContainingBlockWidth,
                           mStylePosition->mOffset.GetLeftUnit(),
                           mStylePosition->mOffset.GetLeft(coord),
                           mComputedOffsets.left);

    // Computed value for 'right' is minus the value of 'left'
    mComputedOffsets.right = -mComputedOffsets.left;
  }

  // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties
  // move relatively positioned elements up and down. They also must be each 
  // other's negative
  PRBool  topIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit();
  PRBool  bottomIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit();

  // Check for percentage based values and a containing block height that
  // depends on the content height. Treat them like 'auto'
  if (NS_AUTOHEIGHT == aContainingBlockHeight) {
    if (eStyleUnit_Percent == mStylePosition->mOffset.GetTopUnit()) {
      topIsAuto = PR_TRUE;
    }
    if (eStyleUnit_Percent == mStylePosition->mOffset.GetBottomUnit()) {
      bottomIsAuto = PR_TRUE;
    }
  }

  // If neither is 'auto', 'bottom' is ignored
  if (!topIsAuto && !bottomIsAuto) {
    bottomIsAuto = PR_TRUE;
  }

  if (topIsAuto) {
    if (bottomIsAuto) {
      // If both are 'auto' (their initial values), the computed values are 0
      mComputedOffsets.top = mComputedOffsets.bottom = 0;
    } else {
      // 'Bottom' isn't 'auto' so compute its value
      ComputeVerticalValue(aContainingBlockHeight,
                           mStylePosition->mOffset.GetBottomUnit(),
                           mStylePosition->mOffset.GetBottom(coord),
                           mComputedOffsets.bottom);
      
      // Computed value for 'top' is minus the value of 'bottom'
      mComputedOffsets.top = -mComputedOffsets.bottom;
    }

  } else {
    NS_ASSERTION(bottomIsAuto, "unexpected specified constraint");
    
    // 'Top' isn't 'auto' so compute its value
    ComputeVerticalValue(aContainingBlockHeight,
                         mStylePosition->mOffset.GetTopUnit(),
                         mStylePosition->mOffset.GetTop(coord),
                         mComputedOffsets.top);

    // Computed value for 'bottom' is minus the value of 'top'
    mComputedOffsets.bottom = -mComputedOffsets.top;
  }
}

// Returns the nearest containing block frame for the specified frame.
// Also returns the left, top, right, and bottom edges of the specified
// frame's content area. These are in the coordinate space of the block
// frame itself
static nsIFrame*
GetNearestContainingBlock(nsIFrame* aFrame, nsMargin& aContentArea)
{
  for (aFrame = aFrame->GetParent(); aFrame && !aFrame->IsContainingBlock();
       aFrame = aFrame->GetParent())
    /* do nothing */;

  if (aFrame) {
    nsSize  size = aFrame->GetSize();

    aContentArea.left = 0;
    aContentArea.top = 0;
    aContentArea.right = size.width;
    aContentArea.bottom = size.height;
  
    // Subtract off for border and padding. If it can't be computed because
    // it's percentage based (for example) then just ignore it
    nsStyleBorderPadding  bPad;
    nsMargin              borderPadding;
    nsStyleContext* styleContext = aFrame->GetStyleContext();
    styleContext->GetBorderPaddingFor(bPad);
    if (bPad.GetBorderPadding(borderPadding)) {
      aContentArea.left += borderPadding.left;
      aContentArea.top += borderPadding.top;
      aContentArea.right -= borderPadding.right;
      aContentArea.bottom -= borderPadding.bottom;
    }
  }

  return aFrame;
}

// When determining the hypothetical box that would have been if the element
// had been in the flow we may not be able to exactly determine both the left
// and right edges. For example, if the element is a non-replaced inline-level
// element we would have to reflow it in order to determine it desired width.
// In that case depending on the progression direction either the left or
// right edge would be marked as not being exact
struct nsHypotheticalBox {
  nscoord       mLeft, mRight;
  nscoord       mTop;
  PRPackedBool  mLeftIsExact, mRightIsExact;

  nsHypotheticalBox() {
    mLeftIsExact = mRightIsExact = PR_FALSE;
  }
};
      
static PRBool
GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize)
{
  // See if it is an image frame
  PRBool    result = PR_FALSE;

  // Currently the only type of replaced frame that we can get the intrinsic
  // size for is an image frame
  // XXX We should add back the GetReflowMetrics() function and one of the
  // things should be the intrinsic size...
  if (aFrame->GetType() == nsLayoutAtoms::imageFrame) {
    nsImageFrame* imageFrame = (nsImageFrame*)aFrame;

    imageFrame->GetIntrinsicImageSize(aIntrinsicSize);
    result = (aIntrinsicSize != nsSize(0, 0));
  }
  return result;
}

nscoord
nsHTMLReflowState::CalculateHorizBorderPaddingMargin(nscoord aContainingBlockWidth)
{
  const nsMargin& border = mStyleBorder->GetBorder();
  nsMargin padding, margin;

  // See if the style system can provide us the padding directly
  if (!mStylePadding->GetPadding(padding)) {
    nsStyleCoord left, right;

    // We have to compute the left and right values
    ComputeHorizontalValue(aContainingBlockWidth,
                           mStylePadding->mPadding.GetLeftUnit(),
                           mStylePadding->mPadding.GetLeft(left),
                           padding.left);
    ComputeHorizontalValue(aContainingBlockWidth,
                           mStylePadding->mPadding.GetRightUnit(),
                           mStylePadding->mPadding.GetRight(right),
                           padding.right);
  }

  // See if the style system can provide us the margin directly
  if (!mStyleMargin->GetMargin(margin)) {
    nsStyleCoord left, right;

    // We have to compute the left and right values
    if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
      margin.left = 0;  // just ignore
    } else {
      ComputeHorizontalValue(aContainingBlockWidth,
                             mStyleMargin->mMargin.GetLeftUnit(),
                             mStyleMargin->mMargin.GetLeft(left),
                             margin.left);
    }
    if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
      margin.right = 0;  // just ignore
    } else {
      ComputeHorizontalValue(aContainingBlockWidth,
                             mStyleMargin->mMargin.GetRightUnit(),
                             mStyleMargin->mMargin.GetRight(right),
                             margin.right);
    }
  }

  return padding.left + padding.right + border.left + border.right +
         margin.left + margin.right;
}

static nsIFrame*
FindImmediateChildOf(nsIFrame* aParent, nsIFrame* aDescendantFrame)
{
  nsIFrame* result = aDescendantFrame;

  while (result) {
    nsIFrame* parent = result->GetParent();
    if (parent == aParent) {
      break;
    }

    // The frame is not an immediate child of aParent so walk up another level
    result = parent;
  }

  return result;
}

/**
 * Returns PR_TRUE iff a pre-order traversal of the normal child
 * frames rooted at aFrame finds no non-empty frame before aDescendant.
 */
static PRBool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
  nsIFrame* aDescendant, PRBool* aFound) {
  if (aFrame == aDescendant) {
    *aFound = PR_TRUE;
    return PR_TRUE;
  }
  if (!aFrame->IsSelfEmpty()) {
    *aFound = PR_FALSE;
    return PR_FALSE;
  }
  for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) {
    PRBool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
    if (*aFound || !allEmpty) {
      return allEmpty;
    }
  }
  *aFound = PR_FALSE;
  return PR_TRUE;
}

// Calculate the hypothetical box that the element would have if it were in
// the flow. The values returned are relative to the padding edge of the
// absolute containing block
void
nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext*    aPresContext,
                                            nsIFrame*          aPlaceholderFrame,
                                            nsIFrame*          aContainingBlock,
                                            nsMargin&          aBlockContentArea,
                                            const nsHTMLReflowState* cbrs,
                                            nsHypotheticalBox& aHypotheticalBox)
{
  NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE,
               "mOriginalDisplay has not been properly initialized");
  
  // If it's a replaced element and it has a 'auto' value for 'width', see if we
  // can get the intrinsic size. This will allow us to exactly determine both the
  // left and right edges
  nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
  nsSize      intrinsicSize;
  PRBool      knowIntrinsicSize = PR_FALSE;
  if (NS_FRAME_IS_REPLACED(mFrameType) && (eStyleUnit_Auto == widthUnit)) {
    // See if we can get the intrinsic size of the element
    knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize);
  }

  // See if we can calculate what the box width would have been if the
  // element had been in the flow
  nscoord boxWidth;
  PRBool  knowBoxWidth = PR_FALSE;
  if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) &&
      !NS_FRAME_IS_REPLACED(mFrameType)) {
    // For non-replaced inline-level elements the 'width' property doesn't apply,
    // so we don't know what the width would have been without reflowing it

  } else {
    // It's either a replaced inline-level element or a block-level element
    nscoord horizBorderPaddingMargin;

    // Determine the total amount of horizontal border/padding/margin that
    // the element would have had if it had been in the flow. Note that we
    // ignore any 'auto' and 'inherit' values
    horizBorderPaddingMargin = CalculateHorizBorderPaddingMargin(aBlockContentArea.right -
                                                                 aBlockContentArea.left);

    if (NS_FRAME_IS_REPLACED(mFrameType) && (eStyleUnit_Auto == widthUnit)) {
      // It's a replaced element with an 'auto' width so the box width is
      // its intrinsic size plus any border/padding/margin
      if (knowIntrinsicSize) {
        boxWidth = intrinsicSize.width + horizBorderPaddingMargin;
        knowBoxWidth = PR_TRUE;
      }

    } else if (eStyleUnit_Auto == widthUnit) {
      // The box width is the containing block width
      boxWidth = aBlockContentArea.right - aBlockContentArea.left;
      knowBoxWidth = PR_TRUE;
    
    } else {
      // We need to compute it. It's important we do this, because if it's
      // percentage based this computed value may be different from the comnputed
      // value calculated using the absolute containing block width
      ComputeHorizontalValue(aBlockContentArea.right - aBlockContentArea.left,
                             widthUnit, mStylePosition->mWidth, boxWidth);
      boxWidth += horizBorderPaddingMargin;
      knowBoxWidth = PR_TRUE;
    }
  }
  
  // Get the 'direction' of the block
  const nsStyleVisibility* blockVis = aContainingBlock->GetStyleVisibility();

  // Get the placeholder x-offset and y-offset in the coordinate
  // space of the block frame that contains it
  // XXXbz the placeholder is not fully reflown yet if our containing block is
  // relatively positioned...
  nsPoint placeholderOffset = aPlaceholderFrame->GetOffsetTo(aContainingBlock);

  // First, determine the hypothetical box's mTop
  nsBlockFrame* blockFrame;
  if (NS_SUCCEEDED(aContainingBlock->QueryInterface(kBlockFrameCID,
                                  NS_REINTERPRET_CAST(void**, &blockFrame)))) {
    // We need the immediate child of the block frame, and that may not be
    // the placeholder frame
    nsIFrame *blockChild = FindImmediateChildOf(blockFrame, aPlaceholderFrame);
    nsBlockFrame::line_iterator lineBox = blockFrame->FindLineFor(blockChild);

    // How we determine the hypothetical box depends on whether the element
    // would have been inline-level or block-level
    if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
      // Use the top of the inline box which the placeholder lives in as the
      // hypothetical box's top.
      aHypotheticalBox.mTop = lineBox->mBounds.y;
    } else {
      // The element would have been block-level which means it would be below
      // the line containing the placeholder frame, unless all the frames
      // before it are empty.  In that case, it would have been just before
      // this line.      
      // XXXbz the line box is not fully reflown yet if our containing block is
      // relatively positioned...
      if (lineBox != blockFrame->end_lines()) {
        nsIFrame * firstFrame = lineBox->mFirstChild;
        PRBool found = PR_FALSE;
        PRBool allEmpty = PR_TRUE;
        while (firstFrame) { // See bug 223064
          allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
            aPlaceholderFrame, &found);
          if (found || !allEmpty)
            break;
          firstFrame = firstFrame->GetNextSibling();
        }
        NS_ASSERTION(firstFrame, "Couldn't find placeholder!");

        if (allEmpty) {
          // The top of the hypothetical box is the top of the line containing
          // the placeholder, since there is nothing in the line before our
          // placeholder except empty frames.
          aHypotheticalBox.mTop = lineBox->mBounds.y;
        } else {
          // The top of the hypothetical box is just below the line containing
          // the placeholder.
          aHypotheticalBox.mTop = lineBox->mBounds.YMost();
        }
      } else {
        // Just use the placeholder's y-offset
        aHypotheticalBox.mTop = placeholderOffset.y;
      }
    }
  } else {
    // The containing block is not a block, so it's probably something
    // like a XUL box, etc.
    // Just use the placeholder's y-offset
    aHypotheticalBox.mTop = placeholderOffset.y;
  }

  // Second, determine the hypothetical box's mLeft & mRight
  // To determine the left and right offsets we need to look at the block's 'direction'
  if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) {
    // How we determine the hypothetical box depends on whether the element
    // would have been inline-level or block-level
    if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
      // The placeholder represents the left edge of the hypothetical box
      aHypotheticalBox.mLeft = placeholderOffset.x;
    } else {
      aHypotheticalBox.mLeft = aBlockContentArea.left;
    }
    aHypotheticalBox.mLeftIsExact = PR_TRUE;

    if (knowBoxWidth) {
      aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth;
      aHypotheticalBox.mRightIsExact = PR_TRUE;
    } else {
      // We can't compute the right edge because we don't know the desired
      // width. So instead use the right content edge of the block parent,
      // but remember it's not exact
      aHypotheticalBox.mRight = aBlockContentArea.right;
      aHypotheticalBox.mRightIsExact = PR_FALSE;
    }

  } else {
    // The placeholder represents the right edge of the hypothetical box
    if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
      aHypotheticalBox.mRight = placeholderOffset.x;
    } else {
      aHypotheticalBox.mRight = aBlockContentArea.right;
    }
    aHypotheticalBox.mRightIsExact = PR_TRUE;
    
    if (knowBoxWidth) {
      aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth;
      aHypotheticalBox.mLeftIsExact = PR_TRUE;
    } else {
      // We can't compute the left edge because we don't know the desired
      // width. So instead use the left content edge of the block parent,
      // but remember it's not exact
      aHypotheticalBox.mLeft = aBlockContentArea.left;
      aHypotheticalBox.mLeftIsExact = PR_FALSE;
    }

  }

  // The current coordinate space is that of the nearest block to the placeholder.
  // Convert to the coordinate space of the absolute containing block
  // One weird thing here is that for fixed-positioned elements we want to do
  // the conversion incorrectly; specifically we want to ignore any scrolling
  // that may have happened;
  nsPoint cbOffset;
  if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED) {
    // In this case, cbrs->frame will always be an ancestor of
    // aContainingBlock, so can just walk our way up the frame tree.
    cbOffset.MoveTo(0, 0);
    do {
      cbOffset += aContainingBlock->GetPosition();
      aContainingBlock = aContainingBlock->GetParent();
      NS_ASSERTION(aContainingBlock,
                   "Should hit cbrs->frame before we run off the frame tree!");
    } while (aContainingBlock != cbrs->frame);
  } else {
    cbOffset = aContainingBlock->GetOffsetTo(cbrs->frame);
  }
  aHypotheticalBox.mLeft += cbOffset.x;
  aHypotheticalBox.mTop += cbOffset.y;
  aHypotheticalBox.mRight += cbOffset.x;
  
  // The specified offsets are relative to the absolute containing block's
  // padding edge and our current values are relative to the border edge, so
  // translate.
  nsMargin border = cbrs->mComputedBorderPadding - cbrs->mComputedPadding;
  aHypotheticalBox.mLeft -= border.left;
  aHypotheticalBox.mRight -= border.right;
  aHypotheticalBox.mTop -= border.top;
}

void
nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext,
                                           const nsHTMLReflowState* cbrs,
                                           nscoord containingBlockWidth,
                                           nscoord containingBlockHeight)
{
  NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT,
                  "containing block height must be constrained");

  // Get the placeholder frame
  nsIFrame*     placeholderFrame;

  aPresContext->PresShell()->GetPlaceholderFrameFor(frame, &placeholderFrame);
  NS_ASSERTION(nsnull != placeholderFrame, "no placeholder frame");

  // Find the nearest containing block frame to the placeholder frame,
  // and return its content area left, top, right, and bottom edges
  nsMargin  blockContentArea;
  nsIFrame* blockFrame = GetNearestContainingBlock(placeholderFrame,
                                                   blockContentArea);
  
  // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
  // 'auto', then compute the hypothetical box of where the element would
  // have been if it had been in the flow
  nsHypotheticalBox hypotheticalBox;
  if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) &&
       (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) ||
      ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) &&
       (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) {

    CalculateHypotheticalBox(aPresContext, placeholderFrame, blockFrame,
                             blockContentArea, cbrs, hypotheticalBox);
  }

  // Initialize the 'left' and 'right' computed offsets
  // XXX Handle new 'static-position' value...
  PRBool        leftIsAuto = PR_FALSE, rightIsAuto = PR_FALSE;
  nsStyleCoord  coord;
  if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) {
    mComputedOffsets.left = 0;
    leftIsAuto = PR_TRUE;
  } else {
    ComputeHorizontalValue(containingBlockWidth, mStylePosition->mOffset.GetLeftUnit(),
                           mStylePosition->mOffset.GetLeft(coord),
                           mComputedOffsets.left);
  }
  if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) {
    mComputedOffsets.right = 0;
    rightIsAuto = PR_TRUE;
  } else {
    ComputeHorizontalValue(containingBlockWidth, mStylePosition->mOffset.GetRightUnit(),
                           mStylePosition->mOffset.GetRight(coord),
                           mComputedOffsets.right);
  }

  PRUint8 direction = mStyleVisibility->mDirection;

  // Initialize the 'width' computed value
  nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
  PRBool      widthIsAuto = (eStyleUnit_Auto == widthUnit);
  if (!widthIsAuto) {
    // Use the specified value for the computed width
    ComputeHorizontalValue(containingBlockWidth, widthUnit,
                           mStylePosition->mWidth, mComputedWidth);

    AdjustComputedWidth(PR_TRUE);
  }

  // See if none of 'left', 'width', and 'right', is 'auto'
  if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
    // See whether we're over-constrained
    PRInt32 availBoxSpace = containingBlockWidth - mComputedOffsets.left - mComputedOffsets.right;
    PRInt32 availContentSpace = availBoxSpace - mComputedBorderPadding.left -
                                mComputedBorderPadding.right;

    if (availContentSpace < mComputedWidth) {
      // We're over-constrained so use 'direction' to dictate which value to
      // ignore
      if (NS_STYLE_DIRECTION_LTR == direction) {
        // Ignore the specified value for 'right'
        mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
          mComputedBorderPadding.left - mComputedWidth - mComputedBorderPadding.right;
      } else {
        // Ignore the specified value for 'left'
        mComputedOffsets.left = containingBlockWidth - mComputedBorderPadding.left -
          mComputedWidth - mComputedBorderPadding.right - mComputedOffsets.right;
      }

    } else {
      // Calculate any 'auto' margin values
      PRBool  marginLeftIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit());
      PRBool  marginRightIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit());
      PRInt32 availMarginSpace = availContentSpace - mComputedWidth;

      if (marginLeftIsAuto) {
        if (marginRightIsAuto) {
          // Both 'margin-left' and 'margin-right' are 'auto', so they get
          // equal values
          mComputedMargin.left = availMarginSpace / 2;
          mComputedMargin.right = availMarginSpace - mComputedMargin.left;
        } else {
          // Just 'margin-left' is 'auto'
          mComputedMargin.left = availMarginSpace - mComputedMargin.right;
        }
      } else {
        // Just 'margin-right' is 'auto'
        mComputedMargin.right = availMarginSpace - mComputedMargin.left;
      }
    }

  } else {
    // See if all three of 'left', 'width', and 'right', are 'auto'
    if (leftIsAuto && widthIsAuto && rightIsAuto) {
      // Use the 'direction' to dictate whether 'left' or 'right' is
      // treated like 'static-position'
      if (NS_STYLE_DIRECTION_LTR == direction) {
        if (hypotheticalBox.mLeftIsExact) {
          mComputedOffsets.left = hypotheticalBox.mLeft;
          leftIsAuto = PR_FALSE;
        } else {
          // Well, we don't know 'left' so we have to use 'right' and
          // then solve for 'left'
          mComputedOffsets.right = hypotheticalBox.mRight;
          rightIsAuto = PR_FALSE;
        }
      } else {
        if (hypotheticalBox.mRightIsExact) {
          mComputedOffsets.right = containingBlockWidth - hypotheticalBox.mRight;
          rightIsAuto = PR_FALSE;
        } else {
          // Well, we don't know 'right' so we have to use 'left' and
          // then solve for 'right'
          mComputedOffsets.left = hypotheticalBox.mLeft;
          leftIsAuto = PR_FALSE;
        }
      }
    }

    // At this point we know that at least one of 'left', 'width', and 'right'
    // is 'auto', but not all three. Examine the various combinations
    if (widthIsAuto) {
      if (leftIsAuto || rightIsAuto) {
        if (NS_FRAME_IS_REPLACED(mFrameType)) {
          // For a replaced element we use the intrinsic size
          mComputedWidth = NS_INTRINSICSIZE;
        } else {
          // The width is shrink-to-fit
          mComputedWidth = NS_SHRINKWRAPWIDTH;
        }

        if (leftIsAuto) {
          mComputedOffsets.left = NS_AUTOOFFSET;   // solve for 'left'
        } else {
          mComputedOffsets.right = NS_AUTOOFFSET;  // solve for 'right'
        }

      } else {
        // Only 'width' is 'auto' so just solve for 'width'
        PRInt32 autoWidth = containingBlockWidth - mComputedOffsets.left -
          mComputedMargin.left - mComputedBorderPadding.left -
          mComputedBorderPadding.right -
          mComputedMargin.right - mComputedOffsets.right;

        if (autoWidth < 0) {
          autoWidth = 0;
        }
        mComputedWidth = autoWidth;

        AdjustComputedWidth(PR_FALSE);

        if (autoWidth != mComputedWidth) {
          // Re-calculate any 'auto' margin values since the computed width
          // was adjusted by a 'min-width' or 'max-width'.
          PRInt32 availMarginSpace = autoWidth - mComputedWidth;

          if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
            if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
              // Both margins are 'auto' so their computed values are equal.
              mComputedMargin.left = availMarginSpace / 2;
              mComputedMargin.right = availMarginSpace - mComputedMargin.left;
            } else {
              mComputedMargin.left = availMarginSpace - mComputedMargin.right;
            }
          } else if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
            mComputedMargin.right = availMarginSpace - mComputedMargin.left;
          } else {
            // We're over-constrained - ignore the value for 'left' or 'right'
            // and solve for that value.
            if (NS_STYLE_DIRECTION_LTR == direction) {
              // ignore 'right'
              mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
                mComputedMargin.left - mComputedBorderPadding.left -
                mComputedWidth - mComputedBorderPadding.right -
                mComputedMargin.right;
            } else {
              // ignore 'left'
              mComputedOffsets.left = containingBlockWidth - 
                mComputedMargin.left - mComputedBorderPadding.left -
                mComputedWidth - mComputedBorderPadding.right -
                mComputedMargin.right - mComputedOffsets.right;
            }
          }
        }
      }

    } else {
      // Either 'left' or 'right' or both is 'auto'
      if (leftIsAuto && rightIsAuto) {
        // Use the 'direction' to dictate whether 'left' or 'right' is treated like
        // 'static-position'
        if (NS_STYLE_DIRECTION_LTR == direction) {
          if (hypotheticalBox.mLeftIsExact) {
            mComputedOffsets.left = hypotheticalBox.mLeft;
            leftIsAuto = PR_FALSE;
          } else {
            // Well, we don't know 'left' so we have to use 'right' and
            // then solve for 'left'
            mComputedOffsets.right = hypotheticalBox.mRight;
            rightIsAuto = PR_FALSE;
          }
        } else {
          if (hypotheticalBox.mRightIsExact) {
            mComputedOffsets.right = containingBlockWidth - hypotheticalBox.mRight;
            rightIsAuto = PR_FALSE;
          } else {
            // Well, we don't know 'right' so we have to use 'left' and
            // then solve for 'right'
            mComputedOffsets.left = hypotheticalBox.mLeft;
            leftIsAuto = PR_FALSE;
          }
        }
      }

      if (leftIsAuto) {
        // Solve for 'left'
        mComputedOffsets.left = containingBlockWidth - mComputedMargin.left -
          mComputedBorderPadding.left - mComputedWidth - mComputedBorderPadding.right - 
          mComputedMargin.right - mComputedOffsets.right;

      } else if (rightIsAuto) {
        // Solve for 'right'
        mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
          mComputedMargin.left - mComputedBorderPadding.left - mComputedWidth -
          mComputedBorderPadding.right - mComputedMargin.right;
      }
    }
  }

  // Initialize the 'top' and 'bottom' computed offsets
  nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
  PRBool      topIsAuto = PR_FALSE, bottomIsAuto = PR_FALSE;
  if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) {
    mComputedOffsets.top = 0;
    topIsAuto = PR_TRUE;
  } else {
    nsStyleCoord c;
    ComputeVerticalValue(containingBlockHeight,
                         mStylePosition->mOffset.GetTopUnit(),
                         mStylePosition->mOffset.GetTop(c),
                         mComputedOffsets.top);
  }
  if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) {
    mComputedOffsets.bottom = 0;        
    bottomIsAuto = PR_TRUE;
  } else {
    nsStyleCoord c;
    ComputeVerticalValue(containingBlockHeight,
                         mStylePosition->mOffset.GetBottomUnit(),
                         mStylePosition->mOffset.GetBottom(c),
                         mComputedOffsets.bottom);
  }

  // Initialize the 'height' computed value
  PRBool  heightIsAuto = (eStyleUnit_Auto == heightUnit);
  if (!heightIsAuto) {
    // Use the specified value for the computed height
    ComputeVerticalValue(containingBlockHeight, heightUnit,
                         mStylePosition->mHeight, mComputedHeight);

    AdjustComputedHeight(PR_TRUE);
  }

  // See if none of 'top', 'height', and 'bottom', is 'auto'
  if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
    // See whether we're over-constrained
    PRInt32 availBoxSpace = containingBlockHeight - mComputedOffsets.top - mComputedOffsets.bottom;
    PRInt32 availContentSpace = availBoxSpace - mComputedBorderPadding.top -
                                mComputedBorderPadding.bottom;

    if (availContentSpace < mComputedHeight) {
      // We're over-constrained so ignore the specified value for 'bottom'
      mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
        mComputedBorderPadding.top - mComputedHeight - mComputedBorderPadding.bottom;

    } else {
      // Calculate any 'auto' margin values
      PRBool  marginTopIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit());
      PRBool  marginBottomIsAuto = (eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit());
      PRInt32 availMarginSpace = availContentSpace - mComputedHeight;

      if (marginTopIsAuto) {
        if (marginBottomIsAuto) {
          // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
          // equal values
          mComputedMargin.top = availMarginSpace / 2;
          mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
        } else {
          // Just 'margin-top' is 'auto'
          mComputedMargin.top = availMarginSpace - mComputedMargin.bottom;
        }
      } else {
        // Just 'margin-bottom' is 'auto'
        mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
      }
    }

  } else {
    // See if all three of 'top', 'height', and 'bottom', are 'auto'
    if (topIsAuto && heightIsAuto && bottomIsAuto) {
      // Treat 'top' like 'static-position'
      mComputedOffsets.top = hypotheticalBox.mTop;
      topIsAuto = PR_FALSE;
    }

    // At this point we know that at least one of 'top', 'height', and 'bottom'
    // is 'auto', but not all three. Examine the various combinations
    if (heightIsAuto) {
      if (topIsAuto || bottomIsAuto) {
        if (NS_FRAME_IS_REPLACED(mFrameType)) {
          // For a replaced element we use the intrinsic size
          mComputedHeight = NS_INTRINSICSIZE;
        } else {
          // The height is based on the content
          mComputedHeight = NS_AUTOHEIGHT;
        }

        if (topIsAuto) {
          mComputedOffsets.top = NS_AUTOOFFSET;     // solve for 'top'
        } else {
          mComputedOffsets.bottom = NS_AUTOOFFSET;  // solve for 'bottom'
        }

      } else {
        // Only 'height' is 'auto' so just solve for 'height'
        PRInt32 autoHeight = containingBlockHeight - mComputedOffsets.top -
          mComputedMargin.top - mComputedBorderPadding.top -
          mComputedBorderPadding.bottom -
          mComputedMargin.bottom - mComputedOffsets.bottom;

        if (autoHeight < 0) {
          autoHeight = 0;
        }
        mComputedHeight = autoHeight;
        
        AdjustComputedHeight(PR_FALSE);

        if (autoHeight != mComputedHeight) {
          // Re-calculate any 'auto' margin values since the computed height
          // was adjusted by a 'min-height' or 'max-height'.
          PRInt32 availMarginSpace = autoHeight - mComputedHeight;
    
          if (eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit()) {
            if (eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit()) {
              // Both margins are 'auto' so their computed values are equal
              mComputedMargin.top = availMarginSpace / 2;
              mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
            } else {
              mComputedMargin.top = availMarginSpace - mComputedMargin.bottom;
            }
          } else if (eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit()) {
            mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
          } else {
            // We're over-constrained - ignore 'bottom'.
            mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
              mComputedMargin.top - mComputedBorderPadding.top -
              mComputedHeight - mComputedBorderPadding.bottom -
              mComputedMargin.bottom;
          }
        }
      }

    } else {
      // Either 'top' or 'bottom' or both is 'auto'
      if (topIsAuto && bottomIsAuto) {
        // Treat 'top' like 'static-position'
        mComputedOffsets.top = hypotheticalBox.mTop;
        topIsAuto = PR_FALSE;
      }

      if (topIsAuto) {
        // Solve for 'top'
        mComputedOffsets.top = containingBlockHeight - mComputedMargin.top -
          mComputedBorderPadding.top - mComputedHeight - mComputedBorderPadding.bottom - 
          mComputedMargin.bottom - mComputedOffsets.bottom;

      } else if (bottomIsAuto) {
        // Solve for 'bottom'
        mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
          mComputedMargin.top - mComputedBorderPadding.top - mComputedHeight -
          mComputedBorderPadding.bottom - mComputedMargin.bottom;
      }
    }
  }
}

nscoord 
GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState)
{
  nscoord result = 0;
  if (!aReflowState) return result;

  // zero auto margins
  nsMargin margin = aReflowState->mComputedMargin;
  if (NS_AUTOMARGIN == margin.top) 
    margin.top = 0;
  if (NS_AUTOMARGIN == margin.bottom) 
    margin.bottom = 0;

  result += margin.top + margin.bottom;
  result += aReflowState->mComputedBorderPadding.top + 
            aReflowState->mComputedBorderPadding.bottom;

  return result;
}

/* Get the height based on the viewport of the containing block specified 
 * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT
 * This will walk up the chain of containing blocks looking for a computed height
 * until it finds the canvas frame, or it encounters a frame that is not a block,
 * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693)
 *
 *  When we encounter scrolledContent area frames, we skip over them, since they are guaranteed to not be useful for computing the containing block.
 */
nscoord
CalcQuirkContainingBlockHeight(const nsHTMLReflowState& aReflowState)
{
  nsHTMLReflowState* firstAncestorRS = nsnull; // a candidate for html frame
  nsHTMLReflowState* secondAncestorRS = nsnull; // a candidate for body frame
  
  // initialize the default to NS_AUTOHEIGHT as this is the containings block
  // computed height when this function is called. It is possible that we 
  // don't alter this height especially if we are restricted to one level
  nscoord result = NS_AUTOHEIGHT; 
                             
  const nsHTMLReflowState* rs = &aReflowState;
  for (; rs && rs->frame; rs = (nsHTMLReflowState *)(rs->parentReflowState)) { 
    nsIAtom* frameType = rs->frame->GetType();
    // if the ancestor is auto height then skip it and continue up if it 
    // is the first block/area frame and possibly the body/html
    if (nsLayoutAtoms::blockFrame == frameType ||
        nsLayoutAtoms::areaFrame == frameType ||
        nsLayoutAtoms::scrollFrame == frameType) {
      
      if (nsLayoutAtoms::areaFrame == frameType) {
        // Skip over scrolled-content area frames
        if (rs->frame->GetStyleContext()->GetPseudoType() ==
            nsCSSAnonBoxes::scrolledContent) {
          continue;
        }
      }

      secondAncestorRS = firstAncestorRS;
      firstAncestorRS = (nsHTMLReflowState*)rs;

      // If the current frame we're looking at is positioned, we don't want to
      // go any further (see bug 221784).  The behavior we want here is: 1) If
      // not auto-height, use this as the percentage base.  2) If auto-height,
      // keep looking, unless the frame is positioned.
      if (NS_AUTOHEIGHT == rs->mComputedHeight) {
        if (rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
          break;
        } else {
          continue;
        }
      }
    }
    else if (nsLayoutAtoms::canvasFrame == frameType) {
      // Use scroll frames' computed height if we have one, this will
      // allow us to get viewport height for native scrollbars.
      nsHTMLReflowState* scrollState = (nsHTMLReflowState *)rs->parentReflowState;
      if (nsLayoutAtoms::scrollFrame == scrollState->frame->GetType()) {
        rs = scrollState;
      }
    }
    else if (nsLayoutAtoms::pageContentFrame == frameType) {
      nsIFrame* prevInFlow = rs->frame->GetPrevInFlow();
      // only use the page content frame for a height basis if it is the first in flow
      if (prevInFlow) 
        break;
    }
    else {
      break;
    }

    // if the ancestor is the page content frame then the percent base is 
    // the avail height, otherwise it is the computed height
    result = (nsLayoutAtoms::pageContentFrame == frameType)
             ? rs->availableHeight : rs->mComputedHeight;
    // if unconstrained - don't sutract borders - would result in huge height
    if (NS_AUTOHEIGHT == result) return result;

    // if we got to the canvas or page content frame, then subtract out 
    // margin/border/padding for the BODY and HTML elements
    if ((nsLayoutAtoms::canvasFrame == frameType) || 
        (nsLayoutAtoms::pageContentFrame == frameType)) {

      result -= GetVerticalMarginBorderPadding(firstAncestorRS); 
      result -= GetVerticalMarginBorderPadding(secondAncestorRS); 

#ifdef DEBUG
      // make sure the first ancestor is the HTML and the second is the BODY
      if (firstAncestorRS) {
        nsIContent* frameContent = firstAncestorRS->frame->GetContent();
        if (frameContent) {
          nsIAtom *contentTag = frameContent->Tag();
          NS_ASSERTION(contentTag == nsHTMLAtoms::html, "First ancestor is not HTML");
        }
      }
      if (secondAncestorRS) {
        nsIContent* frameContent = secondAncestorRS->frame->GetContent();
        if (frameContent) {
          nsIAtom *contentTag = frameContent->Tag();
          NS_ASSERTION(contentTag == nsHTMLAtoms::body, "Second ancestor is not BODY");
        }
      }
#endif
      
    }
    // if we got to the html frame, then subtract out 
    // margin/border/padding for the BODY element
    else if (nsLayoutAtoms::areaFrame == frameType) {
      // make sure it is the body
      if (nsLayoutAtoms::canvasFrame == rs->parentReflowState->frame->GetType()) {
        result -= GetVerticalMarginBorderPadding(secondAncestorRS);
      }
    }
    break;
  }

  // Make sure not to return a negative height here!
  return PR_MAX(result, 0);
}
// Called by InitConstraints() to compute the containing block rectangle for
// the element. Handles the special logic for absolutely positioned elements
void
nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext*          aPresContext,
                                                   const nsHTMLReflowState* aContainingBlockRS,
                                                   nscoord&                 aContainingBlockWidth,
                                                   nscoord&                 aContainingBlockHeight)
{
  // Unless the element is absolutely positioned, the containing block is
  // formed by the content edge of the nearest block-level ancestor
  aContainingBlockWidth = aContainingBlockRS->mComputedWidth;
  aContainingBlockHeight = aContainingBlockRS->mComputedHeight;
  
  if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
    // See if the ancestor is block-level or inline-level
    if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) {
      // Base our size on the actual size of the frame.  In cases when this is
      // completely bogus (eg initial reflow), this code shouldn't even be
      // called, since the code in nsPositionedInlineFrame::Reflow will pass in
      // the containing block dimensions to our constructor.
      // XXXbz we should be taking the in-flows into account too, but
      // that's very hard.
      nsMargin computedBorder = aContainingBlockRS->mComputedBorderPadding -
        aContainingBlockRS->mComputedPadding;
      aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width -
        computedBorder.LeftRight();;
      NS_ASSERTION(aContainingBlockWidth >= 0,
                   "Negative containing block width!");
      aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height -
        computedBorder.TopBottom();
      NS_ASSERTION(aContainingBlockHeight >= 0,
                   "Negative containing block height!");
    } else {
      // If the ancestor is block-level, the containing block is formed by the
      // padding edge of the ancestor
      aContainingBlockWidth += aContainingBlockRS->mComputedPadding.LeftRight();

      // If the containing block is the initial containing block and it has a
      // height that depends on its content, then use the viewport height instead.
      // This gives us a reasonable value against which to compute percentage
      // based heights and to do bottom relative positioning
      if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
          nsLayoutUtils::IsInitialContainingBlock(aContainingBlockRS->frame)) {

        // Use the viewport height as the containing block height
        const nsHTMLReflowState* rs = aContainingBlockRS->parentReflowState;
        while (rs) {
          aContainingBlockHeight = rs->mComputedHeight;
          rs = rs->parentReflowState;
        }

      } else {
        aContainingBlockHeight +=
          aContainingBlockRS->mComputedPadding.TopBottom();
      }
    }
  } else {
    // If this is an unconstrained reflow, then reset the containing block
    // width to NS_UNCONSTRAINEDSIZE. This way percentage based values have
    // no effect
    if (NS_UNCONSTRAINEDSIZE == availableWidth) {
      aContainingBlockWidth = NS_UNCONSTRAINEDSIZE;
    }
    // an element in quirks mode gets a containing block based on looking for a
    // parent with a non-auto height if the element has a percent height
    if (NS_AUTOHEIGHT == aContainingBlockHeight) {
      if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
          mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) {
        aContainingBlockHeight = CalcQuirkContainingBlockHeight(*aContainingBlockRS);
      }
    }
  }
}

// Prefs callback to pick up changes
PR_STATIC_CALLBACK(int)
PrefsChanged(const char *aPrefName, void *instance)
{
  sBlinkIsAllowed =
    nsContentUtils::GetBoolPref("browser.blink_allowed", sBlinkIsAllowed);

  return 0; /* PREF_OK */
}

// Check to see if |text-decoration: blink| is allowed.  The first time
// called, register the callback and then force-load the pref.  After that,
// just use the cached value.
static PRBool BlinkIsAllowed(void)
{
  if (!sPrefIsLoaded) {
    // Set up a listener and check the initial value
    nsContentUtils::RegisterPrefCallback("browser.blink_allowed", PrefsChanged,
                                         nsnull);
    PrefsChanged(nsnull, nsnull);
    sPrefIsLoaded = PR_TRUE;
  }
  return sBlinkIsAllowed;
}

#ifdef FONT_LEADING_APIS_V2
static eNormalLineHeightControl GetNormalLineHeightCalcControl(void)
{
  if (sNormalLineHeightControl == eUninitialized) {
    // browser.display.normal_lineheight_calc_control is not user
    // changable, so no need to register callback for it.
    sNormalLineHeightControl =
      NS_STATIC_CAST(eNormalLineHeightControl,
                     nsContentUtils::GetIntPref("browser.display.normal_lineheight_calc_control", eNoExternalLeading));
  }
  return sNormalLineHeightControl;
}
#endif

// XXX refactor this code to have methods for each set of properties
// we are computing: width,height,line-height; margin; offsets

void
nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
                                   nscoord         aContainingBlockWidth,
                                   nscoord         aContainingBlockHeight,
                                   nsMargin*       aBorder,
                                   nsMargin*       aPadding)
{
  // If this is the root frame, then set the computed width and
  // height equal to the available space
  if (nsnull == parentReflowState) {
    mComputedWidth = availableWidth;
    mComputedHeight = availableHeight;
    mComputedMargin.SizeTo(0, 0, 0, 0);
    mComputedPadding.SizeTo(0, 0, 0, 0);
    mComputedBorderPadding.SizeTo(0, 0, 0, 0);
    mComputedOffsets.SizeTo(0, 0, 0, 0);
    mComputedMinWidth = mComputedMinHeight = 0;
    mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
  } else {
    // Get the containing block reflow state
    const nsHTMLReflowState* cbrs = mCBReflowState;
    NS_ASSERTION(nsnull != cbrs, "no containing block");

    // If we weren't given a containing block width and height, then
    // compute one
    if (aContainingBlockWidth == -1) {
      ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth, 
                                      aContainingBlockHeight);
    }

#if 0
    nsFrame::ListTag(stdout, frame); printf(": cb=");
    nsFrame::ListTag(stdout, cbrs->frame); printf(" size=%d,%d\n", aContainingBlockWidth, aContainingBlockHeight);
#endif

    // See if the containing block height is based on the size of its
    // content
    nsIAtom* fType;
    if (NS_AUTOHEIGHT == aContainingBlockHeight) {
      // See if the containing block is (1) a scrolled frame, i.e. its
      // parent is a scroll frame. The presence of the intervening
      // frame (that the scroll frame scrolls) needs to be hidden from
      // the containingBlockHeight calcuation, or (2) a cell frame which needs
      // to use the mComputedHeight of the cell instead of what the cell block passed in.
      if (cbrs->parentReflowState) {
        nsIFrame* f = cbrs->parentReflowState->frame;
        fType = f->GetType();
        if (nsLayoutAtoms::scrollFrame == fType) {
          // Use the scroll frame's computed height instead
          aContainingBlockHeight = cbrs->parentReflowState->mComputedHeight;
        }
        else {
          fType = cbrs->frame->GetType();
          if (IS_TABLE_CELL(fType)) {
            // use the cell's computed height 
            aContainingBlockHeight = cbrs->mComputedHeight;
          }
        }
      }
    }

    // Compute margins from the specified margin style information. These
    // become the default computed values, and may be adjusted below
    // XXX fix to provide 0,0 for the top&bottom margins for
    // inline-non-replaced elements
    ComputeMargin(aContainingBlockWidth, cbrs);
    if (aPadding) { // padding is an input arg
      mComputedPadding.top    = aPadding->top;
      mComputedPadding.right  = aPadding->right;
      mComputedPadding.bottom = aPadding->bottom;
      mComputedPadding.left   = aPadding->left;
    }
    else {
      ComputePadding(aContainingBlockWidth, cbrs);
    }
    if (aBorder) {  // border is an input arg
      mComputedBorderPadding = *aBorder;
    }
    else {
      mComputedBorderPadding = mStyleBorder->GetBorder();
    }
    mComputedBorderPadding += mComputedPadding;

    nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
    nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();

    // Check for a percentage based width and an unconstrained containing
    // block width
    if (eStyleUnit_Percent == widthUnit) {
      if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
        widthUnit = eStyleUnit_Auto;
      }
    }
    // Check for a percentage based height and a containing block height
    // that depends on the content height
    if (eStyleUnit_Percent == heightUnit) {
      if (NS_AUTOHEIGHT == aContainingBlockHeight) {
        // this if clause enables %-height on replaced inline frames,
        // such as images.  See bug 54119.  The else clause "heightUnit = eStyleUnit_Auto;"
        // used to be called exclusively.
        if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
          // Get the containing block reflow state
          NS_ASSERTION(nsnull != cbrs, "no containing block");
          // in quirks mode, get the cb height using the special quirk method
          if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
            if (!IS_TABLE_CELL(fType)) {
              aContainingBlockHeight = CalcQuirkContainingBlockHeight(*cbrs);
              if (aContainingBlockHeight == NS_AUTOHEIGHT) {
                heightUnit = eStyleUnit_Auto;
              }
            }
            else {
              heightUnit = eStyleUnit_Auto;
            }
          }
          // in standard mode, use the cb height.  if it's "auto", as will be the case
          // by default in BODY, use auto height as per CSS2 spec.
          else 
          {
            if (NS_AUTOHEIGHT != cbrs->mComputedHeight)
              aContainingBlockHeight = cbrs->mComputedHeight;
            else
              heightUnit = eStyleUnit_Auto;
          }
        }
        else {
          // default to interpreting the height like 'auto'
          heightUnit = eStyleUnit_Auto;
        }
      }
    }

    // Compute our offsets if the element is relatively positioned.  We need
    // the correct containing block width and height here, which is why we need
    // to do it after all the quirks-n-such above.
    if (NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
      ComputeRelativeOffsets(cbrs, aContainingBlockWidth, aContainingBlockHeight);
    } else {
      // Initialize offsets to 0
      mComputedOffsets.SizeTo(0, 0, 0, 0);
    }

    // Calculate the computed values for min and max properties
    ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs);

    // Calculate the computed width and height. This varies by frame type
    if ((NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType) ||
        (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_FLOATING) == mFrameType)) {
      // Inline replaced element and floating replaced element are basically
      // treated the same. First calculate the computed width
      if (eStyleUnit_Auto == widthUnit) {
        // A specified value of 'auto' uses the element's intrinsic width
        mComputedWidth = NS_INTRINSICSIZE;
      } else {
        ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
                               mStylePosition->mWidth, mComputedWidth);
      }

      AdjustComputedWidth(PR_TRUE);

      // Now calculate the computed height
      if (eStyleUnit_Auto == heightUnit) {
        // A specified value of 'auto' uses the element's intrinsic height
        mComputedHeight = NS_INTRINSICSIZE;
      } else {
        ComputeVerticalValue(aContainingBlockHeight, heightUnit,
                             mStylePosition->mHeight,
                             mComputedHeight);
      }

      AdjustComputedHeight(PR_TRUE);

    } else if (NS_CSS_FRAME_TYPE_FLOATING == mFrameType) {
      // Floating non-replaced element. First calculate the computed width
      if (eStyleUnit_Auto == widthUnit) {
        if ((NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) &&
            (eStyleUnit_Percent == mStylePosition->mWidth.GetUnit())) {
          // The element has a percentage width, but since the containing
          // block width is unconstrained we set 'widthUnit' to 'auto'
          // above. However, we want the element to be unconstrained, too
          mComputedWidth = NS_UNCONSTRAINEDSIZE;

        } else if (NS_STYLE_DISPLAY_TABLE == mStyleDisplay->mDisplay) {
          // It's an outer table because an inner table is not positioned
          // shrink wrap its width since the outer table is anonymous
          mComputedWidth = NS_SHRINKWRAPWIDTH;

        } else {
          NS_ASSERTION(eStyleUnit_Auto == mStylePosition->mWidth.GetUnit(),
                       "How did we get here?");
          // The CSS2 spec says the computed width should be 0; however, that's
          // not what Nav and IE do and even the spec doesn't really want that
          // to happen.
          //
          // Instead, have the element shrink wrap its width
          mComputedWidth = NS_SHRINKWRAPWIDTH;

          // Limit the width to the available width.  This factors in
          // other floats that impact this float.
          // XXX It's possible that this should be quirks-only.  Probable, in fact.
          nscoord widthFromCB = availableWidth;
          if (NS_UNCONSTRAINEDSIZE != widthFromCB) {
            widthFromCB -= mComputedBorderPadding.left + mComputedBorderPadding.right +
                           mComputedMargin.left + mComputedMargin.right;
          }
          if (mComputedMaxWidth > widthFromCB) {
            mComputedMaxWidth = widthFromCB;
          }
        }

      } else {
        ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
                               mStylePosition->mWidth, mComputedWidth);
      }

      // Take into account minimum and maximum sizes
      AdjustComputedWidth(PR_TRUE);

      // Now calculate the computed height
      if (eStyleUnit_Auto == heightUnit) {
        mComputedHeight = NS_AUTOHEIGHT;  // let it choose its height
      } else {
        ComputeVerticalValue(aContainingBlockHeight, heightUnit,
                             mStylePosition->mHeight,
                             mComputedHeight);
      }

      AdjustComputedHeight(PR_TRUE);
    
    } else if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
      // Internal table elements. The rules vary depending on the type.
      // Calculate the computed width
      PRBool rowOrRowGroup = PR_FALSE;
      if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) ||
          (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) {
        // 'width' property doesn't apply to table rows and row groups
        widthUnit = eStyleUnit_Auto;
        rowOrRowGroup = PR_TRUE;
      }

      if (eStyleUnit_Auto == widthUnit) {
        mComputedWidth = availableWidth;

        if ((mComputedWidth != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
          // Internal table elements don't have margins. Only tables and
          // cells have border and padding
          mComputedWidth -= mComputedBorderPadding.left +
            mComputedBorderPadding.right;
        }
      
      } else {
        ComputeHorizontalValue(aContainingBlockWidth, widthUnit,
                               mStylePosition->mWidth, mComputedWidth);
      }

      // Calculate the computed height
      if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) ||
          (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) {
        // 'height' property doesn't apply to table columns and column groups
        heightUnit = eStyleUnit_Auto;
      }
      if (eStyleUnit_Auto == heightUnit) {
        mComputedHeight = NS_AUTOHEIGHT;
      } else {
        ComputeVerticalValue(aContainingBlockHeight, heightUnit,
                             mStylePosition->mHeight,
                             mComputedHeight);
      }

      // Doesn't apply to table elements
      mComputedMinWidth = mComputedMinHeight = 0;
      mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;

    } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
      // XXX not sure if this belongs here or somewhere else - cwk
      InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth,
                              aContainingBlockHeight);
    } else if (NS_CSS_FRAME_TYPE_INLINE == mFrameType) {
      // Inline non-replaced elements do not have computed widths or heights
      // XXX add this check to HaveFixedContentHeight/Width too
      mComputedWidth = NS_UNCONSTRAINEDSIZE;
      mComputedHeight = NS_UNCONSTRAINEDSIZE;
      mComputedMargin.top = 0;
      mComputedMargin.bottom = 0;
      mComputedMinWidth = mComputedMinHeight = 0;
      mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
    } else {
      ComputeBlockBoxData(aPresContext, cbrs, widthUnit, heightUnit,
                          aContainingBlockWidth,
                          aContainingBlockHeight);
    }
  }
  // Check for blinking text and permission to display it
  mFlags.mBlinks = (parentReflowState && parentReflowState->mFlags.mBlinks);
  if (!mFlags.mBlinks && BlinkIsAllowed()) {
    const nsStyleTextReset* st = frame->GetStyleTextReset();
    mFlags.mBlinks = 
      ((st->mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) != 0);
  }
}

// Compute the box data for block and block-replaced elements in the
// normal flow.
void
nsHTMLReflowState::ComputeBlockBoxData(nsPresContext* aPresContext,
                                       const nsHTMLReflowState* cbrs,
                                       nsStyleUnit aWidthUnit,
                                       nsStyleUnit aHeightUnit,
                                       nscoord aContainingBlockWidth,
                                       nscoord aContainingBlockHeight)
{
  // Compute the content width
  if (eStyleUnit_Auto == aWidthUnit) {
    if (NS_FRAME_IS_REPLACED(mFrameType)) {
      // Block-level replaced element in the flow. A specified value of 
      // 'auto' uses the element's intrinsic width (CSS2 10.3.4)
      mComputedWidth = NS_INTRINSICSIZE;
    } else {
      // Block-level non-replaced element in the flow. 'auto' values
      // for margin-left and margin-right become 0, and the sum of the
      // areas must equal the width of the content-area of the parent
      // element.
      if (NS_UNCONSTRAINEDSIZE == availableWidth) {
        // During pass1 table reflow, auto side margin values are
        // uncomputable (== 0).
        mComputedWidth = NS_UNCONSTRAINEDSIZE;
      } else if (NS_SHRINKWRAPWIDTH == aContainingBlockWidth) {
        // The containing block should shrink wrap its width, so have
        // the child block do the same
        mComputedWidth = NS_UNCONSTRAINEDSIZE;

        // Let its content area be as wide as the containing block's max width
        // minus any margin and border/padding
        nscoord maxWidth = cbrs->mComputedMaxWidth;
        if (NS_UNCONSTRAINEDSIZE != maxWidth) {
          maxWidth -= mComputedMargin.left + mComputedBorderPadding.left + 
                      mComputedMargin.right + mComputedBorderPadding.right;
        }
        if (maxWidth < mComputedMaxWidth) {
          mComputedMaxWidth = maxWidth;
        }

      } else {
        // tables act like replaced elements regarding mComputedWidth 
        nsIAtom* fType = frame->GetType();
        if (nsLayoutAtoms::tableOuterFrame == fType) {
          mComputedWidth = 0; // XXX temp fix for trees
        } else if ((nsLayoutAtoms::tableFrame == fType) ||
                   (nsLayoutAtoms::tableCaptionFrame == fType)) {
          mComputedWidth = NS_SHRINKWRAPWIDTH;
          if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
            mComputedMargin.left = NS_AUTOMARGIN;
          }
          if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
            mComputedMargin.right = NS_AUTOMARGIN;
          }
        } else {
          mComputedWidth = availableWidth - mComputedMargin.left -
            mComputedMargin.right - mComputedBorderPadding.left -
            mComputedBorderPadding.right;
          mComputedWidth = PR_MAX(mComputedWidth, 0);
        }

        AdjustComputedWidth(PR_FALSE);
        CalculateBlockSideMargins(cbrs->mComputedWidth, mComputedWidth);
      }
    }
  } else {
    ComputeHorizontalValue(aContainingBlockWidth, aWidthUnit,
                           mStylePosition->mWidth, mComputedWidth);

    AdjustComputedWidth(PR_TRUE); 

    // Now that we have the computed-width, compute the side margins
    CalculateBlockSideMargins(cbrs->mComputedWidth, mComputedWidth);
  }

  // Compute the content height
  if (eStyleUnit_Auto == aHeightUnit) {
    if (NS_FRAME_IS_REPLACED(mFrameType)) {
      // For replaced elements use the intrinsic size for "auto"
      mComputedHeight = NS_INTRINSICSIZE;
    } else {
      // For non-replaced elements auto means unconstrained
      mComputedHeight = NS_UNCONSTRAINEDSIZE;
    }
  } else {
    ComputeVerticalValue(aContainingBlockHeight, aHeightUnit,
                         mStylePosition->mHeight, mComputedHeight);
  }
  AdjustComputedHeight(PR_TRUE);
}

// This code enforces section 10.3.3 of the CSS2 spec for this formula:
//
// 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
//   'padding-right' + 'border-right-width' + 'margin-right'
//   = width of containing block 
//
// Note: the width unit is not auto when this is called
void
nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth,
                                             nscoord aComputedWidth)
{
  // Because of the ugly way we do intrinsic sizing within Reflow, this method
  // doesn't necessarily produce the right results.  The results will be
  // adjusted in nsBlockReflowContext::AlignBlockHorizontally after reflow.
  // The code for tables is particularly sensitive to regressions; the
  // numerous |isTable| checks are technically incorrect, but necessary
  // for basic testcases.

  // We can only provide values for auto side margins in a constrained
  // reflow. For unconstrained reflow there is no effective width to
  // compute against...
  if (NS_UNCONSTRAINEDSIZE == aComputedWidth ||
      NS_UNCONSTRAINEDSIZE == aAvailWidth)
    return;

  nscoord sum = mComputedMargin.left + mComputedBorderPadding.left +
    aComputedWidth + mComputedBorderPadding.right + mComputedMargin.right;
  if (sum == aAvailWidth)
    // The sum is already correct
    return;

  // Determine the left and right margin values. The width value
  // remains constant while we do this.

  PRBool isTable = mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE ||
                   mStyleDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION;

  // Calculate how much space is available for margins
  nscoord availMarginSpace = aAvailWidth - sum;

  // XXXldb Should this be quirks-mode only?  And why captions?
  if (isTable)
    // XXXldb Why does this break things so badly if this is changed to
    // availMarginSpace += mComputedBorderPadding.left +
    //                     mComputedBorderPadding.right;
    availMarginSpace = aAvailWidth - aComputedWidth;

  // If the available margin space is negative, then don't follow the
  // usual overconstraint rules.
  if (availMarginSpace < 0) {
    if (!isTable) {
      if (mStyleVisibility->mDirection == NS_STYLE_DIRECTION_LTR) {
        mComputedMargin.right += availMarginSpace;
      } else {
        mComputedMargin.left += availMarginSpace;
      }
    } else {
      mComputedMargin.left = 0;
      mComputedMargin.right = 0;
      if (mStyleVisibility->mDirection == NS_STYLE_DIRECTION_RTL) {
        mComputedMargin.left = availMarginSpace;
      }
    }
    return;
  }

  // The css2 spec clearly defines how block elements should behave
  // in section 10.3.3.
  PRBool isAutoLeftMargin =
    eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
  PRBool isAutoRightMargin =
    eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
  if (!isAutoLeftMargin && !isAutoRightMargin && !isTable) {
    // Neither margin is 'auto' so we're over constrained. Use the
    // 'direction' property of the parent to tell which margin to
    // ignore
    // First check if there is an HTML alignment that we should honor
    const nsHTMLReflowState* prs = parentReflowState;
    if (prs &&
        (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
         prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
         prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) {
      isAutoLeftMargin =
        prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
      isAutoRightMargin =
        prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
    }
    // Otherwise apply the CSS rules, and ignore one margin by forcing
    // it to 'auto', depending on 'direction'.
    else if (NS_STYLE_DIRECTION_LTR == mStyleVisibility->mDirection) {
      isAutoRightMargin = PR_TRUE;
    }
    else {
      isAutoLeftMargin = PR_TRUE;
    }
  }

  // Logic which is common to blocks and tables
  if (isAutoLeftMargin) {
    if (isAutoRightMargin) {
      // Both margins are 'auto' so their computed values are equal
      mComputedMargin.left = availMarginSpace / 2;
      mComputedMargin.right = availMarginSpace - mComputedMargin.left;
    } else {
      mComputedMargin.left = availMarginSpace;
    }
  } else if (isAutoRightMargin) {
    mComputedMargin.right = availMarginSpace;
  }
}

#define NORMAL_LINE_HEIGHT_FACTOR 1.2f    // in term of emHeight 
// For "normal" we use the font's normal line height (em height + leading).
// If both internal leading and  external leading specified by font itself
// are zeros, we should compensate this by creating extra (external) leading 
// in eCompensateLeading mode. This is necessary because without this 
// compensation, normal line height might looks too tight. 

// For risk management, we use preference to control the behavior, and 
// eNoExternalLeading is the old behavior.
static nscoord
GetNormalLineHeight(nsIFontMetrics* aFontMetrics)
{
  NS_PRECONDITION(nsnull != aFontMetrics, "no font metrics");

  nscoord normalLineHeight;

#ifdef FONT_LEADING_APIS_V2
  nscoord externalLeading, internalLeading, emHeight;
  aFontMetrics->GetExternalLeading(externalLeading);
  aFontMetrics->GetInternalLeading(internalLeading);
  aFontMetrics->GetEmHeight(emHeight);
  switch (GetNormalLineHeightCalcControl()) {
  case eIncludeExternalLeading:
    normalLineHeight = emHeight+ internalLeading + externalLeading;
    break;
  case eCompensateLeading:
    if (!internalLeading && !externalLeading)
      normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR);
    else
      normalLineHeight = emHeight+ internalLeading + externalLeading;
    break;
  default:
    //case eNoExternalLeading:
    normalLineHeight = emHeight + internalLeading;
  }
#else
  aFontMetrics->GetNormalLineHeight(normalLineHeight);
#endif // FONT_LEADING_APIS_V2
  return normalLineHeight;
}

static nscoord
ComputeLineHeight(nsPresContext* aPresContext,
                  nsIRenderingContext* aRenderingContext,
                  nsStyleContext* aStyleContext)
{
  NS_PRECONDITION(nsnull != aRenderingContext, "no rendering context");

  nscoord lineHeight;

  const nsStyleFont* font = aStyleContext->GetStyleFont();
  const nsStyleCoord& lhCoord = aStyleContext->GetStyleText()->mLineHeight;
  
  nsStyleUnit unit = lhCoord.GetUnit();

  if (unit == eStyleUnit_Coord) {
    // For length values just use the pre-computed value
    lineHeight = lhCoord.GetCoordValue();
  } else if (unit == eStyleUnit_Factor) {
    // For factor units the computed value of the line-height property 
    // is found by multiplying the factor by the font's computed size
    // (adjusted for min-size prefs and text zoom).
    float factor = lhCoord.GetFactorValue();
    lineHeight = NSToCoordRound(factor * font->mFont.size);
  } else {
    NS_ASSERTION(eStyleUnit_Normal == unit, "bad unit");
    nsCOMPtr<nsIDeviceContext> deviceContext;
    aRenderingContext->GetDeviceContext(*getter_AddRefs(deviceContext));
    const nsStyleVisibility* vis = aStyleContext->GetStyleVisibility();
    nsCOMPtr<nsIFontMetrics> fm;
    deviceContext->GetMetricsFor(font->mFont, vis->mLangGroup,
                                 *getter_AddRefs(fm));
    lineHeight = GetNormalLineHeight(fm);
  }
  return lineHeight;
}

nscoord
nsHTMLReflowState::CalcLineHeight(nsPresContext* aPresContext,
                                  nsIRenderingContext* aRenderingContext,
                                  nsIFrame* aFrame)
{
  NS_ASSERTION(aFrame && aFrame->GetStyleContext(),
               "Bogus data passed in to CalcLineHeight");

  nscoord lineHeight = ComputeLineHeight(aPresContext, aRenderingContext,
                                         aFrame->GetStyleContext());

  NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");

  return lineHeight;
}

void
nsHTMLReflowState::ComputeHorizontalValue(nscoord aContainingBlockWidth,
                                          nsStyleUnit aUnit,
                                          const nsStyleCoord& aCoord,
                                          nscoord& aResult)
{
  aResult = 0;
  if (eStyleUnit_Percent == aUnit) {
    if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
      aResult = 0;
    } else {
      float pct = aCoord.GetPercentValue();
      aResult = NSToCoordFloor(aContainingBlockWidth * pct);
    }
  
  } else if (eStyleUnit_Coord == aUnit) {
    aResult = aCoord.GetCoordValue();
  }
  else if (eStyleUnit_Chars == aUnit) {
    if ((nsnull == rendContext) || (nsnull == frame)) {
      // We can't compute it without a rendering context or frame, so
      // pretend its zero...
    }
    else {
      nsStyleContext* styleContext = frame->GetStyleContext();
      SetFontFromStyle(rendContext, styleContext);
      nscoord fontWidth;
      rendContext->GetWidth('M', fontWidth);
      aResult = aCoord.GetIntValue() * fontWidth;
    }
  }
}

void
nsHTMLReflowState::ComputeVerticalValue(nscoord aContainingBlockHeight,
                                        nsStyleUnit aUnit,
                                        const nsStyleCoord& aCoord,
                                        nscoord& aResult)
{
  aResult = 0;
  if (eStyleUnit_Percent == aUnit) {
    // Verify no one is trying to calculate a percentage based height against
    // a height that's shrink wrapping to its content. In that case they should
    // treat the specified value like 'auto'
    NS_ASSERTION(NS_AUTOHEIGHT != aContainingBlockHeight, "unexpected containing block height");
    if (NS_AUTOHEIGHT!=aContainingBlockHeight)
    {
      float pct = aCoord.GetPercentValue();
      aResult = NSToCoordFloor(aContainingBlockHeight * pct);
    }
    else {  // safest thing to do for an undefined height is to make it 0
      aResult = 0;
    }

  } else if (eStyleUnit_Coord == aUnit) {
    aResult = aCoord.GetCoordValue();
  }
}

void
nsHTMLReflowState::ComputeMargin(nscoord aContainingBlockWidth,
                                 const nsHTMLReflowState* aContainingBlockRS)
{
  // If style style can provide us the margin directly, then use it.
  if (!mStyleMargin->GetMargin(mComputedMargin)) {
    // We have to compute the value
    if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
      mComputedMargin.left = 0;
      mComputedMargin.right = 0;

      if (eStyleUnit_Coord == mStyleMargin->mMargin.GetLeftUnit()) {
        nsStyleCoord left;
        
        mStyleMargin->mMargin.GetLeft(left),
        mComputedMargin.left = left.GetCoordValue();
      }
      if (eStyleUnit_Coord == mStyleMargin->mMargin.GetRightUnit()) {
        nsStyleCoord right;
        
        mStyleMargin->mMargin.GetRight(right),
        mComputedMargin.right = right.GetCoordValue();
      }

    } else {
      nsStyleCoord left, right;

      ComputeHorizontalValue(aContainingBlockWidth,
                             mStyleMargin->mMargin.GetLeftUnit(),
                             mStyleMargin->mMargin.GetLeft(left),
                             mComputedMargin.left);
      ComputeHorizontalValue(aContainingBlockWidth,
                             mStyleMargin->mMargin.GetRightUnit(),
                             mStyleMargin->mMargin.GetRight(right),
                             mComputedMargin.right);
    }

    const nsHTMLReflowState* rs2 = GetPageBoxReflowState(parentReflowState);
    nsStyleCoord top, bottom;
    if (nsnull != rs2) {
      // According to the CSS2 spec, margin percentages are
      // calculated with respect to the *height* of the containing
      // block when in a paginated context.
      ComputeVerticalValue(rs2->mComputedHeight,
                           mStyleMargin->mMargin.GetTopUnit(),
                           mStyleMargin->mMargin.GetTop(top),
                           mComputedMargin.top);
      ComputeVerticalValue(rs2->mComputedHeight,
                           mStyleMargin->mMargin.GetBottomUnit(),
                           mStyleMargin->mMargin.GetBottom(bottom),
                           mComputedMargin.bottom);
    }
    else {
      // According to the CSS2 spec, margin percentages are
      // calculated with respect to the *width* of the containing
      // block, even for margin-top and margin-bottom.
      ComputeHorizontalValue(aContainingBlockWidth,
                             mStyleMargin->mMargin.GetTopUnit(),
                             mStyleMargin->mMargin.GetTop(top),
                             mComputedMargin.top);
      ComputeHorizontalValue(aContainingBlockWidth,
                             mStyleMargin->mMargin.GetBottomUnit(),
                             mStyleMargin->mMargin.GetBottom(bottom),
                             mComputedMargin.bottom);
    }
  }
}

void
nsHTMLReflowState::ComputePadding(nscoord aContainingBlockWidth,
                                  const nsHTMLReflowState* aContainingBlockRS)

{
  // If style can provide us the padding directly, then use it.
  if (!mStylePadding->GetPadding(mComputedPadding)) {
    // We have to compute the value
    nsStyleCoord left, right, top, bottom;

    ComputeHorizontalValue(aContainingBlockWidth,
                           mStylePadding->mPadding.GetLeftUnit(),
                           mStylePadding->mPadding.GetLeft(left),
                           mComputedPadding.left);
    ComputeHorizontalValue(aContainingBlockWidth,
                           mStylePadding->mPadding.GetRightUnit(),
                           mStylePadding->mPadding.GetRight(right),
                           mComputedPadding.right);

    // According to the CSS2 spec, percentages are calculated with respect to
    // containing block width for padding-top and padding-bottom
    ComputeHorizontalValue(aContainingBlockWidth,
                           mStylePadding->mPadding.GetTopUnit(),
                           mStylePadding->mPadding.GetTop(top),
                           mComputedPadding.top);
    ComputeHorizontalValue(aContainingBlockWidth,
                           mStylePadding->mPadding.GetBottomUnit(),
                           mStylePadding->mPadding.GetBottom(bottom),
                           mComputedPadding.bottom);
  }
  // a table row/col group, row/col doesn't have padding
  if (frame) {
    nsIAtom* frameType = frame->GetType();
    if ((nsLayoutAtoms::tableRowGroupFrame == frameType) ||
        (nsLayoutAtoms::tableColGroupFrame == frameType) ||
        (nsLayoutAtoms::tableRowFrame      == frameType) ||
        (nsLayoutAtoms::tableColFrame      == frameType)) {
      mComputedPadding.top    = 0;
      mComputedPadding.right  = 0;
      mComputedPadding.bottom = 0;
      mComputedPadding.left   = 0;
    }
  }
}

void
nsHTMLReflowState::ApplyMinMaxConstraints(nscoord* aFrameWidth,
                                          nscoord* aFrameHeight) const
{
  if (aFrameWidth) {
    if (NS_UNCONSTRAINEDSIZE != mComputedMaxWidth) {
      *aFrameWidth = PR_MIN(*aFrameWidth, mComputedMaxWidth);
    }
    *aFrameWidth = PR_MAX(*aFrameWidth, mComputedMinWidth);
  }

  if (aFrameHeight) {
    if (NS_UNCONSTRAINEDSIZE != mComputedMaxHeight) {
      *aFrameHeight = PR_MIN(*aFrameHeight, mComputedMaxHeight);
    }
    *aFrameHeight = PR_MAX(*aFrameHeight, mComputedMinHeight);
  }
}

void
nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth,
                                       nscoord aContainingBlockHeight,
                                       const nsHTMLReflowState* aContainingBlockRS)
{
  nsStyleUnit minWidthUnit = mStylePosition->mMinWidth.GetUnit();
  ComputeHorizontalValue(aContainingBlockWidth, minWidthUnit,
                         mStylePosition->mMinWidth, mComputedMinWidth);
  nsStyleUnit maxWidthUnit = mStylePosition->mMaxWidth.GetUnit();
  if (eStyleUnit_Null == maxWidthUnit) {
    // Specified value of 'none'
    mComputedMaxWidth = NS_UNCONSTRAINEDSIZE;  // no limit
  } else {
    ComputeHorizontalValue(aContainingBlockWidth, maxWidthUnit,
                           mStylePosition->mMaxWidth, mComputedMaxWidth);
  }

  // If the computed value of 'min-width' is greater than the value of
  // 'max-width', 'max-width' is set to the value of 'min-width'
  if (mComputedMinWidth > mComputedMaxWidth) {
    mComputedMaxWidth = mComputedMinWidth;
  }

  nsStyleUnit minHeightUnit = mStylePosition->mMinHeight.GetUnit();
  // Check for percentage based values and a containing block height that
  // depends on the content height. Treat them like 'auto'
  if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
      (eStyleUnit_Percent == minHeightUnit)) {
    mComputedMinHeight = 0;
  } else {
    ComputeVerticalValue(aContainingBlockHeight, minHeightUnit,
                         mStylePosition->mMinHeight, mComputedMinHeight);
  }
  nsStyleUnit maxHeightUnit = mStylePosition->mMaxHeight.GetUnit();
  if (eStyleUnit_Null == maxHeightUnit) {
    // Specified value of 'none'
    mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;  // no limit
  } else {
    // Check for percentage based values and a containing block height that
    // depends on the content height. Treat them like 'auto'
    if ((NS_AUTOHEIGHT == aContainingBlockHeight) && 
        (eStyleUnit_Percent == maxHeightUnit)) {
      mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
    } else {
      ComputeVerticalValue(aContainingBlockHeight, maxHeightUnit,
                           mStylePosition->mMaxHeight, mComputedMaxHeight);
    }
  }

  // If the computed value of 'min-height' is greater than the value of
  // 'max-height', 'max-height' is set to the value of 'min-height'
  if (mComputedMinHeight > mComputedMaxHeight) {
    mComputedMaxHeight = mComputedMinHeight;
  }
}


void nsHTMLReflowState::AdjustComputedHeight(PRBool aAdjustForBoxSizing)
{
  // only do the math if the height  is not a symbolic value
  if (mComputedHeight == NS_UNCONSTRAINEDSIZE) {
    return;
  }
  
  NS_ASSERTION(mComputedHeight >= 0, "Negative Height Input - very bad");

  // Factor in any minimum and maximum size information
  if (mComputedHeight > mComputedMaxHeight) {
    mComputedHeight = mComputedMaxHeight;
  } else if (mComputedHeight < mComputedMinHeight) {
    mComputedHeight = mComputedMinHeight;
  }

  if (aAdjustForBoxSizing) {
    // remove extra padding/border if box-sizing property is set
    switch (mStylePosition->mBoxSizing) {
    case NS_STYLE_BOX_SIZING_PADDING : {
      mComputedHeight -= mComputedPadding.top + mComputedPadding.bottom;
      break;
    }
    case NS_STYLE_BOX_SIZING_BORDER : {
      mComputedHeight -= mComputedBorderPadding.top + mComputedBorderPadding.bottom;
    }
    default : break;
    }
    
    // If it did go bozo because of too much border or padding, set to 0
    if(mComputedHeight < 0) mComputedHeight = 0;
  }  
}

void nsHTMLReflowState::AdjustComputedWidth(PRBool aAdjustForBoxSizing)
{
  // only do the math if the width is not a symbolic value
  if (mComputedWidth == NS_UNCONSTRAINEDSIZE) {
    return;
  }
  
  NS_ASSERTION(mComputedWidth >= 0, "Negative Width Input - very bad");

  // Factor in any minimum and maximum size information
  if (mComputedWidth > mComputedMaxWidth) {
    mComputedWidth = mComputedMaxWidth;
  } else if (mComputedWidth < mComputedMinWidth) {
    mComputedWidth = mComputedMinWidth;
  }
  
  if (aAdjustForBoxSizing) {
    // remove extra padding/border if box-sizing property is set
    switch (mStylePosition->mBoxSizing) {
    case NS_STYLE_BOX_SIZING_PADDING : {
      mComputedWidth -= mComputedPadding.left + mComputedPadding.right;
      break;
    }
    case NS_STYLE_BOX_SIZING_BORDER : {
      mComputedWidth -= mComputedBorderPadding.left + mComputedBorderPadding.right;
    }
    default : break;
    }

    // If it did go bozo because of too much border or padding, set to 0
    if(mComputedWidth < 0) mComputedWidth = 0;
  }
}

#ifdef IBMBIDI
PRBool
nsHTMLReflowState::IsBidiFormControl(nsPresContext* aPresContext)
{
  // This check is only necessary on visual bidi pages, because most
  // visual pages use logical order for form controls so that they will
  // display correctly on native widgets in OSs with Bidi support.
  // So bail out if the page is not Bidi, or not visual, or if the pref is
  // set to use visual order on forms in visual pages
  if (!aPresContext->BidiEnabled()) {
    return PR_FALSE;
  }

  if (!aPresContext->IsVisualMode()) {
    return PR_FALSE;
  }

  PRUint32 options = aPresContext->GetBidi();
  if (IBMBIDI_CONTROLSTEXTMODE_LOGICAL != GET_BIDI_OPTION_CONTROLSTEXTMODE(options)) {
    return PR_FALSE;
  }

  nsIContent* content = frame->GetContent();
  if (!content) {
    return PR_FALSE;
  }

  // If this is a root reflow, we have to walk up the content tree to
  // find out if the reflow root is a descendant of a form control.
  // Otherwise, just test this content node
  if (mReflowDepth == 0) {
    for ( ; content; content = content->GetParent()) {
      if (content->IsContentOfType(nsIContent::eHTML_FORM_CONTROL)) {
        return PR_TRUE;
      }
    }
  } else {
    return (content->IsContentOfType(nsIContent::eHTML_FORM_CONTROL));
  }
  
  return PR_FALSE;
}
#endif
