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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is CSSCalc.h.
16 : *
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.
20 : *
21 : * Contributor(s):
22 : * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
23 : *
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.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 : #ifndef CSSCalc_h_
38 : #define CSSCalc_h_
39 :
40 : #include "nsCSSValue.h"
41 : #include "nsStyleCoord.h"
42 : #include <math.h>
43 :
44 : namespace mozilla {
45 :
46 : namespace css {
47 :
48 : /**
49 : * ComputeCalc computes the result of a calc() expression tree.
50 : *
51 : * It is templatized over a CalcOps class that is expected to provide:
52 : *
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;
61 : *
62 : * typedef ... result_type;
63 : *
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);
68 : *
69 : * result_type
70 : * MergeAdditive(nsCSSUnit aCalcFunction,
71 : * result_type aValue1, result_type aValue2);
72 : *
73 : * result_type
74 : * MergeMultiplicativeL(nsCSSUnit aCalcFunction,
75 : * float aValue1, result_type aValue2);
76 : *
77 : * result_type
78 : * MergeMultiplicativeR(nsCSSUnit aCalcFunction,
79 : * result_type aValue1, float aValue2);
80 : *
81 : * result_type
82 : * ComputeLeafValue(const input_type& aValue);
83 : *
84 : * float
85 : * ComputeNumber(const input_type& aValue);
86 : *
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).
92 : *
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.)
101 : *
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
106 : */
107 : template <class CalcOps>
108 : static typename CalcOps::result_type
109 0 : ComputeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
110 : {
111 0 : switch (CalcOps::GetUnit(aValue)) {
112 : case eCSSUnit_Calc: {
113 0 : typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
114 0 : NS_ABORT_IF_FALSE(arr->Count() == 1, "unexpected length");
115 0 : return ComputeCalc(arr->Item(0), aOps);
116 : }
117 : case eCSSUnit_Calc_Plus:
118 : case eCSSUnit_Calc_Minus: {
119 0 : typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
120 0 : NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
121 0 : typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps),
122 0 : rhs = ComputeCalc(arr->Item(1), aOps);
123 0 : return aOps.MergeAdditive(CalcOps::GetUnit(aValue), lhs, rhs);
124 : }
125 : case eCSSUnit_Calc_Times_L: {
126 0 : typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
127 0 : NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
128 0 : float lhs = aOps.ComputeNumber(arr->Item(0));
129 0 : typename CalcOps::result_type rhs = ComputeCalc(arr->Item(1), aOps);
130 0 : return aOps.MergeMultiplicativeL(CalcOps::GetUnit(aValue), lhs, rhs);
131 : }
132 : case eCSSUnit_Calc_Times_R:
133 : case eCSSUnit_Calc_Divided: {
134 0 : typename CalcOps::input_array_type *arr = aValue.GetArrayValue();
135 0 : NS_ABORT_IF_FALSE(arr->Count() == 2, "unexpected length");
136 0 : typename CalcOps::result_type lhs = ComputeCalc(arr->Item(0), aOps);
137 0 : float rhs = aOps.ComputeNumber(arr->Item(1));
138 0 : return aOps.MergeMultiplicativeR(CalcOps::GetUnit(aValue), lhs, rhs);
139 : }
140 : default: {
141 0 : return aOps.ComputeLeafValue(aValue);
142 : }
143 : }
144 : }
145 :
146 : /**
147 : * The input unit operation for input_type being nsCSSValue.
148 : */
149 : struct CSSValueInputCalcOps
150 0 : {
151 : typedef nsCSSValue input_type;
152 : typedef nsCSSValue::Array input_array_type;
153 :
154 0 : static nsCSSUnit GetUnit(const nsCSSValue& aValue)
155 : {
156 0 : return aValue.GetUnit();
157 : }
158 :
159 : };
160 :
161 : /**
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).
165 : */
166 :
167 : struct BasicCoordCalcOps
168 0 : {
169 : typedef nscoord result_type;
170 :
171 : result_type
172 0 : MergeAdditive(nsCSSUnit aCalcFunction,
173 : result_type aValue1, result_type aValue2)
174 : {
175 0 : if (aCalcFunction == eCSSUnit_Calc_Plus) {
176 0 : return NSCoordSaturatingAdd(aValue1, aValue2);
177 : }
178 0 : NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
179 : "unexpected unit");
180 0 : return NSCoordSaturatingSubtract(aValue1, aValue2, 0);
181 : }
182 :
183 : result_type
184 0 : MergeMultiplicativeL(nsCSSUnit aCalcFunction,
185 : float aValue1, result_type aValue2)
186 : {
187 0 : NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
188 : "unexpected unit");
189 0 : return NSCoordSaturatingMultiply(aValue2, aValue1);
190 : }
191 :
192 : result_type
193 0 : MergeMultiplicativeR(nsCSSUnit aCalcFunction,
194 : result_type aValue1, float aValue2)
195 : {
196 0 : NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R ||
197 : aCalcFunction == eCSSUnit_Calc_Divided,
198 : "unexpected unit");
199 0 : if (aCalcFunction == eCSSUnit_Calc_Divided) {
200 0 : aValue2 = 1.0f / aValue2;
201 : }
202 0 : return NSCoordSaturatingMultiply(aValue1, aValue2);
203 : }
204 : };
205 :
206 : struct BasicFloatCalcOps
207 : {
208 : typedef float result_type;
209 :
210 : result_type
211 0 : MergeAdditive(nsCSSUnit aCalcFunction,
212 : result_type aValue1, result_type aValue2)
213 : {
214 0 : if (aCalcFunction == eCSSUnit_Calc_Plus) {
215 0 : return aValue1 + aValue2;
216 : }
217 0 : NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus,
218 : "unexpected unit");
219 0 : return aValue1 - aValue2;
220 : }
221 :
222 : result_type
223 0 : MergeMultiplicativeL(nsCSSUnit aCalcFunction,
224 : float aValue1, result_type aValue2)
225 : {
226 0 : NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L,
227 : "unexpected unit");
228 0 : return aValue1 * aValue2;
229 : }
230 :
231 : result_type
232 0 : MergeMultiplicativeR(nsCSSUnit aCalcFunction,
233 : result_type aValue1, float aValue2)
234 : {
235 0 : if (aCalcFunction == eCSSUnit_Calc_Times_R) {
236 0 : return aValue1 * aValue2;
237 : }
238 0 : NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Divided,
239 : "unexpected unit");
240 0 : return aValue1 / aValue2;
241 : }
242 : };
243 :
244 : /**
245 : * A ComputeNumber implementation for callers that can assume numbers
246 : * are already normalized (i.e., anything past the parser).
247 : */
248 : struct NumbersAlreadyNormalizedOps : public CSSValueInputCalcOps
249 0 : {
250 0 : float ComputeNumber(const nsCSSValue& aValue)
251 : {
252 0 : NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Number, "unexpected unit");
253 0 : return aValue.GetFloatValue();
254 : }
255 : };
256 :
257 : /**
258 : * SerializeCalc appends the serialization of aValue to a string.
259 : *
260 : * It is templatized over a CalcOps class that is expected to provide:
261 : *
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;
270 : *
271 : * static nsCSSUnit GetUnit(const input_type& aValue);
272 : *
273 : * void Append(const char* aString);
274 : * void AppendLeafValue(const input_type& aValue);
275 : * void AppendNumber(const input_type& aValue);
276 : *
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().
279 : */
280 :
281 : template <class CalcOps>
282 : static void
283 : SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps);
284 :
285 : // Serialize the toplevel value in a calc() tree. See big comment
286 : // above.
287 : template <class CalcOps>
288 : static void
289 0 : SerializeCalc(const typename CalcOps::input_type& aValue, CalcOps &aOps)
290 : {
291 0 : aOps.Append("-moz-calc(");
292 0 : nsCSSUnit unit = CalcOps::GetUnit(aValue);
293 0 : if (unit == eCSSUnit_Calc) {
294 0 : const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
295 0 : NS_ABORT_IF_FALSE(array->Count() == 1, "unexpected length");
296 0 : SerializeCalcInternal(array->Item(0), aOps);
297 : } else {
298 0 : SerializeCalcInternal(aValue, aOps);
299 : }
300 0 : aOps.Append(")");
301 0 : }
302 :
303 : static inline bool
304 0 : IsCalcAdditiveUnit(nsCSSUnit aUnit)
305 : {
306 : return aUnit == eCSSUnit_Calc_Plus ||
307 0 : aUnit == eCSSUnit_Calc_Minus;
308 : }
309 :
310 : static inline bool
311 0 : IsCalcMultiplicativeUnit(nsCSSUnit aUnit)
312 : {
313 : return aUnit == eCSSUnit_Calc_Times_L ||
314 : aUnit == eCSSUnit_Calc_Times_R ||
315 0 : aUnit == eCSSUnit_Calc_Divided;
316 : }
317 :
318 : // Serialize a non-toplevel value in a calc() tree. See big comment
319 : // above.
320 : template <class CalcOps>
321 : /* static */ void
322 0 : SerializeCalcInternal(const typename CalcOps::input_type& aValue, CalcOps &aOps)
323 : {
324 0 : nsCSSUnit unit = CalcOps::GetUnit(aValue);
325 0 : if (IsCalcAdditiveUnit(unit)) {
326 0 : const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
327 0 : NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length");
328 :
329 0 : SerializeCalcInternal(array->Item(0), aOps);
330 :
331 0 : if (eCSSUnit_Calc_Plus == unit) {
332 0 : aOps.Append(" + ");
333 : } else {
334 0 : NS_ABORT_IF_FALSE(eCSSUnit_Calc_Minus == unit, "unexpected unit");
335 0 : aOps.Append(" - ");
336 : }
337 :
338 0 : bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(1)));
339 0 : if (needParens) {
340 0 : aOps.Append("(");
341 : }
342 0 : SerializeCalcInternal(array->Item(1), aOps);
343 0 : if (needParens) {
344 0 : aOps.Append(")");
345 : }
346 0 : } else if (IsCalcMultiplicativeUnit(unit)) {
347 0 : const typename CalcOps::input_array_type *array = aValue.GetArrayValue();
348 0 : NS_ABORT_IF_FALSE(array->Count() == 2, "unexpected length");
349 :
350 0 : bool needParens = IsCalcAdditiveUnit(CalcOps::GetUnit(array->Item(0)));
351 0 : if (needParens) {
352 0 : aOps.Append("(");
353 : }
354 0 : if (unit == eCSSUnit_Calc_Times_L) {
355 0 : aOps.AppendNumber(array->Item(0));
356 : } else {
357 0 : SerializeCalcInternal(array->Item(0), aOps);
358 : }
359 0 : if (needParens) {
360 0 : aOps.Append(")");
361 : }
362 :
363 0 : if (eCSSUnit_Calc_Times_L == unit || eCSSUnit_Calc_Times_R == unit) {
364 0 : aOps.Append(" * ");
365 : } else {
366 0 : NS_ABORT_IF_FALSE(eCSSUnit_Calc_Divided == unit, "unexpected unit");
367 0 : aOps.Append(" / ");
368 : }
369 :
370 0 : nsCSSUnit subUnit = CalcOps::GetUnit(array->Item(1));
371 0 : needParens = IsCalcAdditiveUnit(subUnit) ||
372 : IsCalcMultiplicativeUnit(subUnit);
373 0 : if (needParens) {
374 0 : aOps.Append("(");
375 : }
376 0 : if (unit == eCSSUnit_Calc_Times_L) {
377 0 : SerializeCalcInternal(array->Item(1), aOps);
378 : } else {
379 0 : aOps.AppendNumber(array->Item(1));
380 : }
381 0 : if (needParens) {
382 0 : aOps.Append(")");
383 : }
384 : } else {
385 0 : aOps.AppendLeafValue(aValue);
386 : }
387 0 : }
388 :
389 : }
390 :
391 : }
392 :
393 : #endif /* !defined(CSSCalc_h_) */
|