Simplify storage of computed calc() as a result of removing min() and max(). (Bug 363249) r=bzbarsky a2.0=blocking2.0:beta6
authorL. David Baron <dbaron@dbaron.org>
Sat, 11 Sep 2010 09:27:13 -0700
changeset 52481 26bfc086082229a29d14fa8f1d45b73c98f3af7e
parent 52480 83a79e1e035bbba5d59f26984523b16e85e3c40c
child 52482 3e1939477d6d15422464eb2a6b3d27c20f6708cc
push id15656
push userdbaron@mozilla.com
push dateSat, 11 Sep 2010 16:27:55 +0000
treeherdermozilla-central@26bfc0860822 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs363249
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
Simplify storage of computed calc() as a result of removing min() and max(). (Bug 363249) r=bzbarsky a2.0=blocking2.0:beta6
layout/generic/nsFrame.cpp
layout/style/CSSCalc.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsRuleNode.cpp
layout/style/nsRuleNode.h
layout/style/nsStyleCoord.cpp
layout/style/nsStyleCoord.h
layout/style/nsStyleTransformMatrix.cpp
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3211,117 +3211,59 @@ nsIFrame::InlinePrefWidthData::ForceBrea
 
   currentLine =
     NSCoordSaturatingSubtract(currentLine, trailingWhitespace, nscoord_MAX);
   prevLines = NS_MAX(prevLines, currentLine);
   currentLine = trailingWhitespace = 0;
   skipWhitespace = PR_TRUE;
 }
 
-/**
- * This class does calc() computation of lengths and percents separately.
- *
- * FIXME: remove this in the next patch
- *
- * It is used only for intrinsic width computation, where being an
- * approximation is sometimes ok (although it would be good to fix at
- * some point in the future).
- */
-struct LengthPercentPairWithMinMaxCalcOps : public css::StyleCoordInputCalcOps
-{
-  struct result_type {
-    nscoord mLength;
-    float mPercent;
-
-    result_type(nscoord aLength, float aPercent)
-      : mLength(aLength), mPercent(aPercent) {}
-  };
-
-  result_type ComputeLeafValue(const nsStyleCoord& aValue)
-  {
-    if (aValue.GetUnit() == eStyleUnit_Percent) {
-      return result_type(0, aValue.GetPercentValue());
-    }
-    return result_type(aValue.GetCoordValue(), 0.0f);
-  }
-
-  result_type
-  MergeAdditive(nsCSSUnit aCalcFunction,
-                result_type aValue1, result_type aValue2)
-  {
-    if (aCalcFunction == eCSSUnit_Calc_Plus) {
-      return result_type(NSCoordSaturatingAdd(aValue1.mLength,
-                                              aValue2.mLength),
-                         aValue1.mPercent + aValue2.mPercent);
-    }
-    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
-                      "unexpected unit");
-    return result_type(NSCoordSaturatingSubtract(aValue1.mLength,
-                                                 aValue2.mLength, 0),
-                       aValue1.mPercent - aValue2.mPercent);
-  }
-
-  result_type
-  MergeMultiplicativeL(nsCSSUnit aCalcFunction,
-                       float aValue1, result_type aValue2)
-  {
-    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
-                      "unexpected unit");
-    return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1),
-                       aValue1 * aValue2.mPercent);
-  }
-
-  result_type
-  MergeMultiplicativeR(nsCSSUnit aCalcFunction,
-                       result_type aValue1, float aValue2)
-  {
-    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R ||
-                      aCalcFunction == eCSSUnit_Calc_Divided,
-                      "unexpected unit");
-    if (aCalcFunction == eCSSUnit_Calc_Divided) {
-      aValue2 = 1.0f / aValue2;
-    }
-    return result_type(NSCoordSaturatingMultiply(aValue1.mLength, aValue2),
-                       aValue1.mPercent * aValue2);
-  }
-
-};
-
 static void
 AddCoord(const nsStyleCoord& aStyle,
          nsIRenderingContext* aRenderingContext,
          nsIFrame* aFrame,
          nscoord* aCoord, float* aPercent,
          PRBool aClampNegativeToZero)
 {
-  if (!aStyle.IsCoordPercentCalcUnit()) {
-    return;
-  }
-
-  LengthPercentPairWithMinMaxCalcOps ops;
-  LengthPercentPairWithMinMaxCalcOps::result_type pair =
-    css::ComputeCalc(aStyle, ops);
-  if (aClampNegativeToZero) {
-    // This is far from ideal when one is negative and one is positive.
-    pair.mLength = NS_MAX(pair.mLength, 0);
-    pair.mPercent = NS_MAX(pair.mPercent, 0.0f);
-  }
-  *aCoord += pair.mLength;
-  *aPercent += pair.mPercent;
+  switch (aStyle.GetUnit()) {
+    case eStyleUnit_Coord: {
+      NS_ASSERTION(!aClampNegativeToZero || aStyle.GetCoordValue() >= 0,
+                   "unexpected negative value");
+      *aCoord += aStyle.GetCoordValue();
+      return;
+    }
+    case eStyleUnit_Percent: {
+      NS_ASSERTION(!aClampNegativeToZero || aStyle.GetPercentValue() >= 0.0f,
+                   "unexpected negative value");
+      *aPercent += aStyle.GetPercentValue();
+      return;
+    }
+    case eStyleUnit_Calc: {
+      const nsStyleCoord::Calc *calc = aStyle.GetCalcValue();
+      if (aClampNegativeToZero) {
+        // This is far from ideal when one is negative and one is positive.
+        *aCoord += NS_MAX(calc->mLength, 0);
+        *aPercent += NS_MAX(calc->mPercent, 0.0f);
+      } else {
+        *aCoord += calc->mLength;
+        *aPercent += calc->mPercent;
+      }
+      return;
+    }
+    default: {
+      return;
+    }
+  }
 }
 
 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
 nsFrame::IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext)
 {
   IntrinsicWidthOffsetData result;
 
-  // FIXME: The handling of calc() with min() and max() by AddCoord
-  // is a rough approximation.  It could be improved, but only by
-  // changing the IntrinsicWidthOffsets API substantially.  See the
-  // comment above LengthPercentPairWithMinMaxCalcOps.
   const nsStyleMargin *styleMargin = GetStyleMargin();
   AddCoord(styleMargin->mMargin.GetLeft(), aRenderingContext, this,
            &result.hMargin, &result.hPctMargin, PR_FALSE);
   AddCoord(styleMargin->mMargin.GetRight(), aRenderingContext, this,
            &result.hMargin, &result.hPctMargin, PR_FALSE);
 
   const nsStylePadding *stylePadding = GetStylePadding();
   AddCoord(stylePadding->mPadding.GetLeft(), aRenderingContext, this,
--- a/layout/style/CSSCalc.h
+++ b/layout/style/CSSCalc.h
@@ -138,86 +138,32 @@ ComputeCalc(const typename CalcOps::inpu
       return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
     }
     default: {
       return aOps.ComputeLeafValue(aValue);
     }
   }
 }
 
