Bug 608161 - Don't rely on HasAttr to detect whether gradient attributes have values; r=longsonr

This commit is contained in:
Brian Birtles 2012-03-06 15:58:40 +09:00
parent b9d2e7bc8e
commit 0a8aefa217
5 changed files with 560 additions and 153 deletions

View File

@ -0,0 +1,142 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 900">
<!-- 1. gradientUnits -->
<defs>
<linearGradient id="gradientUnits" x1="0" y1="0" x2="100" y2="0"
gradientUnits="userSpaceOnUse">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<rect width="100" height="100" fill="url(#gradientUnits)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#gradientUnits)"/>
</g>
<!-- 2. gradientTransform -->
<defs>
<linearGradient id="gradientTransform"
gradientTransform="rotate(90 0.5 0.5)">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 100)">
<rect width="100" height="100" fill="url(#gradientTransform)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#gradientTransform)"/>
</g>
</g>
<!-- 3. x1, x2 -->
<defs>
<linearGradient id="x1x2" x1="40%" x2="60%">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 200)">
<rect width="100" height="100" fill="url(#x1x2)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#x1x2)"/>
</g>
</g>
<!-- 4. y1, y2 -->
<defs>
<linearGradient id="y1y2" x2="0%" y1="40%" y2="60%">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 300)">
<rect width="100" height="100" fill="url(#y1y2)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#y1y2)"/>
</g>
</g>
<!-- 5. cx, cy -->
<defs>
<radialGradient id="cxcy" cx="0%" cy="100%">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</radialGradient>
</defs>
<g transform="translate(0 400)">
<rect width="100" height="100" fill="url(#cxcy)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#cxcy)"/>
</g>
</g>
<!-- 6. r -->
<defs>
<radialGradient id="r" r="100%">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</radialGradient>
</defs>
<g transform="translate(0 500)">
<rect width="100" height="100" fill="url(#r)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#r)"/>
</g>
</g>
<!-- 7. fx, fy -->
<defs>
<radialGradient id="fxfy" fx="20%" fy="80%">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</radialGradient>
</defs>
<g transform="translate(0 600)">
<rect width="100" height="100" fill="url(#fxfy)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#fxfy)"/>
</g>
</g>
<!-- 8. spreadMethod -->
<defs>
<linearGradient id="spreadMethod" x1="50%" spreadMethod="reflect">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 700)">
<rect width="100" height="100" fill="url(#spreadMethod)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#spreadMethod)"/>
</g>
</g>
<!-- 9. xlink:href -->
<defs>
<linearGradient id="xlink" xlink:href="#xlinkRef"/>
<linearGradient id="xlinkRef">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 800)">
<rect width="100" height="100" fill="url(#xlink)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#xlinkRef)"/>
</g>
</g>
<!-- We were getting random but very minor anti-aliasing differences on OSX
and OSX64 along the edges of these gradients so we draw a thick stroke
over all the edges. -->
<path stroke="black" stroke-width="3" stroke-linecap="square" fill="none"
d="M0 0H200V900H0V0
M0 100H200
M0 200H200
M0 300H200
M0 400H200
M0 500H200
M0 600H200
M0 700H200
M0 800H200
M0 900H200
M100 0V900"/>
<!-- If adding more tests here, be sure to update the viewBox on the root svg
element -->
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,193 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 900">
<!-- Bug 608161 - SVG SMIL: Don't use HasAttr to test for presence of animated
attributes.
Test animating gradient attributes doesn't rely on the presence of
attributes in the DOM, but also correctly detects attributes set by
animation.
The format of each test is that we have a base gradient named
(attName)Ref which does NOT specify the attribute under test, but instead
sets the attribute to a non-default value using animation.
Then we have a referencing gradient named (attName) that refers to the
base gradient and also does NOT specify the attribute under test.
When we go to look up the attribute under test we begin at (attName),
notice the attribute isn't specified (either in the DOM or by animation),
then go to (attName)Ref and detect that the attribute is specified there
by animation and use that value.
To the right of each test we also include a rect that directly references
(attName)Ref. The two rectangles should be identical but prior to fixing
this bug they were not.
-->
<!-- 1. gradientUnits: defaults to objectBoundingBox -->
<defs>
<linearGradient xlink:href="#gradientUnitsRef" id="gradientUnits"/>
<linearGradient id="gradientUnitsRef" x1="0" y1="0" x2="100" y2="0">
<set attributeName="gradientUnits" to="userSpaceOnUse"/>
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<rect width="100" height="100" fill="url(#gradientUnits)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#gradientUnitsRef)"/>
</g>
<!-- 2. gradientTransform: defaults to identity -->
<defs>
<linearGradient xlink:href="#gradientTransformRef" id="gradientTransform"/>
<linearGradient id="gradientTransformRef">
<animateTransform attributeName="gradientTransform" type="rotate"
values="90 0.5 0.5" fill="freeze"/>
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 100)">
<rect width="100" height="100" fill="url(#gradientTransform)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#gradientTransformRef)"/>
</g>
</g>
<!-- 3. x1: defaults to 0%
x2: defaults to 100% -->
<defs>
<linearGradient xlink:href="#x1x2Ref" id="x1x2"/>
<linearGradient id="x1x2Ref">
<set attributeName="x1" to="40%"/>
<set attributeName="x2" to="60%"/>
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 200)">
<rect width="100" height="100" fill="url(#x1x2)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#x1x2Ref)"/>
</g>
</g>
<!-- 4. y1: defaults to 0%
y2: defaults to 0% -->
<defs>
<linearGradient xlink:href="#y1y2Ref" id="y1y2"/>
<linearGradient id="y1y2Ref" x2="0%">
<set attributeName="y1" to="40%"/>
<set attributeName="y2" to="60%"/>
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 300)">
<rect width="100" height="100" fill="url(#y1y2)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#y1y2Ref)"/>
</g>
</g>
<!-- 5. cx: defaults to 50%
cy: defaults to 50% -->
<defs>
<radialGradient xlink:href="#cxcyRef" id="cxcy"/>
<radialGradient id="cxcyRef">
<set attributeName="cx" to="0%"/>
<set attributeName="cy" to="100%"/>
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</radialGradient>
</defs>
<g transform="translate(0 400)">
<rect width="100" height="100" fill="url(#cxcy)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#cxcyRef)"/>
</g>
</g>
<!-- 6. r: defaults to 50% -->
<defs>
<radialGradient xlink:href="#rRef" id="r"/>
<radialGradient id="rRef">
<set attributeName="r" to="100%"/>
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</radialGradient>
</defs>
<g transform="translate(0 500)">
<rect width="100" height="100" fill="url(#r)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#rRef)"/>
</g>
</g>
<!-- 7. fx: defaults to cx
fy: defaults to cy -->
<defs>
<radialGradient xlink:href="#fxfyRef" id="fxfy"/>
<radialGradient id="fxfyRef">
<set attributeName="fx" to="20%"/>
<set attributeName="fy" to="80%"/>
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</radialGradient>
</defs>
<g transform="translate(0 600)">
<rect width="100" height="100" fill="url(#fxfy)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#fxfyRef)"/>
</g>
</g>
<!-- 8. spreadMethod: defaults to pad -->
<defs>
<linearGradient xlink:href="#spreadMethodRef" id="spreadMethod"/>
<linearGradient id="spreadMethodRef" x1="50%">
<set attributeName="spreadMethod" to="reflect"/>
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 700)">
<rect width="100" height="100" fill="url(#spreadMethod)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#spreadMethodRef)"/>
</g>
</g>
<!-- 9. xlink:href
This attribute is not affected by bug 608161 but we test it here for
completeness.
-->
<defs>
<linearGradient id="xlink">
<set attributeName="xlink:href" to="#xlinkRef"/>
</linearGradient>
<linearGradient id="xlinkRef">
<stop offset="0%" stop-color="#F60" />
<stop offset="100%" stop-color="#FF6" />
</linearGradient>
</defs>
<g transform="translate(0 800)">
<rect width="100" height="100" fill="url(#xlink)"/>
<g transform="translate(100)">
<rect width="100" height="100" fill="url(#xlinkRef)"/>
</g>
</g>
<!-- We were getting random but very minor anti-aliasing differences on OSX
and OSX64 along the edges of these gradients so we draw a thick stroke
over all the edges. -->
<path stroke="black" stroke-width="3" stroke-linecap="square" fill="none"
d="M0 0H200V900H0V0
M0 100H200
M0 200H200
M0 300H200
M0 400H200
M0 500H200
M0 600H200
M0 700H200
M0 800H200
M0 900H200
M100 0V900"/>
<!-- If adding more tests here, be sure to update the viewBox on the root svg
element -->
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -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

