diff --git a/layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg b/layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg new file mode 100644 index 00000000000..44e380bc026 --- /dev/null +++ b/layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg b/layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg new file mode 100644 index 00000000000..71a6aa89cf5 --- /dev/null +++ b/layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/smil/reftest.list b/layout/reftests/svg/smil/reftest.list index a029ca4d4b8..9e713e93bb4 100644 --- a/layout/reftests/svg/smil/reftest.list +++ b/layout/reftests/svg/smil/reftest.list @@ -226,6 +226,7 @@ random == anim-text-x-y-dx-dy-01.svg anim-text-x-y-dx-dy-01-ref.svg # bug 579588 == anim-pattern-attr-presence-01.svg anim-pattern-attr-presence-01-ref.svg fails == anim-pattern-attr-presence-02.svg anim-pattern-attr-presence-02-ref.svg # ^ bug 621651 +== anim-gradient-attr-presence-01.svg anim-gradient-attr-presence-01-ref.svg == api-sanity-1.svg lime.svg diff --git a/layout/svg/base/src/nsSVGGradientFrame.cpp b/layout/svg/base/src/nsSVGGradientFrame.cpp index 9c0e49cd215..8a1b7c2bb53 100644 --- a/layout/svg/base/src/nsSVGGradientFrame.cpp +++ b/layout/svg/base/src/nsSVGGradientFrame.cpp @@ -51,6 +51,27 @@ using mozilla::SVGAnimatedTransformList; +//---------------------------------------------------------------------- +// Helper classes + +class nsSVGGradientFrame::AutoGradientReferencer +{ +public: + AutoGradientReferencer(nsSVGGradientFrame *aFrame) + : mFrame(aFrame) + { + // Reference loops should normally be detected in advance and handled, so + // we're not expecting to encounter them here + NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); + mFrame->mLoopFlag = true; + } + ~AutoGradientReferencer() { + mFrame->mLoopFlag = false; + } +private: + nsSVGGradientFrame *mFrame; +}; + //---------------------------------------------------------------------- // Implementation @@ -134,6 +155,40 @@ nsSVGGradientFrame::GetStopInformation(PRInt32 aIndex, *aStopOpacity = stopFrame->GetStyleSVGReset()->mStopOpacity; } +PRUint16 +nsSVGGradientFrame::GetEnumValue(PRUint32 aIndex, nsIContent *aDefault) +{ + const nsSVGEnum& thisEnum = + static_cast(mContent)->mEnumAttributes[aIndex]; + + if (thisEnum.IsExplicitlySet()) + return thisEnum.GetAnimValue(); + + AutoGradientReferencer gradientRef(this); + + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); + return next ? next->GetEnumValue(aIndex, aDefault) : + static_cast(aDefault)-> + mEnumAttributes[aIndex].GetAnimValue(); +} + +const SVGAnimatedTransformList* +nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault) +{ + SVGAnimatedTransformList *thisTransformList = + static_cast(mContent)->GetAnimatedTransformList(); + + if (thisTransformList->IsExplicitlySet()) + return thisTransformList; + + AutoGradientReferencer gradientRef(this); + + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); + return next ? next->GetGradientTransformList(aDefault) : + static_cast(aDefault) + ->mGradientTransform.get(); +} + gfxMatrix nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, const gfxRect *aOverrideBounds) @@ -150,19 +205,19 @@ nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, else mSource = aSource; } else { - NS_ASSERTION(gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, - "Unknown gradientUnits type"); + NS_ASSERTION( + gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); // objectBoundingBox is the default anyway - gfxRect bbox = aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource); - bboxMatrix = gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); + gfxRect bbox = + aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource); + bboxMatrix = + gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); } - nsSVGGradientElement *element = - GetGradientWithAttr(nsGkAtoms::gradientTransform, mContent); - - SVGAnimatedTransformList* animTransformList = - element->GetAnimatedTransformList(); + const SVGAnimatedTransformList* animTransformList = + GetGradientTransformList(mContent); if (!animTransformList) return bboxMatrix; @@ -171,13 +226,32 @@ nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, return bboxMatrix.PreMultiply(gradientTransform); } -PRUint16 -nsSVGGradientFrame::GetSpreadMethod() +nsSVGLinearGradientElement * +nsSVGGradientFrame::GetLinearGradientWithLength(PRUint32 aIndex, + nsSVGLinearGradientElement* aDefault) { - nsSVGGradientElement *element = - GetGradientWithAttr(nsGkAtoms::spreadMethod, mContent); + // If this was a linear gradient with the required length, we would have + // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength. + // Since we didn't find the length, continue looking down the chain. - return element->mEnumAttributes[nsSVGGradientElement::SPREADMETHOD].GetAnimValue(); + AutoGradientReferencer gradientRef(this); + + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); + return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault; +} + +nsSVGRadialGradientElement * +nsSVGGradientFrame::GetRadialGradientWithLength(PRUint32 aIndex, + nsSVGRadialGradientElement* aDefault) +{ + // If this was a radial gradient with the required length, we would have + // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength. + // Since we didn't find the length, continue looking down the chain. + + AutoGradientReferencer gradientRef(this); + + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); + return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault; } //---------------------------------------------------------------------- @@ -289,53 +363,20 @@ nsSVGGradientFrame::GetReferencedGradient() return static_cast(result); } -nsSVGGradientElement * -nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault) +nsSVGGradientFrame * +nsSVGGradientFrame::GetReferencedGradientIfNotInUse() { - if (mContent->HasAttr(kNameSpaceID_None, aAttrName)) - return static_cast(mContent); + nsSVGGradientFrame *referenced = GetReferencedGradient(); + if (!referenced) + return nsnull; - nsSVGGradientElement *grad = static_cast(aDefault); + if (referenced->mLoopFlag) { + // XXXjwatt: we should really send an error to the JavaScript Console here: + NS_WARNING("gradient reference loop detected while inheriting attribute!"); + return nsnull; + } - nsSVGGradientFrame *next = GetReferencedGradient(); - if (!next) - return grad; - - // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad - mLoopFlag = true; - // XXXjwatt: we should really send an error to the JavaScript Console here: - NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected " - "while inheriting attribute!"); - if (!next->mLoopFlag) - grad = next->GetGradientWithAttr(aAttrName, aDefault); - mLoopFlag = false; - - return grad; -} - -nsSVGGradientElement * -nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType, - nsIContent *aDefault) -{ - if (GetType() == aGradType && mContent->HasAttr(kNameSpaceID_None, aAttrName)) - return static_cast(mContent); - - nsSVGGradientElement *grad = static_cast(aDefault); - - nsSVGGradientFrame *next = GetReferencedGradient(); - if (!next) - return grad; - - // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad - mLoopFlag = true; - // XXXjwatt: we should really send an error to the JavaScript Console here: - NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected " - "while inheriting attribute!"); - if (!next->mLoopFlag) - grad = next->GetGradientWithAttr(aAttrName, aGradType, aDefault); - mLoopFlag = false; - - return grad; + return referenced; } PRInt32 @@ -359,33 +400,12 @@ nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame) // Our gradient element doesn't have stops - try to "inherit" them - nsSVGGradientFrame *next = GetReferencedGradient(); - if (!next) { - if (aStopFrame) - *aStopFrame = nsnull; - return 0; - } + AutoGradientReferencer gradientRef(this); + nsSVGGradientFrame* next = GetReferencedGradientIfNotInUse(); + if (!next) + return nsnull; - // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad - mLoopFlag = true; - // XXXjwatt: we should really send an error to the JavaScript Console here: - NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected " - "while inheriting stop!"); - if (!next->mLoopFlag) - stopCount = next->GetStopFrame(aIndex, aStopFrame); - mLoopFlag = false; - - return stopCount; -} - -PRUint16 -nsSVGGradientFrame::GetGradientUnits() -{ - // This getter is called every time the others are called - maybe cache it? - - nsSVGGradientElement *element = - GetGradientWithAttr(nsGkAtoms::gradientUnits, mContent); - return element->mEnumAttributes[nsSVGGradientElement::GRADIENTUNITS].GetAnimValue(); + return next->GetStopFrame(aIndex, aStopFrame); } // ------------------------------------------------------------------------- @@ -431,11 +451,16 @@ nsSVGLinearGradientFrame::AttributeChanged(PRInt32 aNameSpaceID, //---------------------------------------------------------------------- float -nsSVGLinearGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName, - PRUint16 aEnumName) +nsSVGLinearGradientFrame::GetLengthValue(PRUint32 aIndex) { - nsSVGLinearGradientElement *element = - GetLinearGradientWithAttr(aAtomName, mContent); + nsSVGLinearGradientElement* lengthElement = + GetLinearGradientWithLength(aIndex, + static_cast(mContent)); + // We passed in mContent as a fallback, so, assuming mContent is non-null, the + // return value should also be non-null. + NS_ABORT_IF_FALSE(lengthElement, + "Got unexpected null element from GetLinearGradientWithLength"); + const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex]; // Object bounding box units are handled by setting the appropriate // transform in GetGradientTransform, but we need to handle user @@ -443,15 +468,30 @@ nsSVGLinearGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 gradientUnits = GetGradientUnits(); if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { - return nsSVGUtils::UserSpace(mSource, - &element->mLengthAttributes[aEnumName]); + return nsSVGUtils::UserSpace(mSource, &length); } - NS_ASSERTION(gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, - "Unknown gradientUnits type"); + NS_ASSERTION( + gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); - return element->mLengthAttributes[aEnumName]. - GetAnimValue(static_cast(nsnull)); + return length.GetAnimValue(static_cast(nsnull)); +} + +nsSVGLinearGradientElement * +nsSVGLinearGradientFrame::GetLinearGradientWithLength(PRUint32 aIndex, + nsSVGLinearGradientElement* aDefault) +{ + nsSVGLinearGradientElement* thisElement = + static_cast(mContent); + const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; + + if (length.IsExplicitlySet()) { + return thisElement; + } + + return nsSVGLinearGradientFrameBase::GetLinearGradientWithLength(aIndex, + aDefault); } already_AddRefed @@ -459,10 +499,10 @@ nsSVGLinearGradientFrame::CreateGradient() { float x1, y1, x2, y2; - x1 = GradientLookupAttribute(nsGkAtoms::x1, nsSVGLinearGradientElement::X1); - y1 = GradientLookupAttribute(nsGkAtoms::y1, nsSVGLinearGradientElement::Y1); - x2 = GradientLookupAttribute(nsGkAtoms::x2, nsSVGLinearGradientElement::X2); - y2 = GradientLookupAttribute(nsGkAtoms::y2, nsSVGLinearGradientElement::Y2); + x1 = GetLengthValue(nsSVGLinearGradientElement::X1); + y1 = GetLengthValue(nsSVGLinearGradientElement::Y1); + x2 = GetLengthValue(nsSVGLinearGradientElement::X2); + y2 = GetLengthValue(nsSVGLinearGradientElement::Y2); gfxPattern *pattern = new gfxPattern(x1, y1, x2, y2); NS_IF_ADDREF(pattern); @@ -513,17 +553,33 @@ nsSVGRadialGradientFrame::AttributeChanged(PRInt32 aNameSpaceID, //---------------------------------------------------------------------- float -nsSVGRadialGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName, - PRUint16 aEnumName, - nsSVGRadialGradientElement *aElement) +nsSVGRadialGradientFrame::GetLengthValue(PRUint32 aIndex) { - nsSVGRadialGradientElement *element; + nsSVGRadialGradientElement* lengthElement = + GetRadialGradientWithLength(aIndex, + static_cast(mContent)); + // We passed in mContent as a fallback, so, assuming mContent is non-null, + // the return value should also be non-null. + NS_ABORT_IF_FALSE(lengthElement, + "Got unexpected null element from GetRadialGradientWithLength"); + return GetLengthValueFromElement(aIndex, *lengthElement); +} - if (aElement) { - element = aElement; - } else { - element = GetRadialGradientWithAttr(aAtomName, mContent); - } +float +nsSVGRadialGradientFrame::GetLengthValue(PRUint32 aIndex, float aDefaultValue) +{ + nsSVGRadialGradientElement* lengthElement = + GetRadialGradientWithLength(aIndex, nsnull); + + return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement) + : aDefaultValue; +} + +float +nsSVGRadialGradientFrame::GetLengthValueFromElement(PRUint32 aIndex, + nsSVGRadialGradientElement& aElement) +{ + const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex]; // Object bounding box units are handled by setting the appropriate // transform in GetGradientTransform, but we need to handle user @@ -531,15 +587,30 @@ nsSVGRadialGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 gradientUnits = GetGradientUnits(); if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { - return nsSVGUtils::UserSpace(mSource, - &element->mLengthAttributes[aEnumName]); + return nsSVGUtils::UserSpace(mSource, &length); } - NS_ASSERTION(gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, - "Unknown gradientUnits type"); + NS_ASSERTION( + gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); - return element->mLengthAttributes[aEnumName]. - GetAnimValue(static_cast(nsnull)); + return length.GetAnimValue(static_cast(nsnull)); +} + +nsSVGRadialGradientElement * +nsSVGRadialGradientFrame::GetRadialGradientWithLength(PRUint32 aIndex, + nsSVGRadialGradientElement* aDefault) +{ + nsSVGRadialGradientElement* thisElement = + static_cast(mContent); + const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; + + if (length.IsExplicitlySet()) { + return thisElement; + } + + return nsSVGRadialGradientFrameBase::GetRadialGradientWithLength(aIndex, + aDefault); } already_AddRefed @@ -547,21 +618,12 @@ nsSVGRadialGradientFrame::CreateGradient() { float cx, cy, r, fx, fy; - cx = GradientLookupAttribute(nsGkAtoms::cx, nsSVGRadialGradientElement::CX); - cy = GradientLookupAttribute(nsGkAtoms::cy, nsSVGRadialGradientElement::CY); - r = GradientLookupAttribute(nsGkAtoms::r, nsSVGRadialGradientElement::R); - - nsSVGRadialGradientElement *gradient; - - if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fx, nsnull))) - fx = cx; // if fx isn't set, we must use cx - else - fx = GradientLookupAttribute(nsGkAtoms::fx, nsSVGRadialGradientElement::FX, gradient); - - if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fy, nsnull))) - fy = cy; // if fy isn't set, we must use cy - else - fy = GradientLookupAttribute(nsGkAtoms::fy, nsSVGRadialGradientElement::FY, gradient); + cx = GetLengthValue(nsSVGRadialGradientElement::CX); + cy = GetLengthValue(nsSVGRadialGradientElement::CY); + r = GetLengthValue(nsSVGRadialGradientElement::R); + // If fx or fy are not set, use cx/cy instead + fx = GetLengthValue(nsSVGRadialGradientElement::FX, cx); + fy = GetLengthValue(nsSVGRadialGradientElement::FY, cy); if (fx != cx || fy != cy) { // The focal point (fFx and fFy) must be clamped to be *inside* - not on - diff --git a/layout/svg/base/src/nsSVGGradientFrame.h b/layout/svg/base/src/nsSVGGradientFrame.h index 07294fa29c4..3a19414417c 100644 --- a/layout/svg/base/src/nsSVGGradientFrame.h +++ b/layout/svg/base/src/nsSVGGradientFrame.h @@ -86,45 +86,48 @@ private: // the referenced gradient's frame if available, null otherwise. nsSVGGradientFrame* GetReferencedGradient(); - // Helpers to look at our gradient and then along its reference chain (if any) - // to find the first gradient with the specified attribute. - // Returns aDefault if no content with that attribute is found - nsSVGGradientElement* GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault); - - // Some attributes are only valid on one type of gradient, and we *must* get - // the right type or we won't have the data structures we require. - // Returns aDefault if no content with that attribute is found - nsSVGGradientElement* GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType, - nsIContent *aDefault); - // Optionally get a stop frame (returns stop index/count) PRInt32 GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame); - PRUint16 GetSpreadMethod(); PRUint32 GetStopCount(); void GetStopInformation(PRInt32 aIndex, float *aOffset, nscolor *aColor, float *aStopOpacity); + const mozilla::SVGAnimatedTransformList* GetGradientTransformList( + nsIContent* aDefault); // Will be singular for gradientUnits="objectBoundingBox" with an empty bbox. - gfxMatrix GetGradientTransform(nsIFrame *aSource, const gfxRect *aOverrideBounds); + gfxMatrix GetGradientTransform(nsIFrame *aSource, + const gfxRect *aOverrideBounds); protected: virtual already_AddRefed CreateGradient() = 0; - // Use these inline methods instead of GetGradientWithAttr(..., aGradType) - nsSVGLinearGradientElement* GetLinearGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault) + // Internal methods for handling referenced gradients + class AutoGradientReferencer; + nsSVGGradientFrame* GetReferencedGradientIfNotInUse(); + + // Accessors to lookup gradient attributes + PRUint16 GetEnumValue(PRUint32 aIndex, nsIContent *aDefault); + PRUint16 GetEnumValue(PRUint32 aIndex) { - return static_cast( - GetGradientWithAttr(aAttrName, nsGkAtoms::svgLinearGradientFrame, aDefault)); + return GetEnumValue(aIndex, mContent); } - nsSVGRadialGradientElement* GetRadialGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault) + PRUint16 GetGradientUnits() { - return static_cast( - GetGradientWithAttr(aAttrName, nsGkAtoms::svgRadialGradientFrame, aDefault)); + // This getter is called every time the others are called - maybe cache it? + return GetEnumValue(nsSVGGradientElement::GRADIENTUNITS); + } + PRUint16 GetSpreadMethod() + { + return GetEnumValue(nsSVGGradientElement::SPREADMETHOD); } - // Get the value of our gradientUnits attribute - PRUint16 GetGradientUnits(); + // Gradient-type-specific lookups since the length values differ between + // linear and radial gradients + virtual nsSVGLinearGradientElement * GetLinearGradientWithLength( + PRUint32 aIndex, nsSVGLinearGradientElement* aDefault); + virtual nsSVGRadialGradientElement * GetRadialGradientWithLength( + PRUint32 aIndex, nsSVGRadialGradientElement* aDefault); // The frame our gradient is (currently) being applied to nsIFrame* mSource; @@ -178,7 +181,9 @@ public: #endif // DEBUG protected: - float GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 aEnumName); + float GetLengthValue(PRUint32 aIndex); + virtual nsSVGLinearGradientElement * GetLinearGradientWithLength( + PRUint32 aIndex, nsSVGLinearGradientElement* aDefault); virtual already_AddRefed CreateGradient(); }; @@ -220,8 +225,12 @@ public: #endif // DEBUG protected: - float GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 aEnumName, - nsSVGRadialGradientElement *aElement = nsnull); + float GetLengthValue(PRUint32 aIndex); + float GetLengthValue(PRUint32 aIndex, float aDefaultValue); + float GetLengthValueFromElement(PRUint32 aIndex, + nsSVGRadialGradientElement& aElement); + virtual nsSVGRadialGradientElement * GetRadialGradientWithLength( + PRUint32 aIndex, nsSVGRadialGradientElement* aDefault); virtual already_AddRefed CreateGradient(); };