nsresult nsSVGTransformSMILAttr::ValueFromString(const nsAString& aStr, const nsISMILAnimationElement* aSrcElement, nsSMILValue& aValue, PRBool& aPreventCachingOfSandwich) const { NS_ENSURE_TRUE(aSrcElement, NS_ERROR_FAILURE); NS_ASSERTION(aValue.IsNull(), "aValue should have been cleared before calling ValueFromString"); const nsAttrValue* typeAttr = aSrcElement->GetAnimAttr(nsGkAtoms::type); const nsIAtom* transformType = nsGkAtoms::translate; if (typeAttr) { if (typeAttr->Type() != nsAttrValue::eAtom) { // Recognized values of |type| are parsed as an atom -- so if we have // something other than an atom, then it means our |type| was invalid. return NS_ERROR_FAILURE; } transformType = typeAttr->GetAtomValue(); } ParseValue(aStr, transformType, aValue); aPreventCachingOfSandwich = PR_FALSE; return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK; }
nsresult nsSVGAnimatedTransformList::SMILAnimatedTransformList::ValueFromString( const nsAString& aStr, const dom::SVGAnimationElement* aSrcElement, nsSMILValue& aValue, bool& aPreventCachingOfSandwich) const { NS_ENSURE_TRUE(aSrcElement, NS_ERROR_FAILURE); MOZ_ASSERT(aValue.IsNull(), "aValue should have been cleared before calling ValueFromString"); const nsAttrValue* typeAttr = aSrcElement->GetAnimAttr(nsGkAtoms::type); const nsIAtom* transformType = nsGkAtoms::translate; // default val if (typeAttr) { if (typeAttr->Type() != nsAttrValue::eAtom) { // Recognized values of |type| are parsed as an atom -- so if we have // something other than an atom, then we know already our |type| is // invalid. return NS_ERROR_FAILURE; } transformType = typeAttr->GetAtomValue(); } ParseValue(aStr, transformType, aValue); aPreventCachingOfSandwich = false; return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK; }
void nsSMILFloatType::Init(nsSMILValue& aValue) const { NS_PRECONDITION(aValue.IsNull(), "Unexpected value type"); aValue.mU.mDouble = 0.0; aValue.mType = this; }
void SMILEnumType::Init(nsSMILValue& aValue) const { NS_PRECONDITION(aValue.IsNull(), "Unexpected value type"); aValue.mU.mUint = 0; aValue.mType = this; }
void SMILStringType::Init(nsSMILValue& aValue) const { NS_PRECONDITION(aValue.IsNull(), "Unexpected value type"); aValue.mU.mPtr = new nsString(); aValue.mType = this; }
void SMILBoolType::Init(nsSMILValue& aValue) const { NS_PRECONDITION(aValue.IsNull(), "Unexpected value type"); aValue.mU.mBool = false; aValue.mType = this; }
// static void nsSMILCSSValueType::ValueFromString(nsCSSPropertyID aPropID, Element* aTargetElement, const nsAString& aString, nsSMILValue& aValue, bool* aIsContextSensitive) { MOZ_ASSERT(aValue.IsNull(), "Outparam should be null-typed"); nsPresContext* presContext = GetPresContextForElement(aTargetElement); if (!presContext) { NS_WARNING("Not parsing animation value; unable to get PresContext"); return; } nsIDocument* doc = aTargetElement->GetUncomposedDoc(); if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, doc->NodePrincipal(), doc->GetDocumentURI(), 0, aString, nullptr)) { return; } StyleAnimationValue parsedValue; if (ValueFromStringHelper(aPropID, aTargetElement, presContext, aString, parsedValue, aIsContextSensitive)) { sSingleton.Init(aValue); aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue); } }
void SMILIntegerType::Init(nsSMILValue& aValue) const { NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type"); aValue.mU.mInt = 0; aValue.mType = this; }
void SMILIntegerType::Init(nsSMILValue& aValue) const { MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); aValue.mU.mInt = 0; aValue.mType = this; }
// Class methods // ------------- void nsSMILCSSValueType::Init(nsSMILValue& aValue) const { MOZ_ASSERT(aValue.IsNull(), "Unexpected SMIL value type"); aValue.mU.mPtr = nullptr; aValue.mType = this; }
// Class methods // ------------- void nsSMILCSSValueType::Init(nsSMILValue& aValue) const { NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected SMIL value type"); aValue.mU.mPtr = nsnull; aValue.mType = this; }
void SVGMotionSMILType::Init(nsSMILValue& aValue) const { NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected SMIL type"); aValue.mType = this; aValue.mU.mPtr = new MotionSegmentArray(1); }
void SVGMotionSMILType::Init(nsSMILValue& aValue) const { MOZ_ASSERT(aValue.IsNull(), "Unexpected SMIL type"); aValue.mType = this; aValue.mU.mPtr = new MotionSegmentArray(1); }
void nsSVGAnimatedTransformList::SMILAnimatedTransformList::ParseValue( const nsAString& aSpec, const nsIAtom* aTransformType, nsSMILValue& aResult) { MOZ_ASSERT(aResult.IsNull(), "Unexpected type for SMIL value"); static_assert(SVGTransformSMILData::NUM_SIMPLE_PARAMS == 3, "nsSVGSMILTransform constructor should be expecting array " "with 3 params"); float params[3] = { 0.f }; int32_t numParsed = ParseParameterList(aSpec, params, 3); uint16_t transformType; if (aTransformType == nsGkAtoms::translate) { // tx [ty=0] if (numParsed != 1 && numParsed != 2) return; transformType = SVG_TRANSFORM_TRANSLATE; } else if (aTransformType == nsGkAtoms::scale) { // sx [sy=sx] if (numParsed != 1 && numParsed != 2) return; if (numParsed == 1) { params[1] = params[0]; } transformType = SVG_TRANSFORM_SCALE; } else if (aTransformType == nsGkAtoms::rotate) { // r [cx=0 cy=0] if (numParsed != 1 && numParsed != 3) return; transformType = SVG_TRANSFORM_ROTATE; } else if (aTransformType == nsGkAtoms::skewX) { // x-angle if (numParsed != 1) return; transformType = SVG_TRANSFORM_SKEWX; } else if (aTransformType == nsGkAtoms::skewY) { // y-angle if (numParsed != 1) return; transformType = SVG_TRANSFORM_SKEWY; } else { return; } nsSMILValue val(SVGTransformListSMILType::Singleton()); SVGTransformSMILData transform(transformType, params); if (NS_FAILED(SVGTransformListSMILType::AppendTransform(transform, val))) { return; // OOM } // Success! Populate our outparam with parsed value. aResult.Swap(val); }
void SVGNumberPairSMILType::Init(nsSMILValue& aValue) const { NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type"); aValue.mU.mNumberPair[0] = 0; aValue.mU.mNumberPair[1] = 0; aValue.mType = this; }
void SVGViewBoxSMILType::Init(nsSMILValue& aValue) const { NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type"); nsSVGViewBoxRect* viewBox = new nsSVGViewBoxRect(); aValue.mU.mPtr = viewBox; aValue.mType = this; }
nsresult nsSMILFloatType::Init(nsSMILValue& aValue) const { NS_PRECONDITION(aValue.mType == this || aValue.IsNull(), "Unexpected value type"); aValue.mU.mDouble = 0.0; aValue.mType = this; return NS_OK; }
void SVGTransformListSMILType::Init(nsSMILValue &aValue) const { NS_PRECONDITION(aValue.IsNull(), "Unexpected value type"); TransformArray* transforms = new TransformArray(1); aValue.mU.mPtr = transforms; aValue.mType = this; }
void SVGOrientSMILType::Init(nsSMILValue& aValue) const { NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type"); aValue.mU.mOrient.mAngle = 0.0f; aValue.mU.mOrient.mUnit = SVG_ANGLETYPE_UNSPECIFIED; aValue.mU.mOrient.mOrientType = dom::SVG_MARKER_ORIENT_ANGLE; aValue.mType = this; }
nsresult nsSVGTransformSMILAttr::ValueFromString(const nsAString& aStr, const nsISMILAnimationElement* aSrcElement, nsSMILValue& aValue, PRBool& aCanCache) const { NS_ENSURE_TRUE(aSrcElement, NS_ERROR_FAILURE); NS_ASSERTION(aValue.IsNull(), "aValue should have been cleared before calling ValueFromString"); const nsAttrValue* typeAttr = aSrcElement->GetAnimAttr(nsGkAtoms::type); const nsIAtom* transformType = typeAttr ? typeAttr->GetAtomValue() : nsGkAtoms::translate; ParseValue(aStr, transformType, aValue); aCanCache = PR_TRUE; return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK; }
void SVGPointListSMILType::Init(nsSMILValue &aValue) const { NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type"); SVGPointListAndInfo* pointList = new SVGPointListAndInfo(); aValue.mU.mPtr = pointList; aValue.mType = this; }
void nsSVGTransformSMILAttr::ParseValue(const nsAString& aSpec, const nsIAtom* aTransformType, nsSMILValue& aResult) { NS_ASSERTION(aResult.IsNull(), "Unexpected type for SMIL value"); // nsSVGSMILTransform constructor should be expecting array with 3 params PR_STATIC_ASSERT(nsSVGSMILTransform::NUM_SIMPLE_PARAMS == 3); float params[3] = { 0.f }; PRInt32 numParsed = ParseParameterList(aSpec, params, 3); nsSVGSMILTransform::TransformType transformType; if (aTransformType == nsGkAtoms::translate) { // tx [ty=0] if (numParsed != 1 && numParsed != 2) return; transformType = nsSVGSMILTransform::TRANSFORM_TRANSLATE; } else if (aTransformType == nsGkAtoms::scale) { // sx [sy=sx] if (numParsed != 1 && numParsed != 2) return; if (numParsed == 1) { params[1] = params[0]; } transformType = nsSVGSMILTransform::TRANSFORM_SCALE; } else if (aTransformType == nsGkAtoms::rotate) { // r [cx=0 cy=0] if (numParsed != 1 && numParsed != 3) return; transformType = nsSVGSMILTransform::TRANSFORM_ROTATE; } else if (aTransformType == nsGkAtoms::skewX) { // x-angle if (numParsed != 1) return; transformType = nsSVGSMILTransform::TRANSFORM_SKEWX; } else if (aTransformType == nsGkAtoms::skewY) { // y-angle if (numParsed != 1) return; transformType = nsSVGSMILTransform::TRANSFORM_SKEWY; } else { return; } nsSMILValue val(&nsSVGTransformSMILType::sSingleton); nsSVGSMILTransform transform(transformType, params); if (NS_FAILED(nsSVGTransformSMILType::AppendTransform(transform, val))) { return; } // Success! Initialize our outparam with parsed value. aResult = val; }
// static void nsSMILCSSValueType::ValueFromString(nsCSSPropertyID aPropID, Element* aTargetElement, const nsAString& aString, nsSMILValue& aValue, bool* aIsContextSensitive) { MOZ_ASSERT(aValue.IsNull(), "Outparam should be null-typed"); nsPresContext* presContext = GetPresContextForElement(aTargetElement); if (!presContext) { NS_WARNING("Not parsing animation value; unable to get PresContext"); return; } nsIDocument* doc = aTargetElement->GetUncomposedDoc(); if (doc && !nsStyleUtil::CSPAllowsInlineStyle(nullptr, doc->NodePrincipal(), doc->GetDocumentURI(), 0, aString, nullptr)) { return; } RefPtr<nsStyleContext> styleContext = nsComputedDOMStyle::GetStyleContext(aTargetElement, nullptr, presContext->PresShell()); if (!styleContext) { return; } if (styleContext->IsServo()) { ServoAnimationValues parsedValues = ValueFromStringHelper(aPropID, aTargetElement, presContext, styleContext, aString); if (aIsContextSensitive) { // FIXME: Bug 1358955 - detect context-sensitive values and set this value // appropriately. *aIsContextSensitive = false; } if (!parsedValues.IsEmpty()) { sSingleton.Init(aValue); aValue.mU.mPtr = new ValueWrapper(aPropID, Move(parsedValues)); } return; } StyleAnimationValue parsedValue; if (ValueFromStringHelper(aPropID, aTargetElement, presContext, styleContext->AsGecko(), aString, parsedValue, aIsContextSensitive)) { sSingleton.Init(aValue); aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue); } }
nsresult nsSMILMappedAttribute::ValueFromString(const nsAString& aStr, const mozilla::dom::SVGAnimationElement* aSrcElement, nsSMILValue& aValue, bool& aPreventCachingOfSandwich) const { NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE); nsSMILCSSValueType::ValueFromString(mPropID, mElement, aStr, aValue, &aPreventCachingOfSandwich); return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK; }
void SVGLengthListSMILType::Init(nsSMILValue &aValue) const { NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type"); SVGLengthListAndInfo* lengthList = new SVGLengthListAndInfo(); // See the comment documenting Init() in our header file: lengthList->SetCanZeroPadList(true); aValue.mU.mPtr = lengthList; aValue.mType = this; }
nsresult nsSMILMappedAttribute::ValueFromString(const nsAString& aStr, const nsISMILAnimationElement* aSrcElement, nsSMILValue& aValue, PRBool& aPreventCachingOfSandwich) const { NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE); nsSMILCSSValueType::ValueFromString(mPropID, mElement, aStr, aValue); if (aValue.IsNull()) { return NS_ERROR_FAILURE; } // XXXdholbert: For simplicity, just assume that all CSS values have to // reparsed every sample. See note in nsSMILCSSProperty::ValueFromString. aPreventCachingOfSandwich = PR_TRUE; return NS_OK; }
nsresult nsSMILCSSProperty::ValueFromString(const nsAString& aStr, const nsISMILAnimationElement* aSrcElement, nsSMILValue& aValue, PRBool& aPreventCachingOfSandwich) const { NS_ENSURE_TRUE(IsPropertyAnimatable(mPropID), NS_ERROR_FAILURE); nsSMILCSSValueType::ValueFromString(mPropID, mElement, aStr, aValue, &aPreventCachingOfSandwich); // XXX Due to bug 536660 (or at least that seems to be the most likely // culprit), when we have animation setting display:none on a <use> element, // if we DON'T set the property every sample, chaos ensues. if (!aPreventCachingOfSandwich && mPropID == eCSSProperty_display) { aPreventCachingOfSandwich = PR_TRUE; } return aValue.IsNull() ? NS_ERROR_FAILURE : NS_OK; }
// static void nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID, nsIContent* aTargetElement, const nsAString& aString, nsSMILValue& aValue) { // XXXbz aTargetElement should be an Element NS_ABORT_IF_FALSE(aValue.IsNull(), "Outparam should be null-typed"); nsPresContext* presContext = GetPresContextForElement(aTargetElement); if (!presContext) { NS_WARNING("Not parsing animation value; unable to get PresContext"); return; } nsStyleAnimation::Value parsedValue; if (ValueFromStringHelper(aPropID, aTargetElement, presContext, aString, parsedValue)) { sSingleton.Init(aValue); aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue, presContext); } }
nsresult nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues, nsSMILValue& aResult, nsSMILValue& aBaseValue) { // Sanity check animation values if ((!IsToAnimation() && aValues.Length() < 2) || (IsToAnimation() && aValues.Length() != 1)) { NS_ERROR("Unexpected number of values"); return NS_ERROR_FAILURE; } if (IsToAnimation() && aBaseValue.IsNull()) { return NS_ERROR_FAILURE; } // Get the normalised progress through the simple duration. // // If we have an indefinite simple duration, just set the progress to be // 0 which will give us the expected behaviour of the animation being fixed at // its starting point. double simpleProgress = 0.0; if (mSimpleDuration.IsDefinite()) { nsSMILTime dur = mSimpleDuration.GetMillis(); MOZ_ASSERT(dur >= 0, "Simple duration should not be negative"); MOZ_ASSERT(mSampleTime >= 0, "Sample time should not be negative"); if (mSampleTime >= dur || mSampleTime < 0) { NS_ERROR("Animation sampled outside interval"); return NS_ERROR_FAILURE; } if (dur > 0) { simpleProgress = (double)mSampleTime / dur; } // else leave simpleProgress at 0.0 (e.g. if mSampleTime == dur == 0) } nsresult rv = NS_OK; nsSMILCalcMode calcMode = GetCalcMode(); // Force discrete calcMode for visibility since StyleAnimationValue will // try to interpolate it using the special clamping behavior defined for // CSS. if (nsSMILCSSValueType::PropertyFromValue(aValues[0]) == eCSSProperty_visibility) { calcMode = CALC_DISCRETE; } if (calcMode != CALC_DISCRETE) { // Get the normalised progress between adjacent values const nsSMILValue* from = nullptr; const nsSMILValue* to = nullptr; // Init to -1 to make sure that if we ever forget to set this, the // MOZ_ASSERT that tests that intervalProgress is in range will fail. double intervalProgress = -1.f; if (IsToAnimation()) { from = &aBaseValue; to = &aValues[0]; if (calcMode == CALC_PACED) { // Note: key[Times/Splines/Points] are ignored for calcMode="paced" intervalProgress = simpleProgress; } else { double scaledSimpleProgress = ScaleSimpleProgress(simpleProgress, calcMode); intervalProgress = ScaleIntervalProgress(scaledSimpleProgress, 0); } } else if (calcMode == CALC_PACED) { rv = ComputePacedPosition(aValues, simpleProgress, intervalProgress, from, to); // Note: If the above call fails, we'll skip the "from->Interpolate" // call below, and we'll drop into the CALC_DISCRETE section // instead. (as the spec says we should, because our failure was // presumably due to the values being non-additive) } else { // calcMode == CALC_LINEAR or calcMode == CALC_SPLINE double scaledSimpleProgress = ScaleSimpleProgress(simpleProgress, calcMode); uint32_t index = (uint32_t)floor(scaledSimpleProgress * (aValues.Length() - 1)); from = &aValues[index]; to = &aValues[index + 1]; intervalProgress = scaledSimpleProgress * (aValues.Length() - 1) - index; intervalProgress = ScaleIntervalProgress(intervalProgress, index); } if (NS_SUCCEEDED(rv)) { MOZ_ASSERT(from, "NULL from-value during interpolation"); MOZ_ASSERT(to, "NULL to-value during interpolation"); MOZ_ASSERT(0.0f <= intervalProgress && intervalProgress < 1.0f, "Interval progress should be in the range [0, 1)"); rv = from->Interpolate(*to, intervalProgress, aResult); } } // Discrete-CalcMode case // Note: If interpolation failed (isn't supported for this type), the SVG // spec says to force discrete mode. if (calcMode == CALC_DISCRETE || NS_FAILED(rv)) { double scaledSimpleProgress = ScaleSimpleProgress(simpleProgress, CALC_DISCRETE); // Floating-point errors can mean that, for example, a sample time of 29s in // a 100s duration animation gives us a simple progress of 0.28999999999 // instead of the 0.29 we'd expect. Normally this isn't a noticeable // problem, but when we have sudden jumps in animation values (such as is // the case here with discrete animation) we can get unexpected results. // // To counteract this, before we perform a floor() on the animation // progress, we add a tiny fudge factor to push us into the correct interval // in cases where floating-point errors might cause us to fall short. static const double kFloatingPointFudgeFactor = 1.0e-16; if (scaledSimpleProgress + kFloatingPointFudgeFactor <= 1.0) { scaledSimpleProgress += kFloatingPointFudgeFactor; } if (IsToAnimation()) { // We don't follow SMIL 3, 12.6.4, where discrete to animations // are the same as <set> animations. Instead, we treat it as a // discrete animation with two values (the underlying value and // the to="" value), and honor keyTimes="" as well. uint32_t index = (uint32_t)floor(scaledSimpleProgress * 2); aResult = index == 0 ? aBaseValue : aValues[0]; } else { uint32_t index = (uint32_t)floor(scaledSimpleProgress * aValues.Length()); aResult = aValues[index]; } rv = NS_OK; } return rv; }
void nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr, nsSMILValue& aResult) { mHasChanged = false; mPrevSampleWasSingleValueAnimation = false; mWasSkippedInPrevSample = false; // Skip animations that are inactive or in error if (!IsActiveOrFrozen() || mErrorFlags != 0) return; // Get the animation values nsSMILValueArray values; nsresult rv = GetValues(aSMILAttr, values); if (NS_FAILED(rv)) return; // Check that we have the right number of keySplines and keyTimes CheckValueListDependentAttrs(values.Length()); if (mErrorFlags != 0) return; // If this interval is active, we must have a non-negative mSampleTime MOZ_ASSERT(mSampleTime >= 0 || !mIsActive, "Negative sample time for active animation"); MOZ_ASSERT(mSimpleDuration.IsResolved() || mLastValue, "Unresolved simple duration for active or frozen animation"); // If we want to add but don't have a base value then just fail outright. // This can happen when we skipped getting the base value because there's an // animation function in the sandwich that should replace it but that function // failed unexpectedly. bool isAdditive = IsAdditive(); if (isAdditive && aResult.IsNull()) return; nsSMILValue result; if (values.Length() == 1 && !IsToAnimation()) { // Single-valued animation result = values[0]; mPrevSampleWasSingleValueAnimation = true; } else if (mLastValue) { // Sampling last value const nsSMILValue& last = values[values.Length() - 1]; result = last; // See comment in AccumulateResult: to-animation does not accumulate if (!IsToAnimation() && GetAccumulate() && mRepeatIteration) { // If the target attribute type doesn't support addition Add will // fail leaving result = last result.Add(last, mRepeatIteration); } } else { // Interpolation if (NS_FAILED(InterpolateResult(values, result, aResult))) return; if (NS_FAILED(AccumulateResult(values, result))) return; } // If additive animation isn't required or isn't supported, set the value. if (!isAdditive || NS_FAILED(aResult.SandwichAdd(result))) { aResult = Move(result); } }