Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / layout / style / CSSCalc.h
blob1dc8b1d91798be8d1e82abcb34d82bbb7dc844e2
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
13 * License.
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.
21 * Contributor(s):
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 ***** */
37 #ifndef CSSCalc_h_
38 #define CSSCalc_h_
40 #include "nsCSSValue.h"
41 #include "nsStyleCoord.h"
42 #include <math.h>
44 namespace mozilla {
46 namespace css {
48 /**
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);
69 * result_type
70 * MergeAdditive(nsCSSUnit aCalcFunction,
71 * result_type aValue1, result_type aValue2);
73 * result_type
74 * MergeMultiplicativeL(nsCSSUnit aCalcFunction,
75 * float aValue1, result_type aValue2);
77 * result_type
78 * MergeMultiplicativeR(nsCSSUnit aCalcFunction,
79 * result_type aValue1, float aValue2);
81 * result_type
82 * ComputeLeafValue(const input_type& aValue);
84 * float
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
91 * values).
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);
140 default: {
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;
171 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,
179 "unexpected unit");
180 return NSCoordSaturatingSubtract(aValue1, aValue2, 0);
183 result_type
184 MergeMultiplicativeL(nsCSSUnit aCalcFunction,
185 float aValue1, result_type aValue2)
187 NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
188 "unexpected unit");
189 return NSCoordSaturatingMultiply(aValue2, aValue1);
192 result_type
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,
198 "unexpected unit");
199 if (aCalcFunction == eCSSUnit_Calc_Divided) {
200 aValue2 = 1.0f / aValue2;
202 return NSCoordSaturatingMultiply(aValue1, aValue2);
206 struct BasicFloatCalcOps
208 typedef float result_type;
210 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,
218 "unexpected unit");
219 return aValue1 - aValue2;
222 result_type
223 MergeMultiplicativeL(nsCSSUnit aCalcFunction,
224 float aValue1, result_type aValue2)
226 NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
227 "unexpected unit");
228 return aValue1 * aValue2;
231 result_type
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,
239 "unexpected unit");
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>
282 static void
283 SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps);
285 // Serialize the toplevel value in a calc() tree. See big comment
286 // above.
287 template <class CalcOps>
288 static void
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);
297 } else {
298 SerializeCalcInternal(aValue, aOps);
300 aOps.Append(")");
303 static inline PRBool
304 IsCalcAdditiveUnit(nsCSSUnit aUnit)
306 return aUnit == eCSSUnit_Calc_Plus ||
307 aUnit == eCSSUnit_Calc_Minus;
310 static inline PRBool
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
319 // above.
320 template <class CalcOps>
321 /* static */ void
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) {
332 aOps.Append(" + ");
333 } else {
334 NS_ABORT_IF_FALSE(eCSSUnit_Calc_Minus == unit, "unexpected unit");
335 aOps.Append(" - ");
338 PRBool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(1)));
339 if (needParens) {
340 aOps.Append("(");
342 SerializeCalcInternal(array->Item(1), aOps);
343 if (needParens) {
344 aOps.Append(")");
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)));
351 if (needParens) {
352 aOps.Append("(");
354 if (unit == eCSSUnit_Calc_Times_L) {
355 aOps.AppendNumber(array->Item(0));
356 } else {
357 SerializeCalcInternal(array->Item(0), aOps);
359 if (needParens) {
360 aOps.Append(")");
363 if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) {
364 aOps.Append(" * ");
365 } else {
366 NS_ABORT_IF_FALSE(eCSSUnit_Calc_Divided == unit, "unexpected unit");
367 aOps.Append(" / ");
370 nsCSSUnit subUnit = CalcOps::GetUnit(array->Item(1));
371 needParens = IsCalcAdditiveUnit(subUnit) ||
372 IsCalcMultiplicativeUnit(subUnit);
373 if (needParens) {
374 aOps.Append("(");
376 if (unit == eCSSUnit_Calc_Times_L) {
377 SerializeCalcInternal(array->Item(1), aOps);
378 } else {
379 aOps.AppendNumber(array->Item(1));
381 if (needParens) {
382 aOps.Append(")");
384 } else {
385 aOps.AppendLeafValue(aValue);
393 #endif /* !defined(CSSCalc_h_) */