Setting debug mode should purge call ICs (bug 612640, r=bhackett).
[mozilla-central.git] / content / smil / nsSMILCSSValueType.cpp
blob10b2f48df6b647a6cd48f286bdd0cb203aa2c4bc
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 the Mozilla SMIL module.
17 * The Initial Developer of the Original Code is the Mozilla Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2009
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Daniel Holbert <dholbert@mozilla.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or 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 ***** */
38 /* representation of a value for a SMIL-animated CSS property */
40 #include "nsSMILCSSValueType.h"
41 #include "nsString.h"
42 #include "nsStyleAnimation.h"
43 #include "nsSMILParserUtils.h"
44 #include "nsSMILValue.h"
45 #include "nsCSSValue.h"
46 #include "nsColor.h"
47 #include "nsPresContext.h"
48 #include "nsIContent.h"
49 #include "nsDebug.h"
51 /*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
53 struct ValueWrapper {
54 ValueWrapper(nsCSSProperty aPropID, const nsStyleAnimation::Value& aValue,
55 nsPresContext* aPresContext) :
56 mPropID(aPropID), mCSSValue(aValue), mPresContext(aPresContext) {}
58 nsCSSProperty mPropID;
59 nsStyleAnimation::Value mCSSValue;
60 nsPresContext* mPresContext;
63 // Helper "zero" values of various types
64 // -------------------------------------
65 static const nsStyleAnimation::Value
66 sZeroCoord(0, nsStyleAnimation::Value::CoordConstructor);
67 static const nsStyleAnimation::Value
68 sZeroPercent(0.0f, nsStyleAnimation::Value::PercentConstructor);
69 static const nsStyleAnimation::Value
70 sZeroFloat(0.0f, nsStyleAnimation::Value::FloatConstructor);
71 static const nsStyleAnimation::Value
72 sZeroColor(NS_RGB(0,0,0), nsStyleAnimation::Value::ColorConstructor);
74 // Helper Methods
75 // --------------
76 static const nsStyleAnimation::Value*
77 GetZeroValueForUnit(nsStyleAnimation::Unit aUnit)
79 NS_ABORT_IF_FALSE(aUnit != nsStyleAnimation::eUnit_Null,
80 "Need non-null unit for a zero value");
81 switch (aUnit) {
82 case nsStyleAnimation::eUnit_Coord:
83 return &sZeroCoord;
84 case nsStyleAnimation::eUnit_Percent:
85 return &sZeroPercent;
86 case nsStyleAnimation::eUnit_Float:
87 return &sZeroFloat;
88 case nsStyleAnimation::eUnit_Color:
89 return &sZeroColor;
90 default:
91 return nsnull;
95 // This method requires at least one of its arguments to be non-null.
97 // If one argument is null, this method updates it to point to "zero"
98 // for the other argument's Unit (if applicable; otherwise, we return PR_FALSE).
100 // If neither argument is null, this method generally does nothing, though it
101 // may apply a workaround for the special case where a 0 length-value is mixed
102 // with a eUnit_Float value. (See comment below.)
104 // Returns PR_TRUE on success, or PR_FALSE.
105 static const PRBool
106 FinalizeStyleAnimationValues(const nsStyleAnimation::Value*& aValue1,
107 const nsStyleAnimation::Value*& aValue2)
109 NS_ABORT_IF_FALSE(aValue1 || aValue2,
110 "expecting at least one non-null value");
112 // Are we missing either val? (If so, it's an implied 0 in other val's units)
113 if (!aValue1) {
114 aValue1 = GetZeroValueForUnit(aValue2->GetUnit());
115 return !!aValue1; // Fail if we have no zero value for this unit.
117 if (!aValue2) {
118 aValue2 = GetZeroValueForUnit(aValue1->GetUnit());
119 return !!aValue2; // Fail if we have no zero value for this unit.
122 // Ok, both values were specified.
123 // Need to handle a special-case, though: unitless nonzero length (parsed as
124 // eUnit_Float) mixed with unitless 0 length (parsed as eUnit_Coord). These
125 // won't interoperate in nsStyleAnimation, since their Units don't match.
126 // In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value.
127 if (*aValue1 == sZeroCoord &&
128 aValue2->GetUnit() == nsStyleAnimation::eUnit_Float) {
129 aValue1 = &sZeroFloat;
130 } else if (*aValue2 == sZeroCoord &&
131 aValue1->GetUnit() == nsStyleAnimation::eUnit_Float) {
132 aValue2 = &sZeroFloat;
135 return PR_TRUE;
138 static void
139 InvertSign(nsStyleAnimation::Value& aValue)
141 switch (aValue.GetUnit()) {
142 case nsStyleAnimation::eUnit_Coord:
143 aValue.SetCoordValue(-aValue.GetCoordValue());
144 break;
145 case nsStyleAnimation::eUnit_Percent:
146 aValue.SetPercentValue(-aValue.GetPercentValue());
147 break;
148 case nsStyleAnimation::eUnit_Float:
149 aValue.SetFloatValue(-aValue.GetFloatValue());
150 break;
151 default:
152 NS_NOTREACHED("Calling InvertSign with an unsupported unit");
153 break;
157 static ValueWrapper*
158 ExtractValueWrapper(nsSMILValue& aValue)
160 return static_cast<ValueWrapper*>(aValue.mU.mPtr);
163 static const ValueWrapper*
164 ExtractValueWrapper(const nsSMILValue& aValue)
166 return static_cast<const ValueWrapper*>(aValue.mU.mPtr);
169 // Class methods
170 // -------------
171 void
172 nsSMILCSSValueType::Init(nsSMILValue& aValue) const
174 NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected SMIL value type");
176 aValue.mU.mPtr = nsnull;
177 aValue.mType = this;
180 void
181 nsSMILCSSValueType::Destroy(nsSMILValue& aValue) const
183 NS_ABORT_IF_FALSE(aValue.mType == this, "Unexpected SMIL value type");
184 delete static_cast<ValueWrapper*>(aValue.mU.mPtr);
185 aValue.mType = &nsSMILNullType::sSingleton;
188 nsresult
189 nsSMILCSSValueType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
191 NS_ABORT_IF_FALSE(aDest.mType == aSrc.mType, "Incompatible SMIL types");
192 NS_ABORT_IF_FALSE(aDest.mType == this, "Unexpected SMIL value type");
193 const ValueWrapper* srcWrapper = ExtractValueWrapper(aSrc);
194 ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
196 if (srcWrapper) {
197 if (!destWrapper) {
198 // barely-initialized dest -- need to alloc & copy
199 aDest.mU.mPtr = new ValueWrapper(*srcWrapper);
200 } else {
201 // both already fully-initialized -- just copy straight across
202 *destWrapper = *srcWrapper;
204 } else if (destWrapper) {
205 // fully-initialized dest, barely-initialized src -- clear dest
206 delete destWrapper;
207 aDest.mU.mPtr = destWrapper = nsnull;
208 } // else, both are barely-initialized -- nothing to do.
210 return NS_OK;
213 PRBool
214 nsSMILCSSValueType::IsEqual(const nsSMILValue& aLeft,
215 const nsSMILValue& aRight) const
217 NS_ABORT_IF_FALSE(aLeft.mType == aRight.mType, "Incompatible SMIL types");
218 NS_ABORT_IF_FALSE(aLeft.mType == this, "Unexpected SMIL value");
219 const ValueWrapper* leftWrapper = ExtractValueWrapper(aLeft);
220 const ValueWrapper* rightWrapper = ExtractValueWrapper(aRight);
222 if (leftWrapper) {
223 if (rightWrapper) {
224 // Both non-null
225 NS_WARN_IF_FALSE(leftWrapper != rightWrapper,
226 "Two nsSMILValues with matching ValueWrapper ptr");
227 // mPresContext doesn't really matter for equality comparison
228 return (leftWrapper->mPropID == rightWrapper->mPropID &&
229 leftWrapper->mCSSValue == rightWrapper->mCSSValue);
231 // Left non-null, right null
232 return PR_FALSE;
234 if (rightWrapper) {
235 // Left null, right non-null
236 return PR_FALSE;
238 // Both null
239 return PR_TRUE;
242 nsresult
243 nsSMILCSSValueType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
244 PRUint32 aCount) const
246 NS_ABORT_IF_FALSE(aValueToAdd.mType == aDest.mType,
247 "Trying to add invalid types");
248 NS_ABORT_IF_FALSE(aValueToAdd.mType == this, "Unexpected source type");
250 ValueWrapper* destWrapper = ExtractValueWrapper(aDest);
251 const ValueWrapper* valueToAddWrapper = ExtractValueWrapper(aValueToAdd);
252 NS_ABORT_IF_FALSE(destWrapper || valueToAddWrapper,
253 "need at least one fully-initialized value");
255 nsCSSProperty property = (valueToAddWrapper ? valueToAddWrapper->mPropID :
256 destWrapper->mPropID);
257 // Special case: font-size-adjust and stroke-dasharray are explicitly
258 // non-additive (even though nsStyleAnimation *could* support adding them)
259 if (property == eCSSProperty_font_size_adjust ||
260 property == eCSSProperty_stroke_dasharray) {
261 return NS_ERROR_FAILURE;
264 const nsStyleAnimation::Value* valueToAdd = valueToAddWrapper ?
265 &valueToAddWrapper->mCSSValue : nsnull;
266 const nsStyleAnimation::Value* destValue = destWrapper ?
267 &destWrapper->mCSSValue : nsnull;
268 if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) {
269 return NS_ERROR_FAILURE;
271 // Did FinalizeStyleAnimationValues change destValue?
272 // If so, update outparam to use the new value.
273 if (destWrapper && &destWrapper->mCSSValue != destValue) {
274 destWrapper->mCSSValue = *destValue;
277 // Handle barely-initialized "zero" destination.
278 if (!destWrapper) {
279 aDest.mU.mPtr = destWrapper =
280 new ValueWrapper(property, *destValue, valueToAddWrapper->mPresContext);
283 return nsStyleAnimation::Add(property,
284 destWrapper->mCSSValue, *valueToAdd, aCount) ?
285 NS_OK : NS_ERROR_FAILURE;
288 nsresult
289 nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
290 const nsSMILValue& aTo,
291 double& aDistance) const
293 NS_ABORT_IF_FALSE(aFrom.mType == aTo.mType,
294 "Trying to compare different types");
295 NS_ABORT_IF_FALSE(aFrom.mType == this, "Unexpected source type");
297 const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom);
298 const ValueWrapper* toWrapper = ExtractValueWrapper(aTo);
299 NS_ABORT_IF_FALSE(toWrapper, "expecting non-null endpoint");
301 const nsStyleAnimation::Value* fromCSSValue = fromWrapper ?
302 &fromWrapper->mCSSValue : nsnull;
303 const nsStyleAnimation::Value* toCSSValue = &toWrapper->mCSSValue;
304 if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) {
305 return NS_ERROR_FAILURE;
308 return nsStyleAnimation::ComputeDistance(toWrapper->mPropID,
309 *fromCSSValue, *toCSSValue,
310 aDistance) ?
311 NS_OK : NS_ERROR_FAILURE;
314 nsresult
315 nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
316 const nsSMILValue& aEndVal,
317 double aUnitDistance,
318 nsSMILValue& aResult) const
320 NS_ABORT_IF_FALSE(aStartVal.mType == aEndVal.mType,
321 "Trying to interpolate different types");
322 NS_ABORT_IF_FALSE(aStartVal.mType == this,
323 "Unexpected types for interpolation");
324 NS_ABORT_IF_FALSE(aResult.mType == this, "Unexpected result type");
325 NS_ABORT_IF_FALSE(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
326 "unit distance value out of bounds");
327 NS_ABORT_IF_FALSE(!aResult.mU.mPtr, "expecting barely-initialized outparam");
329 const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
330 const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
331 NS_ABORT_IF_FALSE(endWrapper, "expecting non-null endpoint");
333 const nsStyleAnimation::Value* startCSSValue = startWrapper ?
334 &startWrapper->mCSSValue : nsnull;
335 const nsStyleAnimation::Value* endCSSValue = &endWrapper->mCSSValue;
336 if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) {
337 return NS_ERROR_FAILURE;
340 nsStyleAnimation::Value resultValue;
341 if (nsStyleAnimation::Interpolate(endWrapper->mPropID,
342 *startCSSValue, *endCSSValue,
343 aUnitDistance, resultValue)) {
344 aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue,
345 endWrapper->mPresContext);
346 return NS_OK;
348 return NS_ERROR_FAILURE;
351 // Helper function to extract presContext
352 static nsPresContext*
353 GetPresContextForElement(nsIContent* aElem)
355 nsIDocument* doc = aElem->GetCurrentDoc();
356 if (!doc) {
357 // This can happen if we process certain types of restyles mid-sample
358 // and remove anonymous animated content from the document as a result.
359 // See bug 534975.
360 return nsnull;
362 nsIPresShell* shell = doc->GetShell();
363 return shell ? shell->GetPresContext() : nsnull;
366 // Helper function to parse a string into a nsStyleAnimation::Value
367 static PRBool
368 ValueFromStringHelper(nsCSSProperty aPropID,
369 nsIContent* aTargetElement,
370 nsPresContext* aPresContext,
371 const nsAString& aString,
372 nsStyleAnimation::Value& aStyleAnimValue)
374 // If value is negative, we'll strip off the "-" so the CSS parser won't
375 // barf, and then manually make the parsed value negative.
376 // (This is a partial solution to let us accept some otherwise out-of-bounds
377 // CSS values. Bug 501188 will provide a more complete fix.)
378 PRBool isNegative = PR_FALSE;
379 PRUint32 subStringBegin = 0;
380 PRInt32 absValuePos = nsSMILParserUtils::CheckForNegativeNumber(aString);
381 if (absValuePos > 0) {
382 isNegative = PR_TRUE;
383 subStringBegin = (PRUint32)absValuePos; // Start parsing after '-' sign
385 nsDependentSubstring subString(aString, subStringBegin);
386 if (!nsStyleAnimation::ComputeValue(aPropID, aTargetElement, subString,
387 PR_TRUE, aStyleAnimValue)) {
388 return PR_FALSE;
390 if (isNegative) {
391 InvertSign(aStyleAnimValue);
394 if (aPropID == eCSSProperty_font_size) {
395 // Divide out text-zoom, since SVG is supposed to ignore it
396 NS_ABORT_IF_FALSE(aStyleAnimValue.GetUnit() ==
397 nsStyleAnimation::eUnit_Coord,
398 "'font-size' value with unexpected style unit");
399 aStyleAnimValue.SetCoordValue(aStyleAnimValue.GetCoordValue() /
400 aPresContext->TextZoom());
402 return PR_TRUE;
405 // static
406 void
407 nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID,
408 nsIContent* aTargetElement,
409 const nsAString& aString,
410 nsSMILValue& aValue)
412 // XXXbz aTargetElement should be an Element
413 NS_ABORT_IF_FALSE(aValue.IsNull(), "Outparam should be null-typed");
414 nsPresContext* presContext = GetPresContextForElement(aTargetElement);
415 if (!presContext) {
416 NS_WARNING("Not parsing animation value; unable to get PresContext");
417 return;
420 nsStyleAnimation::Value parsedValue;
421 if (ValueFromStringHelper(aPropID, aTargetElement, presContext,
422 aString, parsedValue)) {
423 sSingleton.Init(aValue);
424 aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue, presContext);
428 // static
429 PRBool
430 nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue,
431 nsAString& aString)
433 NS_ABORT_IF_FALSE(aValue.mType == &nsSMILCSSValueType::sSingleton,
434 "Unexpected SMIL value type");
435 const ValueWrapper* wrapper = ExtractValueWrapper(aValue);
436 return !wrapper ||
437 nsStyleAnimation::UncomputeValue(wrapper->mPropID, wrapper->mPresContext,
438 wrapper->mCSSValue, aString);