Add support for calc() to the 'height', 'min-height', and 'max-height' properties. (Bug 585715) r=bzbarsky a2.0=blocking+
authorL. David Baron <dbaron@dbaron.org>
Wed, 25 Aug 2010 12:17:55 +0200
changeset 51406 fcc2aa4bd4519f51ced150cc460f714ee3dfc1c6
parent 51405 fb7ecc5f447c7df9ff59c68054367b50e1ce38bb
child 51407 4cc74816505e650952796f6e462a82beed02d036
push id15305
push userdbaron@mozilla.com
push dateWed, 25 Aug 2010 10:19:34 +0000
treeherdermozilla-central@5a32f9f00418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs585715
milestone2.0b5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Add support for calc() to the 'height', 'min-height', and 'max-height' properties. (Bug 585715) r=bzbarsky a2.0=blocking+
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsHTMLReflowMetrics.h
layout/generic/nsHTMLReflowState.cpp
layout/reftests/css-calc/height-block-1-ref.html
layout/reftests/css-calc/height-block-1.html
layout/reftests/css-calc/height-table-1-ref.html
layout/reftests/css-calc/height-table-1.html
layout/reftests/css-calc/max-height-block-1-ref.html
layout/reftests/css-calc/max-height-block-1.html
layout/reftests/css-calc/min-height-block-1.html
layout/reftests/css-calc/reftest.list
layout/style/nsCSSParser.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/tables/nsTableFrame.cpp
layout/tables/nsTableRowFrame.cpp
layout/xul/base/src/nsBox.cpp
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1768,31 +1768,36 @@ static nscoord AddPercents(nsLayoutUtils
     if (aPercent >= 1.0f)
       result = nscoord_MAX;
     else
       result = NSToCoordRound(float(result) / (1.0f - aPercent));
   }
   return result;
 }
 