-#define CHECK_UNIT(u_)                                                        \
-  PR_STATIC_ASSERT(int(eCSSUnit_##u_) + 14 == int(eStyleUnit_##u_));          \
-  PR_STATIC_ASSERT(eCSSUnit_##u_ >= eCSSUnit_Calc);                           \
-  PR_STATIC_ASSERT(eCSSUnit_##u_ <= eCSSUnit_Calc_Divided);
-
-CHECK_UNIT(Calc)
-CHECK_UNIT(Calc_Plus)
-CHECK_UNIT(Calc_Minus)
-CHECK_UNIT(Calc_Times_L)
-CHECK_UNIT(Calc_Times_R)
-CHECK_UNIT(Calc_Divided)
-
-#undef CHECK_UNIT
-
-inline nsStyleUnit
-ConvertCalcUnit(nsCSSUnit aUnit)
-{
-  NS_ABORT_IF_FALSE(eCSSUnit_Calc <= aUnit &&
-                    aUnit <= eCSSUnit_Calc_Divided, "out of range");
-  return nsStyleUnit(aUnit + 14);
-}
-
-inline nsCSSUnit
-ConvertCalcUnit(nsStyleUnit aUnit)
-{
-  NS_ABORT_IF_FALSE(eStyleUnit_Calc <= aUnit &&
-                    aUnit <= eStyleUnit_Calc_Divided, "out of range");
-  return nsCSSUnit(aUnit - 14);
-}
-
 /**
  * The input unit operation for input_type being nsCSSValue.
  */
 struct CSSValueInputCalcOps
 {
   typedef nsCSSValue input_type;
   typedef nsCSSValue::Array input_array_type;
 
   static nsCSSUnit GetUnit(const nsCSSValue& aValue)
   {
     return aValue.GetUnit();
   }
 
 };
 
 /**
- * The input unit operation for input_type being nsStyleCoord
- */
-struct StyleCoordInputCalcOps
-{
-  typedef nsStyleCoord input_type;
-  typedef nsStyleCoord::Array input_array_type;
-
-  static nsCSSUnit GetUnit(const nsStyleCoord& aValue)
-  {
-    if (aValue.IsCalcUnit()) {
-      return css::ConvertCalcUnit(aValue.GetUnit());
-    }
-    return eCSSUnit_Null;
-  }
-
-  float ComputeNumber(const nsStyleCoord& aValue)
-  {
-    NS_ABORT_IF_FALSE(PR_FALSE, "SpecifiedToComputedCalcOps should not "
-                                "leave numbers in structure");
-    return 0.0f;
-  }
-};
-
-/**
  * Basic*CalcOps provide a partial implementation of the CalcOps
  * template parameter to ComputeCalc, for those callers whose merging
  * just consists of mathematics (rather than tree construction).
  */
 
 struct BasicCoordCalcOps
 {
   typedef nscoord result_type;
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -3797,64 +3797,16 @@ nsComputedDOMStyle::GetBorderStyleFor(mo
   val->SetIdent(
     nsCSSProps::ValueToKeywordEnum(GetStyleBorder()->GetBorderStyle(aSide),
                                    nsCSSProps::kBorderStyleKTable));
 
   NS_ADDREF(*aValue = val);
   return NS_OK;
 }
 
-struct StyleCoordSerializeCalcOps {
-  StyleCoordSerializeCalcOps(nsAString& aResult)
-    : mResult(aResult)
-  {
-  }
-
-  typedef nsStyleCoord input_type;
-  typedef nsStyleCoord::Array input_array_type;
-
-  static nsCSSUnit GetUnit(const input_type& aValue) {
-    if (aValue.IsCalcUnit()) {
-      return css::ConvertCalcUnit(aValue.GetUnit());
-    }
-    return eCSSUnit_Null;
-  }
-
-  void Append(const char* aString)
-  {
-    mResult.AppendASCII(aString);
-  }
-
-  void AppendLeafValue(const input_type& aValue)
-  {
-    nsRefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue();
-    if (aValue.GetUnit() == eStyleUnit_Percent) {
-      val->SetPercent(aValue.GetPercentValue());
-    } else {
-      NS_ABORT_IF_FALSE(aValue.GetUnit() == eStyleUnit_Coord,
-                        "unexpected unit");
-      val->SetAppUnits(aValue.GetCoordValue());
-    }
-
-    nsAutoString tmp;
-    val->GetCssText(tmp);
-    mResult.Append(tmp);
-  }
-
-  void AppendNumber(const input_type& aValue)
-  {
-    NS_ABORT_IF_FALSE(PR_FALSE,
-                      "should not have numbers in nsStyleCoord calc()");
-  }
-
-private:
-  nsAString &mResult;
-};
-
-
 void
 nsComputedDOMStyle::SetValueToCoord(nsROCSSPrimitiveValue* aValue,
                                     const nsStyleCoord& aCoord,
                                     PRBool aClampNegativeCalc,
                                     PercentageBaseGetter aPercentageBaseGetter,
                                     const PRInt32 aTable[],
                                     nscoord aMinAppUnits,
                                     nscoord aMaxAppUnits)
@@ -3904,46 +3856,60 @@ nsComputedDOMStyle::SetValueToCoord(nsRO
       aValue->SetIdent(nsCSSProps::ValueToKeywordEnum(aCoord.GetIntValue(),
                                                       aTable));
       break;
 
     case eStyleUnit_None:
       aValue->SetIdent(eCSSKeyword_none);
       break;
 
+    case eStyleUnit_Calc:
+      nscoord percentageBase;
+      if (!aCoord.CalcHasPercent()) {
+        nscoord val = nsRuleNode::ComputeCoordPercentCalc(aCoord, 0);
+        if (aClampNegativeCalc && val < 0) {
+          NS_ABORT_IF_FALSE(aCoord.IsCalcUnit(),
+                            "parser should have rejected value");
+          val = 0;
+        }
+        aValue->SetAppUnits(NS_MAX(aMinAppUnits, NS_MIN(val, aMaxAppUnits)));
+      } else if (aPercentageBaseGetter &&
+                 (this->*aPercentageBaseGetter)(percentageBase)) {
+        nscoord val =
+          nsRuleNode::ComputeCoordPercentCalc(aCoord, percentageBase);
+        if (aClampNegativeCalc && val < 0) {
+          NS_ABORT_IF_FALSE(aCoord.IsCalcUnit(),
+                            "parser should have rejected value");
+          val = 0;
+        }
+        aValue->SetAppUnits(NS_MAX(aMinAppUnits, NS_MIN(val, aMaxAppUnits)));
+      } else {
+        nsStyleCoord::Calc *calc = aCoord.GetCalcValue();
+        nsRefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue();
+        nsAutoString tmp, result;
+
+        result.AppendLiteral("-moz-calc(");
+
+        val->SetAppUnits(calc->mLength);
+        val->GetCssText(tmp);
+        result.Append(tmp);
+
+        result.AppendLiteral(" + ");
+
+        val->SetPercent(calc->mPercent);
+        val->GetCssText(tmp);
+        result.Append(tmp);
+
+        result.AppendLiteral(")");
+
+        aValue->SetString(result); // not really SetString
+      }
+      break;
     default:
-      if (aCoord.IsCalcUnit()) {
-        nscoord percentageBase;
-        if (!aCoord.CalcHasPercent()) {
-          nscoord val = nsRuleNode::ComputeCoordPercentCalc(aCoord, 0);
-          if (aClampNegativeCalc && val < 0) {
-            NS_ABORT_IF_FALSE(aCoord.IsCalcUnit(),
-                              "parser should have rejected value");
-            val = 0;
-          }
-          aValue->SetAppUnits(NS_MAX(aMinAppUnits, NS_MIN(val, aMaxAppUnits)));
-        } else if (aPercentageBaseGetter &&
-                   (this->*aPercentageBaseGetter)(percentageBase)) {
-          nscoord val =
-            nsRuleNode::ComputeCoordPercentCalc(aCoord, percentageBase);
-          if (aClampNegativeCalc && val < 0) {
-            NS_ABORT_IF_FALSE(aCoord.IsCalcUnit(),
-                              "parser should have rejected value");
-            val = 0;
-          }
-          aValue->SetAppUnits(NS_MAX(aMinAppUnits, NS_MIN(val, aMaxAppUnits)));
-        } else {
-          nsAutoString tmp;
-          StyleCoordSerializeCalcOps ops(tmp);
-          css::SerializeCalc(aCoord, ops);
-          aValue->SetString(tmp); // not really SetString
-        }
-      } else {
-        NS_ERROR("Can't handle this unit");
-      }
+      NS_ERROR("Can't handle this unit");
       break;
   }
 }
 
 nscoord
 nsComputedDOMStyle::StyleCoordToNSCoord(const nsStyleCoord& aCoord,
                                         PercentageBaseGetter aPercentageBaseGetter,
                                         nscoord aDefaultValue,
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -366,195 +366,146 @@ nsRuleNode::CalcLengthWithInitialFont(ns
                                       const nsCSSValue& aValue)
 {
   nsStyleFont defaultFont(aPresContext);
   PRBool canStoreInRuleTree;
   return CalcLengthWith(aValue, -1, &defaultFont, nsnull, aPresContext,
                         PR_TRUE, PR_FALSE, canStoreInRuleTree);
 }
 
-struct SpecifiedToComputedCalcOps : public css::NumbersAlreadyNormalizedOps
+struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps
 {
-  // FIXME (perf): Is there too much copying as a result of returning
-  // nsStyleCoord objects?
-  typedef nsStyleCoord result_type;
-
-  nsStyleContext* const mStyleContext;
-  nsPresContext* const mPresContext;
+  typedef nsRuleNode::ComputedCalc result_type;
+
+  LengthPercentPairCalcOps(nsStyleContext* aContext,
+                           nsPresContext* aPresContext,
+                           PRBool& aCanStoreInRuleTree)
+    : mContext(aContext),
+      mPresContext(aPresContext),
+      mCanStoreInRuleTree(aCanStoreInRuleTree),
+      mHasPercent(PR_FALSE) {}
+
+  nsStyleContext* mContext;
+  nsPresContext* mPresContext;
   PRBool& mCanStoreInRuleTree;
-
-  SpecifiedToComputedCalcOps(nsStyleContext* aStyleContext,
-                             nsPresContext* aPresContext,
-                             PRBool& aCanStoreInRuleTree)
-    : mStyleContext(aStyleContext),
-      mPresContext(aPresContext),
-      mCanStoreInRuleTree(aCanStoreInRuleTree)
+  PRBool mHasPercent;
+
+  result_type ComputeLeafValue(const nsCSSValue& aValue)
   {
+    if (aValue.GetUnit() == eCSSUnit_Percent) {
+      mHasPercent = PR_TRUE;
+      return result_type(0, aValue.GetPercentValue());
+    }
+    return result_type(CalcLength(aValue, mContext, mPresContext,
+                                  mCanStoreInRuleTree),
+                       0.0f);
   }
 
   result_type
   MergeAdditive(nsCSSUnit aCalcFunction,
                 result_type aValue1, result_type aValue2)
   {
-    nsStyleUnit unit1 = aValue1.GetUnit();
-    nsStyleUnit unit2 = aValue2.GetUnit();
-    NS_ABORT_IF_FALSE(unit1 == eStyleUnit_Coord ||
-                      unit1 == eStyleUnit_Percent ||
-                      aValue1.IsCalcUnit(),
-                      "unexpected unit");
-    NS_ABORT_IF_FALSE(unit2 == eStyleUnit_Coord ||
-                      unit2 == eStyleUnit_Percent ||
-                      aValue2.IsCalcUnit(),
-                      "unexpected unit");
-    nsStyleCoord result;
-    if (unit1 == unit2 && !aValue1.IsCalcUnit()) {
-      // Merge nodes that we don't need to keep separate.
-      if (unit1 == eStyleUnit_Percent) {
-        css::BasicFloatCalcOps ops;
-        result.SetPercentValue(ops.MergeAdditive(aCalcFunction,
-                                                 aValue1.GetPercentValue(),
-                                                 aValue2.GetPercentValue()));
-      } else {
-        css::BasicCoordCalcOps ops;
-        result.SetCoordValue(ops.MergeAdditive(aCalcFunction,
-                                               aValue1.GetCoordValue(),
-                                               aValue2.GetCoordValue()));
-      }
-    } else {
-      nsStyleCoord::Array *array =
-        nsStyleCoord::Array::Create(mStyleContext, mCanStoreInRuleTree, 2);
-      array->Item(0) = aValue1;
-      array->Item(1) = aValue2;
-      result.SetArrayValue(array, css::ConvertCalcUnit(aCalcFunction));
-    }
-    return result;
+    if (aCalcFunction == eCSSUnit_Calc_Plus) {
+      return result_type(NSCoordSaturatingAdd(aValue1.mLength,
+                                              aValue2.mLength),
+                         aValue1.mPercent + aValue2.mPercent);
+    }
+    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
+                      "min() and max() are not allowed in calc() on "
+                      "transform");
+    return result_type(NSCoordSaturatingSubtract(aValue1.mLength,
+                                                 aValue2.mLength, 0),
+                       aValue1.mPercent - aValue2.mPercent);
   }
 
   result_type
   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
                        float aValue1, result_type aValue2)
   {
-    nsStyleCoord result;
-    switch (aValue2.GetUnit()) {
-      case eStyleUnit_Percent: {
-        css::BasicFloatCalcOps ops;
-        result.SetPercentValue(ops.MergeMultiplicativeL(
-          aCalcFunction, aValue1, aValue2.GetPercentValue()));
-        break;
-      }
-      case eStyleUnit_Coord: {
-        css::BasicCoordCalcOps ops;
-        result.SetCoordValue(ops.MergeMultiplicativeL(
-          aCalcFunction, aValue1, aValue2.GetCoordValue()));
-        break;
-      }
-      default:
-        NS_ABORT_IF_FALSE(aValue2.IsCalcUnit(), "unexpected unit");
-        nsStyleCoord::Array *array =
-          nsStyleCoord::Array::Create(mStyleContext, mCanStoreInRuleTree, 2);
-        array->Item(0).SetFactorValue(aValue1);
-        array->Item(1) = aValue2;
-        result.SetArrayValue(array, css::ConvertCalcUnit(aCalcFunction));
-        break;
-    }
-    return result;
+    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
+                      "unexpected unit");
+    return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1),
+                       aValue1 * aValue2.mPercent);
   }
 
   result_type
   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
                        result_type aValue1, float aValue2)
   {
-    nsStyleCoord result;
-    switch (aValue1.GetUnit()) {
-      case eStyleUnit_Percent: {
-        css::BasicFloatCalcOps ops;
-        result.SetPercentValue(ops.MergeMultiplicativeR(
-          aCalcFunction, aValue1.GetPercentValue(), aValue2));
-        break;
-      }
-      case eStyleUnit_Coord: {
-        css::BasicCoordCalcOps ops;
-        result.SetCoordValue(ops.MergeMultiplicativeR(
-          aCalcFunction, aValue1.GetCoordValue(), aValue2));
-        break;
-      }
-      default:
-        NS_ABORT_IF_FALSE(aValue1.IsCalcUnit(), "unexpected unit");
-        nsStyleCoord::Array *array =
-          nsStyleCoord::Array::Create(mStyleContext, mCanStoreInRuleTree, 2);
-        array->Item(0) = aValue1;
-        array->Item(1).SetFactorValue(aValue2);
-        result.SetArrayValue(array, css::ConvertCalcUnit(aCalcFunction));
-        break;
-    }
-    return result;
-  }
-
-  result_type ComputeLeafValue(const nsCSSValue& aValue)
-  {
-    nsStyleCoord result;
-    if (aValue.GetUnit() == eCSSUnit_Percent) {
-      result.SetPercentValue(aValue.GetPercentValue());
-    } else {
-      result.SetCoordValue(CalcLength(aValue, mStyleContext, mPresContext,
-                                      mCanStoreInRuleTree));
-    }
-    return result;
-  }
+    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R ||
+                      aCalcFunction == eCSSUnit_Calc_Divided,
+                      "unexpected unit");
+    if (aCalcFunction == eCSSUnit_Calc_Divided) {
+      aValue2 = 1.0f / aValue2;
+    }
+    return result_type(NSCoordSaturatingMultiply(aValue1.mLength, aValue2),
+                       aValue1.mPercent * aValue2);
+  }
+
 };
 
 static void
 SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleCoord& aCoord, 
                             nsStyleContext* aStyleContext,
                             PRBool& aCanStoreInRuleTree)
 {
-  SpecifiedToComputedCalcOps ops(aStyleContext, aStyleContext->PresContext(),
-                                 aCanStoreInRuleTree);
-  aCoord = ComputeCalc(aValue, ops);
-  if (!aCoord.IsCalcUnit()) {
-    // Some callers distinguish between calc(50%) and 50%, or calc(50px)
-    // and 50px.
-    nsStyleCoord::Array *array =
-      nsStyleCoord::Array::Create(aStyleContext, aCanStoreInRuleTree, 1);
-    array->Item(0) = aCoord;
-    aCoord.SetArrayValue(array, eStyleUnit_Calc);
-  }
+  LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(),
+                               aCanStoreInRuleTree);
+  nsRuleNode::ComputedCalc vals = ComputeCalc(aValue, ops);
+
+  nsStyleCoord::Calc *calcObj =
+    new (aStyleContext->Alloc(sizeof(nsStyleCoord::Calc))) nsStyleCoord::Calc;
+  // Because we use aStyleContext->Alloc(), we have to store the result
+  // on the style context and not in the rule tree.
+  aCanStoreInRuleTree = PR_FALSE;
+
+  calcObj->mLength = vals.mLength;
+  calcObj->mPercent = vals.mPercent;
+  calcObj->mHasPercent = ops.mHasPercent;
+
+  aCoord.SetCalcValue(calcObj);
 }
 
-struct ComputeComputedCalcCalcOps : public css::StyleCoordInputCalcOps,
-                                    public css::BasicCoordCalcOps
+/* static */ nsRuleNode::ComputedCalc
+nsRuleNode::SpecifiedCalcToComputedCalc(const nsCSSValue& aValue,
+                                        nsStyleContext* aStyleContext,
+                                        nsPresContext* aPresContext,
+                                        PRBool& aCanStoreInRuleTree)
 {
-  const nscoord mPercentageBasis;
-
-  ComputeComputedCalcCalcOps(nscoord aPercentageBasis)
-    : mPercentageBasis(aPercentageBasis)
-  {
-  }
-
-  result_type ComputeLeafValue(const nsStyleCoord& aValue)
-  {
-    nscoord result;
-    if (aValue.GetUnit() == eStyleUnit_Percent) {
-      result =
-        NSToCoordFloorClamped(mPercentageBasis * aValue.GetPercentValue());
-    } else {
-      result = aValue.GetCoordValue();
-    }
-    return result;
-  }
-};
+  LengthPercentPairCalcOps ops(aStyleContext, aPresContext,
+                               aCanStoreInRuleTree);
+  return ComputeCalc(aValue, ops);
+}
 
 // This is our public API for handling calc() expressions that involve
 // percentages.
 /* static */ nscoord
 nsRuleNode::ComputeComputedCalc(const nsStyleCoord& aValue,
                                 nscoord aPercentageBasis)
 {
-  ComputeComputedCalcCalcOps ops(aPercentageBasis);
-  return css::ComputeCalc(aValue, ops);
+  nsStyleCoord::Calc *calc = aValue.GetCalcValue();
+  return calc->mLength +
+         NSToCoordFloorClamped(aPercentageBasis * calc->mPercent);
+}
+
+/* static */ nscoord
+nsRuleNode::ComputeCoordPercentCalc(const nsStyleCoord& aCoord,
+                                    nscoord aPercentageBasis)
+{
+  switch (aCoord.GetUnit()) {
+    case eStyleUnit_Coord:
+      return aCoord.GetCoordValue();
+    case eStyleUnit_Percent:
+      return NSToCoordFloorClamped(aPercentageBasis * aCoord.GetPercentValue());
+    case eStyleUnit_Calc:
+      return ComputeComputedCalc(aCoord, aPercentageBasis);
+    default:
+      NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
+      return 0;
+  }
 }
 
 /* Given an enumerated value that represents a box position, converts it to
  * a float representing the percentage of the box it corresponds to.  For
  * example, "center" becomes 0.5f.
  *
  * @param aEnumValue The enumerated value.
  * @return The float percent it corresponds to.
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -753,30 +753,39 @@ public:
   static nscoord CalcLengthWithInitialFont(nsPresContext* aPresContext,
                                            const nsCSSValue& aValue);
   // Expose this so nsTransformFunctions can use it.
   static nscoord CalcLength(const nsCSSValue& aValue,
                             nsStyleContext* aStyleContext,
                             nsPresContext* aPresContext,
                             PRBool& aCanStoreInRuleTree);
 
+  struct ComputedCalc {
+    nscoord mLength;
+    float mPercent;
+
+    ComputedCalc(nscoord aLength, float aPercent)
+      : mLength(aLength), mPercent(aPercent) {}
+  };
+  static ComputedCalc
+  SpecifiedCalcToComputedCalc(const nsCSSValue& aValue,
+                              nsStyleContext* aStyleContext,
+                              nsPresContext* aPresContext,
+                              PRBool& aCanStoreInRuleTree);
+
   // Compute the value of an nsStyleCoord that IsCalcUnit().
   // (Values that don't require aPercentageBasis should be handled
   // inside nsRuleNode rather than through this API.)
   static nscoord ComputeComputedCalc(const nsStyleCoord& aCoord,
                                      nscoord aPercentageBasis);
 
   // Compute the value of an nsStyleCoord that is either a coord, a
   // percent, or a calc expression.
   static nscoord ComputeCoordPercentCalc(const nsStyleCoord& aCoord,
-                                         nscoord aPercentageBasis)
-  {
-    // ComputeComputedCalc will handle coords and percents correctly
-    return ComputeComputedCalc(aCoord, aPercentageBasis);
-  }
+                                         nscoord aPercentageBasis);
 
   // Return whether the rule tree for which this node is the root has
   // cached data such that we need to do dynamic change handling for
   // changes that change the results of media queries or require
   // rebuilding all style data.
   PRBool TreeHasCachedData() const {
     NS_ASSERTION(IsRoot(), "should only be called on root of rule tree");
     return HaveChildren() || mStyleData.mInheritedData || mStyleData.mResetData;
--- a/layout/style/nsStyleCoord.cpp
+++ b/layout/style/nsStyleCoord.cpp
@@ -86,17 +86,17 @@ nsStyleCoord::nsStyleCoord(float aValue,
 // default copy construction is defined properly for unions.  But when
 // can we actually use that?  (It seems to work in gcc 4.4.)
 nsStyleCoord& nsStyleCoord::operator=(const nsStyleCoord& aCopy)
 {
   mUnit = aCopy.mUnit;
   if ((eStyleUnit_Percent <= mUnit) && (mUnit < eStyleUnit_Coord)) {
     mValue.mFloat = aCopy.mValue.mFloat;
   }
-  else if (IsArrayValue()) {
+  else if (IsPointerValue()) {
     mValue.mPointer = aCopy.mValue.mPointer;
   }
   else {
     mValue.mInt = aCopy.mValue.mInt;
   }
   return *this;
 }
 
@@ -117,22 +117,17 @@ PRBool nsStyleCoord::operator==(const ns
     case eStyleUnit_Grad:
     case eStyleUnit_Radian:
       return mValue.mFloat == aOther.mValue.mFloat;
     case eStyleUnit_Coord:
     case eStyleUnit_Integer:
     case eStyleUnit_Enumerated:
       return mValue.mInt == aOther.mValue.mInt;
     case eStyleUnit_Calc:
-    case eStyleUnit_Calc_Plus:
-    case eStyleUnit_Calc_Minus:
-    case eStyleUnit_Calc_Times_L:
-    case eStyleUnit_Calc_Times_R:
-    case eStyleUnit_Calc_Divided:
-      return *this->GetArrayValue() == *aOther.GetArrayValue();
+      return *this->GetCalcValue() == *aOther.GetCalcValue();
   }
   NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
   return PR_FALSE;
 }
 
 void nsStyleCoord::Reset()
 {
   mUnit = eStyleUnit_Null;
@@ -179,25 +174,20 @@ void nsStyleCoord::SetAngleValue(float a
     mUnit = aUnit;
     mValue.mFloat = aValue;
   } else {
     NS_NOTREACHED("not an angle value");
     Reset();
   }
 }
 
-void nsStyleCoord::SetArrayValue(Array* aValue, nsStyleUnit aUnit)
+void nsStyleCoord::SetCalcValue(Calc* aValue)
 {
-  mUnit = aUnit;
-  if (IsArrayValue()) {
-    mValue.mPointer = aValue;
-  } else {
-    NS_NOTREACHED("not a pointer value");
-    Reset();
-  }
+  mUnit = eStyleUnit_Calc;
+  mValue.mPointer = aValue;
 }
 
 void nsStyleCoord::SetNormalValue()
 {
   mUnit = eStyleUnit_Normal;
   mValue.mInt = 0;
 }
 
@@ -226,73 +216,16 @@ nsStyleCoord::GetAngleValueInRadians() c
   case eStyleUnit_Grad:   return angle * M_PI / 200.0;
 
   default:
     NS_NOTREACHED("unrecognized angular unit");
     return 0.0;
   }
 }
 
-PRBool
-nsStyleCoord::CalcHasPercent() const
-{
-  NS_ABORT_IF_FALSE(IsCalcUnit(), "caller should check IsCalcUnit()");
-  nsStyleCoord::Array *a = GetArrayValue();
-  for (size_t i = 0, i_end = a->Count(); i < i_end; ++i) {
-    const nsStyleCoord &v = a->Item(i);
-    if (v.GetUnit() == eStyleUnit_Percent) {
-      return PR_TRUE;
-    }
-    if (v.IsCalcUnit() && v.CalcHasPercent()) {
-      return PR_TRUE;
-    }
-  }
-  return PR_FALSE;
-}
-
-
-inline void*
-nsStyleCoord::Array::operator new(size_t aSelfSize,
-                                  nsStyleContext *aAllocationContext,
-                                  size_t aItemCount) CPP_THROW_NEW
-{
-  NS_ABORT_IF_FALSE(aItemCount > 0, "cannot have 0 item count");
-  return aAllocationContext->Alloc(
-           aSelfSize + sizeof(nsStyleCoord) * (aItemCount - 1));
-}
-
-/* static */ nsStyleCoord::Array*
-nsStyleCoord::Array::Create(nsStyleContext *aAllocationContext,
-                            PRBool& aCanStoreInRuleTree,
-                            size_t aCount)
-{
-  // While it's not ideal that every time we use an array, we force it
-  // not to be stored in the rule tree, it's the easiest option for now.
-  // (This is done only because of the style-context-scoped allocation.)
-  aCanStoreInRuleTree = PR_FALSE;
-
-  return new(aAllocationContext, aCount) Array(aCount);
-}
-
-bool
-nsStyleCoord::Array::operator==(const Array& aOther) const
-{
-  if (Count() != aOther.Count()) {
-    return false;
-  }
-
-  for (size_t i = 0; i < mCount; ++i) {
-    if ((*this)[i] != aOther[i]) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
 // used by nsStyleSides and nsStyleCorners
 #define COMPARE_INDEXED_COORD(i)                                              \
   PR_BEGIN_MACRO                                                              \
   if (mUnits[i] != aOther.mUnits[i])                                          \
     return PR_FALSE;                                                          \
   if ((eStyleUnit_Percent <= mUnits[i]) &&                                    \
       (mUnits[i] < eStyleUnit_Coord)) {                                       \
     if (mValues[i].mFloat != aOther.mValues[i].mFloat)                        \
--- a/layout/style/nsStyleCoord.h
+++ b/layout/style/nsStyleCoord.h
@@ -55,52 +55,57 @@ enum nsStyleUnit {
   eStyleUnit_Percent      = 10,     // (float) 1.0 == 100%
   eStyleUnit_Factor       = 11,     // (float) a multiplier
   eStyleUnit_Degree       = 12,     // (float) angle in degrees
   eStyleUnit_Grad         = 13,     // (float) angle in grads
   eStyleUnit_Radian       = 14,     // (float) angle in radians
   eStyleUnit_Coord        = 20,     // (nscoord) value is twips
   eStyleUnit_Integer      = 30,     // (int) value is simple integer
   eStyleUnit_Enumerated   = 32,     // (int) value has enumerated meaning
-  // The following are all of the eCSSUnit_Calc_* types.  They are weak
-  // pointers to a calc tree allocated by nsStyleContext::Alloc.
-  // NOTE:  They are in the same order as the eCSSUnit_Calc_* values so
-  // that converting between the two sets is just addition/subtraction.
-  eStyleUnit_Calc         = 39,     // (Array*) calc() toplevel, to
-                                    // distinguish 50% from calc(50%), etc.
-  eStyleUnit_Calc_Plus    = 40,     // (Array*) + node within calc()
-  eStyleUnit_Calc_Minus   = 41,     // (Array*) - within calc
-  eStyleUnit_Calc_Times_L = 42,     // (Array*) num * val within calc
-  eStyleUnit_Calc_Times_R = 43,     // (Array*) val * num within calc
-  eStyleUnit_Calc_Divided = 44      // (Array*) / within calc
+
+  // The following are allocated types.  They are weak pointers to
+  // values allocated by nsStyleContext::Alloc.
+  eStyleUnit_Calc         = 40      // (Calc*) calc() toplevel; always present
+                                    // to distinguish 50% from calc(50%), etc.
 };
 
 typedef union {
   PRInt32     mInt;   // nscoord is a PRInt32 for now
   float       mFloat;
   // An mPointer is a weak pointer to a value that is guaranteed to
-  // outlive the nsStyleCoord.  In the case of nsStyleCoord::Array*, it
+  // outlive the nsStyleCoord.  In the case of nsStyleCoord::Calc*, it
   // is a pointer owned by the style context, allocated through
   // nsStyleContext::Alloc (and, therefore, is never stored in the rule
   // tree).
   void*       mPointer;
 } nsStyleUnion;
 
 /**
  * Class that hold a single size specification used by the style
  * system.  The size specification consists of two parts -- a number
  * and a unit.  The number is an integer, a floating point value, an
  * nscoord, or undefined, and the unit is an nsStyleUnit.  Checking
  * the unit is a must before asking for the value in any particular
  * form.
  */
 class nsStyleCoord {
 public:
-  struct Array;
-  friend struct Array;
+  struct Calc {
+    // Every calc() expression evaluates to a length plus a percentage.
+    nscoord mLength;
+    float mPercent;
+    PRPackedBool mHasPercent; // whether there was any % syntax, even if 0
+
+    bool operator==(const Calc& aOther) const {
+      return mLength == aOther.mLength &&
+             mPercent == aOther.mPercent &&
+             mHasPercent == aOther.mHasPercent;
+    }
+    bool operator!=(const Calc& aOther) const { return !(*this == aOther); }
+  };
 
   nsStyleCoord(nsStyleUnit aUnit = eStyleUnit_Null);
   enum CoordConstructorType { CoordConstructor };
   inline nsStyleCoord(nscoord aValue, CoordConstructorType);
   nsStyleCoord(PRInt32 aValue, nsStyleUnit aUnit);
   nsStyleCoord(float aValue, nsStyleUnit aUnit);
   inline nsStyleCoord(const nsStyleCoord& aCopy);
   inline nsStyleCoord(const nsStyleUnion& aValue, nsStyleUnit aUnit);
@@ -114,31 +119,33 @@ public:
     return mUnit;
   }
 
   PRBool IsAngleValue() const {
     return eStyleUnit_Degree <= mUnit && mUnit <= eStyleUnit_Radian;
   }
 
   PRBool IsCalcUnit() const {
-    return eStyleUnit_Calc <= mUnit && mUnit <= eStyleUnit_Calc_Divided;
+    return eStyleUnit_Calc == mUnit;
+  }
+
+  PRBool IsPointerValue() const {
+    return IsCalcUnit();
   }
 
   PRBool IsCoordPercentCalcUnit() const {
     return mUnit == eStyleUnit_Coord ||
            mUnit == eStyleUnit_Percent ||
            IsCalcUnit();
   }
 
   // Does this calc() expression have any percentages inside it?  Can be
   // called only when IsCalcUnit() is true.
-  PRBool CalcHasPercent() const;
-
-  PRBool IsArrayValue() const {
-    return IsCalcUnit();
+  PRBool CalcHasPercent() const {
+    return GetCalcValue()->mHasPercent;
   }
 
   PRBool HasPercent() const {
     return mUnit == eStyleUnit_Percent ||
            (IsCalcUnit() && CalcHasPercent());
   }
 
   PRBool ConvertsToLength() const {
@@ -147,87 +154,35 @@ public:
   }
 
   nscoord     GetCoordValue() const;
   PRInt32     GetIntValue() const;
   float       GetPercentValue() const;
   float       GetFactorValue() const;
   float       GetAngleValue() const;
   double      GetAngleValueInRadians() const;
-  Array*      GetArrayValue() const;
+  Calc*       GetCalcValue() const;
   void        GetUnionValue(nsStyleUnion& aValue) const;
 
   void  Reset();  // sets to null
   void  SetCoordValue(nscoord aValue);
   void  SetIntValue(PRInt32 aValue, nsStyleUnit aUnit);
   void  SetPercentValue(float aValue);
   void  SetFactorValue(float aValue);
   void  SetAngleValue(float aValue, nsStyleUnit aUnit);
   void  SetNormalValue();
   void  SetAutoValue();
   void  SetNoneValue();
-  void  SetArrayValue(Array* aValue, nsStyleUnit aUnit);
+  void  SetCalcValue(Calc* aValue);
 
 public: // FIXME: private!
   nsStyleUnit   mUnit;
   nsStyleUnion  mValue;
 };
 
-// A fixed-size array, that, like everything else in nsStyleCoord,
-// doesn't require that its destructors be called.
-struct nsStyleCoord::Array {
-  static Array* Create(nsStyleContext *aAllocationContext,
-                       PRBool& aCanStoreInRuleTree,
-                       size_t aCount);
-
-  size_t Count() const { return mCount; }
-
-  nsStyleCoord& operator[](size_t aIndex) {
-    NS_ABORT_IF_FALSE(aIndex < mCount, "out of range");
-    return mArray[aIndex];
-  }
-
-  const nsStyleCoord& operator[](size_t aIndex) const {
-    NS_ABORT_IF_FALSE(aIndex < mCount, "out of range");
-    return mArray[aIndex];
-  }
-
-  // Easier to use with an Array*:
-  nsStyleCoord& Item(size_t aIndex) { return (*this)[aIndex]; }
-  const nsStyleCoord& Item(size_t aIndex) const { return (*this)[aIndex]; }
-
-  bool operator==(const Array& aOther) const;
-
-  bool operator!=(const Array& aOther) const {
-    return !(*this == aOther);
-  }
-
-private:
-  inline void* operator new(size_t aSelfSize,
-                            nsStyleContext *aAllocationContext,
-                            size_t aItemCount) CPP_THROW_NEW;
-
-  Array(size_t aCount)
-    : mCount(aCount)
-  {
-    // Initialize all entries not in the class.
-    for (size_t i = 1; i < aCount; ++i) {
-      new (mArray + i) nsStyleCoord();
-    }
-  }
-
-  size_t mCount;
-  nsStyleCoord mArray[1]; // for alignment, have the first element in the class
-
-  // not to be implemented
-  Array(const Array& aOther);
-  Array& operator=(const Array& aOther);
-  ~Array();
-};
-
 /**
  * Class that represents a set of top/right/bottom/left nsStyleCoords.
  * This is commonly used to hold the widths of the borders, margins,
  * or paddings of a box.
  */
 class nsStyleSides {
 public:
   nsStyleSides();
@@ -303,17 +258,17 @@ inline nsStyleCoord::nsStyleCoord(nscoor
 // default copy construction is defined properly for unions.  But when
 // can we actually use that?  (It seems to work in gcc 4.4.)
 inline nsStyleCoord::nsStyleCoord(const nsStyleCoord& aCopy)
   : mUnit(aCopy.mUnit)
 {
   if ((eStyleUnit_Percent <= mUnit) && (mUnit < eStyleUnit_Coord)) {
     mValue.mFloat = aCopy.mValue.mFloat;
   }
-  else if (IsArrayValue()) {
+  else if (IsPointerValue()) {
     mValue.mPointer = aCopy.mValue.mPointer;
   }
   else {
     mValue.mInt = aCopy.mValue.mInt;
   }
 }
 
 inline nsStyleCoord::nsStyleCoord(const nsStyleUnion& aValue, nsStyleUnit aUnit)
@@ -370,21 +325,21 @@ inline float nsStyleCoord::GetAngleValue
   NS_ASSERTION(mUnit >= eStyleUnit_Degree &&
                mUnit <= eStyleUnit_Radian, "not an angle value");
   if (mUnit >= eStyleUnit_Degree && mUnit <= eStyleUnit_Radian) {
     return mValue.mFloat;
   }
   return 0.0f;
 }
 
-inline nsStyleCoord::Array* nsStyleCoord::GetArrayValue() const
+inline nsStyleCoord::Calc* nsStyleCoord::GetCalcValue() const
 {
-  NS_ASSERTION(IsArrayValue(), "not a pointer value");
-  if (IsArrayValue()) {
-    return static_cast<Array*>(mValue.mPointer);
+  NS_ASSERTION(IsCalcUnit(), "not a pointer value");
+  if (IsCalcUnit()) {
+    return static_cast<Calc*>(mValue.mPointer);
   }
   return nsnull;
 }
 
 
 inline void nsStyleCoord::GetUnionValue(nsStyleUnion& aValue) const
 {
   memcpy(&aValue, &mValue, sizeof(nsStyleUnion));
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -224,103 +224,28 @@ static nscoord CalcLength(const nsCSSVal
     // nsStyleAnimation does) that all lengths within the transform
     // function have already been computed to pixels and percents.
     return nsPresContext::CSSPixelsToAppUnits(aValue.GetFloatValue());
   }
   return nsRuleNode::CalcLength(aValue, aContext, aPresContext,
                                 aCanStoreInRuleTree);
 }
 
-struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps
-{
-  struct result_type {
-    nscoord mLength;
-    float mPercent;
-
-    result_type(nscoord aLength, float aPercent)
-      : mLength(aLength), mPercent(aPercent) {}
-  };
-
-  LengthPercentPairCalcOps(nsStyleContext* aContext,
-                           nsPresContext* aPresContext,
-                           PRBool& aCanStoreInRuleTree)
-    : mContext(aContext),
-      mPresContext(aPresContext),
-      mCanStoreInRuleTree(aCanStoreInRuleTree) {}
-
-  nsStyleContext* mContext;
-  nsPresContext* mPresContext;
-  PRBool& mCanStoreInRuleTree;
-
-  result_type ComputeLeafValue(const nsCSSValue& aValue)
-  {
-    if (aValue.GetUnit() == eCSSUnit_Percent) {
-      return result_type(0, aValue.GetPercentValue());
-    } else {
-      return result_type(CalcLength(aValue, mContext, mPresContext,
-                                    mCanStoreInRuleTree),
-                         0.0f);
-    }
-  }
-
-  result_type
-  MergeAdditive(nsCSSUnit aCalcFunction,
-                result_type aValue1, result_type aValue2)
-  {
-    if (aCalcFunction == eCSSUnit_Calc_Plus) {
-      return result_type(NSCoordSaturatingAdd(aValue1.mLength,
-                                              aValue2.mLength),
-                         aValue1.mPercent + aValue2.mPercent);
-    }
-    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
-                      "min() and max() are not allowed in calc() on "
-                      "transform");
-    return result_type(NSCoordSaturatingSubtract(aValue1.mLength,
-                                                 aValue2.mLength, 0),
-                       aValue1.mPercent - aValue2.mPercent);
-  }
-
-  result_type
-  MergeMultiplicativeL(nsCSSUnit aCalcFunction,
-                       float aValue1, result_type aValue2)
-  {
-    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
-                      "unexpected unit");
-    return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1),
-                       aValue1 * aValue2.mPercent);
-  }
-
-  result_type
-  MergeMultiplicativeR(nsCSSUnit aCalcFunction,
-                       result_type aValue1, float aValue2)
-  {
-    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R ||
-                      aCalcFunction == eCSSUnit_Calc_Divided,
-                      "unexpected unit");
-    if (aCalcFunction == eCSSUnit_Calc_Divided) {
-      aValue2 = 1.0f / aValue2;
-    }
-    return result_type(NSCoordSaturatingMultiply(aValue1.mLength, aValue2),
-                       aValue1.mPercent * aValue2);
-  }
-
-};
-
 static void ProcessTranslatePart(nscoord& aOffset, float& aPercent,
                                  const nsCSSValue& aValue,
                                  nsStyleContext* aContext,
                                  nsPresContext* aPresContext,
                                  PRBool& aCanStoreInRuleTree)
 {
   if (aValue.GetUnit() == eCSSUnit_Percent) {
     aPercent = aValue.GetPercentValue();
   } else if (aValue.IsCalcUnit()) {
-    LengthPercentPairCalcOps ops(aContext, aPresContext, aCanStoreInRuleTree);
-    LengthPercentPairCalcOps::result_type result =
-      css::ComputeCalc(aValue, ops);
+    nsRuleNode::ComputedCalc result =
+      nsRuleNode::SpecifiedCalcToComputedCalc(aValue, aContext, aPresContext,
+                                              aCanStoreInRuleTree);
     aPercent = result.mPercent;
     aOffset = result.mLength;
   } else {
     aOffset = CalcLength(aValue, aContext, aPresContext,
                          aCanStoreInRuleTree);
   }
 }