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
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.
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"
42 #include "nsStyleAnimation.h"
43 #include "nsSMILParserUtils.h"
44 #include "nsSMILValue.h"
45 #include "nsCSSValue.h"
47 #include "nsPresContext.h"
48 #include "nsIContent.h"
51 /*static*/ nsSMILCSSValueType
nsSMILCSSValueType::sSingleton
;
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
);
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");
82 case nsStyleAnimation::eUnit_Coord
:
84 case nsStyleAnimation::eUnit_Percent
:
86 case nsStyleAnimation::eUnit_Float
:
88 case nsStyleAnimation::eUnit_Color
:
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.
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)
114 aValue1
= GetZeroValueForUnit(aValue2
->GetUnit());
115 return !!aValue1
; // Fail if we have no zero value for this unit.
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
;
139 InvertSign(nsStyleAnimation::Value
& aValue
)
141 switch (aValue
.GetUnit()) {
142 case nsStyleAnimation::eUnit_Coord
:
143 aValue
.SetCoordValue(-aValue
.GetCoordValue());
145 case nsStyleAnimation::eUnit_Percent
:
146 aValue
.SetPercentValue(-aValue
.GetPercentValue());
148 case nsStyleAnimation::eUnit_Float
:
149 aValue
.SetFloatValue(-aValue
.GetFloatValue());
152 NS_NOTREACHED("Calling InvertSign with an unsupported unit");
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
);
172 nsSMILCSSValueType::Init(nsSMILValue
& aValue
) const
174 NS_ABORT_IF_FALSE(aValue
.IsNull(), "Unexpected SMIL value type");
176 aValue
.mU
.mPtr
= nsnull
;
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
;
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
);
198 // barely-initialized dest -- need to alloc & copy
199 aDest
.mU
.mPtr
= new ValueWrapper(*srcWrapper
);
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
207 aDest
.mU
.mPtr
= destWrapper
= nsnull
;
208 } // else, both are barely-initialized -- nothing to do.
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
);
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
235 // Left null, right non-null
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.
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
;
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
,
311 NS_OK
: NS_ERROR_FAILURE
;
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
);
348 return NS_ERROR_FAILURE
;
351 // Helper function to extract presContext
352 static nsPresContext
*
353 GetPresContextForElement(nsIContent
* aElem
)
355 nsIDocument
* doc
= aElem
->GetCurrentDoc();
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.
362 nsIPresShell
* shell
= doc
->GetShell();
363 return shell
? shell
->GetPresContext() : nsnull
;
366 // Helper function to parse a string into a nsStyleAnimation::Value
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
)) {
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());
407 nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID
,
408 nsIContent
* aTargetElement
,
409 const nsAString
& aString
,
412 // XXXbz aTargetElement should be an Element
413 NS_ABORT_IF_FALSE(aValue
.IsNull(), "Outparam should be null-typed");
414 nsPresContext
* presContext
= GetPresContextForElement(aTargetElement
);
416 NS_WARNING("Not parsing animation value; unable to get PresContext");
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
);
430 nsSMILCSSValueType::ValueToString(const nsSMILValue
& aValue
,
433 NS_ABORT_IF_FALSE(aValue
.mType
== &nsSMILCSSValueType::sSingleton
,
434 "Unexpected SMIL value type");
435 const ValueWrapper
* wrapper
= ExtractValueWrapper(aValue
);
437 nsStyleAnimation::UncomputeValue(wrapper
->mPropID
, wrapper
->mPresContext
,
438 wrapper
->mCSSValue
, aString
);