+// Use only for widths/heights (or their min/max), since it clamps
+// negative calc() results to 0.
 static PRBool GetAbsoluteCoord(const nsStyleCoord& aStyle, nscoord& aResult)
 {
   if (aStyle.IsCalcUnit()) {
     if (aStyle.CalcHasPercent()) {
       return PR_FALSE;
     }
     // If it has no percents, we can pass 0 for the percentage basis.
     aResult = nsRuleNode::ComputeComputedCalc(aStyle, 0);
+    if (aResult < 0)
+      aResult = 0;
     return PR_TRUE;
   }
 
   if (eStyleUnit_Coord != aStyle.GetUnit())
     return PR_FALSE;
 
   aResult = aStyle.GetCoordValue();
+  NS_ASSERTION(aResult >= 0, "negative widths not allowed");
   return PR_TRUE;
 }
 
 static PRBool
 GetPercentHeight(const nsStyleCoord& aStyle,
                  nsIFrame* aFrame,
                  nscoord& aResult)
 {
@@ -1808,17 +1813,17 @@ GetPercentHeight(const nsStyleCoord& aSt
     return PR_FALSE;
   }
 
   const nsStylePosition *pos = f->GetStylePosition();
   nscoord h;
   if (!GetAbsoluteCoord(pos->mHeight, h) &&
       !GetPercentHeight(pos->mHeight, f, h)) {
     NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto ||
-                 pos->mHeight.GetUnit() == eStyleUnit_Percent,
+                 pos->mHeight.HasPercent(),
                  "unknown height unit");
     nsIAtom* fType = f->GetType();
     if (fType != nsGkAtoms::viewportFrame && fType != nsGkAtoms::canvasFrame &&
         fType != nsGkAtoms::pageContentFrame) {
       // There's no basis for the percentage height, so it acts like auto.
       // Should we consider a max-height < min-height pair a basis for
       // percentage heights?  The spec is somewhat unclear, and not doing
       // so is simpler and avoids troubling discontinuities in behavior,
@@ -1839,27 +1844,27 @@ GetPercentHeight(const nsStyleCoord& aSt
 
   nscoord maxh;
   if (GetAbsoluteCoord(pos->mMaxHeight, maxh) ||
       GetPercentHeight(pos->mMaxHeight, f, maxh)) {
     if (maxh < h)
       h = maxh;
   } else {
     NS_ASSERTION(pos->mMaxHeight.GetUnit() == eStyleUnit_None ||
-                 pos->mMaxHeight.GetUnit() == eStyleUnit_Percent,
+                 pos->mMaxHeight.HasPercent(),
                  "unknown max-height unit");
   }
 
   nscoord minh;
   if (GetAbsoluteCoord(pos->mMinHeight, minh) ||
       GetPercentHeight(pos->mMinHeight, f, minh)) {
     if (minh > h)
       h = minh;
   } else {
-    NS_ASSERTION(pos->mMinHeight.GetUnit() == eStyleUnit_Percent,
+    NS_ASSERTION(pos->mMinHeight.HasPercent(),
                  "unknown min-height unit");
   }
 
   aResult = NSToCoordRound(aStyle.GetPercentValue() * h);
   return PR_TRUE;
 }
 
 // Handles only -moz-max-content and -moz-min-content, and
@@ -2243,18 +2248,17 @@ nsLayoutUtils::ComputeHeightDependentVal
 }
 
 inline PRBool
 IsAutoHeight(const nsStyleCoord &aCoord, nscoord aCBHeight)
 {
   nsStyleUnit unit = aCoord.GetUnit();
   return unit == eStyleUnit_Auto ||  // only for 'height'
          unit == eStyleUnit_None ||  // only for 'max-height'
-         (unit == eStyleUnit_Percent &&
-          aCBHeight == NS_AUTOHEIGHT);
+         (aCBHeight == NS_AUTOHEIGHT && aCoord.HasPercent());
 }
 
 #define MULDIV(a,b,c) (nscoord(PRInt64(a) * PRInt64(b) / PRInt64(c)))
 
 /* static */ nsSize
 nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
                    nsIRenderingContext* aRenderingContext, nsIFrame* aFrame,
                    const nsIFrame::IntrinsicSize& aIntrinsicSize,
@@ -2304,35 +2308,35 @@ nsLayoutUtils::ComputeSizeWithIntrinsicD
 
   minWidth = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
                aFrame, aCBSize.width, boxSizingAdjust.width,
                boxSizingToMarginEdgeWidth, stylePos->mMinWidth);
   NS_ASSERTION(minWidth >= 0, "negative result from ComputeWidthValue");
 
   if (!isAutoHeight) {
     height = nsLayoutUtils::
-      ComputeHeightDependentValue(aCBSize.height, stylePos->mHeight) -
+      ComputeHeightValue(aCBSize.height, stylePos->mHeight) -
       boxSizingAdjust.height;
     if (height < 0)
       height = 0;
   }
 
   if (!IsAutoHeight(stylePos->mMaxHeight, aCBSize.height)) {
     maxHeight = nsLayoutUtils::
-      ComputeHeightDependentValue(aCBSize.height, stylePos->mMaxHeight) -
+      ComputeHeightValue(aCBSize.height, stylePos->mMaxHeight) -
       boxSizingAdjust.height;
     if (maxHeight < 0)
       maxHeight = 0;
   } else {
     maxHeight = nscoord_MAX;
   }
 
   if (!IsAutoHeight(stylePos->mMinHeight, aCBSize.height)) {
     minHeight = nsLayoutUtils::
-      ComputeHeightDependentValue(aCBSize.height, stylePos->mMinHeight) -
+      ComputeHeightValue(aCBSize.height, stylePos->mMinHeight) -
       boxSizingAdjust.height;
     if (minHeight < 0)
       minHeight = 0;
   } else {
     minHeight = 0;
   }
 
   // Resolve percentage intrinsic width/height as necessary:
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -772,16 +772,29 @@ public:
    * Convert nsStyleCoord to nscoord when percentages depend on the
    * containing block height.
    */
   static nscoord ComputeHeightDependentValue(
                    nscoord              aContainingBlockHeight,
                    const nsStyleCoord&  aCoord);
 
   /*
+   * Likewise, but for 'height', 'min-height', or 'max-height'.
+   */
+  static nscoord ComputeHeightValue(nscoord aContainingBlockHeight,
+                                    const nsStyleCoord& aCoord)
+  {
+    nscoord result =
+      ComputeHeightDependentValue(aContainingBlockHeight, aCoord);
+    if (result < 0)
+      result = 0; // clamp calc()
+    return result;
+  }
+
+  /*
    * Calculate the used values for 'width' and 'height' for a replaced element.
    *
    *   http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
    */
   static nsSize ComputeSizeWithIntrinsicDimensions(
                     nsIRenderingContext* aRenderingContext, nsIFrame* aFrame,
                     const nsIFrame::IntrinsicSize& aIntrinsicSize,
                     nsSize aIntrinsicRatio, nsSize aCBSize,
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -2746,54 +2746,47 @@ nsBlockFrame::AttributeChanged(PRInt32  
 
 static inline PRBool
 IsPaddingZero(nsStyleUnit aUnit, const nsStyleCoord &aCoord)
 {
     return ((aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) ||
             (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0));
 }
 
+static inline PRBool
+IsNonAutoNonZeroHeight(const nsStyleCoord& aCoord)
+{
+  if (aCoord.GetUnit() == eStyleUnit_Auto)
+    return PR_FALSE;
+  if (aCoord.IsCoordPercentCalcUnit()) {
+    // If we evaluate the length/percent/calc at a percentage basis of
+    // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
+    // length, percent, or combination thereof.  Test > 0 so we clamp
+    // negative calc() results to 0.
+    return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
+           nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
+  }
+  NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit for height or min-height");
+  return PR_TRUE;
+}
+
 /* virtual */ PRBool
 nsBlockFrame::IsSelfEmpty()
 {
   // Blocks which are margin-roots (including inline-blocks) cannot be treated
   // as empty for margin-collapsing and other purposes. They're more like
   // replaced elements.
   if (GetStateBits() & NS_BLOCK_MARGIN_ROOT)
     return PR_FALSE;
 
   const nsStylePosition* position = GetStylePosition();
 
-  switch (position->mMinHeight.GetUnit()) {
-    case eStyleUnit_Coord:
-      if (position->mMinHeight.GetCoordValue() != 0)
-        return PR_FALSE;
-      break;
-    case eStyleUnit_Percent:
-      if (position->mMinHeight.GetPercentValue() != 0.0f)
-        return PR_FALSE;
-      break;
-    default:
-      return PR_FALSE;
-  }
-
-  switch (position->mHeight.GetUnit()) {
-    case eStyleUnit_Auto:
-      break;
-    case eStyleUnit_Coord:
-      if (position->mHeight.GetCoordValue() != 0)
-        return PR_FALSE;
-      break;
-    case eStyleUnit_Percent:
-      if (position->mHeight.GetPercentValue() != 0.0f)
-        return PR_FALSE;
-      break;
-    default:
-      return PR_FALSE;
-  }
+  if (IsNonAutoNonZeroHeight(position->mMinHeight) ||
+      IsNonAutoNonZeroHeight(position->mHeight))
+    return PR_FALSE;
 
   const nsStyleBorder* border = GetStyleBorder();
   const nsStylePadding* padding = GetStylePadding();
   if (border->GetActualBorderWidth(NS_SIDE_TOP) != 0 ||
       border->GetActualBorderWidth(NS_SIDE_BOTTOM) != 0 ||
       !IsPaddingZero(padding->mPadding.GetTopUnit(),
                      padding->mPadding.GetTop()) ||
       !IsPaddingZero(padding->mPadding.GetBottomUnit(),
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3097,18 +3097,17 @@ nsFrame::GetIntrinsicRatio()
 }
 
 inline PRBool
 IsAutoHeight(const nsStyleCoord &aCoord, nscoord aCBHeight)
 {
   nsStyleUnit unit = aCoord.GetUnit();
   return unit == eStyleUnit_Auto ||  // only for 'height'
          unit == eStyleUnit_None ||  // only for 'max-height'
-         (unit == eStyleUnit_Percent && 
-          aCBHeight == NS_AUTOHEIGHT);
+         (aCBHeight == NS_AUTOHEIGHT && aCoord.HasPercent());
 }
 
 /* virtual */ nsSize
 nsFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
                      nsSize aCBSize, nscoord aAvailableWidth,
                      nsSize aMargin, nsSize aBorder, nsSize aPadding,
                      PRBool aShrinkWrap)
 {
@@ -3151,35 +3150,32 @@ nsFrame::ComputeSize(nsIRenderingContext
       stylePos->mMinWidth);
   if (minWidth > result.width)
     result.width = minWidth;
 
   // Compute height
 
   if (!IsAutoHeight(stylePos->mHeight, aCBSize.height)) {
     result.height =
-      nsLayoutUtils::ComputeHeightDependentValue(aCBSize.height,
-                                                 stylePos->mHeight) -
+      nsLayoutUtils::ComputeHeightValue(aCBSize.height, stylePos->mHeight) -
       boxSizingAdjust.height;
   }
 
   if (result.height != NS_UNCONSTRAINEDSIZE) {
     if (!IsAutoHeight(stylePos->mMaxHeight, aCBSize.height)) {
       nscoord maxHeight =
-        nsLayoutUtils::ComputeHeightDependentValue(aCBSize.height,
-                                                   stylePos->mMaxHeight) -
+        nsLayoutUtils::ComputeHeightValue(aCBSize.height, stylePos->mMaxHeight) -
         boxSizingAdjust.height;
       if (maxHeight < result.height)
         result.height = maxHeight;
     }
 
     if (!IsAutoHeight(stylePos->mMinHeight, aCBSize.height)) {
       nscoord minHeight =
-        nsLayoutUtils::ComputeHeightDependentValue(aCBSize.height,
-                                                   stylePos->mMinHeight) -
+        nsLayoutUtils::ComputeHeightValue(aCBSize.height, stylePos->mMinHeight) -
         boxSizingAdjust.height;
       if (minHeight > result.height)
         result.height = minHeight;
     }
   }
 
   const nsStyleDisplay *disp = GetStyleDisplay();
   if (IsThemed(disp)) {
@@ -3308,20 +3304,19 @@ nsFrame::DidReflow(nsPresContext*       
                 NS_FRAME_HAS_DIRTY_CHILDREN);
   }
 
   // Notify the percent height observer if there is a percent height.
   // The observer may be able to initiate another reflow with a computed
   // height. This happens in the case where a table cell has no computed
   // height but can fabricate one when the cell height is known.
   if (aReflowState && aReflowState->mPercentHeightObserver &&
-      (eStyleUnit_Percent == aReflowState->mStylePosition->mHeight.GetUnit())) {
-
-    nsIFrame* prevInFlow = GetPrevInFlow();
-    if (!prevInFlow) { // 1st in flow
+      !GetPrevInFlow()) {
+    const nsStyleCoord &height = aReflowState->mStylePosition->mHeight;
+    if (height.HasPercent()) {
       aReflowState->mPercentHeightObserver->NotifyPercentHeight(*aReflowState);
     }
   }
 
   return NS_OK;
 }
 
 /* virtual */ PRBool
--- a/layout/generic/nsHTMLReflowMetrics.h
+++ b/layout/generic/nsHTMLReflowMetrics.h
@@ -160,17 +160,17 @@ struct nsHTMLReflowMetrics {
   
   // For frames that have content that overflow their content area
   // (HasOverflowRect() is true) this rectangle represents the total area
   // of the frame including visible overflow, i.e., don't include overflowing
   // content that is hidden.
   // The rect is in the local coordinate space of the frame, and should be at
   // least as big as the desired size. If there is no content that overflows,
   // then the overflow area is identical to the desired size and should be
-  // {0, 0, mWidth, mHeight}.
+  // {0, 0, width, height}.
   nsRect mOverflowArea;
 
   PRUint32 mFlags;
  
   // XXXldb Should |aFlags| generally be passed from parent to child?
   // Some places do it, and some don't.  |aFlags| should perhaps go away
   // entirely.
   nsHTMLReflowMetrics(PRUint32 aFlags = 0) {
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -390,19 +390,25 @@ nsHTMLReflowState::InitResizeFlags(nsPre
     mFlags.mVResize = mFlags.mVResize || NS_SUBTREE_DIRTY(frame);
   } else {
     // not 'auto' height
     mFlags.mVResize = frame->GetSize().height !=
                         mComputedHeight + mComputedBorderPadding.TopBottom();
   }
 
   PRBool dependsOnCBHeight =
-    mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent ||
-    mStylePosition->mMinHeight.GetUnit() == eStyleUnit_Percent ||
-    mStylePosition->mMaxHeight.GetUnit() == eStyleUnit_Percent ||
+    (mStylePosition->HeightDependsOnContainer() &&
+     // FIXME: condition this on not-abspos?
+     mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) ||
+    (mStylePosition->MinHeightDependsOnContainer() &&
+     // FIXME: condition this on not-abspos?
+     mStylePosition->mMinHeight.GetUnit() != eStyleUnit_Auto) ||
+    (mStylePosition->MaxHeightDependsOnContainer() &&
+     // FIXME: condition this on not-abspos?
+     mStylePosition->mMaxHeight.GetUnit() != eStyleUnit_Auto) ||
     mStylePosition->mOffset.GetTopUnit() == eStyleUnit_Percent ||
     mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto ||
     frame->IsBoxFrame() ||
     (mStylePosition->mHeight.GetUnit() == eStyleUnit_Auto &&
      frame->GetIntrinsicSize().height.GetUnit() == eStyleUnit_Percent);
 
   if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) {
     NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() ==
@@ -1574,16 +1580,17 @@ nsHTMLReflowState::ComputeContainingBloc
       // If the ancestor is block-level, the containing block is formed by the
       // padding edge of the ancestor
       aContainingBlockWidth += aContainingBlockRS->mComputedPadding.LeftRight();
       aContainingBlockHeight += aContainingBlockRS->mComputedPadding.TopBottom();
     }
   } else {
     // 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
+    // Note: We don't emulate this quirk for percents in calc().
     if (NS_AUTOHEIGHT == aContainingBlockHeight) {
       if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
           mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) {
         aContainingBlockHeight = CalcQuirkContainingBlockHeight(aContainingBlockRS);
       }
     }
   }
 }
@@ -1703,22 +1710,24 @@ nsHTMLReflowState::InitConstraints(nsPre
           // use the cell's computed height 
           aContainingBlockHeight = cbrs->mComputedHeight;
         }
       }
     }
 
     InitOffsets(aContainingBlockWidth, aBorder, aPadding);
 
-    nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
+    const nsStyleCoord &height = mStylePosition->mHeight;
+    nsStyleUnit heightUnit = height.GetUnit();
 
     // Check for a percentage based height and a containing block height
     // that depends on the content height
     // XXX twiddling heightUnit doesn't help anymore
-    if (eStyleUnit_Percent == heightUnit) {
+    // FIXME Shouldn't we fix that?
+    if (height.HasPercent()) {
       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 ||
             NS_FRAME_REPLACED_CONTAINS_BLOCK(
                 NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
           // Get the containing block reflow state
@@ -1767,25 +1776,27 @@ nsHTMLReflowState::InitConstraints(nsPre
     ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs);
 
     // Calculate the computed width and height. This varies by frame type
 
     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;
-      nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
+      const nsStyleCoord &width = mStylePosition->mWidth;
+      nsStyleUnit widthUnit = width.GetUnit();
       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) {
+      // calc() acts like auto on internal table elements
+      if (eStyleUnit_Auto == widthUnit || width.IsCalcUnit()) {
         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;
           if (mComputedWidth < 0)
@@ -1802,24 +1813,24 @@ nsHTMLReflowState::InitConstraints(nsPre
       }
 
       // 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) {
+      // calc() acts like 'auto' on internal table elements
+      if (eStyleUnit_Auto == heightUnit || height.IsCalcUnit()) {
         mComputedHeight = NS_AUTOHEIGHT;
       } else {
         NS_ASSERTION(heightUnit == mStylePosition->mHeight.GetUnit(),
                      "unexpected height unit change");
         mComputedHeight = nsLayoutUtils::
-          ComputeHeightDependentValue(aContainingBlockHeight,
-                                      mStylePosition->mHeight);
+          ComputeHeightValue(aContainingBlockHeight, mStylePosition->mHeight);
       }
 
       // 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
@@ -2272,38 +2283,46 @@ nsHTMLReflowState::ComputeMinMaxValues(n
   // 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;
   }
 
   // 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 == mStylePosition->mMinHeight.GetUnit())) {
+  // Likewise, check for calc() on internal table elements; calc() on
+  // such elements is unsupported.
+  const nsStyleCoord &minHeight = mStylePosition->mMinHeight;
+  if ((NS_AUTOHEIGHT == aContainingBlockHeight &&
+       minHeight.HasPercent()) ||
+      (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
+       minHeight.IsCalcUnit())) {
     mComputedMinHeight = 0;
   } else {
     mComputedMinHeight = nsLayoutUtils::
-      ComputeHeightDependentValue(aContainingBlockHeight,
-                                  mStylePosition->mMinHeight);
+      ComputeHeightValue(aContainingBlockHeight, minHeight);
   }
-  nsStyleUnit maxHeightUnit = mStylePosition->mMaxHeight.GetUnit();
+  const nsStyleCoord &maxHeight = mStylePosition->mMaxHeight;
+  nsStyleUnit maxHeightUnit = maxHeight.GetUnit();
   if (eStyleUnit_None == 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)) {
+    // Likewise, check for calc() on internal table elements; calc() on
+    // such elements is unsupported.
+    if ((NS_AUTOHEIGHT == aContainingBlockHeight && 
+         maxHeight.HasPercent()) ||
+        (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
+         maxHeight.IsCalcUnit())) {
       mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
     } else {
       mComputedMaxHeight = nsLayoutUtils::
-        ComputeHeightDependentValue(aContainingBlockHeight,
-                                    mStylePosition->mMaxHeight);
+        ComputeHeightValue(aContainingBlockHeight, maxHeight);
     }
   }
 
   // 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;
   }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/height-block-1-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<title>Test for height:calc() or min-height:calc() on blocks</title>
+<style>
+body { margin: 0 }
+body > div { float: left; width: 1px; background: blue; }
+</style>
+
+<!-- tests with a fixed-height container -->
+<div style="height: 50px"></div>
+<div style="height: 50px"></div>
+<div style="height: 75px"></div>
+<div style="height: 45px"></div>
+<div style="height: 40px"></div>
+<div style="height: 30px"></div>
+
+<!-- tests with an auto-height container -->
+<div style="height: 50px"></div>
+<div style="height: 10px"></div>
+<div style="height: 10px"></div>
+<div style="height: 10px"></div>
+<div style="height: 10px"></div>
+<div style="height: 10px"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/height-block-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<title>Test for height:calc() on blocks</title>
+<style>
+body { margin: 0 }
+body > div { float: left; height: 100px; width: 1px; }
+body > div > div { background: blue }
+
+/* for auto-height tests */
+body > div > div > div > div { height: 10px }
+</style>
+
+<!-- tests with a fixed-height container -->
+<div><div style="height: -moz-calc(50px)"></div></div>
+<div><div style="height: -moz-calc(50%)"></div></div>
+<div><div style="height: -moz-calc(25px + 50%)"></div></div>
+<div><div style="height: -moz-calc(150% / 2 - 30px)"></div></div>
+<div><div style="height: -moz-calc(40px + 10% - 20% / 2)"></div></div>
+<div><div style="height: -moz-calc(40px - 10%)"></div></div>
+
+<!-- tests with an auto-height container -->
+<div><div><div style="height: -moz-calc(50px)"><div></div></div></div></div>
+<div><div><div style="height: -moz-calc(50%)"><div></div></div></div></div>
+<div><div><div style="height: -moz-calc(25px + 50%)"><div></div></div></div></div>
+<div><div><div style="height: -moz-calc(150% / 2 - 30px)"><div></div></div></div></div>
+<div><div><div style="height: -moz-calc(40px + 10% - 20% / 2)"><div></div></div></div></div>
+<div><div><div style="height: -moz-calc(40px - 10%)"><div></div></div></div></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/height-table-1-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<title>Test that height:calc() has no effect on inner table elements</title>
+<table border>
+  <tbody>
+    <tr>
+      <td>cell</td>
+      <td>cell</td>
+    </tr>
+    <tr>
+      <td>cell</td>
+      <td>cell</td>
+    </tr>
+  </tbody>
+</table>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/height-table-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<title>Test that height:calc() has no effect on inner table elements</title>
+<style>
+tbody, tr, td {
+  height: -moz-calc(500px);
+  min-height: -moz-calc(1000px);
+  max-height: -moz-calc(2px);
+}
+</style>
+<table border>
+  <tbody>
+    <tr>
+      <td>cell</td>
+      <td>cell</td>
+    </tr>
+    <tr>
+      <td>cell</td>
+      <td>cell</td>
+    </tr>
+  </tbody>
+</table>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/max-height-block-1-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<title>Test for max-height:calc() on blocks</title>
+<style>
+body { margin: 0 }
+body > div { float: left; width: 1px; background: blue; }
+</style>
+
+<!-- tests with a fixed-height container -->
+<div style="height: 50px"></div>
+<div style="height: 50px"></div>
+<div style="height: 75px"></div>
+<div style="height: 45px"></div>
+<div style="height: 40px"></div>
+<div style="height: 30px"></div>
+
+<!-- tests with an auto-height container -->
+<div style="height: 50px"></div>
+<div style="height: 300px"></div>
+<div style="height: 300px"></div>
+<div style="height: 300px"></div>
+<div style="height: 300px"></div>
+<div style="height: 300px"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/max-height-block-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<title>Test for max-height:calc() on blocks</title>
+<style>
+body { margin: 0 }
+body > div { float: left; height: 100px; width: 1px; }
+body > div > div { background: blue }
+
+/* to give the max-height something to restrict */
+span { display: block; height: 300px }
+</style>
+
+<!-- tests with a fixed-height container -->
+<div><div style="max-height: -moz-calc(50px)"><span></span></div></div>
+<div><div style="max-height: -moz-calc(50%)"><span></span></div></div>
+<div><div style="max-height: -moz-calc(25px + 50%)"><span></span></div></div>
+<div><div style="max-height: -moz-calc(150% / 2 - 30px)"><span></span></div></div>
+<div><div style="max-height: -moz-calc(40px + 10% - 20% / 2)"><span></span></div></div>
+<div><div style="max-height: -moz-calc(40px - 10%)"><span></span></div></div>
+
+<!-- tests with an auto-height container -->
+<div><div><div style="max-height: -moz-calc(50px)"><span></span></div></div></div>
+<div><div><div style="max-height: -moz-calc(50%)"><span></span></div></div></div>
+<div><div><div style="max-height: -moz-calc(25px + 50%)"><span></span></div></div></div>
+<div><div><div style="max-height: -moz-calc(150% / 2 - 30px)"><span></span></div></div></div>
+<div><div><div style="max-height: -moz-calc(40px + 10% - 20% / 2)"><span></span></div></div></div>
+<div><div><div style="max-height: -moz-calc(40px - 10%)"><span></span></div></div></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/min-height-block-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<title>Test for min-height:calc() on blocks</title>
+<style>
+body { margin: 0 }
+body > div { float: left; height: 100px; width: 1px; }
+body > div > div { background: blue }
+
+/* for auto-height tests */
+body > div > div > div > div { height: 10px }
+</style>
+
+<!-- tests with a fixed-height container -->
+<div><div style="min-height: -moz-calc(50px)"></div></div>
+<div><div style="min-height: -moz-calc(50%)"></div></div>
+<div><div style="min-height: -moz-calc(25px + 50%)"></div></div>
+<div><div style="min-height: -moz-calc(150% / 2 - 30px)"></div></div>
+<div><div style="min-height: -moz-calc(40px + 10% - 20% / 2)"></div></div>
+<div><div style="min-height: -moz-calc(40px - 10%)"></div></div>
+
+<!-- tests with an auto-height container -->
+<div><div><div style="min-height: -moz-calc(50px)"><div></div></div></div></div>
+<div><div><div style="min-height: -moz-calc(50%)"><div></div></div></div></div>
+<div><div><div style="min-height: -moz-calc(25px + 50%)"><div></div></div></div></div>
+<div><div><div style="min-height: -moz-calc(150% / 2 - 30px)"><div></div></div></div></div>
+<div><div><div style="min-height: -moz-calc(40px + 10% - 20% / 2)"><div></div></div></div></div>
+<div><div><div style="min-height: -moz-calc(40px - 10%)"><div></div></div></div></div>
--- a/layout/reftests/css-calc/reftest.list
+++ b/layout/reftests/css-calc/reftest.list
@@ -1,4 +1,8 @@
+== height-block-1.html height-block-1-ref.html
+== height-table-1.html height-table-1-ref.html
+== max-height-block-1.html max-height-block-1-ref.html
+== min-height-block-1.html height-block-1-ref.html
 == width-block-1.html width-block-1-ref.html
 == width-block-intrinsic-1.html width-block-intrinsic-1-ref.html
 == width-table-auto-1.html width-table-auto-1-ref.html
 == width-table-fixed-1.html width-table-fixed-1-ref.html
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -5726,17 +5726,18 @@ CSSParserImpl::ParseSingleValueProperty(
   case eCSSProperty_text_rendering:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kTextRenderingKTable);
 #endif
   case eCSSProperty_box_sizing:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kBoxSizingKTable);
   case eCSSProperty_height:
-    return ParseNonNegativeVariant(aValue, VARIANT_AHLP, nsnull);
+    return ParseNonNegativeVariant(aValue, VARIANT_AHLP | VARIANT_CALC,
+                                   nsnull);
   case eCSSProperty_width:
     return ParseNonNegativeVariant(aValue, VARIANT_AHKLP | VARIANT_CALC,
                                    nsCSSProps::kWidthKTable);
   case eCSSProperty_force_broken_image_icon:
     return ParseNonNegativeVariant(aValue, VARIANT_HI, nsnull);
   case eCSSProperty_caption_side:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kCaptionSideKTable);
@@ -5825,22 +5826,24 @@ CSSParserImpl::ParseSingleValueProperty(
   case eCSSProperty_margin_start_value: // for internal use
   case eCSSProperty_margin_top:
     return ParseVariant(aValue, VARIANT_AHLP, nsnull);
   case eCSSProperty_marker_offset:
     return ParseVariant(aValue, VARIANT_AHL | VARIANT_CALC, nsnull);
   case eCSSProperty_marks:
     return ParseMarks(aValue);
   case eCSSProperty_max_height:
-    return ParseNonNegativeVariant(aValue, VARIANT_HLPO, nsnull);
+    return ParseNonNegativeVariant(aValue, VARIANT_HLPO | VARIANT_CALC,
+                                   nsnull);
   case eCSSProperty_max_width:
     return ParseNonNegativeVariant(aValue, VARIANT_HKLPO,
                                    nsCSSProps::kWidthKTable);
   case eCSSProperty_min_height:
-    return ParseNonNegativeVariant(aValue, VARIANT_HLP, nsnull);
+    return ParseNonNegativeVariant(aValue, VARIANT_HLP | VARIANT_CALC,
+                                   nsnull);
   case eCSSProperty_min_width:
     return ParseNonNegativeVariant(aValue, VARIANT_HKLP,
                                    nsCSSProps::kWidthKTable);
   case eCSSProperty_opacity:
     return ParseVariant(aValue, VARIANT_HN, nsnull);
   case eCSSProperty_orphans:
   case eCSSProperty_widows:
     return ParsePositiveNonZeroVariant(aValue, VARIANT_HI, nsnull);
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -5543,24 +5543,24 @@ nsRuleNode::ComputePositionData(void* aS
   SetCoord(posData.mMinWidth, pos->mMinWidth, parentPos->mMinWidth,
            SETCOORD_LPEH | SETCOORD_INITIAL_ZERO, aContext,
            mPresContext, canStoreInRuleTree);
   SetCoord(posData.mMaxWidth, pos->mMaxWidth, parentPos->mMaxWidth,
            SETCOORD_LPOEH | SETCOORD_INITIAL_NONE, aContext,
            mPresContext, canStoreInRuleTree);
 
   SetCoord(posData.mHeight, pos->mHeight, parentPos->mHeight,
-           SETCOORD_LPAH | SETCOORD_INITIAL_AUTO, aContext,
-           mPresContext, canStoreInRuleTree);
+           SETCOORD_LPAH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC,
+           aContext, mPresContext, canStoreInRuleTree);
   SetCoord(posData.mMinHeight, pos->mMinHeight, parentPos->mMinHeight,
-           SETCOORD_LPH | SETCOORD_INITIAL_ZERO, aContext,
-           mPresContext, canStoreInRuleTree);
+           SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC,
+           aContext, mPresContext, canStoreInRuleTree);
   SetCoord(posData.mMaxHeight, pos->mMaxHeight, parentPos->mMaxHeight,
-           SETCOORD_LPOH | SETCOORD_INITIAL_NONE, aContext,
-           mPresContext, canStoreInRuleTree);
+           SETCOORD_LPOH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC,
+           aContext, mPresContext, canStoreInRuleTree);
 
   // box-sizing: enum, inherit, initial
   SetDiscrete(posData.mBoxSizing, pos->mBoxSizing, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentPos->mBoxSizing,
               NS_STYLE_BOX_SIZING_CONTENT, 0, 0, 0, 0);
 
   // z-index
   if (! SetCoord(posData.mZIndex, pos->mZIndex, parentPos->mZIndex,
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1084,19 +1084,19 @@ struct nsStylePosition {
   static nsChangeHint MaxDifference();
 #endif
   static PRBool ForceCompare() { return PR_TRUE; }
 
   nsStyleSides  mOffset;                // [reset] coord, percent, auto
   nsStyleCoord  mWidth;                 // [reset] coord, percent, enum, auto
   nsStyleCoord  mMinWidth;              // [reset] coord, percent, enum
   nsStyleCoord  mMaxWidth;              // [reset] coord, percent, enum, none
-  nsStyleCoord  mHeight;                // [reset] coord, percent, auto
-  nsStyleCoord  mMinHeight;             // [reset] coord, percent
-  nsStyleCoord  mMaxHeight;             // [reset] coord, percent, none
+  nsStyleCoord  mHeight;                // [reset] coord, percent, calc, auto
+  nsStyleCoord  mMinHeight;             // [reset] coord, percent, calc
+  nsStyleCoord  mMaxHeight;             // [reset] coord, percent, calc, none
   PRUint8       mBoxSizing;             // [reset] see nsStyleConsts.h
   nsStyleCoord  mZIndex;                // [reset] integer, auto
 
   PRBool WidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mWidth); }
   PRBool MinWidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mMinWidth); }
   PRBool MaxWidthDependsOnContainer() const
