Add support for calc() to 'vertical-align' and 'text-indent'. (Bug 585715) r=bzbarsky a2.0=blocking:beta6+
authorL. David Baron <dbaron@dbaron.org>
Tue, 31 Aug 2010 12:05:12 -0400
changeset 51778 4b05a762af721b336b3989dfa45af709e0d17c90
parent 51777 4744aeff506a2bd50ccf9f199fc008c5186a7c27
child 51779 4edcf6c4cd03867862bc0aee95f5942acee29274
push id15424
push userdbaron@mozilla.com
push dateTue, 31 Aug 2010 16:05:34 +0000
treeherdermozilla-central@4edcf6c4cd03 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs585715
milestone2.0b6pre
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 'vertical-align' and 'text-indent'. (Bug 585715) r=bzbarsky a2.0=blocking:beta6+
layout/generic/nsBlockFrame.cpp
layout/generic/nsLineLayout.cpp
layout/reftests/css-calc/reftest.list
layout/reftests/css-calc/text-indent-1-ref.html
layout/reftests/css-calc/text-indent-1.html
layout/reftests/css-calc/text-indent-intrinsic-1-ref.html
layout/reftests/css-calc/text-indent-intrinsic-1.html
layout/reftests/css-calc/vertical-align-1-ref.html
layout/reftests/css-calc/vertical-align-1.html
layout/style/nsCSSParser.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -710,19 +710,22 @@ nsBlockFrame::GetMinWidth(nsIRenderingCo
       if (line->IsBlock()) {
         data.ForceBreak(aRenderingContext);
         data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                         line->mFirstChild, nsLayoutUtils::MIN_WIDTH);
         data.ForceBreak(aRenderingContext);
       } else {
         if (!curFrame->GetPrevContinuation() &&
             line == curFrame->begin_lines()) {
+          // Only add text-indent if it has no percentages; using a
+          // percentage basis of 0 unconditionally would give strange
+          // behavior for calc(10%-3px).
           const nsStyleCoord &indent = GetStyleText()->mTextIndent;
-          if (indent.GetUnit() == eStyleUnit_Coord)
-            data.currentLine += indent.GetCoordValue();
+          if (indent.ConvertsToLength())
+            data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
         }
         // XXX Bug NNNNNN Should probably handle percentage text-indent.
 
         data.line = &line;
         data.lineContainer = curFrame;
         nsIFrame *kid = line->mFirstChild;
         for (PRInt32 i = 0, i_end = line->GetChildCount(); i != i_end;
              ++i, kid = kid->GetNextSibling()) {
@@ -785,19 +788,22 @@ nsBlockFrame::GetPrefWidth(nsIRenderingC
       if (line->IsBlock()) {
         data.ForceBreak(aRenderingContext);
         data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                         line->mFirstChild, nsLayoutUtils::PREF_WIDTH);
         data.ForceBreak(aRenderingContext);
       } else {
         if (!curFrame->GetPrevContinuation() &&
             line == curFrame->begin_lines()) {
+          // Only add text-indent if it has no percentages; using a
+          // percentage basis of 0 unconditionally would give strange
+          // behavior for calc(10%-3px).
           const nsStyleCoord &indent = GetStyleText()->mTextIndent;
-          if (indent.GetUnit() == eStyleUnit_Coord)
-            data.currentLine += indent.GetCoordValue();
+          if (indent.ConvertsToLength())
+            data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
         }
         // XXX Bug NNNNNN Should probably handle percentage text-indent.
 
         data.line = &line;
         data.lineContainer = curFrame;
         nsIFrame *kid = line->mFirstChild;
         for (PRInt32 i = 0, i_end = line->GetChildCount(); i != i_end;
              ++i, kid = kid->GetNextSibling()) {
@@ -5939,29 +5945,27 @@ nsBlockFrame::PaintTextDecorationLine(gf
 /*virtual*/ void
 nsBlockFrame::AdjustForTextIndent(const nsLineBox* aLine,
                                   nscoord& start,
                                   nscoord& width)
 {
   if (!GetPrevContinuation() && aLine == begin_lines().get()) {
     // Adjust for the text-indent.  See similar code in
     // nsLineLayout::BeginLineReflow.
-    nscoord indent = 0;
-    const nsStyleText* styleText = GetStyleText();
-    nsStyleUnit unit = styleText->mTextIndent.GetUnit();
-    if (eStyleUnit_Coord == unit) {
-      indent = styleText->mTextIndent.GetCoordValue();
-    } else if (eStyleUnit_Percent == unit) {
+    const nsStyleCoord &textIndent = GetStyleText()->mTextIndent;
+    nscoord pctBasis = 0;
+    if (textIndent.HasPercent()) {
+      // Only work out the percentage basis if we need to.
       // It's a percentage of the containing block width.
       nsIFrame* containingBlock =
         nsHTMLReflowState::GetContainingBlockFor(this);
       NS_ASSERTION(containingBlock, "Must have containing block!");
-      indent = nscoord(styleText->mTextIndent.GetPercentValue() *
-                       containingBlock->GetContentRect().width);
-    }
+      pctBasis = containingBlock->GetContentRect().width;
+    }
+    nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis);
 
     // Adjust the start position and the width of the decoration by the
     // value of the indent.  Note that indent can be negative; that's OK.
     // It'll just increase the width (which can also happen to be
     // negative!).
     start += indent;
     width -= indent;
   }
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -226,31 +226,27 @@ nsLineLayout::BeginLineReflow(nscoord aX
   psd->mNoWrap = !mStyleText->WhiteSpaceCanWrap();
   psd->mDirection = mBlockReflowState->mStyleVisibility->mDirection;
   psd->mChangedFrameDirection = PR_FALSE;
 
   // If this is the first line of a block then see if the text-indent
   // property amounts to anything.
 
   if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowState->frame)) {
-    nscoord indent = 0;
-    nsStyleUnit unit = mStyleText->mTextIndent.GetUnit();
-    if (eStyleUnit_Coord == unit) {
-      indent = mStyleText->mTextIndent.GetCoordValue();
-    }
-    else if (eStyleUnit_Percent == unit) {
-      nscoord width =
+    const nsStyleCoord &textIndent = mStyleText->mTextIndent;
+    nscoord pctBasis = 0;
+    if (textIndent.HasPercent()) {
+      pctBasis =
         nsHTMLReflowState::GetContainingBlockContentWidth(mBlockReflowState);
-      if ((0 != width) && (NS_UNCONSTRAINEDSIZE != width)) {
-        indent = nscoord(mStyleText->mTextIndent.GetPercentValue() * width);
-      }
+
       if (GetFlag(LL_GOTLINEBOX)) {
         mLineBox->DisableResizeReflowOptimization();
       }
     }
+    nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis);
 
     mTextIndent = indent;
 
     if (NS_STYLE_DIRECTION_RTL == psd->mDirection) {
       psd->mRightEdge -= indent;
     }
     else {
       psd->mX += indent;
@@ -1778,186 +1774,184 @@ nsLineLayout::VerticalAlignFrames(PerSpa
       // For other elements the logical height is the same as the
       // frames height plus its margins.
       logicalHeight = pfd->mBounds.height + pfd->mMargin.top +
         pfd->mMargin.bottom;
       topLeading = 0;
     }
 
     // Get vertical-align property
-    const nsStyleTextReset* textStyle = frame->GetStyleTextReset();
-    nsStyleUnit verticalAlignUnit = textStyle->mVerticalAlign.GetUnit();
+    const nsStyleCoord& verticalAlign =
+      frame->GetStyleTextReset()->mVerticalAlign;
 #ifdef NOISY_VERTICAL_ALIGN
     printf("  [frame]");
     nsFrame::ListTag(stdout, frame);
     printf(": verticalAlignUnit=%d (enum == %d)\n",
            verticalAlignUnit,
-           ((eStyleUnit_Enumerated == verticalAlignUnit)
-            ? textStyle->mVerticalAlign.GetIntValue()
+           ((eStyleUnit_Enumerated == verticalAlign.GetUnit())
+            ? verticalAlign.GetIntValue()
             : -1));
 #endif
 
-    PRUint8 verticalAlignEnum;
-    nscoord parentAscent, parentDescent, parentXHeight;
-    nscoord parentSuperscript, parentSubscript;
-    nscoord coordOffset, percentOffset, elementLineHeight;
-    nscoord revisedBaselineY;
-    switch (verticalAlignUnit) {
-      case eStyleUnit_Enumerated:
-      default:
-        if (eStyleUnit_Enumerated == verticalAlignUnit) {
-          verticalAlignEnum = textStyle->mVerticalAlign.GetIntValue();
-        }
-        else {
-          verticalAlignEnum = NS_STYLE_VERTICAL_ALIGN_BASELINE;
+    if (verticalAlign.GetUnit() == eStyleUnit_Enumerated) {
+      switch (verticalAlign.GetIntValue()) {
+        default:
+        case NS_STYLE_VERTICAL_ALIGN_BASELINE:
+        {
+          // The element's baseline is aligned with the baseline of
+          // the parent.
+          pfd->mBounds.y = baselineY - pfd->mAscent;
+          pfd->mVerticalAlign = VALIGN_OTHER;
+          break;
         }
-        switch (verticalAlignEnum) {
-          default:
-          case NS_STYLE_VERTICAL_ALIGN_BASELINE:
-            // The elements baseline is aligned with the baseline of
-            // the parent.
-            pfd->mBounds.y = baselineY - pfd->mAscent;
-            pfd->mVerticalAlign = VALIGN_OTHER;
-            break;
 
-          case NS_STYLE_VERTICAL_ALIGN_SUB:
-            // Lower the baseline of the box to the subscript offset
-            // of the parent's box. This is identical to the baseline
-            // alignment except for the addition of the subscript
-            // offset to the baseline Y.
-            fm->GetSubscriptOffset(parentSubscript);
-            revisedBaselineY = baselineY + parentSubscript;
-            pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
-            pfd->mVerticalAlign = VALIGN_OTHER;
-            break;
+        case NS_STYLE_VERTICAL_ALIGN_SUB:
+        {
+          // Lower the baseline of the box to the subscript offset
+          // of the parent's box. This is identical to the baseline
+          // alignment except for the addition of the subscript
+          // offset to the baseline Y.
+          nscoord parentSubscript;
+          fm->GetSubscriptOffset(parentSubscript);
+          nscoord revisedBaselineY = baselineY + parentSubscript;
+          pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
+          pfd->mVerticalAlign = VALIGN_OTHER;
+          break;
+        }
 
-          case NS_STYLE_VERTICAL_ALIGN_SUPER:
-            // Raise the baseline of the box to the superscript offset
-            // of the parent's box. This is identical to the baseline
-            // alignment except for the subtraction of the superscript
-            // offset to the baseline Y.
-            fm->GetSuperscriptOffset(parentSuperscript);
-            revisedBaselineY = baselineY - parentSuperscript;
-            pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
-            pfd->mVerticalAlign = VALIGN_OTHER;
-            break;
+        case NS_STYLE_VERTICAL_ALIGN_SUPER:
+        {
+          // Raise the baseline of the box to the superscript offset
+          // of the parent's box. This is identical to the baseline
+          // alignment except for the subtraction of the superscript
+          // offset to the baseline Y.
+          nscoord parentSuperscript;
+          fm->GetSuperscriptOffset(parentSuperscript);
+          nscoord revisedBaselineY = baselineY - parentSuperscript;
+          pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
+          pfd->mVerticalAlign = VALIGN_OTHER;
+          break;
+        }
 
-          case NS_STYLE_VERTICAL_ALIGN_TOP:
-          {
-            pfd->mVerticalAlign = VALIGN_TOP;
-            nscoord subtreeHeight = logicalHeight;
-            if (frameSpan) {
-              subtreeHeight = frameSpan->mMaxY - frameSpan->mMinY;
-              NS_ASSERTION(subtreeHeight >= logicalHeight,
-                           "unexpected subtree height");
-            }
-            if (subtreeHeight > maxTopBoxHeight) {
-              maxTopBoxHeight = subtreeHeight;
-            }
-            break;
+        case NS_STYLE_VERTICAL_ALIGN_TOP:
+        {
+          pfd->mVerticalAlign = VALIGN_TOP;
+          nscoord subtreeHeight = logicalHeight;
+          if (frameSpan) {
+            subtreeHeight = frameSpan->mMaxY - frameSpan->mMinY;
+            NS_ASSERTION(subtreeHeight >= logicalHeight,
+                         "unexpected subtree height");
           }
+          if (subtreeHeight > maxTopBoxHeight) {
+            maxTopBoxHeight = subtreeHeight;
+          }
+          break;
+        }
 
-          case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
-          {
-            pfd->mVerticalAlign = VALIGN_BOTTOM;
-            nscoord subtreeHeight = logicalHeight;
-            if (frameSpan) {
-              subtreeHeight = frameSpan->mMaxY - frameSpan->mMinY;
-              NS_ASSERTION(subtreeHeight >= logicalHeight,
-                           "unexpected subtree height");
-            }
-            if (subtreeHeight > maxBottomBoxHeight) {
-              maxBottomBoxHeight = subtreeHeight;
-            }
-            break;
+        case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
+        {
+          pfd->mVerticalAlign = VALIGN_BOTTOM;
+          nscoord subtreeHeight = logicalHeight;
+          if (frameSpan) {
+            subtreeHeight = frameSpan->mMaxY - frameSpan->mMinY;
+            NS_ASSERTION(subtreeHeight >= logicalHeight,
+                         "unexpected subtree height");
           }
+          if (subtreeHeight > maxBottomBoxHeight) {
+            maxBottomBoxHeight = subtreeHeight;
+          }
+          break;
+        }
 
-          case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
-            // Align the midpoint of the frame with 1/2 the parents
-            // x-height above the baseline.
-            fm->GetXHeight(parentXHeight);
-            if (frameSpan) {
-              pfd->mBounds.y = baselineY -
-                (parentXHeight + pfd->mBounds.height)/2;
-            }
-            else {
-              pfd->mBounds.y = baselineY - (parentXHeight + logicalHeight)/2 +
-                pfd->mMargin.top;
-            }
-            pfd->mVerticalAlign = VALIGN_OTHER;
-            break;
+        case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
+        {
+          // Align the midpoint of the frame with 1/2 the parents
+          // x-height above the baseline.
+          nscoord parentXHeight;
+          fm->GetXHeight(parentXHeight);
+          if (frameSpan) {
+            pfd->mBounds.y = baselineY -
+              (parentXHeight + pfd->mBounds.height)/2;
+          }
+          else {
+            pfd->mBounds.y = baselineY - (parentXHeight + logicalHeight)/2 +
+              pfd->mMargin.top;
+          }
+          pfd->mVerticalAlign = VALIGN_OTHER;
+          break;
+        }
 
-          case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
-            // The top of the logical box is aligned with the top of
-            // the parent elements text.
-            fm->GetMaxAscent(parentAscent);
-            if (frameSpan) {
-              pfd->mBounds.y = baselineY - parentAscent -
-                pfd->mBorderPadding.top + frameSpan->mTopLeading;
-            }
-            else {
-              pfd->mBounds.y = baselineY - parentAscent + pfd->mMargin.top;
-            }
-            pfd->mVerticalAlign = VALIGN_OTHER;
-            break;
+        case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
+        {
+          // The top of the logical box is aligned with the top of
+          // the parent element's text.
+          nscoord parentAscent;
+          fm->GetMaxAscent(parentAscent);
+          if (frameSpan) {
+            pfd->mBounds.y = baselineY - parentAscent -
+              pfd->mBorderPadding.top + frameSpan->mTopLeading;
+          }
+          else {
+            pfd->mBounds.y = baselineY - parentAscent + pfd->mMargin.top;
+          }
+          pfd->mVerticalAlign = VALIGN_OTHER;
+          break;
+        }
 
-          case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
-            // The bottom of the logical box is aligned with the
-            // bottom of the parent elements text.
-            fm->GetMaxDescent(parentDescent);
-            if (frameSpan) {
-              pfd->mBounds.y = baselineY + parentDescent -
-                pfd->mBounds.height + pfd->mBorderPadding.bottom -
-                frameSpan->mBottomLeading;
-            }
-            else {
-              pfd->mBounds.y = baselineY + parentDescent -
-                pfd->mBounds.height - pfd->mMargin.bottom;
-            }
-            pfd->mVerticalAlign = VALIGN_OTHER;
-            break;
-
-          case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
-            // Align the midpoint of the frame with the baseline of the parent.
-            if (frameSpan) {
-              pfd->mBounds.y = baselineY - pfd->mBounds.height/2;
-            }
-            else {
-              pfd->mBounds.y = baselineY - logicalHeight/2 + pfd->mMargin.top;
-            }
-            pfd->mVerticalAlign = VALIGN_OTHER;
-            break; 	    
+        case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
+        {
+          // The bottom of the logical box is aligned with the
+          // bottom of the parent elements text.
+          nscoord parentDescent;
+          fm->GetMaxDescent(parentDescent);
+          if (frameSpan) {
+            pfd->mBounds.y = baselineY + parentDescent -
+              pfd->mBounds.height + pfd->mBorderPadding.bottom -
+              frameSpan->mBottomLeading;
+          }
+          else {
+            pfd->mBounds.y = baselineY + parentDescent -
+              pfd->mBounds.height - pfd->mMargin.bottom;
+          }
+          pfd->mVerticalAlign = VALIGN_OTHER;
+          break;
         }
-        break;
 
-      case eStyleUnit_Coord:
-        // According to the CSS2 spec (10.8.1), a positive value
-        // "raises" the box by the given distance while a negative value
-        // "lowers" the box by the given distance (with zero being the
-        // baseline). Since Y coordinates increase towards the bottom of
-        // the screen we reverse the sign.
-        coordOffset = textStyle->mVerticalAlign.GetCoordValue();
-        revisedBaselineY = baselineY - coordOffset;
-        pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
-        pfd->mVerticalAlign = VALIGN_OTHER;
-        break;
-
-      case eStyleUnit_Percent:
-        // Similar to a length value (eStyleUnit_Coord) except that the
-        // percentage is a function of the elements line-height value.
-        elementLineHeight = nsHTMLReflowState::
-          CalcLineHeight(frame->GetStyleContext(),
-                         mBlockReflowState->ComputedHeight());
-        percentOffset = nscoord(
-          textStyle->mVerticalAlign.GetPercentValue() * elementLineHeight
-          );
-        revisedBaselineY = baselineY - percentOffset;
-        pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
-        pfd->mVerticalAlign = VALIGN_OTHER;
-        break;
+        case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
+        {
+          // Align the midpoint of the frame with the baseline of the parent.
+          if (frameSpan) {
+            pfd->mBounds.y = baselineY - pfd->mBounds.height/2;
+          }
+          else {
+            pfd->mBounds.y = baselineY - logicalHeight/2 + pfd->mMargin.top;
+          }
+          pfd->mVerticalAlign = VALIGN_OTHER;
+          break;
+        }
+      }
+    } else {
+      // We have either a coord, a percent, or a calc().
+      nscoord pctBasis = 0;
+      if (verticalAlign.HasPercent()) {
+        // Percentages are like lengths, except treated as a percentage
+        // of the elements line-height value.
+        pctBasis = nsHTMLReflowState::CalcLineHeight(
+          frame->GetStyleContext(), mBlockReflowState->ComputedHeight());
+      }
+      nscoord offset =
+        nsRuleNode::ComputeCoordPercentCalc(verticalAlign, pctBasis);
+      // According to the CSS2 spec (10.8.1), a positive value
+      // "raises" the box by the given distance while a negative value
+      // "lowers" the box by the given distance (with zero being the
+      // baseline). Since Y coordinates increase towards the bottom of
+      // the screen we reverse the sign.
+      nscoord revisedBaselineY = baselineY - offset;
+      pfd->mBounds.y = revisedBaselineY - pfd->mAscent;
+      pfd->mVerticalAlign = VALIGN_OTHER;
     }
 
     // Update minY/maxY for frames that we just placed. Do not factor
     // text into the equation.
     if (pfd->mVerticalAlign == VALIGN_OTHER) {
       // Text frames do not contribute to the min/max Y values for the
       // line (instead their parent frame's font-size contributes).
       // XXXrbs -- relax this restriction because it causes text frames
--- a/layout/reftests/css-calc/reftest.list
+++ b/layout/reftests/css-calc/reftest.list
@@ -11,12 +11,15 @@
 == offsets-absolute-left-1.html offsets-relative-left-1-ref.html
 == offsets-absolute-right-1.html offsets-relative-left-1-ref.html
 == offsets-absolute-top-1.html offsets-absolute-top-1-ref.html
 == offsets-relative-bottom-1.html offsets-relative-top-1-ref.html
 == offsets-relative-left-1.html offsets-relative-left-1-ref.html
 == offsets-relative-right-1.html offsets-relative-left-1-ref.html
 == offsets-relative-top-1.html offsets-relative-top-1-ref.html
 == padding-block-1.html padding-block-1-ref.html
+== text-indent-1.html text-indent-1-ref.html
+== text-indent-intrinsic-1.html text-indent-intrinsic-1-ref.html
+== vertical-align-1.html vertical-align-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
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/text-indent-1-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<title>text-indent: calc()</title>
+<style>
+
+body { width: 500px }
+p { font-size: 10px }
+
+</style>
+
+<p style="text-indent: 247px">50% - 3px</p>
+<p style="text-indent: 247px">25% - 3px + 25%</p>
+<p style="text-indent: 247px">25% - 3px + 12.5% * 2</p>
+<p style="text-indent: 247px">25% - 3px + 12.5%*2</p>
+<p style="text-indent: 247px">25% - 3px + 2*12.5%</p>
+<p style="text-indent: 247px">25% - 3px + 2 * 12.5%</p>
+<p style="text-indent: 125px">min(25%, 150px)</p>
+<p style="text-indent: 100px">min(25%, 100px)</p>
+<p style="text-indent: 150px">max(25%, 150px)</p>
+<p style="text-indent: 125px">max(25%, 100px)</p>
+<p style="text-indent: 150px">min(25%, 150px) + 5%</p>
+<p style="text-indent: 125px">min(25%, 100px) + 5%</p>
+<p style="text-indent: 175px">max(25%, 150px) + 5%</p>
+<p style="text-indent: 150px">max(25%, 100px) + 5%</p>
+<p style="text-indent: 105px">min(25%, 150px) - 2em</p>
+<p style="text-indent: 80px">min(25%, 100px) - 2em</p>
+<p style="text-indent: 130px">max(25%, 150px) - 2em</p>
+<p style="text-indent: 105px">max(25%, 100px) - 2em</p>
+<p style="text-indent: 250px">30% + 20%</p>
+<p style="text-indent: 250px">30% + max(20%, 1px)</p>
+<p style="text-indent: 250px">max(25%, 50%)</p>
+<p style="text-indent: 125px">max(25%, 50%)</p>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/text-indent-1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<title>text-indent: calc() on blocks</title>
+<style>
+
+body { width: 500px }
+p { font-size: 10px }
+
+</style>
+
+<p style="text-indent: -moz-calc(50% - 3px)">50% - 3px</p>
+<p style="text-indent: -moz-calc(25% - 3px + 25%)">25% - 3px + 25%</p>
+<p style="text-indent: -moz-calc(25% - 3px + 12.5% * 2)">25% - 3px + 12.5% * 2</p>
+<p style="text-indent: -moz-calc(25% - 3px + 12.5%*2)">25% - 3px + 12.5%*2</p>
+<p style="text-indent: -moz-calc(25% - 3px + 2*12.5%)">25% - 3px + 2*12.5%</p>
+<p style="text-indent: -moz-calc(25% - 3px + 2 * 12.5%)">25% - 3px + 2 * 12.5%</p>
+<p style="text-indent: -moz-min(25%, 150px)">min(25%, 150px)</p>
+<p style="text-indent: -moz-calc(min(25%, 100px))">min(25%, 100px)</p>
+<p style="text-indent: -moz-calc(max(25%, 150px))">max(25%, 150px)</p>
+<p style="text-indent: -moz-max(25%, 100px)">max(25%, 100px)</p>
+<p style="text-indent: -moz-calc(min(25%, 150px) + 5%)">min(25%, 150px) + 5%</p>
+<p style="text-indent: -moz-calc(min(25%, 100px) + 5%)">min(25%, 100px) + 5%</p>
+<p style="text-indent: -moz-calc(max(25%, 150px) + 5%)">max(25%, 150px) + 5%</p>
+<p style="text-indent: -moz-calc(max(25%, 100px) + 5%)">max(25%, 100px) + 5%</p>
+<p style="text-indent: -moz-calc(min(25%, 150px) - 2em)">min(25%, 150px) - 2em</p>
+<p style="text-indent: -moz-calc(min(25%, 100px) - 2em)">min(25%, 100px) - 2em</p>
+<p style="text-indent: -moz-calc(max(25%, 150px) - 2em)">max(25%, 150px) - 2em</p>
+<p style="text-indent: -moz-calc(max(25%, 100px) - 2em)">max(25%, 100px) - 2em</p>
+<p style="text-indent: -moz-calc(30% + 20%)">30% + 20%</p>
+<p style="text-indent: -moz-calc(30% + max(20%, 1px))">30% + max(20%, 1px)</p>
+<p style="text-indent: -moz-calc(max(25%, 50%))">max(25%, 50%)</p>
+<p style="text-indent: -moz-calc(min(25%, 50%))">max(25%, 50%)</p>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/text-indent-intrinsic-1-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<title>intrinsic width of text-indent: calc() on blocks</title>
+<style>
+
+body > div { margin: 0 0 1px 0; background: blue; color: white; height: 5px }
+
+</style>
+
+<div style="width: 10px"></div>
+<div style="width: 57px"></div>
+<div style="width: 57px"></div>
+<div style="width: 10px"></div>
+<div style="width: 10px"></div>
+<div style="width: 60px"></div>
+<div style="width: 10px"></div>
+<div style="width: 10px"></div>
+<div style="width: 60px"></div>
+<div style="width: 10px"></div>
+<div style="width: 10px"></div>
+<div style="width: 10px"></div>
+<div style="width: 10px"></div>
+<div style="width: 10px"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/text-indent-intrinsic-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<title>intrinsic width of text-indent: calc() on blocks</title>
+<style>
+
+body { font-size: 10px }
+div { float: left; clear: left;
+       margin: 0 0 1px 0; background: blue; color: white; height: 5px }
+span { display: inline-block; width: 10px }
+
+</style>
+
+<div style="text-indent: -moz-calc(50% - 3px)"><span></span></div>
+<div style="text-indent: -moz-calc(5em - 3px)"><span></span></div>
+<div style="text-indent: -moz-calc(max(5em, 0) - 3px)"><span></span></div>
+<div style="text-indent: -moz-calc(max(5em, 0%) - 3px)"><span></span></div>
+<div style="text-indent: -moz-calc(5em - min(3px, 0%))"><span></span></div>
+<div style="text-indent: -moz-calc(5em - min(3px, 0))"><span></span></div>
+<div style="text-indent: -moz-calc(5em - 0%)"><span></span></div>
+<div style="text-indent: -moz-calc(50%)"><span></span></div>
+<div style="text-indent: -moz-calc(50px)"><span></span></div>
+<div style="text-indent: -moz-calc(25% + 25%)"><span></span></div>
+<div style="text-indent: -moz-calc(min(25%, 50%))"><span></span></div>
+<div style="text-indent: -moz-calc(max(25%, 50%))"><span></span></div>
+<div style="text-indent: -moz-calc(min(25%, 100px))"><span></span></div>
+<div style="text-indent: -moz-calc(max(25%, 100px))"><span></span></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/vertical-align-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<title>Test for vertical-align:calc()</title>
+
+<div style="line-height: 100px; margin-top: 100px">
+  <span>x</span>
+  <span style="vertical-align: 50px">x</span>
+  <span style="vertical-align: 50px">x</span>
+  <span style="vertical-align: 75px">x</span>
+  <span style="vertical-align: 45px">x</span>
+  <span style="vertical-align: 40px">x</span>
+  <span style="vertical-align: 30px">x</span>
+</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-calc/vertical-align-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<title>Test for vertical-align:calc()</title>
+
+<div style="line-height: 100px; margin-top: 100px">
+  <span>x</span>
+  <span style="vertical-align: -moz-calc(50px)">x</span>
+  <span style="vertical-align: -moz-calc(50%)">x</span>
+  <span style="vertical-align: -moz-calc(25px + 50%)">x</span>
+  <span style="vertical-align: -moz-calc(150% / 2 - 30px)">x</span>
+  <span style="vertical-align: -moz-calc(40px + 10% - 20% / 2)">x</span>
+  <span style="vertical-align: -moz-calc(40px - 10%)">x</span>
+</div>
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -5934,17 +5934,17 @@ CSSParserImpl::ParseSingleValueProperty(
   case eCSSProperty_text_align:
     // When we support aligning on a string, we can parse text-align
     // as a string....
     return ParseVariant(aValue, VARIANT_HK /* | VARIANT_STRING */,
                         nsCSSProps::kTextAlignKTable);
   case eCSSProperty_text_decoration:
     return ParseTextDecoration(aValue);
   case eCSSProperty_text_indent:
-    return ParseVariant(aValue, VARIANT_HLP, nsnull);
+    return ParseVariant(aValue, VARIANT_HLP | VARIANT_CALC, nsnull);
   case eCSSProperty_text_transform:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kTextTransformKTable);
   case eCSSProperty_unicode_bidi:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kUnicodeBidiKTable);
   case eCSSProperty_user_focus:
     return ParseVariant(aValue, VARIANT_HK,
@@ -5954,17 +5954,17 @@ CSSParserImpl::ParseSingleValueProperty(
                         nsCSSProps::kUserInputKTable);
   case eCSSProperty_user_modify:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kUserModifyKTable);
   case eCSSProperty_user_select:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kUserSelectKTable);
   case eCSSProperty_vertical_align:
-    return ParseVariant(aValue, VARIANT_HKLP,
+    return ParseVariant(aValue, VARIANT_HKLP | VARIANT_CALC,
                         nsCSSProps::kVerticalAlignKTable);
   case eCSSProperty_visibility:
     return ParseVariant(aValue, VARIANT_HK,
                         nsCSSProps::kVisibilityKTable);
   case eCSSProperty_voice_family:
     return ParseFamily(aValue);
   case eCSSProperty_volume:
     return ParseVariant(aValue, VARIANT_HPN | VARIANT_KEYWORD,
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3585,20 +3585,20 @@ nsRuleNode::ComputeTextData(void* aStart
     text->mTextAlign = (NS_STYLE_TEXT_ALIGN_DEFAULT == parentAlign) ?
       NS_STYLE_TEXT_ALIGN_CENTER : parentAlign;
   } else
     SetDiscrete(textData.mTextAlign, text->mTextAlign, canStoreInRuleTree,
                 SETDSC_ENUMERATED, parentText->mTextAlign,
                 NS_STYLE_TEXT_ALIGN_DEFAULT,
                 0, 0, 0, 0);
 
-  // text-indent: length, percent, inherit, initial
+  // text-indent: length, percent, calc, inherit, initial
   SetCoord(textData.mTextIndent, text->mTextIndent, parentText->mTextIndent,
-           SETCOORD_LPH | SETCOORD_INITIAL_ZERO, aContext,
-           mPresContext, canStoreInRuleTree);
+           SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC,
+           aContext, mPresContext, canStoreInRuleTree);
 
   // text-transform: enum, inherit, initial
   SetDiscrete(textData.mTextTransform, text->mTextTransform, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentText->mTextTransform,
               NS_STYLE_TEXT_TRANSFORM_NONE, 0, 0, 0, 0);
 
   // white-space: enum, inherit, initial
   SetDiscrete(textData.mWhiteSpace, text->mWhiteSpace, canStoreInRuleTree,
@@ -3638,19 +3638,20 @@ nsRuleNode::ComputeTextResetData(void* a
                                  const nsRuleDataStruct& aData,
                                  nsStyleContext* aContext,
                                  nsRuleNode* aHighestNode,
                                  const RuleDetail aRuleDetail,
                                  const PRBool aCanStoreInRuleTree)
 {
   COMPUTE_START_RESET(TextReset, (), text, parentText, Text, textData)
 
-  // vertical-align: enum, length, percent, inherit
+  // vertical-align: enum, length, percent, calc, inherit
   if (!SetCoord(textData.mVerticalAlign, text->mVerticalAlign,
-                parentText->mVerticalAlign, SETCOORD_LPH | SETCOORD_ENUMERATED,
+                parentText->mVerticalAlign,
+                SETCOORD_LPH | SETCOORD_ENUMERATED | SETCOORD_STORE_CALC,
                 aContext, mPresContext, canStoreInRuleTree)) {
     if (eCSSUnit_Initial == textData.mVerticalAlign.GetUnit()) {
       text->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE,
                                        eStyleUnit_Enumerated);
     }
   }
 
   // text-decoration: enum (bit field), inherit, initial
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1146,17 +1146,17 @@ struct nsStyleTextReset {
 #ifdef DEBUG
   static nsChangeHint MaxDifference();
 #endif
   static PRBool ForceCompare() { return PR_FALSE; }
 
   PRUint8 mTextDecoration;              // [reset] see nsStyleConsts.h
   PRUint8 mUnicodeBidi;                 // [reset] see nsStyleConsts.h
 
-  nsStyleCoord  mVerticalAlign;         // [reset] coord, percent, enum (see nsStyleConsts.h)
+  nsStyleCoord  mVerticalAlign;         // [reset] coord, percent, calc, enum (see nsStyleConsts.h)
 };
 
 struct nsStyleText {
   nsStyleText(void);
   nsStyleText(const nsStyleText& aOther);
   ~nsStyleText(void);
 
   void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
@@ -1176,17 +1176,17 @@ struct nsStyleText {
   PRUint8 mTextAlign;                   // [inherited] see nsStyleConsts.h
   PRUint8 mTextTransform;               // [inherited] see nsStyleConsts.h
   PRUint8 mWhiteSpace;                  // [inherited] see nsStyleConsts.h
   PRUint8 mWordWrap;                    // [inherited] see nsStyleConsts.h
   PRInt32 mTabSize;                     // [inherited] see nsStyleConsts.h
 
   nsStyleCoord  mLetterSpacing;         // [inherited] coord, normal
   nsStyleCoord  mLineHeight;            // [inherited] coord, factor, normal
-  nsStyleCoord  mTextIndent;            // [inherited] coord, percent
+  nsStyleCoord  mTextIndent;            // [inherited] coord, percent, calc
   nscoord mWordSpacing;                 // [inherited]
 
   nsRefPtr<nsCSSShadowArray> mTextShadow; // [inherited] NULL in case of a zero-length
 
   PRBool WhiteSpaceIsSignificant() const {
     return mWhiteSpace == NS_STYLE_WHITESPACE_PRE ||
            mWhiteSpace == NS_STYLE_WHITESPACE_PRE_WRAP;
   }
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -2287,17 +2287,24 @@ var gCSSProperties = {
 		other_values: [ "underline", "overline", "line-through", "blink line-through underline", "underline overline line-through blink", "-moz-anchor-decoration", "blink -moz-anchor-decoration" ],
 		invalid_values: [ "underline none", "none underline", "line-through blink line-through" ]
 	},
 	"text-indent": {
 		domProp: "textIndent",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "0" ],
-		other_values: [ "2em", "5%", "-10px" ],
+		other_values: [ "2em", "5%", "-10px",
+			"-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: []
 	},
 	"text-shadow": {
 		domProp: "textShadow",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		prerequisites: { "color": "blue" },
 		initial_values: [ "none" ],
@@ -2391,17 +2398,24 @@ var gCSSProperties = {
 		other_values: [ "embed", "bidi-override" ],
 		invalid_values: [ "auto", "none" ]
 	},
 	"vertical-align": {
 		domProp: "verticalAlign",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "baseline" ],
-		other_values: [ "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "15%", "3px", "0.2em", "-5px", "-3%" ],
+		other_values: [ "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "15%", "3px", "0.2em", "-5px", "-3%",
+			"-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: []
 	},
 	"visibility": {
 		domProp: "visibility",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "visible" ],
 		other_values: [ "hidden", "collapse" ],