View File

@ -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<nsSVGGradientElement *>(mContent)->mEnumAttributes[aIndex];
if (thisEnum.IsExplicitlySet())
return thisEnum.GetAnimValue();
AutoGradientReferencer gradientRef(this);
nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
return next ? next->GetEnumValue(aIndex, aDefault) :
static_cast<nsSVGGradientElement *>(aDefault)->
mEnumAttributes[aIndex].GetAnimValue();
}
const SVGAnimatedTransformList*
nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault)
{
SVGAnimatedTransformList *thisTransformList =
static_cast<nsSVGGradientElement *>(mContent)->GetAnimatedTransformList();
if (thisTransformList->IsExplicitlySet())
return thisTransformList;
AutoGradientReferencer gradientRef(this);
nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
return next ? next->GetGradientTransformList(aDefault) :
static_cast<const nsSVGGradientElement *>(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<nsSVGGradientFrame*>(result);
}
nsSVGGradientElement *
nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
nsSVGGradientFrame *
nsSVGGradientFrame::GetReferencedGradientIfNotInUse()
{
if (mContent->HasAttr(kNameSpaceID_None, aAttrName))
return static_cast<nsSVGGradientElement *>(mContent);
nsSVGGradientFrame *referenced = GetReferencedGradient();
if (!referenced)
return nsnull;
nsSVGGradientElement *grad = static_cast<nsSVGGradientElement *>(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<nsSVGGradientElement *>(mContent);
nsSVGGradientElement *grad = static_cast<nsSVGGradientElement *>(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<nsSVGLinearGradientElement *>(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<nsSVGSVGElement*>(nsnull));
return length.GetAnimValue(static_cast<nsSVGSVGElement*>(nsnull));
}
nsSVGLinearGradientElement *
nsSVGLinearGradientFrame::GetLinearGradientWithLength(PRUint32 aIndex,
nsSVGLinearGradientElement* aDefault)
{
nsSVGLinearGradientElement* thisElement =
static_cast<nsSVGLinearGradientElement *>(mContent);
const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
if (length.IsExplicitlySet()) {
return thisElement;
}
return nsSVGLinearGradientFrameBase::GetLinearGradientWithLength(aIndex,
aDefault);
}
already_AddRefed<gfxPattern>
@ -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<nsSVGRadialGradientElement *>(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<nsSVGSVGElement*>(nsnull));
return length.GetAnimValue(static_cast<nsSVGSVGElement*>(nsnull));
}
nsSVGRadialGradientElement *
nsSVGRadialGradientFrame::GetRadialGradientWithLength(PRUint32 aIndex,
nsSVGRadialGradientElement* aDefault)
{
nsSVGRadialGradientElement* thisElement =
static_cast<nsSVGRadialGradientElement *>(mContent);
const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
if (length.IsExplicitlySet()) {
return thisElement;
}
return nsSVGRadialGradientFrameBase::GetRadialGradientWithLength(aIndex,
aDefault);
}
already_AddRefed<gfxPattern>
@ -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 -

View File

@ -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<gfxPattern> 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<nsSVGLinearGradientElement*>(
GetGradientWithAttr(aAttrName, nsGkAtoms::svgLinearGradientFrame, aDefault));
return GetEnumValue(aIndex, mContent);
}
nsSVGRadialGradientElement* GetRadialGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
PRUint16 GetGradientUnits()
{
return static_cast<nsSVGRadialGradientElement*>(
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<gfxPattern> 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<gfxPattern> CreateGradient();
};