@@ -1108,17 +1108,17 @@ struct nsStylePosition {
   PRBool MaxHeightDependsOnContainer() const
     { return HeightCoordDependsOnContainer(mMaxHeight); }
 
 private:
   static PRBool WidthCoordDependsOnContainer(const nsStyleCoord &aCoord);
   static PRBool HeightCoordDependsOnContainer(const nsStyleCoord &aCoord)
   {
     return aCoord.GetUnit() == eStyleUnit_Auto || // CSS 2.1, 10.6.4, item (5)
-           aCoord.GetUnit() == eStyleUnit_Percent;
+           aCoord.HasPercent();
   }
 };
 
 struct nsStyleTextReset {
   nsStyleTextReset(void);
   nsStyleTextReset(const nsStyleTextReset& aOther);
   ~nsStyleTextReset(void);
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -1579,17 +1579,24 @@ var gCSSProperties = {
 	},
 	"height": {
 		domProp: "height",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ " auto" ],
 		/* computed value tests for height test more with display:block */
 		prerequisites: { "display": "block" },
-		other_values: [ "15px", "3em", "15%" ],
+		other_values: [ "15px", "3em", "15%",
+			"-moz-calc(2px)",
+			"-moz-calc(50%)",
+			"-moz-calc(3*25px)",
+			"-moz-calc(25px*3)",
+			"-moz-calc(3*25px + 50%)",
+			"-moz-min(30%, 30em,200px, min(500px ,40em))",
+		],
 		invalid_values: [ "none", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ]
 	},
 	"ime-mode": {
 		domProp: "imeMode",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "auto" ],
 		other_values: [ "normal", "disabled", "active", "inactive" ],
@@ -1754,17 +1761,24 @@ var gCSSProperties = {
 		invalid_values: [ "none none", "crop none", "none crop", "cross none", "none cross" ]
 	},
 	"max-height": {
 		domProp: "maxHeight",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		prerequisites: { "display": "block" },
 		initial_values: [ "none" ],
-		other_values: [ "30px", "50%", "0" ],
+		other_values: [ "30px", "50%", "0",
+			"-moz-calc(2px)",
+			"-moz-calc(50%)",
+			"-moz-calc(3*25px)",
+			"-moz-calc(25px*3)",
+			"-moz-calc(3*25px + 50%)",
+			"-moz-min(30%, 30em,200px, min(500px ,40em))",
+		],
 		invalid_values: [ "auto", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ]
 	},
 	"max-width": {
 		domProp: "maxWidth",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		prerequisites: { "display": "block" },
 		initial_values: [ "none" ],
@@ -1772,17 +1786,24 @@ var gCSSProperties = {
 		invalid_values: [ "auto" ]
 	},
 	"min-height": {
 		domProp: "minHeight",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		prerequisites: { "display": "block" },
 		initial_values: [ "0" ],
-		other_values: [ "30px", "50%" ],
+		other_values: [ "30px", "50%",
+			"-moz-calc(2px)",
+			"-moz-calc(50%)",
+			"-moz-calc(3*25px)",
+			"-moz-calc(25px*3)",
+			"-moz-calc(3*25px + 50%)",
+			"-moz-min(30%, 30em,200px, min(500px ,40em))",
+		],
 		invalid_values: [ "auto", "none", "-moz-max-content", "-moz-min-content", "-moz-fit-content", "-moz-available" ]
 	},
 	"min-width": {
 		domProp: "minWidth",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		prerequisites: { "display": "block" },
 		initial_values: [ "0" ],
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1584,17 +1584,19 @@ PRBool
 nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState)
 {
   for (const nsHTMLReflowState* rs = &aParentReflowState;
        rs && rs->frame; rs = rs->parentReflowState) {
     nsIAtom* frameType = rs->frame->GetType();
     if (IS_TABLE_CELL(frameType)                     ||
         (nsGkAtoms::tableRowFrame      == frameType) ||
         (nsGkAtoms::tableRowGroupFrame == frameType)) {
-      if (rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) {
+      const nsStyleCoord &height = rs->mStylePosition->mHeight;
+      // calc() treated like 'auto' on internal table elements
+      if (height.GetUnit() != eStyleUnit_Auto && !height.IsCalcUnit()) {
         return PR_TRUE;
       }
     }
     else if (nsGkAtoms::tableFrame == frameType) {
       // we reached the containing table, so always return
       if (rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) {
         return PR_TRUE;
       }
@@ -1603,16 +1605,21 @@ nsTableFrame::AncestorsHaveStyleHeight(c
   }
   return PR_FALSE;
 }
 
 // See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow
 void
 nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
 {
+  NS_ASSERTION(IS_TABLE_CELL(aReflowState.frame->GetType()) ||
+               aReflowState.frame->GetType() == nsGkAtoms::tableRowFrame ||
+               aReflowState.frame->GetType() == nsGkAtoms::tableRowGroupFrame ||
+               aReflowState.frame->GetType() == nsGkAtoms::tableFrame,
+               "unexpected frame type");
   if (!aReflowState.frame->GetPrevInFlow() &&  // 1st in flow
       (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() ||  // no computed height
        0                    == aReflowState.ComputedHeight()) &&
       eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height
       nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) {
     nsTableFrame::RequestSpecialHeightReflow(aReflowState);
   }
 }
@@ -3344,36 +3351,21 @@ nsTableFrame::GetTableFrame(nsIFrame* aS
   }
   NS_NOTREACHED("unable to find table parent");
   return nsnull;
 }
 
 PRBool
 nsTableFrame::IsAutoHeight()
 {
-  PRBool isAuto = PR_TRUE;  // the default
-
-  const nsStylePosition* position = GetStylePosition();
-
-  switch (position->mHeight.GetUnit()) {
-    case eStyleUnit_Auto:         // specified auto width
-      break;
-    case eStyleUnit_Coord:
-      isAuto = PR_FALSE;
-      break;
-    case eStyleUnit_Percent:
-      if (position->mHeight.GetPercentValue() > 0.0f) {
-        isAuto = PR_FALSE;
-      }
-      break;
-    default:
-      break;
-  }
-
-  return isAuto;
+  const nsStyleCoord &height = GetStylePosition()->mHeight;
+  // Don't consider calc() here like this quirk for percent.
+  return height.GetUnit() == eStyleUnit_Auto ||
+         (height.GetUnit() == eStyleUnit_Percent &&
+          height.GetPercentValue() <= 0.0f);
 }
 
 nscoord
 nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState)
 {
   nscoord height = aState.ComputedHeight();
   if (NS_AUTOHEIGHT != height) {
     nsMargin borderPadding = GetChildAreaOffset(&aState);
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -542,16 +542,17 @@ nsTableRowFrame::CalcHeight(const nsHTML
 
   const nsStylePosition* position = GetStylePosition();
   if (eStyleUnit_Coord == position->mHeight.GetUnit()) {
     SetFixedHeight(position->mHeight.GetCoordValue());
   }
   else if (eStyleUnit_Percent == position->mHeight.GetUnit()) {
     SetPctHeight(position->mHeight.GetPercentValue());
   }
+  // calc() is treated like 'auto' on table rows.
 
   for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
        kidFrame = kidFrame->GetNextSibling()) {
     nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
     if (cellFrame) {
       nsSize desSize = cellFrame->GetDesiredSize();
       if ((NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) && !GetPrevInFlow()) {
         CalculateCellActualHeight(cellFrame, desSize.height);
@@ -670,17 +671,17 @@ nsTableRowFrame::CalculateCellActualHeig
       break;
     case eStyleUnit_Percent: {
       if (1 == rowSpan) 
         SetPctHeight(position->mHeight.GetPercentValue());
       // pct heights are handled when all of the cells are finished, so don't set specifiedHeight 
       break;
     }
     case eStyleUnit_Auto:
-    default:
+    default: // includes calc(), which we treat like 'auto'
       break;
   }
 
   // If the specified height is greater than the desired height, then use the specified height
   if (specifiedHeight > aDesiredHeight)
     aDesiredHeight = specifiedHeight;
 
   return NS_OK;
@@ -1400,18 +1401,20 @@ void nsTableRowFrame::InitHasCellWithSty
 
   for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) {
     nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
     if (!cellFrame) {
       NS_NOTREACHED("Table row has a non-cell child.");
       continue;
     }
     // Ignore row-spanning cells
+    const nsStyleCoord &cellHeight = cellFrame->GetStylePosition()->mHeight;
     if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 &&
-        cellFrame->GetStylePosition()->mHeight.GetUnit() != eStyleUnit_Auto) {
+        cellHeight.GetUnit() != eStyleUnit_Auto &&
+        !cellHeight.IsCalcUnit() /* calc() treated like 'auto' */) {
       AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT);
       return;
     }
   }
   RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT);
 }
 
 /* ----- global methods ----- */
--- a/layout/xul/base/src/nsBox.cpp
+++ b/layout/xul/base/src/nsBox.cpp
@@ -682,25 +682,36 @@ nsIBox::AddCSSPrefSize(nsIBox* aBox, nsS
     const nsStyleCoord &width = position->mWidth;
     if (width.GetUnit() == eStyleUnit_Coord) {
         aSize.width = width.GetCoordValue();
         aWidthSet = PR_TRUE;
     } else if (width.IsCalcUnit()) {
         if (!width.CalcHasPercent()) {
             // pass 0 for percentage basis since we know there are no %s
             aSize.width = nsRuleNode::ComputeComputedCalc(width, 0);
+            if (aSize.width < 0)
+                aSize.width = 0;
             aWidthSet = PR_TRUE;
         }
     }
 
-    if (position->mHeight.GetUnit() == eStyleUnit_Coord) {
-        aSize.height = position->mHeight.GetCoordValue();     
+    const nsStyleCoord &height = position->mHeight;
+    if (height.GetUnit() == eStyleUnit_Coord) {
+        aSize.height = height.GetCoordValue();
         aHeightSet = PR_TRUE;
+    } else if (height.IsCalcUnit()) {
+        if (!height.CalcHasPercent()) {
+            // pass 0 for percentage basis since we know there are no %s
+            aSize.height = nsRuleNode::ComputeComputedCalc(height, 0);
+            if (aSize.height < 0)
+                aSize.height = 0;
+            aHeightSet = PR_TRUE;
+        }
     }
-    
+
     nsIContent* content = aBox->GetContent();
     // ignore 'height' and 'width' attributes if the actual element is not XUL
     // For example, we might be magic XUL frames whose primary content is an HTML
     // <select>
     if (content && content->IsXUL()) {
         nsAutoString value;
         PRInt32 error;
 
@@ -775,28 +786,33 @@ nsIBox::AddCSSMinSize(nsBoxLayoutState& 
         aSize.width = 0;
         aWidthSet = PR_TRUE;
     }
     // XXX Handle eStyleUnit_Enumerated?
     // (Handling the eStyleUnit_Enumerated types requires
     // GetPrefSize/GetMinSize methods that don't consider
     // (min-/max-/)(width/height) properties.
 
-    if (position->mMinHeight.GetUnit() == eStyleUnit_Coord) {
-        nscoord min = position->mMinHeight.GetCoordValue();
-        if (min && (!aHeightSet || (min > aSize.height && canOverride))) {
+    const nsStyleCoord &minHeight = position->mMinHeight;
+    if ((minHeight.GetUnit() == eStyleUnit_Coord &&
+         minHeight.GetCoordValue() != 0) ||
+        (minHeight.IsCalcUnit() && !minHeight.CalcHasPercent())) {
+        nscoord min = nsRuleNode::ComputeCoordPercentCalc(minHeight, 0);
+        if (!aHeightSet || (min > aSize.height && canOverride)) {
            aSize.height = min;
            aHeightSet = PR_TRUE;
         }
-    } else if (position->mMinHeight.GetUnit() == eStyleUnit_Percent) {
+    } else if (minHeight.GetUnit() == eStyleUnit_Percent) {
         NS_ASSERTION(position->mMinHeight.GetPercentValue() == 0.0f,
           "Non-zero percentage values not currently supported");
         aSize.height = 0;
-        aHeightSet = PR_TRUE;
+        aHeightSet = PR_TRUE; // FIXME: should we really do this for
+                              // nonzero values?
     }
+    // calc() with percentage is treated like '0' (unset)
 
     nsIContent* content = aBox->GetContent();
     if (content) {
         nsAutoString value;
         PRInt32 error;
 
         content->GetAttr(kNameSpaceID_None, nsGkAtoms::minwidth, value);
         if (!value.IsEmpty())
@@ -842,20 +858,22 @@ nsIBox::AddCSSMaxSize(nsIBox* aBox, nsSi
     // (Handling the eStyleUnit_Enumerated types requires
     // GetPrefSize/GetMinSize methods that don't consider
     // (min-/max-/)(width/height) properties.)
     if (position->mMaxWidth.GetUnit() == eStyleUnit_Coord) {
         aSize.width = position->mMaxWidth.GetCoordValue();
         aWidthSet = PR_TRUE;
     }
 
-    if (position->mMaxHeight.GetUnit() == eStyleUnit_Coord) {
-        aSize.height = position->mMaxHeight.GetCoordValue();
+    const nsStyleCoord &maxHeight = position->mMaxHeight;
+    if (maxHeight.ConvertsToLength()) {
+        aSize.height = nsRuleNode::ComputeCoordPercentCalc(maxHeight, 0);
         aHeightSet = PR_TRUE;
     }
+    // percentages and calc() with percentages are treated like 'none'
 
     nsIContent* content = aBox->GetContent();
     if (content) {
         nsAutoString value;
         PRInt32 error;
 
         content->GetAttr(kNameSpaceID_None, nsGkAtoms::maxwidth, value);
         if (!value.IsEmpty()) {