1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/smil/nsSMILCSSValueType.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,423 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* representation of a value for a SMIL-animated CSS property */ 1.10 + 1.11 +#include "nsSMILCSSValueType.h" 1.12 +#include "nsString.h" 1.13 +#include "nsStyleAnimation.h" 1.14 +#include "nsSMILParserUtils.h" 1.15 +#include "nsSMILValue.h" 1.16 +#include "nsCSSValue.h" 1.17 +#include "nsColor.h" 1.18 +#include "nsPresContext.h" 1.19 +#include "mozilla/dom/Element.h" 1.20 +#include "nsDebug.h" 1.21 +#include "nsStyleUtil.h" 1.22 +#include "nsIDocument.h" 1.23 + 1.24 +using namespace mozilla::dom; 1.25 + 1.26 +/*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton; 1.27 + 1.28 +struct ValueWrapper { 1.29 + ValueWrapper(nsCSSProperty aPropID, const nsStyleAnimation::Value& aValue) : 1.30 + mPropID(aPropID), mCSSValue(aValue) {} 1.31 + 1.32 + nsCSSProperty mPropID; 1.33 + nsStyleAnimation::Value mCSSValue; 1.34 +}; 1.35 + 1.36 +// Helper Methods 1.37 +// -------------- 1.38 +static const nsStyleAnimation::Value* 1.39 +GetZeroValueForUnit(nsStyleAnimation::Unit aUnit) 1.40 +{ 1.41 + static const nsStyleAnimation::Value 1.42 + sZeroCoord(0, nsStyleAnimation::Value::CoordConstructor); 1.43 + static const nsStyleAnimation::Value 1.44 + sZeroPercent(0.0f, nsStyleAnimation::Value::PercentConstructor); 1.45 + static const nsStyleAnimation::Value 1.46 + sZeroFloat(0.0f, nsStyleAnimation::Value::FloatConstructor); 1.47 + static const nsStyleAnimation::Value 1.48 + sZeroColor(NS_RGB(0,0,0), nsStyleAnimation::Value::ColorConstructor); 1.49 + 1.50 + NS_ABORT_IF_FALSE(aUnit != nsStyleAnimation::eUnit_Null, 1.51 + "Need non-null unit for a zero value"); 1.52 + switch (aUnit) { 1.53 + case nsStyleAnimation::eUnit_Coord: 1.54 + return &sZeroCoord; 1.55 + case nsStyleAnimation::eUnit_Percent: 1.56 + return &sZeroPercent; 1.57 + case nsStyleAnimation::eUnit_Float: 1.58 + return &sZeroFloat; 1.59 + case nsStyleAnimation::eUnit_Color: 1.60 + return &sZeroColor; 1.61 + default: 1.62 + return nullptr; 1.63 + } 1.64 +} 1.65 + 1.66 +// This method requires at least one of its arguments to be non-null. 1.67 +// 1.68 +// If one argument is null, this method updates it to point to "zero" 1.69 +// for the other argument's Unit (if applicable; otherwise, we return false). 1.70 +// 1.71 +// If neither argument is null, this method generally does nothing, though it 1.72 +// may apply a workaround for the special case where a 0 length-value is mixed 1.73 +// with a eUnit_Float value. (See comment below.) 1.74 +// 1.75 +// Returns true on success, or false. 1.76 +static const bool 1.77 +FinalizeStyleAnimationValues(const nsStyleAnimation::Value*& aValue1, 1.78 + const nsStyleAnimation::Value*& aValue2) 1.79 +{ 1.80 + NS_ABORT_IF_FALSE(aValue1 || aValue2, 1.81 + "expecting at least one non-null value"); 1.82 + 1.83 + // Are we missing either val? (If so, it's an implied 0 in other val's units) 1.84 + if (!aValue1) { 1.85 + aValue1 = GetZeroValueForUnit(aValue2->GetUnit()); 1.86 + return !!aValue1; // Fail if we have no zero value for this unit. 1.87 + } 1.88 + if (!aValue2) { 1.89 + aValue2 = GetZeroValueForUnit(aValue1->GetUnit()); 1.90 + return !!aValue2; // Fail if we have no zero value for this unit. 1.91 + } 1.92 + 1.93 + // Ok, both values were specified. 1.94 + // Need to handle a special-case, though: unitless nonzero length (parsed as 1.95 + // eUnit_Float) mixed with unitless 0 length (parsed as eUnit_Coord). These 1.96 + // won't interoperate in nsStyleAnimation, since their Units don't match. 1.97 + // In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value. 1.98 + const nsStyleAnimation::Value& zeroCoord = 1.99 + *GetZeroValueForUnit(nsStyleAnimation::eUnit_Coord); 1.100 + if (*aValue1 == zeroCoord && 1.101 + aValue2->GetUnit() == nsStyleAnimation::eUnit_Float) { 1.102 + aValue1 = GetZeroValueForUnit(nsStyleAnimation::eUnit_Float); 1.103 + } else if (*aValue2 == zeroCoord && 1.104 + aValue1->GetUnit() == nsStyleAnimation::eUnit_Float) { 1.105 + aValue2 = GetZeroValueForUnit(nsStyleAnimation::eUnit_Float); 1.106 + } 1.107 + 1.108 + return true; 1.109 +} 1.110 + 1.111 +static void 1.112 +InvertSign(nsStyleAnimation::Value& aValue) 1.113 +{ 1.114 + switch (aValue.GetUnit()) { 1.115 + case nsStyleAnimation::eUnit_Coord: 1.116 + aValue.SetCoordValue(-aValue.GetCoordValue()); 1.117 + break; 1.118 + case nsStyleAnimation::eUnit_Percent: 1.119 + aValue.SetPercentValue(-aValue.GetPercentValue()); 1.120 + break; 1.121 + case nsStyleAnimation::eUnit_Float: 1.122 + aValue.SetFloatValue(-aValue.GetFloatValue()); 1.123 + break; 1.124 + default: 1.125 + NS_NOTREACHED("Calling InvertSign with an unsupported unit"); 1.126 + break; 1.127 + } 1.128 +} 1.129 + 1.130 +static ValueWrapper* 1.131 +ExtractValueWrapper(nsSMILValue& aValue) 1.132 +{ 1.133 + return static_cast<ValueWrapper*>(aValue.mU.mPtr); 1.134 +} 1.135 + 1.136 +static const ValueWrapper* 1.137 +ExtractValueWrapper(const nsSMILValue& aValue) 1.138 +{ 1.139 + return static_cast<const ValueWrapper*>(aValue.mU.mPtr); 1.140 +} 1.141 + 1.142 +// Class methods 1.143 +// ------------- 1.144 +void 1.145 +nsSMILCSSValueType::Init(nsSMILValue& aValue) const 1.146 +{ 1.147 + NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected SMIL value type"); 1.148 + 1.149 + aValue.mU.mPtr = nullptr; 1.150 + aValue.mType = this; 1.151 +} 1.152 + 1.153 +void 1.154 +nsSMILCSSValueType::Destroy(nsSMILValue& aValue) const 1.155 +{ 1.156 + NS_ABORT_IF_FALSE(aValue.mType == this, "Unexpected SMIL value type"); 1.157 + delete static_cast<ValueWrapper*>(aValue.mU.mPtr); 1.158 + aValue.mType = nsSMILNullType::Singleton(); 1.159 +} 1.160 + 1.161 +nsresult 1.162 +nsSMILCSSValueType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const 1.163 +{ 1.164 + NS_ABORT_IF_FALSE(aDest.mType == aSrc.mType, "Incompatible SMIL types"); 1.165 + NS_ABORT_IF_FALSE(aDest.mType == this, "Unexpected SMIL value type"); 1.166 + const ValueWrapper* srcWrapper = ExtractValueWrapper(aSrc); 1.167 + ValueWrapper* destWrapper = ExtractValueWrapper(aDest); 1.168 + 1.169 + if (srcWrapper) { 1.170 + if (!destWrapper) { 1.171 + // barely-initialized dest -- need to alloc & copy 1.172 + aDest.mU.mPtr = new ValueWrapper(*srcWrapper); 1.173 + } else { 1.174 + // both already fully-initialized -- just copy straight across 1.175 + *destWrapper = *srcWrapper; 1.176 + } 1.177 + } else if (destWrapper) { 1.178 + // fully-initialized dest, barely-initialized src -- clear dest 1.179 + delete destWrapper; 1.180 + aDest.mU.mPtr = destWrapper = nullptr; 1.181 + } // else, both are barely-initialized -- nothing to do. 1.182 + 1.183 + return NS_OK; 1.184 +} 1.185 + 1.186 +bool 1.187 +nsSMILCSSValueType::IsEqual(const nsSMILValue& aLeft, 1.188 + const nsSMILValue& aRight) const 1.189 +{ 1.190 + NS_ABORT_IF_FALSE(aLeft.mType == aRight.mType, "Incompatible SMIL types"); 1.191 + NS_ABORT_IF_FALSE(aLeft.mType == this, "Unexpected SMIL value"); 1.192 + const ValueWrapper* leftWrapper = ExtractValueWrapper(aLeft); 1.193 + const ValueWrapper* rightWrapper = ExtractValueWrapper(aRight); 1.194 + 1.195 + if (leftWrapper) { 1.196 + if (rightWrapper) { 1.197 + // Both non-null 1.198 + NS_WARN_IF_FALSE(leftWrapper != rightWrapper, 1.199 + "Two nsSMILValues with matching ValueWrapper ptr"); 1.200 + return (leftWrapper->mPropID == rightWrapper->mPropID && 1.201 + leftWrapper->mCSSValue == rightWrapper->mCSSValue); 1.202 + } 1.203 + // Left non-null, right null 1.204 + return false; 1.205 + } 1.206 + if (rightWrapper) { 1.207 + // Left null, right non-null 1.208 + return false; 1.209 + } 1.210 + // Both null 1.211 + return true; 1.212 +} 1.213 + 1.214 +nsresult 1.215 +nsSMILCSSValueType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd, 1.216 + uint32_t aCount) const 1.217 +{ 1.218 + NS_ABORT_IF_FALSE(aValueToAdd.mType == aDest.mType, 1.219 + "Trying to add invalid types"); 1.220 + NS_ABORT_IF_FALSE(aValueToAdd.mType == this, "Unexpected source type"); 1.221 + 1.222 + ValueWrapper* destWrapper = ExtractValueWrapper(aDest); 1.223 + const ValueWrapper* valueToAddWrapper = ExtractValueWrapper(aValueToAdd); 1.224 + NS_ABORT_IF_FALSE(destWrapper || valueToAddWrapper, 1.225 + "need at least one fully-initialized value"); 1.226 + 1.227 + nsCSSProperty property = (valueToAddWrapper ? valueToAddWrapper->mPropID : 1.228 + destWrapper->mPropID); 1.229 + // Special case: font-size-adjust and stroke-dasharray are explicitly 1.230 + // non-additive (even though nsStyleAnimation *could* support adding them) 1.231 + if (property == eCSSProperty_font_size_adjust || 1.232 + property == eCSSProperty_stroke_dasharray) { 1.233 + return NS_ERROR_FAILURE; 1.234 + } 1.235 + 1.236 + const nsStyleAnimation::Value* valueToAdd = valueToAddWrapper ? 1.237 + &valueToAddWrapper->mCSSValue : nullptr; 1.238 + const nsStyleAnimation::Value* destValue = destWrapper ? 1.239 + &destWrapper->mCSSValue : nullptr; 1.240 + if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) { 1.241 + return NS_ERROR_FAILURE; 1.242 + } 1.243 + // Did FinalizeStyleAnimationValues change destValue? 1.244 + // If so, update outparam to use the new value. 1.245 + if (destWrapper && &destWrapper->mCSSValue != destValue) { 1.246 + destWrapper->mCSSValue = *destValue; 1.247 + } 1.248 + 1.249 + // Handle barely-initialized "zero" destination. 1.250 + if (!destWrapper) { 1.251 + aDest.mU.mPtr = destWrapper = 1.252 + new ValueWrapper(property, *destValue); 1.253 + } 1.254 + 1.255 + return nsStyleAnimation::Add(property, 1.256 + destWrapper->mCSSValue, *valueToAdd, aCount) ? 1.257 + NS_OK : NS_ERROR_FAILURE; 1.258 +} 1.259 + 1.260 +nsresult 1.261 +nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom, 1.262 + const nsSMILValue& aTo, 1.263 + double& aDistance) const 1.264 +{ 1.265 + NS_ABORT_IF_FALSE(aFrom.mType == aTo.mType, 1.266 + "Trying to compare different types"); 1.267 + NS_ABORT_IF_FALSE(aFrom.mType == this, "Unexpected source type"); 1.268 + 1.269 + const ValueWrapper* fromWrapper = ExtractValueWrapper(aFrom); 1.270 + const ValueWrapper* toWrapper = ExtractValueWrapper(aTo); 1.271 + NS_ABORT_IF_FALSE(toWrapper, "expecting non-null endpoint"); 1.272 + 1.273 + const nsStyleAnimation::Value* fromCSSValue = fromWrapper ? 1.274 + &fromWrapper->mCSSValue : nullptr; 1.275 + const nsStyleAnimation::Value* toCSSValue = &toWrapper->mCSSValue; 1.276 + if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) { 1.277 + return NS_ERROR_FAILURE; 1.278 + } 1.279 + 1.280 + return nsStyleAnimation::ComputeDistance(toWrapper->mPropID, 1.281 + *fromCSSValue, *toCSSValue, 1.282 + aDistance) ? 1.283 + NS_OK : NS_ERROR_FAILURE; 1.284 +} 1.285 + 1.286 +nsresult 1.287 +nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal, 1.288 + const nsSMILValue& aEndVal, 1.289 + double aUnitDistance, 1.290 + nsSMILValue& aResult) const 1.291 +{ 1.292 + NS_ABORT_IF_FALSE(aStartVal.mType == aEndVal.mType, 1.293 + "Trying to interpolate different types"); 1.294 + NS_ABORT_IF_FALSE(aStartVal.mType == this, 1.295 + "Unexpected types for interpolation"); 1.296 + NS_ABORT_IF_FALSE(aResult.mType == this, "Unexpected result type"); 1.297 + NS_ABORT_IF_FALSE(aUnitDistance >= 0.0 && aUnitDistance <= 1.0, 1.298 + "unit distance value out of bounds"); 1.299 + NS_ABORT_IF_FALSE(!aResult.mU.mPtr, "expecting barely-initialized outparam"); 1.300 + 1.301 + const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal); 1.302 + const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal); 1.303 + NS_ABORT_IF_FALSE(endWrapper, "expecting non-null endpoint"); 1.304 + 1.305 + const nsStyleAnimation::Value* startCSSValue = startWrapper ? 1.306 + &startWrapper->mCSSValue : nullptr; 1.307 + const nsStyleAnimation::Value* endCSSValue = &endWrapper->mCSSValue; 1.308 + if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) { 1.309 + return NS_ERROR_FAILURE; 1.310 + } 1.311 + 1.312 + nsStyleAnimation::Value resultValue; 1.313 + if (nsStyleAnimation::Interpolate(endWrapper->mPropID, 1.314 + *startCSSValue, *endCSSValue, 1.315 + aUnitDistance, resultValue)) { 1.316 + aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue); 1.317 + return NS_OK; 1.318 + } 1.319 + return NS_ERROR_FAILURE; 1.320 +} 1.321 + 1.322 +// Helper function to extract presContext 1.323 +static nsPresContext* 1.324 +GetPresContextForElement(Element* aElem) 1.325 +{ 1.326 + nsIDocument* doc = aElem->GetCurrentDoc(); 1.327 + if (!doc) { 1.328 + // This can happen if we process certain types of restyles mid-sample 1.329 + // and remove anonymous animated content from the document as a result. 1.330 + // See bug 534975. 1.331 + return nullptr; 1.332 + } 1.333 + nsIPresShell* shell = doc->GetShell(); 1.334 + return shell ? shell->GetPresContext() : nullptr; 1.335 +} 1.336 + 1.337 +// Helper function to parse a string into a nsStyleAnimation::Value 1.338 +static bool 1.339 +ValueFromStringHelper(nsCSSProperty aPropID, 1.340 + Element* aTargetElement, 1.341 + nsPresContext* aPresContext, 1.342 + const nsAString& aString, 1.343 + nsStyleAnimation::Value& aStyleAnimValue, 1.344 + bool* aIsContextSensitive) 1.345 +{ 1.346 + // If value is negative, we'll strip off the "-" so the CSS parser won't 1.347 + // barf, and then manually make the parsed value negative. 1.348 + // (This is a partial solution to let us accept some otherwise out-of-bounds 1.349 + // CSS values. Bug 501188 will provide a more complete fix.) 1.350 + bool isNegative = false; 1.351 + uint32_t subStringBegin = 0; 1.352 + 1.353 + // NOTE: We need to opt-out 'stroke-dasharray' from the negative-number 1.354 + // check. Its values might look negative (e.g. by starting with "-1"), but 1.355 + // they're more complicated than our simple negation logic here can handle. 1.356 + if (aPropID != eCSSProperty_stroke_dasharray) { 1.357 + int32_t absValuePos = nsSMILParserUtils::CheckForNegativeNumber(aString); 1.358 + if (absValuePos > 0) { 1.359 + isNegative = true; 1.360 + subStringBegin = (uint32_t)absValuePos; // Start parsing after '-' sign 1.361 + } 1.362 + } 1.363 + nsDependentSubstring subString(aString, subStringBegin); 1.364 + if (!nsStyleAnimation::ComputeValue(aPropID, aTargetElement, subString, 1.365 + true, aStyleAnimValue, 1.366 + aIsContextSensitive)) { 1.367 + return false; 1.368 + } 1.369 + if (isNegative) { 1.370 + InvertSign(aStyleAnimValue); 1.371 + } 1.372 + 1.373 + if (aPropID == eCSSProperty_font_size) { 1.374 + // Divide out text-zoom, since SVG is supposed to ignore it 1.375 + NS_ABORT_IF_FALSE(aStyleAnimValue.GetUnit() == 1.376 + nsStyleAnimation::eUnit_Coord, 1.377 + "'font-size' value with unexpected style unit"); 1.378 + aStyleAnimValue.SetCoordValue(aStyleAnimValue.GetCoordValue() / 1.379 + aPresContext->TextZoom()); 1.380 + } 1.381 + return true; 1.382 +} 1.383 + 1.384 +// static 1.385 +void 1.386 +nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID, 1.387 + Element* aTargetElement, 1.388 + const nsAString& aString, 1.389 + nsSMILValue& aValue, 1.390 + bool* aIsContextSensitive) 1.391 +{ 1.392 + NS_ABORT_IF_FALSE(aValue.IsNull(), "Outparam should be null-typed"); 1.393 + nsPresContext* presContext = GetPresContextForElement(aTargetElement); 1.394 + if (!presContext) { 1.395 + NS_WARNING("Not parsing animation value; unable to get PresContext"); 1.396 + return; 1.397 + } 1.398 + 1.399 + nsIDocument* doc = aTargetElement->GetCurrentDoc(); 1.400 + if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, 1.401 + doc->NodePrincipal(), 1.402 + doc->GetDocumentURI(), 1.403 + 0, aString, nullptr)) { 1.404 + return; 1.405 + } 1.406 + 1.407 + nsStyleAnimation::Value parsedValue; 1.408 + if (ValueFromStringHelper(aPropID, aTargetElement, presContext, 1.409 + aString, parsedValue, aIsContextSensitive)) { 1.410 + sSingleton.Init(aValue); 1.411 + aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue); 1.412 + } 1.413 +} 1.414 + 1.415 +// static 1.416 +bool 1.417 +nsSMILCSSValueType::ValueToString(const nsSMILValue& aValue, 1.418 + nsAString& aString) 1.419 +{ 1.420 + NS_ABORT_IF_FALSE(aValue.mType == &nsSMILCSSValueType::sSingleton, 1.421 + "Unexpected SMIL value type"); 1.422 + const ValueWrapper* wrapper = ExtractValueWrapper(aValue); 1.423 + return !wrapper || 1.424 + nsStyleAnimation::UncomputeValue(wrapper->mPropID, 1.425 + wrapper->mCSSValue, aString); 1.426 +}