1 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is CSSCalc.h.
17 * The Initial Developer of the Original Code is the Mozilla Foundation.
18 * Portions created by the Initial Developer are Copyright (C) 2009
19 * the Initial Developer. All Rights Reserved.
22 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
40 #include "nsCSSValue.h"
41 #include "nsStyleCoord.h"
49 * ComputeCalc computes the result of a calc() expression tree.
51 * It is templatized over a CalcOps class that is expected to provide:
53 * // input_type and input_array_type have a bunch of very specific
54 * // expectations (which happen to be met by two classes (nsCSSValue
55 * // and nsStyleCoord). There must be methods (roughly):
56 * // input_array_type* input_type::GetArrayValue();
57 * // PRUint32 input_array_type::Count() const;
58 * // input_type& input_array_type::Item(PRUint32);
59 * typedef ... input_type;
60 * typedef ... input_array_type;
62 * typedef ... result_type;
64 * // GetUnit(avalue) must return the correct nsCSSUnit for any
65 * // value that represents a calc tree node (eCSSUnit_Calc*). For
66 * // other nodes, it may return any non eCSSUnit_Calc* unit.
67 * static nsCSSUnit GetUnit(const input_type& aValue);
70 * MergeAdditive(nsCSSUnit aCalcFunction,
71 * result_type aValue1, result_type aValue2);
74 * MergeMultiplicativeL(nsCSSUnit aCalcFunction,
75 * float aValue1, result_type aValue2);
78 * MergeMultiplicativeR(nsCSSUnit aCalcFunction,
79 * result_type aValue1, float aValue2);
82 * ComputeLeafValue(const input_type& aValue);
85 * ComputeNumber(const input_type& aValue);
87 * The CalcOps methods might compute the calc() expression down to a
88 * number, reduce some parts of it to a number but replicate other
89 * parts, or produce a tree with a different data structure (for
90 * example, nsCSS* for specified values vs nsStyle* for computed
93 * For each leaf in the calc() expression, ComputeCalc will call either
94 * ComputeNumber (when the leaf is the left side of a Times_L or the
95 * right side of a Times_R or Divided) or ComputeLeafValue (otherwise).
96 * (The CalcOps in the CSS parser that reduces purely numeric
97 * expressions in turn calls ComputeCalc on numbers; other ops can
98 * presume that expressions in the number positions have already been
99 * normalized to a single numeric value and derive from
100 * NumbersAlreadyNormalizedCalcOps.)
102 * For non-leaves, one of the Merge functions will be called:
103 * MergeAdditive for Plus and Minus
104 * MergeMultiplicativeL for Times_L (number * value)
105 * MergeMultiplicativeR for Times_R (value * number) and Divided
107 template <class CalcOps
>
108 static typename
CalcOps::result_type
109 ComputeCalc(const typename
CalcOps::input_type
& aValue
, CalcOps
&aOps
)
111 switch (CalcOps::GetUnit(aValue
)) {
112 case eCSSUnit_Calc
: {
113 typename
CalcOps::input_array_type
*arr
= aValue
.GetArrayValue();
114 NS_ABORT_IF_FALSE(arr
->Count() == 1, "unexpected length");
115 return ComputeCalc(arr
->Item(0), aOps
);
117 case eCSSUnit_Calc_Plus
:
118 case eCSSUnit_Calc_Minus
: {
119 typename
CalcOps::input_array_type
*arr
= aValue
.GetArrayValue();
120 NS_ABORT_IF_FALSE(arr
->Count() == 2, "unexpected length");
121 typename
CalcOps::result_type lhs
= ComputeCalc(arr
->Item(0), aOps
),
122 rhs
= ComputeCalc(arr
->Item(1), aOps
);
123 return aOps
.MergeAdditive(CalcOps::GetUnit(aValue
), lhs
, rhs
);
125 case eCSSUnit_Calc_Times_L
: {
126 typename
CalcOps::input_array_type
*arr
= aValue
.GetArrayValue();
127 NS_ABORT_IF_FALSE(arr
->Count() == 2, "unexpected length");
128 float lhs
= aOps
.ComputeNumber(arr
->Item(0));
129 typename
CalcOps::result_type rhs
= ComputeCalc(arr
->Item(1), aOps
);
130 return aOps
.MergeMultiplicativeL(CalcOps::GetUnit(aValue
), lhs
, rhs
);
132 case eCSSUnit_Calc_Times_R
:
133 case eCSSUnit_Calc_Divided
: {
134 typename
CalcOps::input_array_type
*arr
= aValue
.GetArrayValue();
135 NS_ABORT_IF_FALSE(arr
->Count() == 2, "unexpected length");
136 typename
CalcOps::result_type lhs
= ComputeCalc(arr
->Item(0), aOps
);
137 float rhs
= aOps
.ComputeNumber(arr
->Item(1));
138 return aOps
.MergeMultiplicativeR(CalcOps::GetUnit(aValue
), lhs
, rhs
);
141 return aOps
.ComputeLeafValue(aValue
);
147 * The input unit operation for input_type being nsCSSValue.
149 struct CSSValueInputCalcOps
151 typedef nsCSSValue input_type
;
152 typedef nsCSSValue::Array input_array_type
;
154 static nsCSSUnit
GetUnit(const nsCSSValue
& aValue
)
156 return aValue
.GetUnit();
162 * Basic*CalcOps provide a partial implementation of the CalcOps
163 * template parameter to ComputeCalc, for those callers whose merging
164 * just consists of mathematics (rather than tree construction).
167 struct BasicCoordCalcOps
169 typedef nscoord result_type
;
172 MergeAdditive(nsCSSUnit aCalcFunction
,
173 result_type aValue1
, result_type aValue2
)
175 if (aCalcFunction
== eCSSUnit_Calc_Plus
) {
176 return NSCoordSaturatingAdd(aValue1
, aValue2
);
178 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Minus
,
180 return NSCoordSaturatingSubtract(aValue1
, aValue2
, 0);
184 MergeMultiplicativeL(nsCSSUnit aCalcFunction
,
185 float aValue1
, result_type aValue2
)
187 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Times_L
,
189 return NSCoordSaturatingMultiply(aValue2
, aValue1
);
193 MergeMultiplicativeR(nsCSSUnit aCalcFunction
,
194 result_type aValue1
, float aValue2
)
196 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Times_R
||
197 aCalcFunction
== eCSSUnit_Calc_Divided
,
199 if (aCalcFunction
== eCSSUnit_Calc_Divided
) {
200 aValue2
= 1.0f
/ aValue2
;
202 return NSCoordSaturatingMultiply(aValue1
, aValue2
);
206 struct BasicFloatCalcOps
208 typedef float result_type
;
211 MergeAdditive(nsCSSUnit aCalcFunction
,
212 result_type aValue1
, result_type aValue2
)
214 if (aCalcFunction
== eCSSUnit_Calc_Plus
) {
215 return aValue1
+ aValue2
;
217 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Minus
,
219 return aValue1
- aValue2
;
223 MergeMultiplicativeL(nsCSSUnit aCalcFunction
,
224 float aValue1
, result_type aValue2
)
226 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Times_L
,
228 return aValue1
* aValue2
;
232 MergeMultiplicativeR(nsCSSUnit aCalcFunction
,
233 result_type aValue1
, float aValue2
)
235 if (aCalcFunction
== eCSSUnit_Calc_Times_R
) {
236 return aValue1
* aValue2
;
238 NS_ABORT_IF_FALSE(aCalcFunction
== eCSSUnit_Calc_Divided
,
240 return aValue1
/ aValue2
;
245 * A ComputeNumber implementation for callers that can assume numbers
246 * are already normalized (i.e., anything past the parser).
248 struct NumbersAlreadyNormalizedOps
: public CSSValueInputCalcOps
250 float ComputeNumber(const nsCSSValue
& aValue
)
252 NS_ABORT_IF_FALSE(aValue
.GetUnit() == eCSSUnit_Number
, "unexpected unit");
253 return aValue
.GetFloatValue();
258 * SerializeCalc appends the serialization of aValue to a string.
260 * It is templatized over a CalcOps class that is expected to provide:
262 * // input_type and input_array_type have a bunch of very specific
263 * // expectations (which happen to be met by two classes (nsCSSValue
264 * // and nsStyleCoord). There must be methods (roughly):
265 * // input_array_type* input_type::GetArrayValue();
266 * // PRUint32 input_array_type::Count() const;
267 * // input_type& input_array_type::Item(PRUint32);
268 * typedef ... input_type;
269 * typedef ... input_array_type;
271 * static nsCSSUnit GetUnit(const input_type& aValue);
273 * void Append(const char* aString);
274 * void AppendLeafValue(const input_type& aValue);
275 * void AppendNumber(const input_type& aValue);
277 * Data structures given may or may not have a toplevel eCSSUnit_Calc
278 * node representing a calc whose toplevel is not min() or max().
281 template <class CalcOps
>
283 SerializeCalcInternal(const typename
CalcOps::input_type
& aValue
, CalcOps
&aOps
);
285 // Serialize the toplevel value in a calc() tree. See big comment
287 template <class CalcOps
>
289 SerializeCalc(const typename
CalcOps::input_type
& aValue
, CalcOps
&aOps
)
291 aOps
.Append("-moz-calc(");
292 nsCSSUnit unit
= CalcOps::GetUnit(aValue
);
293 if (unit
== eCSSUnit_Calc
) {
294 const typename
CalcOps::input_array_type
*array
= aValue
.GetArrayValue();
295 NS_ABORT_IF_FALSE(array
->Count() == 1, "unexpected length");
296 SerializeCalcInternal(array
->Item(0), aOps
);
298 SerializeCalcInternal(aValue
, aOps
);
304 IsCalcAdditiveUnit(nsCSSUnit aUnit
)
306 return aUnit
== eCSSUnit_Calc_Plus
||
307 aUnit
== eCSSUnit_Calc_Minus
;
311 IsCalcMultiplicativeUnit(nsCSSUnit aUnit
)
313 return aUnit
== eCSSUnit_Calc_Times_L
||
314 aUnit
== eCSSUnit_Calc_Times_R
||
315 aUnit
== eCSSUnit_Calc_Divided
;
318 // Serialize a non-toplevel value in a calc() tree. See big comment
320 template <class CalcOps
>
322 SerializeCalcInternal(const typename
CalcOps::input_type
& aValue
, CalcOps
&aOps
)
324 nsCSSUnit unit
= CalcOps::GetUnit(aValue
);
325 if (IsCalcAdditiveUnit(unit
)) {
326 const typename
CalcOps::input_array_type
*array
= aValue
.GetArrayValue();
327 NS_ABORT_IF_FALSE(array
->Count() == 2, "unexpected length");
329 SerializeCalcInternal(array
->Item(0), aOps
);
331 if (eCSSUnit_Calc_Plus
== unit
) {
334 NS_ABORT_IF_FALSE(eCSSUnit_Calc_Minus
== unit
, "unexpected unit");
338 PRBool needParens
= IsCalcAdditiveUnit(CalcOps::GetUnit(array
->Item(1)));
342 SerializeCalcInternal(array
->Item(1), aOps
);
346 } else if (IsCalcMultiplicativeUnit(unit
)) {
347 const typename
CalcOps::input_array_type
*array
= aValue
.GetArrayValue();
348 NS_ABORT_IF_FALSE(array
->Count() == 2, "unexpected length");
350 PRBool needParens
= IsCalcAdditiveUnit(CalcOps::GetUnit(array
->Item(0)));
354 if (unit
== eCSSUnit_Calc_Times_L
) {
355 aOps
.AppendNumber(array
->Item(0));
357 SerializeCalcInternal(array
->Item(0), aOps
);
363 if (eCSSUnit_Calc_Times_L
== unit
|| eCSSUnit_Calc_Times_R
== unit
) {
366 NS_ABORT_IF_FALSE(eCSSUnit_Calc_Divided
== unit
, "unexpected unit");
370 nsCSSUnit subUnit
= CalcOps::GetUnit(array
->Item(1));
371 needParens
= IsCalcAdditiveUnit(subUnit
) ||
372 IsCalcMultiplicativeUnit(subUnit
);
376 if (unit
== eCSSUnit_Calc_Times_L
) {
377 SerializeCalcInternal(array
->Item(1), aOps
);
379 aOps
.AppendNumber(array
->Item(1));
385 aOps
.AppendLeafValue(aValue
);
393 #endif /* !defined(CSSCalc_h_) */