LCOV - code coverage report
Current view: top level - layout/style - CSSCalc.h (source / functions) Hit Total Coverage
Test: output.info Lines: 31 137 22.6 %
Date: 2017-07-14 16:53:18 Functions: 7 29 24.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : #ifndef CSSCalc_h_
       6             : #define CSSCalc_h_
       7             : 
       8             : #include "nsCSSValue.h"
       9             : #include "nsStyleCoord.h"
      10             : #include <math.h>
      11             : 
      12             : namespace mozilla {
      13             : 
      14             : namespace css {
      15             : 
      16             : /**
      17             :  * ComputeCalc computes the result of a calc() expression tree.
      18             :  *
      19             :  * It is templatized over a CalcOps class that is expected to provide:
      20             :  *
      21             :  *   // input_type and input_array_type have a bunch of very specific
      22             :  *   // expectations (which happen to be met by two classes (nsCSSValue
      23             :  *   // and nsStyleCoord).  There must be methods (roughly):
      24             :  *   //   input_array_type* input_type::GetArrayValue();
      25             :  *   //   uint32_t input_array_type::Count() const;
      26             :  *   //   input_type& input_array_type::Item(uint32_t);
      27             :  *   typedef ... input_type;
      28             :  *   typedef ... input_array_type;
      29             :  *
      30             :  *   typedef ... coeff_type;
      31             :  *
      32             :  *   typedef ... result_type;
      33             :  *
      34             :  *   // GetUnit(avalue) must return the correct nsCSSUnit for any
      35             :  *   // value that represents a calc tree node (eCSSUnit_Calc*).  For
      36             :  *   // other nodes, it may return any non eCSSUnit_Calc* unit.
      37             :  *   static nsCSSUnit GetUnit(const input_type& aValue);
      38             :  *
      39             :  *   result_type
      40             :  *   MergeAdditive(nsCSSUnit aCalcFunction,
      41             :  *                 result_type aValue1, result_type aValue2);
      42             :  *
      43             :  *   result_type
      44             :  *   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
      45             :  *                        coeff_type aValue1, result_type aValue2);
      46             :  *
      47             :  *   result_type
      48             :  *   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
      49             :  *                        result_type aValue1, coeff_type aValue2);
      50             :  *
      51             :  *   result_type
      52             :  *   ComputeLeafValue(const input_type& aValue);
      53             :  *
      54             :  *   coeff_type
      55             :  *   ComputeCoefficient(const coeff_type& aValue);
      56             :  *
      57             :  * The CalcOps methods might compute the calc() expression down to a
      58             :  * number, reduce some parts of it to a number but replicate other
      59             :  * parts, or produce a tree with a different data structure (for
      60             :  * example, nsCSS* for specified values vs nsStyle* for computed
      61             :  * values).
      62             :  *
      63             :  * For each leaf in the calc() expression, ComputeCalc will call either
      64             :  * ComputeCoefficient (when the leaf is the left side of a Times_L or the
      65             :  * right side of a Times_R or Divided) or ComputeLeafValue (otherwise).
      66             :  * (The CalcOps in the CSS parser that reduces purely numeric
      67             :  * expressions in turn calls ComputeCalc on numbers; other ops can
      68             :  * presume that expressions in the coefficient positions have already been
      69             :  * normalized to a single numeric value and derive from, if their coefficient
      70             :  * types are floats, FloatCoeffsAlreadyNormalizedCalcOps.)
      71             :  *
      72             :  * coeff_type will be float most of the time, but it's templatized so that
      73             :  * ParseCalc can be used with <integer>s too.
      74             :  *
      75             :  * For non-leaves, one of the Merge functions will be called:
      76             :  *   MergeAdditive for Plus and Minus
      77             :  *   MergeMultiplicativeL for Times_L (coeff * value)
      78             :  *   MergeMultiplicativeR for Times_R (value * coeff) and Divided
      79             :  */
      80             : template <class CalcOps>
      81             : static typename CalcOps::result_type
      82         774 : ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
      83             : {
      84         774 :   switch (CalcOps::GetUnit(aValue)) {
      85             :     case eCSSUnit_Calc: {
      86         127 :       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
      87         127 :       MOZ_ASSERT(arr->Count() == 1, "unexpected length");
      88         127 :       return ComputeCalc(arr->Item(0), aOps);
      89             :     }
      90             :     case eCSSUnit_Calc_Plus:
      91             :     case eCSSUnit_Calc_Minus: {
      92          93 :       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
      93          93 :       MOZ_ASSERT(arr->Count() == 2, "unexpected length");
      94          93 :       typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps),
      95          93 :                                     rhs = ComputeCalc(arr->Item(1), aOps);
      96          93 :       return aOps.MergeAdditive(CalcOps::GetUnit(aValue), lhs, rhs);
      97             :     }
      98             :     case eCSSUnit_Calc_Times_L: {
      99          88 :       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
     100          88 :       MOZ_ASSERT(arr->Count() == 2, "unexpected length");
     101          88 :       typename CalcOps::coeff_type lhs = aOps.ComputeCoefficient(arr->Item(0));
     102          88 :       typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps);
     103          88 :       return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs);
     104             :     }
     105             :     case eCSSUnit_Calc_Times_R:
     106             :     case eCSSUnit_Calc_Divided: {
     107          13 :       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
     108          13 :       MOZ_ASSERT(arr->Count() == 2, "unexpected length");
     109          13 :       typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps);
     110          13 :       typename CalcOps::coeff_type rhs = aOps.ComputeCoefficient(arr->Item(1));
     111          13 :       return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
     112             :     }
     113             :     default: {
     114         453 :       return aOps.ComputeLeafValue(aValue);
     115             :     }
     116             :   }
     117             : }
     118             : 
     119             : /**
     120             :  * The input unit operation for input_type being nsCSSValue.
     121             :  */
     122         127 : struct CSSValueInputCalcOps
     123             : {
     124             :   typedef nsCSSValue input_type;
     125             :   typedef nsCSSValue::Array input_array_type;
     126             : 
     127         968 :   static nsCSSUnit GetUnit(const nsCSSValue& aValue)
     128             :   {
     129         968 :     return aValue.GetUnit();
     130             :   }
     131             : 
     132             : };
     133             : 
     134             : /**
     135             :  * Basic*CalcOps provide a partial implementation of the CalcOps
     136             :  * template parameter to ComputeCalc, for those callers whose merging
     137             :  * just consists of mathematics (rather than tree construction).
     138             :  */
     139             : 
     140           0 : struct BasicCoordCalcOps
     141             : {
     142             :   typedef nscoord result_type;
     143             :   typedef float coeff_type;
     144             : 
     145             :   result_type
     146           0 :   MergeAdditive(nsCSSUnit aCalcFunction,
     147             :                 result_type aValue1, result_type aValue2)
     148             :   {
     149           0 :     if (aCalcFunction == eCSSUnit_Calc_Plus) {
     150           0 :       return NSCoordSaturatingAdd(aValue1, aValue2);
     151             :     }
     152           0 :     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
     153             :                "unexpected unit");
     154           0 :     return NSCoordSaturatingSubtract(aValue1, aValue2, 0);
     155             :   }
     156             : 
     157             :   result_type
     158           0 :   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
     159             :                        coeff_type aValue1, result_type aValue2)
     160             :   {
     161           0 :     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
     162             :                "unexpected unit");
     163           0 :     return NSCoordSaturatingMultiply(aValue2, aValue1);
     164             :   }
     165             : 
     166             :   result_type
     167           0 :   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
     168             :                        result_type aValue1, coeff_type aValue2)
     169             :   {
     170           0 :     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_R ||
     171             :                aCalcFunction == eCSSUnit_Calc_Divided,
     172             :                "unexpected unit");
     173           0 :     if (aCalcFunction == eCSSUnit_Calc_Divided) {
     174           0 :       aValue2 = 1.0f / aValue2;
     175             :     }
     176           0 :     return NSCoordSaturatingMultiply(aValue1, aValue2);
     177             :   }
     178             : };
     179             : 
     180             : struct BasicFloatCalcOps
     181             : {
     182             :   typedef float result_type;
     183             :   typedef float coeff_type;
     184             : 
     185             :   result_type
     186           0 :   MergeAdditive(nsCSSUnit aCalcFunction,
     187             :                 result_type aValue1, result_type aValue2)
     188             :   {
     189           0 :     if (aCalcFunction == eCSSUnit_Calc_Plus) {
     190           0 :       return aValue1 + aValue2;
     191             :     }
     192           0 :     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
     193             :                "unexpected unit");
     194           0 :     return aValue1 - aValue2;
     195             :   }
     196             : 
     197             :   result_type
     198           0 :   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
     199             :                        coeff_type aValue1, result_type aValue2)
     200             :   {
     201           0 :     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
     202             :                "unexpected unit");
     203           0 :     return aValue1 * aValue2;
     204             :   }
     205             : 
     206             :   result_type
     207           0 :   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
     208             :                        result_type aValue1, coeff_type aValue2)
     209             :   {
     210           0 :     if (aCalcFunction == eCSSUnit_Calc_Times_R) {
     211           0 :       return aValue1 * aValue2;
     212             :     }
     213           0 :     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Divided,
     214             :                "unexpected unit");
     215           0 :     return aValue1 / aValue2;
     216             :   }
     217             : };
     218             : 
     219             : struct BasicIntegerCalcOps
     220             : {
     221             :   typedef int result_type;
     222             :   typedef int coeff_type;
     223             : 
     224             :   result_type
     225           0 :   MergeAdditive(nsCSSUnit aCalcFunction,
     226             :                 result_type aValue1, result_type aValue2)
     227             :   {
     228           0 :     if (aCalcFunction == eCSSUnit_Calc_Plus) {
     229           0 :       return aValue1 + aValue2;
     230             :     }
     231           0 :     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus,
     232             :                "unexpected unit");
     233           0 :     return aValue1 - aValue2;
     234             :   }
     235             : 
     236             :   result_type
     237           0 :   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
     238             :                        coeff_type aValue1, result_type aValue2)
     239             :   {
     240           0 :     MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L,
     241             :                "unexpected unit");
     242           0 :     return aValue1 * aValue2;
     243             :   }
     244             : 
     245             :   result_type
     246           0 :   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
     247             :                        result_type aValue1, coeff_type aValue2)
     248             :   {
     249           0 :     if (aCalcFunction == eCSSUnit_Calc_Times_R) {
     250           0 :       return aValue1 * aValue2;
     251             :     }
     252           0 :     MOZ_ASSERT_UNREACHABLE("We should catch and prevent divisions in integer "
     253             :                            "calc()s in the parser.");
     254             :     return 1;
     255             :   }
     256             : };
     257             : 
     258             : /**
     259             :  * A ComputeCoefficient implementation for callers that can assume coefficients
     260             :  * are floats and are already normalized (i.e., anything past the parser except
     261             :  * pure-integer calcs, whose coefficients are integers).
     262             :  */
     263         127 : struct FloatCoeffsAlreadyNormalizedOps : public CSSValueInputCalcOps
     264             : {
     265             :   typedef float coeff_type;
     266             : 
     267         101 :   coeff_type ComputeCoefficient(const nsCSSValue& aValue)
     268             :   {
     269         101 :     MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
     270         101 :     return aValue.GetFloatValue();
     271             :   }
     272             : };
     273             : 
     274             : /**
     275             :  * SerializeCalc appends the serialization of aValue to a string.
     276             :  *
     277             :  * It is templatized over a CalcOps class that is expected to provide:
     278             :  *
     279             :  *   // input_type and input_array_type have a bunch of very specific
     280             :  *   // expectations (which happen to be met by two classes (nsCSSValue
     281             :  *   // and nsStyleCoord).  There must be methods (roughly):
     282             :  *   //   input_array_type* input_type::GetArrayValue();
     283             :  *   //   uint32_t input_array_type::Count() const;
     284             :  *   //   input_type& input_array_type::Item(uint32_t);
     285             :  *   typedef ... input_type;
     286             :  *   typedef ... input_array_type;
     287             :  *
     288             :  *   static nsCSSUnit GetUnit(const input_type& aValue);
     289             :  *
     290             :  *   void Append(const char* aString);
     291             :  *   void AppendLeafValue(const input_type& aValue);
     292             :  *
     293             :  *   // AppendCoefficient accepts an input_type value, which represents a
     294             :  *   // value in the coefficient position, not a value of coeff_type,
     295             :  *   // because we're serializing the calc() expression itself.
     296             :  *   void AppendCoefficient(const input_type& aValue);
     297             :  *
     298             :  * Data structures given may or may not have a toplevel eCSSUnit_Calc
     299             :  * node representing a calc whose toplevel is not min() or max().
     300             :  */
     301             : 
     302             : template <class CalcOps>
     303             : static void
     304             : SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps);
     305             : 
     306             : // Serialize the toplevel value in a calc() tree.  See big comment
     307             : // above.
     308             : template <class CalcOps>
     309             : static void
     310           0 : SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
     311             : {
     312           0 :   aOps.Append("calc(");
     313           0 :   nsCSSUnit unit = CalcOps::GetUnit(aValue);
     314           0 :   if (unit == eCSSUnit_Calc) {
     315           0 :     const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
     316           0 :     MOZ_ASSERT(array->Count() == 1, "unexpected length");
     317           0 :     SerializeCalcInternal(array->Item(0), aOps);
     318             :   } else {
     319           0 :     SerializeCalcInternal(aValue, aOps);
     320             :   }
     321           0 :   aOps.Append(")");
     322           0 : }
     323             : 
     324             : static inline bool
     325           0 : IsCalcAdditiveUnit(nsCSSUnit aUnit)
     326             : {
     327           0 :   return aUnit == eCSSUnit_Calc_Plus ||
     328           0 :          aUnit == eCSSUnit_Calc_Minus;
     329             : }
     330             : 
     331             : static inline bool
     332           0 : IsCalcMultiplicativeUnit(nsCSSUnit aUnit)
     333             : {
     334           0 :   return aUnit == eCSSUnit_Calc_Times_L ||
     335           0 :          aUnit == eCSSUnit_Calc_Times_R ||
     336           0 :          aUnit == eCSSUnit_Calc_Divided;
     337             : }
     338             : 
     339             : // Serialize a non-toplevel value in a calc() tree.  See big comment
     340             : // above.
     341             : template <class CalcOps>
     342             : /* static */ void
     343           0 : SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
     344             : {
     345           0 :   nsCSSUnit unit = CalcOps::GetUnit(aValue);
     346           0 :   if (IsCalcAdditiveUnit(unit)) {
     347           0 :     const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
     348           0 :     MOZ_ASSERT(array->Count() == 2, "unexpected length");
     349             : 
     350           0 :     SerializeCalcInternal(array->Item(0), aOps);
     351             : 
     352           0 :     if (eCSSUnit_Calc_Plus == unit) {
     353           0 :       aOps.Append(" + ");
     354             :     } else {
     355           0 :       MOZ_ASSERT(eCSSUnit_Calc_Minus == unit, "unexpected unit");
     356           0 :       aOps.Append(" - ");
     357             :     }
     358             : 
     359           0 :     bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(1)));
     360           0 :     if (needParens) {
     361           0 :       aOps.Append("(");
     362             :     }
     363           0 :     SerializeCalcInternal(array->Item(1), aOps);
     364           0 :     if (needParens) {
     365           0 :       aOps.Append(")");
     366             :     }
     367           0 :   } else if (IsCalcMultiplicativeUnit(unit)) {
     368           0 :     const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
     369           0 :     MOZ_ASSERT(array->Count() == 2, "unexpected length");
     370             : 
     371           0 :     bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(0)));
     372           0 :     if (needParens) {
     373           0 :       aOps.Append("(");
     374             :     }
     375           0 :     if (unit == eCSSUnit_Calc_Times_L) {
     376           0 :       aOps.AppendCoefficient(array->Item(0));
     377             :     } else {
     378           0 :       SerializeCalcInternal(array->Item(0), aOps);
     379             :     }
     380           0 :     if (needParens) {
     381           0 :       aOps.Append(")");
     382             :     }
     383             : 
     384           0 :     if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) {
     385           0 :       aOps.Append(" * ");
     386             :     } else {
     387           0 :       MOZ_ASSERT(eCSSUnit_Calc_Divided == unit, "unexpected unit");
     388           0 :       aOps.Append(" / ");
     389             :     }
     390             : 
     391           0 :     nsCSSUnit subUnit = CalcOps::GetUnit(array->Item(1));
     392           0 :     needParens = IsCalcAdditiveUnit(subUnit) ||
     393           0 :                  IsCalcMultiplicativeUnit(subUnit);
     394           0 :     if (needParens) {
     395           0 :       aOps.Append("(");
     396             :     }
     397           0 :     if (unit == eCSSUnit_Calc_Times_L) {
     398           0 :       SerializeCalcInternal(array->Item(1), aOps);
     399             :     } else {
     400           0 :       aOps.AppendCoefficient(array->Item(1));
     401             :     }
     402           0 :     if (needParens) {
     403           0 :       aOps.Append(")");
     404             :     }
     405             :   } else {
     406           0 :     aOps.AppendLeafValue(aValue);
     407             :   }
     408           0 : }
     409             : 
     410             : /**
     411             :  * ReduceNumberCalcOps is a CalcOps implementation for pure-number calc()
     412             :  * (sub-)expressions, input as nsCSSValues.
     413             :  * For example, nsCSSParser::ParseCalcMultiplicativeExpression uses it to
     414             :  * simplify numeric sub-expressions in order to check for division-by-zero.
     415             :  */
     416             : struct ReduceNumberCalcOps : public mozilla::css::BasicFloatCalcOps,
     417             :                              public mozilla::css::CSSValueInputCalcOps
     418             : {
     419         233 :   result_type ComputeLeafValue(const nsCSSValue& aValue)
     420             :   {
     421         233 :     MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
     422         233 :     return aValue.GetFloatValue();
     423             :   }
     424             : 
     425           0 :   coeff_type ComputeCoefficient(const nsCSSValue& aValue)
     426             :   {
     427           0 :     return mozilla::css::ComputeCalc(aValue, *this);
     428             :   }
     429             : };
     430             : 
     431             : /**
     432             :  * ReduceIntegerCalcOps is a CalcOps implementation for pure-integer calc()
     433             :  * (sub-)expressions, input as nsCSSValues.
     434             :  */
     435             : struct ReduceIntegerCalcOps : public mozilla::css::BasicIntegerCalcOps,
     436             :                               public mozilla::css::CSSValueInputCalcOps
     437             : {
     438             :   result_type
     439           0 :   ComputeLeafValue(const nsCSSValue& aValue)
     440             :   {
     441           0 :     MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Integer, "unexpected unit");
     442           0 :     return aValue.GetIntValue();
     443             :   }
     444             : 
     445             :   coeff_type
     446           0 :   ComputeCoefficient(const nsCSSValue& aValue)
     447             :   {
     448           0 :     return mozilla::css::ComputeCalc(aValue, *this);
     449             :   }
     450             : };
     451             : 
     452             : } // namespace css
     453             : 
     454             : } // namespace mozilla
     455             : 
     456             : #endif /* !defined(CSSCalc_h_) */

Generated by: LCOV version 1.13