layout/style/CSSCalc.h

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     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_
     8 #include "nsCSSValue.h"
     9 #include "nsStyleCoord.h"
    10 #include <math.h>
    12 namespace mozilla {
    14 namespace css {
    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 ... result_type;
    31  *
    32  *   // GetUnit(avalue) must return the correct nsCSSUnit for any
    33  *   // value that represents a calc tree node (eCSSUnit_Calc*).  For
    34  *   // other nodes, it may return any non eCSSUnit_Calc* unit.
    35  *   static nsCSSUnit GetUnit(const input_type& aValue);
    36  *
    37  *   result_type
    38  *   MergeAdditive(nsCSSUnit aCalcFunction,
    39  *                 result_type aValue1, result_type aValue2);
    40  *
    41  *   result_type
    42  *   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
    43  *                        float aValue1, result_type aValue2);
    44  *
    45  *   result_type
    46  *   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
    47  *                        result_type aValue1, float aValue2);
    48  *
    49  *   result_type
    50  *   ComputeLeafValue(const input_type& aValue);
    51  *
    52  *   float
    53  *   ComputeNumber(const input_type& aValue);
    54  *
    55  * The CalcOps methods might compute the calc() expression down to a
    56  * number, reduce some parts of it to a number but replicate other
    57  * parts, or produce a tree with a different data structure (for
    58  * example, nsCSS* for specified values vs nsStyle* for computed
    59  * values).
    60  *
    61  * For each leaf in the calc() expression, ComputeCalc will call either
    62  * ComputeNumber (when the leaf is the left side of a Times_L or the
    63  * right side of a Times_R or Divided) or ComputeLeafValue (otherwise).
    64  * (The CalcOps in the CSS parser that reduces purely numeric
    65  * expressions in turn calls ComputeCalc on numbers; other ops can
    66  * presume that expressions in the number positions have already been
    67  * normalized to a single numeric value and derive from
    68  * NumbersAlreadyNormalizedCalcOps.)
    69  *
    70  * For non-leaves, one of the Merge functions will be called:
    71  *   MergeAdditive for Plus and Minus
    72  *   MergeMultiplicativeL for Times_L (number * value)
    73  *   MergeMultiplicativeR for Times_R (value * number) and Divided
    74  */
    75 template <class CalcOps>
    76 static typename CalcOps::result_type
    77 ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
    78 {
    79   switch (CalcOps::GetUnit(aValue)) {
    80     case eCSSUnit_Calc: {
    81       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
    82       NS_ABORT_IF_FALSE(arr->Count() == 1, "unexpected length");
    83       return ComputeCalc(arr->Item(0), aOps);
    84     }
    85     case eCSSUnit_Calc_Plus:
    86     case eCSSUnit_Calc_Minus: {
    87       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
    88       NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
    89       typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps),
    90                                     rhs = ComputeCalc(arr->Item(1), aOps);
    91       return aOps.MergeAdditive(CalcOps::GetUnit(aValue), lhs, rhs);
    92     }
    93     case eCSSUnit_Calc_Times_L: {
    94       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
    95       NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
    96       float lhs = aOps.ComputeNumber(arr->Item(0));
    97       typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps);
    98       return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs);
    99     }
   100     case eCSSUnit_Calc_Times_R:
   101     case eCSSUnit_Calc_Divided: {
   102       typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
   103       NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
   104       typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps);
   105       float rhs = aOps.ComputeNumber(arr->Item(1));
   106       return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
   107     }
   108     default: {
   109       return aOps.ComputeLeafValue(aValue);
   110     }
   111   }
   112 }
   114 /**
   115  * The input unit operation for input_type being nsCSSValue.
   116  */
   117 struct CSSValueInputCalcOps
   118 {
   119   typedef nsCSSValue input_type;
   120   typedef nsCSSValue::Array input_array_type;
   122   static nsCSSUnit GetUnit(const nsCSSValue& aValue)
   123   {
   124     return aValue.GetUnit();
   125   }
   127 };
   129 /**
   130  * Basic*CalcOps provide a partial implementation of the CalcOps
   131  * template parameter to ComputeCalc, for those callers whose merging
   132  * just consists of mathematics (rather than tree construction).
   133  */
   135 struct BasicCoordCalcOps
   136 {
   137   typedef nscoord result_type;
   139   result_type
   140   MergeAdditive(nsCSSUnit aCalcFunction,
   141                 result_type aValue1, result_type aValue2)
   142   {
   143     if (aCalcFunction == eCSSUnit_Calc_Plus) {
   144       return NSCoordSaturatingAdd(aValue1, aValue2);
   145     }
   146     NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
   147                       "unexpected unit");
   148     return NSCoordSaturatingSubtract(aValue1, aValue2, 0);
   149   }
   151   result_type
   152   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
   153                        float aValue1, result_type aValue2)
   154   {
   155     NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
   156                       "unexpected unit");
   157     return NSCoordSaturatingMultiply(aValue2, aValue1);
   158   }
   160   result_type
   161   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
   162                        result_type aValue1, float aValue2)
   163   {
   164     NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R ||
   165                       aCalcFunction == eCSSUnit_Calc_Divided,
   166                       "unexpected unit");
   167     if (aCalcFunction == eCSSUnit_Calc_Divided) {
   168       aValue2 = 1.0f / aValue2;
   169     }
   170     return NSCoordSaturatingMultiply(aValue1, aValue2);
   171   }
   172 };
   174 struct BasicFloatCalcOps
   175 {
   176   typedef float result_type;
   178   result_type
   179   MergeAdditive(nsCSSUnit aCalcFunction,
   180                 result_type aValue1, result_type aValue2)
   181   {
   182     if (aCalcFunction == eCSSUnit_Calc_Plus) {
   183       return aValue1 + aValue2;
   184     }
   185     NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
   186                       "unexpected unit");
   187     return aValue1 - aValue2;
   188   }
   190   result_type
   191   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
   192                        float aValue1, result_type aValue2)
   193   {
   194     NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
   195                       "unexpected unit");
   196     return aValue1 * aValue2;
   197   }
   199   result_type
   200   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
   201                        result_type aValue1, float aValue2)
   202   {
   203     if (aCalcFunction == eCSSUnit_Calc_Times_R) {
   204       return aValue1 * aValue2;
   205     }
   206     NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Divided,
   207                       "unexpected unit");
   208     return aValue1 / aValue2;
   209   }
   210 };
   212 /**
   213  * A ComputeNumber implementation for callers that can assume numbers
   214  * are already normalized (i.e., anything past the parser).
   215  */
   216 struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps
   217 {
   218   float ComputeNumber(const nsCSSValue& aValue)
   219   {
   220     NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
   221     return aValue.GetFloatValue();
   222   }
   223 };
   225 /**
   226  * SerializeCalc appends the serialization of aValue to a string.
   227  *
   228  * It is templatized over a CalcOps class that is expected to provide:
   229  *
   230  *   // input_type and input_array_type have a bunch of very specific
   231  *   // expectations (which happen to be met by two classes (nsCSSValue
   232  *   // and nsStyleCoord).  There must be methods (roughly):
   233  *   //   input_array_type* input_type::GetArrayValue();
   234  *   //   uint32_t input_array_type::Count() const;
   235  *   //   input_type& input_array_type::Item(uint32_t);
   236  *   typedef ... input_type;
   237  *   typedef ... input_array_type;
   238  *
   239  *   static nsCSSUnit GetUnit(const input_type& aValue);
   240  *
   241  *   void Append(const char* aString);
   242  *   void AppendLeafValue(const input_type& aValue);
   243  *   void AppendNumber(const input_type& aValue);
   244  *
   245  * Data structures given may or may not have a toplevel eCSSUnit_Calc
   246  * node representing a calc whose toplevel is not min() or max().
   247  */
   249 template <class CalcOps>
   250 static void
   251 SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps);
   253 // Serialize the toplevel value in a calc() tree.  See big comment
   254 // above.
   255 template <class CalcOps>
   256 static void
   257 SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
   258 {
   259   aOps.Append("calc(");
   260   nsCSSUnit unit = CalcOps::GetUnit(aValue);
   261   if (unit == eCSSUnit_Calc) {
   262     const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
   263     NS_ABORT_IF_FALSE(array->Count() == 1, "unexpected length");
   264     SerializeCalcInternal(array->Item(0), aOps);
   265   } else {
   266     SerializeCalcInternal(aValue, aOps);
   267   }
   268   aOps.Append(")");
   269 }
   271 static inline bool
   272 IsCalcAdditiveUnit(nsCSSUnit aUnit)
   273 {
   274   return aUnit == eCSSUnit_Calc_Plus ||
   275          aUnit == eCSSUnit_Calc_Minus;
   276 }
   278 static inline bool
   279 IsCalcMultiplicativeUnit(nsCSSUnit aUnit)
   280 {
   281   return aUnit == eCSSUnit_Calc_Times_L ||
   282          aUnit == eCSSUnit_Calc_Times_R ||
   283          aUnit == eCSSUnit_Calc_Divided;
   284 }
   286 // Serialize a non-toplevel value in a calc() tree.  See big comment
   287 // above.
   288 template <class CalcOps>
   289 /* static */ void
   290 SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
   291 {
   292   nsCSSUnit unit = CalcOps::GetUnit(aValue);
   293   if (IsCalcAdditiveUnit(unit)) {
   294     const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
   295     NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length");
   297     SerializeCalcInternal(array->Item(0), aOps);
   299     if (eCSSUnit_Calc_Plus == unit) {
   300       aOps.Append(" + ");
   301     } else {
   302       NS_ABORT_IF_FALSE(eCSSUnit_Calc_Minus == unit, "unexpected unit");
   303       aOps.Append(" - ");
   304     }
   306     bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(1)));
   307     if (needParens) {
   308       aOps.Append("(");
   309     }
   310     SerializeCalcInternal(array->Item(1), aOps);
   311     if (needParens) {
   312       aOps.Append(")");
   313     }
   314   } else if (IsCalcMultiplicativeUnit(unit)) {
   315     const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
   316     NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length");
   318     bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(0)));
   319     if (needParens) {
   320       aOps.Append("(");
   321     }
   322     if (unit == eCSSUnit_Calc_Times_L) {
   323       aOps.AppendNumber(array->Item(0));
   324     } else {
   325       SerializeCalcInternal(array->Item(0), aOps);
   326     }
   327     if (needParens) {
   328       aOps.Append(")");
   329     }
   331     if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) {
   332       aOps.Append(" * ");
   333     } else {
   334       NS_ABORT_IF_FALSE(eCSSUnit_Calc_Divided == unit, "unexpected unit");
   335       aOps.Append(" / ");
   336     }
   338     nsCSSUnit subUnit = CalcOps::GetUnit(array->Item(1));
   339     needParens = IsCalcAdditiveUnit(subUnit) ||
   340                  IsCalcMultiplicativeUnit(subUnit);
   341     if (needParens) {
   342       aOps.Append("(");
   343     }
   344     if (unit == eCSSUnit_Calc_Times_L) {
   345       SerializeCalcInternal(array->Item(1), aOps);
   346     } else {
   347       aOps.AppendNumber(array->Item(1));
   348     }
   349     if (needParens) {
   350       aOps.Append(")");
   351     }
   352   } else {
   353     aOps.AppendLeafValue(aValue);
   354   }
   355 }
   357 }
   359 }
   361 #endif /* !defined(CSSCalc_h_) */

mercurial