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/. */
8 #include "nsCSSValue.h"
9 #include "nsStyleCoord.h"
17 * ComputeCalc computes the result of a calc() expression tree.
19 * It is templatized over a CalcOps class that is expected to provide:
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;
30 * typedef ... result_type;
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);
38 * MergeAdditive(nsCSSUnit aCalcFunction,
39 * result_type aValue1, result_type aValue2);
42 * MergeMultiplicativeL(nsCSSUnit aCalcFunction,
43 * float aValue1, result_type aValue2);
46 * MergeMultiplicativeR(nsCSSUnit aCalcFunction,
47 * result_type aValue1, float aValue2);
50 * ComputeLeafValue(const input_type& aValue);
53 * ComputeNumber(const input_type& aValue);
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
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.)
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
75 template <class CalcOps
>
76 static typename
CalcOps::result_type
77 ComputeCalc(const typename
CalcOps::input_type
& aValue
, CalcOps
&aOps
)
79 switch (CalcOps::GetUnit(aValue
)) {
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
);
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
);
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
);
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
);
109 return aOps
.ComputeLeafValue(aValue
);
115 * The input unit operation for input_type being nsCSSValue.
117 struct CSSValueInputCalcOps
119 typedef nsCSSValue input_type
;
120 typedef nsCSSValue::Array input_array_type
;
122 static nsCSSUnit
GetUnit(const nsCSSValue
& aValue
)
124 return aValue
.GetUnit();
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).
135 struct BasicCoordCalcOps
137 typedef nscoord result_type
;
140 MergeAdditive(nsCSSUnit aCalcFunction
,
141 result_type aValue1
, result_type aValue2
)
143 if (aCalcFunction
== eCSSUnit_Calc_Plus
) {
144 return NSCoordSaturatingAdd(aValue1
, aValue2
);
146 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Minus
,
148 return NSCoordSaturatingSubtract(aValue1
, aValue2
, 0);
152 MergeMultiplicativeL(nsCSSUnit aCalcFunction
,
153 float aValue1
, result_type aValue2
)
155 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Times_L
,
157 return NSCoordSaturatingMultiply(aValue2
, aValue1
);
161 MergeMultiplicativeR(nsCSSUnit aCalcFunction
,
162 result_type aValue1
, float aValue2
)
164 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Times_R
||
165 aCalcFunction
== eCSSUnit_Calc_Divided
,
167 if (aCalcFunction
== eCSSUnit_Calc_Divided
) {
168 aValue2
= 1.0f
/ aValue2
;
170 return NSCoordSaturatingMultiply(aValue1
, aValue2
);
174 struct BasicFloatCalcOps
176 typedef float result_type
;
179 MergeAdditive(nsCSSUnit aCalcFunction
,
180 result_type aValue1
, result_type aValue2
)
182 if (aCalcFunction
== eCSSUnit_Calc_Plus
) {
183 return aValue1
+ aValue2
;
185 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Minus
,
187 return aValue1
- aValue2
;
191 MergeMultiplicativeL(nsCSSUnit aCalcFunction
,
192 float aValue1
, result_type aValue2
)
194 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Times_L
,
196 return aValue1
* aValue2
;
200 MergeMultiplicativeR(nsCSSUnit aCalcFunction
,
201 result_type aValue1
, float aValue2
)
203 if (aCalcFunction
== eCSSUnit_Calc_Times_R
) {
204 return aValue1
* aValue2
;
206 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Divided
,
208 return aValue1
/ aValue2
;
213 * A ComputeNumber implementation for callers that can assume numbers
214 * are already normalized (i.e., anything past the parser).
216 struct NumbersAlreadyNormalizedOps
: public CSSValueInputCalcOps
218 float ComputeNumber(const nsCSSValue
& aValue
)
220 NS_ABORT_IF_FALSE(aValue
.GetUnit() == eCSSUnit_Number
, "unexpected unit");
221 return aValue
.GetFloatValue();
226 * SerializeCalc appends the serialization of aValue to a string.
228 * It is templatized over a CalcOps class that is expected to provide:
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;
239 * static nsCSSUnit GetUnit(const input_type& aValue);
241 * void Append(const char* aString);
242 * void AppendLeafValue(const input_type& aValue);
243 * void AppendNumber(const input_type& aValue);
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().
249 template <class CalcOps
>
251 SerializeCalcInternal(const typename
CalcOps::input_type
& aValue
, CalcOps
&aOps
);
253 // Serialize the toplevel value in a calc() tree. See big comment
255 template <class CalcOps
>
257 SerializeCalc(const typename
CalcOps::input_type
& aValue
, CalcOps
&aOps
)
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
);
266 SerializeCalcInternal(aValue
, aOps
);
272 IsCalcAdditiveUnit(nsCSSUnit aUnit
)
274 return aUnit
== eCSSUnit_Calc_Plus
||
275 aUnit
== eCSSUnit_Calc_Minus
;
279 IsCalcMultiplicativeUnit(nsCSSUnit aUnit
)
281 return aUnit
== eCSSUnit_Calc_Times_L
||
282 aUnit
== eCSSUnit_Calc_Times_R
||
283 aUnit
== eCSSUnit_Calc_Divided
;
286 // Serialize a non-toplevel value in a calc() tree. See big comment
288 template <class CalcOps
>
290 SerializeCalcInternal(const typename
CalcOps::input_type
& aValue
, CalcOps
&aOps
)
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
) {
302 NS_ABORT_IF_FALSE(eCSSUnit_Calc_Minus
== unit
, "unexpected unit");
306 bool needParens
= IsCalcAdditiveUnit(CalcOps::GetUnit(array
->Item(1)));
310 SerializeCalcInternal(array
->Item(1), aOps
);
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)));
322 if (unit
== eCSSUnit_Calc_Times_L
) {
323 aOps
.AppendNumber(array
->Item(0));
325 SerializeCalcInternal(array
->Item(0), aOps
);
331 if (eCSSUnit_Calc_Times_L
== unit
|| eCSSUnit_Calc_Times_R
== unit
) {
334 NS_ABORT_IF_FALSE(eCSSUnit_Calc_Divided
== unit
, "unexpected unit");
338 nsCSSUnit subUnit
= CalcOps::GetUnit(array
->Item(1));
339 needParens
= IsCalcAdditiveUnit(subUnit
) ||
340 IsCalcMultiplicativeUnit(subUnit
);
344 if (unit
== eCSSUnit_Calc_Times_L
) {
345 SerializeCalcInternal(array
->Item(1), aOps
);
347 aOps
.AppendNumber(array
->Item(1));
353 aOps
.AppendLeafValue(aValue
);
361 #endif /* !defined(CSSCalc_h_) */