bug 1363349 (part 1) - refactor CSSCalc.h to share code better, via a templated ReduceCalcOps class r=dholbert draft
authorKevin Hsieh <kevin.hsieh@ucla.edu>
Wed, 12 Jul 2017 18:13:07 -0700
changeset 608068 28eded78186364632c9429e0aff2950945249fe1
parent 599048 2403cb851fe3e56b9018eaa645c78e913d927812
child 608069 7cbd0018efd49f15e5ab3b45f4af3ff32e0c0de1
push id68172
push userbmo:kevin.hsieh@ucla.edu
push dateThu, 13 Jul 2017 06:06:19 +0000
reviewersdholbert
bugs1363349
milestone56.0a1
bug 1363349 (part 1) - refactor CSSCalc.h to share code better, via a templated ReduceCalcOps class r=dholbert MozReview-Commit-ID: IvU2StpKHLm
layout/style/CSSCalc.h
layout/style/nsCSSParser.cpp
layout/style/nsRuleNode.cpp
layout/style/nsRuleNode.h
--- a/layout/style/CSSCalc.h
+++ b/layout/style/CSSCalc.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef CSSCalc_h_
 #define CSSCalc_h_
 
 #include "nsCSSValue.h"
 #include "nsStyleCoord.h"
 #include <math.h>
+#include <type_traits>
 
 namespace mozilla {
 
 namespace css {
 
 /**
  * ComputeCalc computes the result of a calc() expression tree.
  *
@@ -43,18 +44,18 @@ namespace css {
  *   result_type
  *   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
  *                        coeff_type aValue1, result_type aValue2);
  *
  *   result_type
  *   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
  *                        result_type aValue1, coeff_type aValue2);
  *
- *   result_type
- *   ComputeLeafValue(const input_type& aValue);
+ *   bool
+ *   ComputeLeafValue(result_type& aResult, const input_type& aValue);
  *
  *   coeff_type
  *   ComputeCoefficient(const coeff_type& aValue);
  *
  * The CalcOps methods might compute the calc() expression down to a
  * number, reduce some parts of it to a number but replicate other
  * parts, or produce a tree with a different data structure (for
  * example, nsCSS* for specified values vs nsStyle* for computed
@@ -64,59 +65,76 @@ namespace css {
  * ComputeCoefficient (when the leaf is the left side of a Times_L or the
  * right side of a Times_R or Divided) or ComputeLeafValue (otherwise).
  * (The CalcOps in the CSS parser that reduces purely numeric
  * expressions in turn calls ComputeCalc on numbers; other ops can
  * presume that expressions in the coefficient positions have already been
  * normalized to a single numeric value and derive from, if their coefficient
  * types are floats, FloatCoeffsAlreadyNormalizedCalcOps.)
  *
+ * ComputeCalc will fail and return false if ComputeLeafValue returns false at
+ * any point during the computation. ComputeLeafValue shall return false if and
+ * only if an unexpected (i.e., inconsistent) unit is encountered.
+ *
  * coeff_type will be float most of the time, but it's templatized so that
  * ParseCalc can be used with <integer>s too.
  *
  * For non-leaves, one of the Merge functions will be called:
  *   MergeAdditive for Plus and Minus
  *   MergeMultiplicativeL for Times_L (coeff * value)
  *   MergeMultiplicativeR for Times_R (value * coeff) and Divided
  */
 template <class CalcOps>
-static typename CalcOps::result_type
-ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
+static bool
+ComputeCalc(typename CalcOps::result_type& aResult,
+            const typename CalcOps::input_type& aValue, CalcOps &aOps)
 {
   switch (CalcOps::GetUnit(aValue)) {
     case eCSSUnit_Calc: {
       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
       MOZ_ASSERT(arr->Count() == 1, "unexpected length");
-      return ComputeCalc(arr->Item(0), aOps);
+      return ComputeCalc(aResult, arr->Item(0), aOps);
     }
     case eCSSUnit_Calc_Plus:
     case eCSSUnit_Calc_Minus: {
       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
       MOZ_ASSERT(arr->Count() == 2, "unexpected length");
-      typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps),
-                                    rhs = ComputeCalc(arr->Item(1), aOps);
-      return aOps.MergeAdditive(CalcOps::GetUnit(aValue), lhs, rhs);
+      typename CalcOps::result_type lhs, rhs;
+      if (!ComputeCalc(lhs, arr->Item(0), aOps) ||
+          !ComputeCalc(rhs, arr->Item(1), aOps)) {
+        return false;
+      }
+      aResult = aOps.MergeAdditive(CalcOps::GetUnit(aValue), lhs, rhs);
+      return true;
     }
     case eCSSUnit_Calc_Times_L: {
       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
       MOZ_ASSERT(arr->Count() == 2, "unexpected length");
       typename CalcOps::coeff_type lhs = aOps.ComputeCoefficient(arr->Item(0));
-      typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps);
-      return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs);
+      typename CalcOps::result_type rhs;
+      if (!ComputeCalc(rhs, arr->Item(1), aOps)) {
+        return false;
+      }
+      aResult = aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs);
+      return true;
     }
     case eCSSUnit_Calc_Times_R:
     case eCSSUnit_Calc_Divided: {
       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
       MOZ_ASSERT(arr->Count() == 2, "unexpected length");
-      typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps);
+      typename CalcOps::result_type lhs;
+      if (!ComputeCalc(lhs, arr->Item(0), aOps)) {
+        return false;
+      }
       typename CalcOps::coeff_type rhs = aOps.ComputeCoefficient(arr->Item(1));
