layout/style/CSSCalc.h

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial