layout/style/CSSCalc.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/style/CSSCalc.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,361 @@
     1.4 +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +#ifndef CSSCalc_h_
     1.9 +#define CSSCalc_h_
    1.10 +
    1.11 +#include "nsCSSValue.h"
    1.12 +#include "nsStyleCoord.h"
    1.13 +#include <math.h>
    1.14 +
    1.15 +namespace mozilla {
    1.16 +
    1.17 +namespace css {
    1.18 +
    1.19 +/**
    1.20 + * ComputeCalc computes the result of a calc() expression tree.
    1.21 + *
    1.22 + * It is templatized over a CalcOps class that is expected to provide:
    1.23 + *
    1.24 + *   // input_type and input_array_type have a bunch of very specific
    1.25 + *   // expectations (which happen to be met by two classes (nsCSSValue
    1.26 + *   // and nsStyleCoord).  There must be methods (roughly):
    1.27 + *   //   input_array_type* input_type::GetArrayValue();
    1.28 + *   //   uint32_t input_array_type::Count() const;
    1.29 + *   //   input_type& input_array_type::Item(uint32_t);
    1.30 + *   typedef ... input_type;
    1.31 + *   typedef ... input_array_type;
    1.32 + *
    1.33 + *   typedef ... result_type;
    1.34 + *
    1.35 + *   // GetUnit(avalue) must return the correct nsCSSUnit for any
    1.36 + *   // value that represents a calc tree node (eCSSUnit_Calc*).  For
    1.37 + *   // other nodes, it may return any non eCSSUnit_Calc* unit.
    1.38 + *   static nsCSSUnit GetUnit(const input_type& aValue);
    1.39 + *
    1.40 + *   result_type
    1.41 + *   MergeAdditive(nsCSSUnit aCalcFunction,
    1.42 + *                 result_type aValue1, result_type aValue2);
    1.43 + *
    1.44 + *   result_type
    1.45 + *   MergeMultiplicativeL(nsCSSUnit aCalcFunction,
    1.46 + *                        float aValue1, result_type aValue2);
    1.47 + *
    1.48 + *   result_type
    1.49 + *   MergeMultiplicativeR(nsCSSUnit aCalcFunction,
    1.50 + *                        result_type aValue1, float aValue2);
    1.51 + *
    1.52 + *   result_type
    1.53 + *   ComputeLeafValue(const input_type& aValue);
    1.54 + *
    1.55 + *   float
    1.56 + *   ComputeNumber(const input_type& aValue);
    1.57 + *
    1.58 + * The CalcOps methods might compute the calc() expression down to a
    1.59 + * number, reduce some parts of it to a number but replicate other
    1.60 + * parts, or produce a tree with a different data structure (for
    1.61 + * example, nsCSS* for specified values vs nsStyle* for computed
    1.62 + * values).
    1.63 + *
    1.64 + * For each leaf in the calc() expression, ComputeCalc will call either
    1.65 + * ComputeNumber (when the leaf is the left side of a Times_L or the
    1.66 + * right side of a Times_R or Divided) or ComputeLeafValue (otherwise).
    1.67 + * (The CalcOps in the CSS parser that reduces purely numeric
    1.68 + * expressions in turn calls ComputeCalc on numbers; other ops can
    1.69 + * presume that expressions in the number positions have already been
    1.70 + * normalized to a single numeric value and derive from
    1.71 + * NumbersAlreadyNormalizedCalcOps.)
    1.72 + *
    1.73 + * For non-leaves, one of the Merge functions will be called:
    1.74 + *   MergeAdditive for Plus and Minus
    1.75 + *   MergeMultiplicativeL for Times_L (number * value)
    1.76 + *   MergeMultiplicativeR for Times_R (value * number) and Divided
    1.77 + */
    1.78 +template <class CalcOps>
    1.79 +static typename CalcOps::result_type
    1.80 +ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
    1.81 +{
    1.82 +  switch (CalcOps::GetUnit(aValue)) {
    1.83 +    case eCSSUnit_Calc: {
    1.84 +      typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
    1.85 +      NS_ABORT_IF_FALSE(arr->Count() == 1, "unexpected length");
    1.86 +      return ComputeCalc(arr->Item(0), aOps);
    1.87 +    }
    1.88 +    case eCSSUnit_Calc_Plus:
    1.89 +    case eCSSUnit_Calc_Minus: {
    1.90 +      typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
    1.91 +      NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
    1.92 +      typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps),
    1.93 +                                    rhs = ComputeCalc(arr->Item(1), aOps);
    1.94 +      return aOps.MergeAdditive(CalcOps::GetUnit(aValue), lhs, rhs);
    1.95 +    }
    1.96 +    case eCSSUnit_Calc_Times_L: {
    1.97 +      typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
    1.98 +      NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
    1.99 +      float lhs = aOps.ComputeNumber(arr->Item(0));
   1.100 +      typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps);
   1.101 +      return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs);
   1.102 +    }
   1.103 +    case eCSSUnit_Calc_Times_R:
   1.104 +    case eCSSUnit_Calc_Divided: {
   1.105 +      typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
   1.106 +      NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
   1.107 +      typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps);
   1.108 +      float rhs = aOps.ComputeNumber(arr->Item(1));
   1.109 +      return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
   1.110 +    }
   1.111 +    default: {
   1.112 +      return aOps.ComputeLeafValue(aValue);
   1.113 +    }
   1.114 +  }
   1.115 +}
   1.116 +
   1.117 +/**
   1.118 + * The input unit operation for input_type being nsCSSValue.
   1.119 + */
   1.120 +struct CSSValueInputCalcOps
   1.121 +{
   1.122 +  typedef nsCSSValue input_type;
   1.123 +  typedef nsCSSValue::Array input_array_type;
   1.124 +
   1.125 +  static nsCSSUnit GetUnit(const nsCSSValue& aValue)
   1.126 +  {
   1.127 +    return aValue.GetUnit();
   1.128 +  }
   1.129 +
   1.130 +};
   1.131 +
   1.132 +/**
   1.133 + * Basic*CalcOps provide a partial implementation of the CalcOps
   1.134 + * template parameter to ComputeCalc, for those callers whose merging
   1.135 + * just consists of mathematics (rather than tree construction).
   1.136 + */
   1.137 +
   1.138 +struct BasicCoordCalcOps
   1.139 +{
   1.140 +  typedef nscoord result_type;
   1.141 +
   1.142 +  result_type
   1.143 +  MergeAdditive(nsCSSUnit aCalcFunction,
   1.144 +                result_type aValue1, result_type aValue2)
   1.145 +  {
   1.146 +    if (aCalcFunction == eCSSUnit_Calc_Plus) {
   1.147 +      return NSCoordSaturatingAdd(aValue1, aValue2);
   1.148 +    }
   1.149 +    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
   1.150 +                      "unexpected unit");
   1.151 +    return NSCoordSaturatingSubtract(aValue1, aValue2, 0);
   1.152 +  }
   1.153 +
   1.154 +  result_type
   1.155 +  MergeMultiplicativeL(nsCSSUnit aCalcFunction,
   1.156 +                       float aValue1, result_type aValue2)
   1.157 +  {
   1.158 +    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
   1.159 +                      "unexpected unit");
   1.160 +    return NSCoordSaturatingMultiply(aValue2, aValue1);
   1.161 +  }
   1.162 +
   1.163 +  result_type
   1.164 +  MergeMultiplicativeR(nsCSSUnit aCalcFunction,
   1.165 +                       result_type aValue1, float aValue2)
   1.166 +  {
   1.167 +    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R ||
   1.168 +                      aCalcFunction == eCSSUnit_Calc_Divided,
   1.169 +                      "unexpected unit");
   1.170 +    if (aCalcFunction == eCSSUnit_Calc_Divided) {
   1.171 +      aValue2 = 1.0f / aValue2;
   1.172 +    }
   1.173 +    return NSCoordSaturatingMultiply(aValue1, aValue2);
   1.174 +  }
   1.175 +};
   1.176 +
   1.177 +struct BasicFloatCalcOps
   1.178 +{
   1.179 +  typedef float result_type;
   1.180 +
   1.181 +  result_type
   1.182 +  MergeAdditive(nsCSSUnit aCalcFunction,
   1.183 +                result_type aValue1, result_type aValue2)
   1.184 +  {
   1.185 +    if (aCalcFunction == eCSSUnit_Calc_Plus) {
   1.186 +      return aValue1 + aValue2;
   1.187 +    }
   1.188 +    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
   1.189 +                      "unexpected unit");
   1.190 +    return aValue1 - aValue2;
   1.191 +  }
   1.192 +
   1.193 +  result_type
   1.194 +  MergeMultiplicativeL(nsCSSUnit aCalcFunction,
   1.195 +                       float aValue1, result_type aValue2)
   1.196 +  {
   1.197 +    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
   1.198 +                      "unexpected unit");
   1.199 +    return aValue1 * aValue2;
   1.200 +  }
   1.201 +
   1.202 +  result_type
   1.203 +  MergeMultiplicativeR(nsCSSUnit aCalcFunction,
   1.204 +                       result_type aValue1, float aValue2)
   1.205 +  {
   1.206 +    if (aCalcFunction == eCSSUnit_Calc_Times_R) {
   1.207 +      return aValue1 * aValue2;
   1.208 +    }
   1.209 +    NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Divided,
   1.210 +                      "unexpected unit");
   1.211 +    return aValue1 / aValue2;
   1.212 +  }
   1.213 +};
   1.214 +
   1.215 +/**
   1.216 + * A ComputeNumber implementation for callers that can assume numbers
   1.217 + * are already normalized (i.e., anything past the parser).
   1.218 + */
   1.219 +struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps
   1.220 +{
   1.221 +  float ComputeNumber(const nsCSSValue& aValue)
   1.222 +  {
   1.223 +    NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
   1.224 +    return aValue.GetFloatValue();
   1.225 +  }
   1.226 +};
   1.227 +
   1.228 +/**
   1.229 + * SerializeCalc appends the serialization of aValue to a string.
   1.230 + *
   1.231 + * It is templatized over a CalcOps class that is expected to provide:
   1.232 + *
   1.233 + *   // input_type and input_array_type have a bunch of very specific
   1.234 + *   // expectations (which happen to be met by two classes (nsCSSValue
   1.235 + *   // and nsStyleCoord).  There must be methods (roughly):
   1.236 + *   //   input_array_type* input_type::GetArrayValue();
   1.237 + *   //   uint32_t input_array_type::Count() const;
   1.238 + *   //   input_type& input_array_type::Item(uint32_t);
   1.239 + *   typedef ... input_type;
   1.240 + *   typedef ... input_array_type;
   1.241 + *
   1.242 + *   static nsCSSUnit GetUnit(const input_type& aValue);
   1.243 + *
   1.244 + *   void Append(const char* aString);
   1.245 + *   void AppendLeafValue(const input_type& aValue);
   1.246 + *   void AppendNumber(const input_type& aValue);
   1.247 + *
   1.248 + * Data structures given may or may not have a toplevel eCSSUnit_Calc
   1.249 + * node representing a calc whose toplevel is not min() or max().
   1.250 + */
   1.251 +
   1.252 +template <class CalcOps>
   1.253 +static void
   1.254 +SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps);
   1.255 +
   1.256 +// Serialize the toplevel value in a calc() tree.  See big comment
   1.257 +// above.
   1.258 +template <class CalcOps>
   1.259 +static void
   1.260 +SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
   1.261 +{
   1.262 +  aOps.Append("calc(");
   1.263 +  nsCSSUnit unit = CalcOps::GetUnit(aValue);
   1.264 +  if (unit == eCSSUnit_Calc) {
   1.265 +    const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
   1.266 +    NS_ABORT_IF_FALSE(array->Count() == 1, "unexpected length");
   1.267 +    SerializeCalcInternal(array->Item(0), aOps);
   1.268 +  } else {
   1.269 +    SerializeCalcInternal(aValue, aOps);
   1.270 +  }
   1.271 +  aOps.Append(")");
   1.272 +}
   1.273 +
   1.274 +static inline bool
   1.275 +IsCalcAdditiveUnit(nsCSSUnit aUnit)
   1.276 +{
   1.277 +  return aUnit == eCSSUnit_Calc_Plus ||
   1.278 +         aUnit == eCSSUnit_Calc_Minus;
   1.279 +}
   1.280 +
   1.281 +static inline bool
   1.282 +IsCalcMultiplicativeUnit(nsCSSUnit aUnit)
   1.283 +{
   1.284 +  return aUnit == eCSSUnit_Calc_Times_L ||
   1.285 +         aUnit == eCSSUnit_Calc_Times_R ||
   1.286 +         aUnit == eCSSUnit_Calc_Divided;
   1.287 +}
   1.288 +
   1.289 +// Serialize a non-toplevel value in a calc() tree.  See big comment
   1.290 +// above.
   1.291 +template <class CalcOps>
   1.292 +/* static */ void
   1.293 +SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
   1.294 +{
   1.295 +  nsCSSUnit unit = CalcOps::GetUnit(aValue);
   1.296 +  if (IsCalcAdditiveUnit(unit)) {
   1.297 +    const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
   1.298 +    NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length");
   1.299 +
   1.300 +    SerializeCalcInternal(array->Item(0), aOps);
   1.301 +
   1.302 +    if (eCSSUnit_Calc_Plus == unit) {
   1.303 +      aOps.Append(" + ");
   1.304 +    } else {
   1.305 +      NS_ABORT_IF_FALSE(eCSSUnit_Calc_Minus == unit, "unexpected unit");
   1.306 +      aOps.Append(" - ");
   1.307 +    }
   1.308 +
   1.309 +    bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(1)));
   1.310 +    if (needParens) {
   1.311 +      aOps.Append("(");
   1.312 +    }
   1.313 +    SerializeCalcInternal(array->Item(1), aOps);
   1.314 +    if (needParens) {
   1.315 +      aOps.Append(")");
   1.316 +    }
   1.317 +  } else if (IsCalcMultiplicativeUnit(unit)) {
   1.318 +    const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
   1.319 +    NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length");
   1.320 +
   1.321 +    bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(0)));
   1.322 +    if (needParens) {
   1.323 +      aOps.Append("(");
   1.324 +    }
   1.325 +    if (unit == eCSSUnit_Calc_Times_L) {
   1.326 +      aOps.AppendNumber(array->Item(0));
   1.327 +    } else {
   1.328 +      SerializeCalcInternal(array->Item(0), aOps);
   1.329 +    }
   1.330 +    if (needParens) {
   1.331 +      aOps.Append(")");
   1.332 +    }
   1.333 +
   1.334 +    if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) {
   1.335 +      aOps.Append(" * ");
   1.336 +    } else {
   1.337 +      NS_ABORT_IF_FALSE(eCSSUnit_Calc_Divided == unit, "unexpected unit");
   1.338 +      aOps.Append(" / ");
   1.339 +    }
   1.340 +
   1.341 +    nsCSSUnit subUnit = CalcOps::GetUnit(array->Item(1));
   1.342 +    needParens = IsCalcAdditiveUnit(subUnit) ||
   1.343 +                 IsCalcMultiplicativeUnit(subUnit);
   1.344 +    if (needParens) {
   1.345 +      aOps.Append("(");
   1.346 +    }
   1.347 +    if (unit == eCSSUnit_Calc_Times_L) {
   1.348 +      SerializeCalcInternal(array->Item(1), aOps);
   1.349 +    } else {
   1.350 +      aOps.AppendNumber(array->Item(1));
   1.351 +    }
   1.352 +    if (needParens) {
   1.353 +      aOps.Append(")");
   1.354 +    }
   1.355 +  } else {
   1.356 +    aOps.AppendLeafValue(aValue);
   1.357 +  }
   1.358 +}
   1.359 +
   1.360 +}
   1.361 +
   1.362 +}
   1.363 +
   1.364 +#endif /* !defined(CSSCalc_h_) */

mercurial