-      return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
+      aResult = aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
+      return true;
     }
     default: {
-      return aOps.ComputeLeafValue(aValue);
+      return aOps.ComputeLeafValue(aResult, aValue);
     }
   }
 }
 
 /**
  * The input unit operation for input_type being nsCSSValue.
  */
 struct CSSValueInputCalcOps
@@ -172,94 +190,16 @@ struct BasicCoordCalcOps
                "unexpected unit");
     if (aCalcFunction == eCSSUnit_Calc_Divided) {
       aValue2 = 1.0f / aValue2;
     }
     return NSCoordSaturatingMultiply(aValue1, aValue2);
   }
 };
 
-struct BasicFloatCalcOps
-{
-  typedef float result_type;
-  typedef float coeff_type;
-
-  result_type
-  MergeAdditive(nsCSSUnit aCalcFunction,
-                result_type aValue1, result_type aValue2)
-  {
-    if (aCalcFunction == eCSSUnit_Calc_Plus) {
-      return aValue1 + aValue2;
-    }
-    MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
-               "unexpected unit");
-    return aValue1 - aValue2;
-  }
-
-  result_type
-  MergeMultiplicativeL(nsCSSUnit aCalcFunction,
-                       coeff_type aValue1, result_type aValue2)
-  {
-    MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
-               "unexpected unit");
-    return aValue1 * aValue2;
-  }
-
-  result_type
-  MergeMultiplicativeR(nsCSSUnit aCalcFunction,
-                       result_type aValue1, coeff_type aValue2)
-  {
-    if (aCalcFunction == eCSSUnit_Calc_Times_R) {
-      return aValue1 * aValue2;
-    }
-    MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Divided,
-               "unexpected unit");
-    return aValue1 / aValue2;
-  }
-};
-
-struct BasicIntegerCalcOps
-{
-  typedef int result_type;
-  typedef int coeff_type;
-
-  result_type
-  MergeAdditive(nsCSSUnit aCalcFunction,
-                result_type aValue1, result_type aValue2)
-  {
-    if (aCalcFunction == eCSSUnit_Calc_Plus) {
-      return aValue1 + aValue2;
-    }
-    MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
-               "unexpected unit");
-    return aValue1 - aValue2;
-  }
-
-  result_type
-  MergeMultiplicativeL(nsCSSUnit aCalcFunction,
-                       coeff_type aValue1, result_type aValue2)
-  {
-    MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
-               "unexpected unit");
-    return aValue1 * aValue2;
-  }
-
-  result_type
-  MergeMultiplicativeR(nsCSSUnit aCalcFunction,
-                       result_type aValue1, coeff_type aValue2)
-  {
-    if (aCalcFunction == eCSSUnit_Calc_Times_R) {
-      return aValue1 * aValue2;
-    }
-    MOZ_ASSERT_UNREACHABLE("We should catch and prevent divisions in integer "
-                           "calc()s in the parser.");
-    return 1;
-  }
-};
-
 /**
  * A ComputeCoefficient implementation for callers that can assume coefficients
  * are floats and are already normalized (i.e., anything past the parser except
  * pure-integer calcs, whose coefficients are integers).
  */
 struct FloatCoeffsAlreadyNormalizedOps : public CSSValueInputCalcOps
 {
   typedef float coeff_type;
@@ -403,54 +343,93 @@ SerializeCalcInternal(const typename Cal
       aOps.Append(")");
     }
   } else {
     aOps.AppendLeafValue(aValue);
   }
 }
 
 /**
- * ReduceNumberCalcOps is a CalcOps implementation for pure-number calc()
- * (sub-)expressions, input as nsCSSValues.
+ * ReduceCalcOps is a CalcOps implementation for pure-number, pure-percent, and
+ * pure-integer calc() (sub-)expressions, input as nsCSSValues.
+ *
+ * Instantiate with the appropriate coeff/result type and unit, for example:
+ *   ReduceCalcOps<float, eCSSUnit_Percent>
+ *   ReduceCalcOps<int, eCSSUnit_Integer>
+ *   ReduceCalcOps<float, eCSSUnit_Number>
+ *
  * For example, nsCSSParser::ParseCalcMultiplicativeExpression uses it to
  * simplify numeric sub-expressions in order to check for division-by-zero.
  */
-struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
-                             public mozilla::css::CSSValueInputCalcOps
+template<typename type, nsCSSUnit unit>
+struct ReduceCalcOps : public mozilla::css::CSSValueInputCalcOps
 {
-  result_type ComputeLeafValue(const nsCSSValue& aValue)
+  static_assert((std::is_same<type, int>::value && unit == eCSSUnit_Integer) ||
+                (std::is_same<type, float>::value &&
+                 (unit == eCSSUnit_Number || unit == eCSSUnit_Percent)),
+                "ReduceCalcOps: Invalid template arguments: must use "
+                "int coefficient with eCSSUnit_Integer, or "
+                "float coefficient with (eCSSUnit_Number or eCSSUnit_Percent)");
+
+  typedef type result_type;
+  typedef type coeff_type;
+
+  result_type
+  MergeAdditive(nsCSSUnit aCalcFunction,
+                result_type aValue1, result_type aValue2)
+  {
+    if (aCalcFunction == eCSSUnit_Calc_Plus) {
+      return aValue1 + aValue2;
+    }
+    MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus, "unexpected unit");
+    return aValue1 - aValue2;
+  }
+
+  result_type
+  MergeMultiplicativeL(nsCSSUnit aCalcFunction,
+                       coeff_type aValue1, result_type aValue2)
   {
-    MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
-    return aValue.GetFloatValue();
+    MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, "unexpected unit");
+    return aValue1 * aValue2;
+  }
+
+  result_type
+  MergeMultiplicativeR(nsCSSUnit aCalcFunction,
+                       result_type aValue1, coeff_type aValue2)
+  {
+    if (aCalcFunction == eCSSUnit_Calc_Times_R) {
+      return aValue1 * aValue2;
+    }
+    MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Divided, "unexpected unit");
+    MOZ_ASSERT(unit != eCSSUnit_Integer,
+               "We should catch and prevent divisions in integer "
+               "calc()s in the parser");
+    return aValue1 / aValue2;
+  }
+
+  bool ComputeLeafValue(result_type& aResult, const nsCSSValue& aValue)
+  {
+    if (aValue.GetUnit() != unit) {
+      return false;
+    }
+    aResult = unit == eCSSUnit_Percent ? aValue.GetPercentValue() :
+              unit == eCSSUnit_Integer ? aValue.GetIntValue() :
+                                         aValue.GetFloatValue();
+    return true;
   }
 
   coeff_type ComputeCoefficient(const nsCSSValue& aValue)
   {
-    return mozilla::css::ComputeCalc(aValue, *this);
-  }
-};
-
-/**
- * ReduceIntegerCalcOps is a CalcOps implementation for pure-integer calc()
- * (sub-)expressions, input as nsCSSValues.
- */
-struct ReduceIntegerCalcOps : public mozilla::css::BasicIntegerCalcOps,
-                              public mozilla::css::CSSValueInputCalcOps
-{
-  result_type
-  ComputeLeafValue(const nsCSSValue& aValue)
-  {
-    MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Integer, "unexpected unit");
-    return aValue.GetIntValue();
-  }
-
-  coeff_type
-  ComputeCoefficient(const nsCSSValue& aValue)
-  {
-    return mozilla::css::ComputeCalc(aValue, *this);
+    coeff_type coeff;
+    if (!mozilla::css::ComputeCalc(coeff, aValue, *this)) {
+      // Something's wrong; parser should enforce that calc() coefficients are
+      // unitless, but failure in ComputeCalc means there was a unit mismatch.
+      MOZ_ASSERT_UNREACHABLE("unexpected unit");
+    }
+    return coeff;
   }
 };
 
 } // namespace css
 
 } // namespace mozilla
 
 #endif /* !defined(CSSCalc_h_) */
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -13621,37 +13621,46 @@ CSSParserImpl::ParseCalcMultiplicativeEx
                "ParseCalcTerm did not set variantMask appropriately");
     MOZ_ASSERT(!(variantMask & VARIANT_NUMBER) ||
                !(variantMask & ~int32_t(VARIANT_NUMBER)),
                "ParseCalcTerm did not set variantMask appropriately");
 
     if (variantMask & VARIANT_NUMBER) {
       // Simplify the value immediately so we can check for division by
       // zero.
-      mozilla::css::ReduceNumberCalcOps ops;
-      float number = mozilla::css::ComputeCalc(*storage, ops);
+      float number;
+      mozilla::css::ReduceCalcOps<float, eCSSUnit_Number> ops;
+      if (!mozilla::css::ComputeCalc(number, *storage, ops)) {
+        MOZ_ASSERT_UNREACHABLE("unexpected unit");
+      }
       if (number == 0.0 && afterDivision)
         return false;
       storage->SetFloatValue(number, eCSSUnit_Number);
     } else {
       gotValue = true;
 
       if (storage != &aValue) {
         // Simplify any numbers in the Times_L position (which are
         // not simplified by the check above).
         MOZ_ASSERT(storage == &aValue.GetArrayValue()->Item(1),
                    "unexpected relationship to current storage");
         nsCSSValue &leftValue = aValue.GetArrayValue()->Item(0);
         if (variantMask & VARIANT_INTEGER) {
-          mozilla::css::ReduceIntegerCalcOps ops;
-          int integer = mozilla::css::ComputeCalc(leftValue, ops);
+          int integer;
+          mozilla::css::ReduceCalcOps<int, eCSSUnit_Integer> ops;
+          if (!mozilla::css::ComputeCalc(integer, leftValue, ops)) {
+            MOZ_ASSERT_UNREACHABLE("unexpected unit");
+          }
           leftValue.SetIntValue(integer, eCSSUnit_Integer);
         } else {
-          mozilla::css::ReduceNumberCalcOps ops;
-          float number = mozilla::css::ComputeCalc(leftValue, ops);
+          float number;
+          mozilla::css::ReduceCalcOps<float, eCSSUnit_Number> ops;
+          if (!mozilla::css::ComputeCalc(number, leftValue, ops)) {
+            MOZ_ASSERT_UNREACHABLE("unexpected unit");
+          }
           leftValue.SetFloatValue(number, eCSSUnit_Number);
         }
       }
     }
 
     bool hadWS = RequireWhitespace();
     if (!GetToken(false)) {
       *aHadFinalWS = hadWS;
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -339,21 +339,23 @@ struct CalcLengthCalcOps : public css::B
       mStyleContext(aStyleContext),
       mPresContext(aPresContext),
       mUseProvidedRootEmSize(aUseProvidedRootEmSize),
       mUseUserFontSet(aUseUserFontSet),
       mConditions(aConditions)
   {
   }
 
-  result_type ComputeLeafValue(const nsCSSValue& aValue)
+  bool ComputeLeafValue(result_type& aResult, const nsCSSValue& aValue)
   {
-    return CalcLengthWith(aValue, mFontSize, mStyleFont,
-                          mStyleContext, mPresContext, mUseProvidedRootEmSize,
-                          mUseUserFontSet, mConditions);
+    aResult = CalcLengthWith(aValue, mFontSize, mStyleFont,
+                             mStyleContext, mPresContext,
+                             mUseProvidedRootEmSize, mUseUserFontSet,
+                             mConditions);
+    return true;
   }
 };
 
 static inline nscoord ScaleCoordRound(const nsCSSValue& aValue, float aFactor)
 {
   return NSToCoordRoundWithClamp(aValue.GetFloatValue() * aFactor);
 }
 
@@ -547,17 +549,21 @@ static nscoord CalcLengthWith(const nsCS
     // calc(), we can handle calc() here and just compute a final
     // result.  We ensure that we don't get to this code for other
     // properties by not calling CalcLength in those cases:  SetCoord
     // only calls CalcLength for a calc when it is appropriate to do so.
     CalcLengthCalcOps ops(aFontSize, aStyleFont,
                           aStyleContext, aPresContext,
                           aUseProvidedRootEmSize, aUseUserFontSet,
                           aConditions);
-    return css::ComputeCalc(aValue, ops);
+    nscoord result;
+    if (!css::ComputeCalc(result, aValue, ops)) {
+      MOZ_ASSERT_UNREACHABLE("unexpected ComputeCalc failure");
+    }
+    return result;
   }
   switch (aValue.GetUnit()) {
     // nsPresContext::SetVisibleArea and
     // nsPresContext::MediaFeatureValuesChanged handle dynamic changes
     // of the basis for viewport units by rebuilding the rule tree and
     // style context tree.  Not caching them in the rule tree wouldn't
     // be sufficient to handle these changes because we also need a way
     // to get rid of cached values in the style context tree without any
@@ -741,25 +747,27 @@ struct LengthPercentPairCalcOps : public
       mConditions(aConditions),
       mHasPercent(false) {}
 
   nsStyleContext* mContext;
   nsPresContext* mPresContext;
   RuleNodeCacheConditions& mConditions;
   bool mHasPercent;
 
-  result_type ComputeLeafValue(const nsCSSValue& aValue)
+  bool ComputeLeafValue(result_type& aResult, const nsCSSValue& aValue)
   {
     if (aValue.GetUnit() == eCSSUnit_Percent) {
       mHasPercent = true;
-      return result_type(0, aValue.GetPercentValue());
-    }
-    return result_type(CalcLength(aValue, mContext, mPresContext,
-                                  mConditions),
-                       0.0f);
+      aResult = result_type(0, aValue.GetPercentValue());
+      return true;
+    }
+    aResult = result_type(CalcLength(aValue, mContext, mPresContext,
+                                     mConditions),
+                          0.0f);
+    return true;
   }
 
   result_type
   MergeAdditive(nsCSSUnit aCalcFunction,
                 result_type aValue1, result_type aValue2)
   {
     if (aCalcFunction == eCSSUnit_Calc_Plus) {
       return result_type(NSCoordSaturatingAdd(aValue1.mLength,
@@ -801,17 +809,20 @@ struct LengthPercentPairCalcOps : public
 
 static void
 SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleCoord& aCoord,
                             nsStyleContext* aStyleContext,
                             RuleNodeCacheConditions& aConditions)
 {
   LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(),
                                aConditions);
-  nsRuleNode::ComputedCalc vals = ComputeCalc(aValue, ops);
+  nsRuleNode::ComputedCalc vals;
+  if (!ComputeCalc(vals, aValue, ops)) {
+    MOZ_ASSERT_UNREACHABLE("unexpected ComputeCalc failure");
+  }
 
   nsStyleCoord::Calc* calcObj = new nsStyleCoord::Calc;
 
   calcObj->mLength = vals.mLength;
   calcObj->mPercent = vals.mPercent;
   calcObj->mHasPercent = ops.mHasPercent;
 
   aCoord.SetCalcValue(calcObj);
@@ -820,17 +831,21 @@ SpecifiedCalcToComputedCalc(const nsCSSV
 /* static */ nsRuleNode::ComputedCalc
 nsRuleNode::SpecifiedCalcToComputedCalc(const nsCSSValue& aValue,
                                         nsStyleContext* aStyleContext,
                                         nsPresContext* aPresContext,
                                         RuleNodeCacheConditions& aConditions)
 {
   LengthPercentPairCalcOps ops(aStyleContext, aPresContext,
                                aConditions);
-  return ComputeCalc(aValue, ops);
+  nsRuleNode::ComputedCalc result;
+  if (!ComputeCalc(result, aValue, ops)) {
+    MOZ_ASSERT_UNREACHABLE("unexpected ComputeCalc failure");
+  }
+  return result;
 }
 
 // This is our public API for handling calc() expressions that involve
 // percentages.
 /* static */ nscoord
 nsRuleNode::ComputeComputedCalc(const nsStyleCoord& aValue,
                                 nscoord aPercentageBasis)
 {
@@ -3358,17 +3373,17 @@ struct SetFontSizeCalcOps : public css::
       mParentFont(aParentFont),
       mPresContext(aPresContext),
       mStyleContext(aStyleContext),
       mAtRoot(aAtRoot),
       mConditions(aConditions)
   {
   }
 
-  result_type ComputeLeafValue(const nsCSSValue& aValue)
+  bool ComputeLeafValue(result_type& aResult, const nsCSSValue& aValue)
   {
     nscoord size;
     if (aValue.IsLengthUnit()) {
       // Note that font-based length units use the parent's size
       // unadjusted for scriptlevel changes. A scriptlevel change
       // between us and the parent is simply ignored.
       size = CalcLengthWith(aValue, mParentSize,
                             mParentFont,
@@ -3385,17 +3400,18 @@ struct SetFontSizeCalcOps : public css::
       // ignored.
       // aValue.GetPercentValue() may be negative for, e.g., calc(-50%)
       size = NSCoordSaturatingMultiply(mParentSize, aValue.GetPercentValue());
     } else {
       MOZ_ASSERT(false, "unexpected value");
       size = mParentSize;
     }
 
-    return size;
+    aResult = size;
+    return true;
   }
 };
 
 /* static */ void
 nsRuleNode::SetFontSize(nsPresContext* aPresContext,
                         GeckoStyleContext* aContext,
                         const nsRuleData* aRuleData,
                         const nsStyleFont* aFont,
@@ -3451,17 +3467,19 @@ nsRuleNode::SetFontSize(nsPresContext* a
   }
   else if (sizeValue->IsLengthUnit() ||
            sizeValue->GetUnit() == eCSSUnit_Percent ||
            sizeValue->IsCalcUnit()) {
     SetFontSizeCalcOps ops(aParentSize, aParentFont,
                            aPresContext, aContext,
                            aAtRoot,
                            aConditions);
-    *aSize = css::ComputeCalc(*sizeValue, ops);
+    if (!css::ComputeCalc(*aSize, *sizeValue, ops)) {
+      MOZ_ASSERT_UNREACHABLE("unexpected ComputeCalc failure");
+    }
     if (*aSize < 0) {
       MOZ_ASSERT(sizeValue->IsCalcUnit(),
                  "negative lengths and percents should be rejected by parser");
       *aSize = 0;
     }
     // The calc ops will always zoom its result according to the value
     // of aParentFont->mAllowZoom.
     sizeIsZoomedAccordingToParent = true;
@@ -4594,70 +4612,68 @@ struct LengthNumberCalcOps : public css:
       return result;
     }
     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Divided,
                "unexpected unit");
     result.mValue = aValue1.mValue / aValue2;
     return result;
   }
 
-  result_type ComputeLeafValue(const nsCSSValue& aValue)
+  bool ComputeLeafValue(result_type& aResult, const nsCSSValue& aValue)
   {
-    LengthNumberCalcObj result;
     if (aValue.IsLengthUnit()) {
-      result.mIsNumber = false;
-      result.mValue = CalcLength(aValue, mStyleContext,
+      aResult.mIsNumber = false;
+      aResult.mValue = CalcLength(aValue, mStyleContext,
                                       mPresContext, mConditions);
     }
     else if (eCSSUnit_Number == aValue.GetUnit()) {
-      result.mIsNumber = true;
-      result.mValue = aValue.GetFloatValue();
+      aResult.mIsNumber = true;
+      aResult.mValue = aValue.GetFloatValue();
     } else {
       MOZ_ASSERT(false, "unexpected value");
-      result.mIsNumber = true;
-      result.mValue = 1.0f;
-    }
-
-    return result;
+      aResult.mIsNumber = true;
+      aResult.mValue = 1.0f;
+    }
+
+    return true;
   }
 };
 
 struct SetLineHeightCalcOps : public LengthNumberCalcOps
 {
   SetLineHeightCalcOps(GeckoStyleContext* aStyleContext,
                        nsPresContext* aPresContext,
                        RuleNodeCacheConditions& aConditions)
     : LengthNumberCalcOps(aStyleContext, aPresContext, aConditions)
   {
   }
 
-  result_type ComputeLeafValue(const nsCSSValue& aValue)
+  bool ComputeLeafValue(result_type& aResult, const nsCSSValue& aValue)
   {
-    LengthNumberCalcObj result;
     if (aValue.IsLengthUnit()) {
-      result.mIsNumber = false;
-      result.mValue = CalcLength(aValue, mStyleContext,
+      aResult.mIsNumber = false;
+      aResult.mValue = CalcLength(aValue, mStyleContext,
                                       mPresContext, mConditions);
     }
     else if (eCSSUnit_Percent == aValue.GetUnit()) {
       mConditions.SetUncacheable();
-      result.mIsNumber = false;
+      aResult.mIsNumber = false;
       nscoord fontSize = mStyleContext->StyleFont()->mFont.size;
-      result.mValue = fontSize * aValue.GetPercentValue();
+      aResult.mValue = fontSize * aValue.GetPercentValue();
     }
     else if (eCSSUnit_Number == aValue.GetUnit()) {
-      result.mIsNumber = true;
-      result.mValue = aValue.GetFloatValue();
+      aResult.mIsNumber = true;
+      aResult.mValue = aValue.GetFloatValue();
     } else {
       MOZ_ASSERT(false, "unexpected value");
-      result.mIsNumber = true;
-      result.mValue = 1.0f;
-    }
-
-    return result;
+      aResult.mIsNumber = true;
+      aResult.mValue = 1.0f;
+    }
+
+    return true;
   }
 };
 
 const void*
 nsRuleNode::ComputeTextData(void* aStartStruct,
                             const nsRuleData* aRuleData,
                             GeckoStyleContext* aContext,
                             nsRuleNode* aHighestNode,
@@ -4674,17 +4690,20 @@ nsRuleNode::ComputeTextData(void* aStart
   };
 
   // tab-size: number, length, calc, inherit
   const nsCSSValue* tabSizeValue = aRuleData->ValueForTabSize();
   if (tabSizeValue->GetUnit() == eCSSUnit_Initial) {
     text->mTabSize = nsStyleCoord(float(NS_STYLE_TABSIZE_INITIAL), eStyleUnit_Factor);
   } else if (eCSSUnit_Calc == tabSizeValue->GetUnit()) {
     LengthNumberCalcOps ops(aContext, mPresContext, conditions);
-    LengthNumberCalcObj obj = css::ComputeCalc(*tabSizeValue, ops);
+    LengthNumberCalcObj obj;
+    if (!css::ComputeCalc(obj, *tabSizeValue, ops)) {
+      MOZ_ASSERT_UNREACHABLE("unexpected ComputeCalc failure");
+    }
     float value = obj.mValue < 0 ? 0 : obj.mValue;
     if (obj.mIsNumber) {
       text->mTabSize.SetFactorValue(value);
     } else {
       text->mTabSize.SetCoordValue(
         NSToCoordRoundWithClamp(value));
     }
   } else {
@@ -4729,17 +4748,20 @@ nsRuleNode::ComputeTextData(void* aStart
                        lineHeightValue->GetPercentValue()));
   }
   else if (eCSSUnit_Initial == lineHeightValue->GetUnit() ||
            eCSSUnit_System_Font == lineHeightValue->GetUnit()) {
     text->mLineHeight.SetNormalValue();
   }
   else if (eCSSUnit_Calc == lineHeightValue->GetUnit()) {
     SetLineHeightCalcOps ops(aContext, mPresContext, conditions);
-    LengthNumberCalcObj obj = css::ComputeCalc(*lineHeightValue, ops);
+    LengthNumberCalcObj obj;
+    if (!css::ComputeCalc(obj, *lineHeightValue, ops)) {
+      MOZ_ASSERT_UNREACHABLE("unexpected ComputeCalc failure");
+    }
     if (obj.mIsNumber) {
       text->mLineHeight.SetFactorValue(obj.mValue);
     } else {
       text->mLineHeight.SetCoordValue(
         NSToCoordRoundWithClamp(obj.mValue));
     }
   }
   else {
@@ -6871,17 +6893,20 @@ ComputePositionCoord(GeckoStyleContext* 
                                   aStyleContext->PresContext(),
                                   aConditions);
     aResult->mPercent = 0.0f;
     aResult->mHasPercent = false;
   } else if (aOffset.IsCalcUnit()) {
     LengthPercentPairCalcOps ops(aStyleContext,
                                  aStyleContext->PresContext(),
                                  aConditions);
-    nsRuleNode::ComputedCalc vals = ComputeCalc(aOffset, ops);
+    nsRuleNode::ComputedCalc vals;
+    if (!ComputeCalc(vals, aOffset, ops)) {
+      MOZ_ASSERT_UNREACHABLE("unexpected ComputeCalc failure");
+    }
     aResult->mLength = vals.mLength;
     aResult->mPercent = vals.mPercent;
     aResult->mHasPercent = ops.mHasPercent;
   } else {
     aResult->mLength = 0;
     aResult->mPercent = 0.0f;
     aResult->mHasPercent = false;
     NS_ASSERTION(aOffset.GetUnit() == eCSSUnit_Null, "unexpected unit");
@@ -7044,17 +7069,20 @@ struct BackgroundItemComputer<nsCSSValue
         (size.*(axis->result)).mPercent = 0.0f;
         (size.*(axis->result)).mHasPercent = false;
         size.*(axis->type) = nsStyleImageLayers::Size::eLengthPercentage;
       } else {
         MOZ_ASSERT(specified.IsCalcUnit(), "unexpected unit");
         LengthPercentPairCalcOps ops(aStyleContext,
                                      aStyleContext->PresContext(),
                                      aConditions);
-        nsRuleNode::ComputedCalc vals = ComputeCalc(specified, ops);
+        nsRuleNode::ComputedCalc vals;
+        if (!ComputeCalc(vals, specified, ops)) {
+          MOZ_ASSERT_UNREACHABLE("unexpected ComputeCalc failure");
+        }
         (size.*(axis->result)).mLength = vals.mLength;
         (size.*(axis->result)).mPercent = vals.mPercent;
         (size.*(axis->result)).mHasPercent = ops.mHasPercent;
         size.*(axis->type) = nsStyleImageLayers::Size::eLengthPercentage;
       }
     }
 
     MOZ_ASSERT(size.mWidthType < nsStyleImageLayers::Size::eDimensionType_COUNT,
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -1010,16 +1010,18 @@ public:
                             nsStyleContext* aStyleContext,
                             nsPresContext* aPresContext,
                             mozilla::RuleNodeCacheConditions& aConditions);
 
   struct ComputedCalc {
     nscoord mLength;
     float mPercent;
 
+    ComputedCalc() {}
+
     ComputedCalc(nscoord aLength, float aPercent)
       : mLength(aLength), mPercent(aPercent) {}
   };
   static ComputedCalc
   SpecifiedCalcToComputedCalc(const nsCSSValue& aValue,
                               nsStyleContext* aStyleContext,
                               nsPresContext* aPresContext,
                               mozilla::RuleNodeCacheConditions& aConditions);