diff options
Diffstat (limited to 'layout/svg/SVGGradientFrame.cpp')
-rw-r--r-- | layout/svg/SVGGradientFrame.cpp | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/layout/svg/SVGGradientFrame.cpp b/layout/svg/SVGGradientFrame.cpp new file mode 100644 index 0000000000..f316c0180d --- /dev/null +++ b/layout/svg/SVGGradientFrame.cpp @@ -0,0 +1,619 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Main header first: +#include "SVGGradientFrame.h" +#include <algorithm> + +// Keep others in (case-insensitive) order: +#include "AutoReferenceChainGuard.h" +#include "gfxPattern.h" +#include "gfxUtils.h" +#include "mozilla/PresShell.h" +#include "mozilla/SVGObserverUtils.h" +#include "mozilla/SVGUtils.h" +#include "mozilla/dom/SVGGradientElement.h" +#include "mozilla/dom/SVGGradientElementBinding.h" +#include "mozilla/dom/SVGStopElement.h" +#include "mozilla/dom/SVGUnitTypesBinding.h" +#include "nsContentUtils.h" +#include "SVGAnimatedTransformList.h" + +using namespace mozilla::dom; +using namespace mozilla::dom::SVGGradientElement_Binding; +using namespace mozilla::dom::SVGUnitTypes_Binding; +using namespace mozilla::gfx; + +namespace mozilla { + +//---------------------------------------------------------------------- +// Implementation + +SVGGradientFrame::SVGGradientFrame(ComputedStyle* aStyle, + nsPresContext* aPresContext, ClassID aID) + : SVGPaintServerFrame(aStyle, aPresContext, aID), + mSource(nullptr), + mLoopFlag(false), + mNoHRefURI(false) {} + +NS_QUERYFRAME_HEAD(SVGGradientFrame) + NS_QUERYFRAME_ENTRY(SVGGradientFrame) +NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame) + +//---------------------------------------------------------------------- +// nsIFrame methods: + +nsresult SVGGradientFrame::AttributeChanged(int32_t aNameSpaceID, + nsAtom* aAttribute, + int32_t aModType) { + if (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::gradientUnits || + aAttribute == nsGkAtoms::gradientTransform || + aAttribute == nsGkAtoms::spreadMethod)) { + SVGObserverUtils::InvalidateRenderingObservers(this); + } else if ((aNameSpaceID == kNameSpaceID_XLink || + aNameSpaceID == kNameSpaceID_None) && + aAttribute == nsGkAtoms::href) { + // Blow away our reference, if any + SVGObserverUtils::RemoveTemplateObserver(this); + mNoHRefURI = false; + // And update whoever references us + SVGObserverUtils::InvalidateRenderingObservers(this); + } + + return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute, + aModType); +} + +//---------------------------------------------------------------------- + +uint16_t SVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) { + const SVGAnimatedEnumeration& thisEnum = + static_cast<dom::SVGGradientElement*>(GetContent()) + ->mEnumAttributes[aIndex]; + + if (thisEnum.IsExplicitlySet()) { + return thisEnum.GetAnimValue(); + } + + // Before we recurse, make sure we'll break reference loops and over long + // reference chains: + static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; + AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, + &sRefChainLengthCounter); + if (MOZ_UNLIKELY(!refChainGuard.Reference())) { + // Break reference chain + return static_cast<dom::SVGGradientElement*>(aDefault) + ->mEnumAttributes[aIndex] + .GetAnimValue(); + } + + SVGGradientFrame* next = GetReferencedGradient(); + + return next ? next->GetEnumValue(aIndex, aDefault) + : static_cast<dom::SVGGradientElement*>(aDefault) + ->mEnumAttributes[aIndex] + .GetAnimValue(); +} + +uint16_t SVGGradientFrame::GetGradientUnits() { + // This getter is called every time the others are called - maybe cache it? + return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS); +} + +uint16_t SVGGradientFrame::GetSpreadMethod() { + return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD); +} + +const SVGAnimatedTransformList* SVGGradientFrame::GetGradientTransformList( + nsIContent* aDefault) { + SVGAnimatedTransformList* thisTransformList = + static_cast<dom::SVGGradientElement*>(GetContent()) + ->GetAnimatedTransformList(); + + if (thisTransformList && thisTransformList->IsExplicitlySet()) + return thisTransformList; + + // Before we recurse, make sure we'll break reference loops and over long + // reference chains: + static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; + AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, + &sRefChainLengthCounter); + if (MOZ_UNLIKELY(!refChainGuard.Reference())) { + // Break reference chain + return static_cast<const dom::SVGGradientElement*>(aDefault) + ->mGradientTransform.get(); + } + + SVGGradientFrame* next = GetReferencedGradient(); + + return next ? next->GetGradientTransformList(aDefault) + : static_cast<const dom::SVGGradientElement*>(aDefault) + ->mGradientTransform.get(); +} + +gfxMatrix SVGGradientFrame::GetGradientTransform( + nsIFrame* aSource, const gfxRect* aOverrideBounds) { + gfxMatrix bboxMatrix; + + uint16_t gradientUnits = GetGradientUnits(); + if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) { + NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); + // objectBoundingBox is the default anyway + + gfxRect bbox = aOverrideBounds + ? *aOverrideBounds + : SVGUtils::GetBBox( + aSource, SVGUtils::eUseFrameBoundsForOuterSVG | + SVGUtils::eBBoxIncludeFillGeometry); + bboxMatrix = + gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); + } + + const SVGAnimatedTransformList* animTransformList = + GetGradientTransformList(GetContent()); + if (!animTransformList) { + return bboxMatrix; + } + + gfxMatrix gradientTransform = + animTransformList->GetAnimValue().GetConsolidationMatrix(); + return bboxMatrix.PreMultiply(gradientTransform); +} + +dom::SVGLinearGradientElement* SVGGradientFrame::GetLinearGradientWithLength( + uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) { + // If this was a linear gradient with the required length, we would have + // already found it in SVGLinearGradientFrame::GetLinearGradientWithLength. + // Since we didn't find the length, continue looking down the chain. + + // Before we recurse, make sure we'll break reference loops and over long + // reference chains: + static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; + AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, + &sRefChainLengthCounter); + if (MOZ_UNLIKELY(!refChainGuard.Reference())) { + // Break reference chain + return aDefault; + } + + SVGGradientFrame* next = GetReferencedGradient(); + return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault; +} + +dom::SVGRadialGradientElement* SVGGradientFrame::GetRadialGradientWithLength( + uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) { + // If this was a radial gradient with the required length, we would have + // already found it in SVGRadialGradientFrame::GetRadialGradientWithLength. + // Since we didn't find the length, continue looking down the chain. + + // Before we recurse, make sure we'll break reference loops and over long + // reference chains: + static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; + AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, + &sRefChainLengthCounter); + if (MOZ_UNLIKELY(!refChainGuard.Reference())) { + // Break reference chain + return aDefault; + } + + SVGGradientFrame* next = GetReferencedGradient(); + return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault; +} + +//---------------------------------------------------------------------- +// SVGPaintServerFrame methods: + +// helpers + +static ColorStop GetStopInformation(const nsIFrame* aStopFrame, + float aGraphicOpacity, + float& aLastPosition) { + nsIContent* stopContent = aStopFrame->GetContent(); + MOZ_ASSERT(stopContent && stopContent->IsSVGElement(nsGkAtoms::stop)); + + float position; + static_cast<SVGStopElement*>(stopContent) + ->GetAnimatedNumberValues(&position, nullptr); + + position = clamped(position, 0.0f, 1.0f); + + if (position < aLastPosition) { + position = aLastPosition; + } else { + aLastPosition = position; + } + + const auto* svgReset = aStopFrame->StyleSVGReset(); + + sRGBColor stopColor = + sRGBColor::FromABGR(svgReset->mStopColor.CalcColor(aStopFrame)); + stopColor.a *= svgReset->mStopOpacity * aGraphicOpacity; + + return ColorStop(position, false, + StyleAbsoluteColor::FromColor(stopColor.ToABGR())); +} + +class MOZ_STACK_CLASS SVGColorStopInterpolator + : public ColorStopInterpolator<SVGColorStopInterpolator> { + public: + SVGColorStopInterpolator( + gfxPattern* aGradient, const nsTArray<ColorStop>& aStops, + const StyleColorInterpolationMethod& aStyleColorInterpolationMethod) + : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod), + mGradient(aGradient) {} + + void CreateStop(float aPosition, DeviceColor aColor) { + mGradient->AddColorStop(aPosition, aColor); + } + + private: + gfxPattern* mGradient; +}; + +already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern( + nsIFrame* aSource, const DrawTarget* aDrawTarget, + const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::*aFillOrStroke, + float aGraphicOpacity, imgDrawingParams& aImgParams, + const gfxRect* aOverrideBounds) { + uint16_t gradientUnits = GetGradientUnits(); + MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX || + gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE); + if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { + // Set mSource for this consumer. + // If this gradient is applied to text, our caller will be the glyph, which + // is not an element, so we need to get the parent + mSource = aSource->IsTextFrame() ? aSource->GetParent() : aSource; + } + + AutoTArray<ColorStop, 8> stops; + GetStops(&stops, aGraphicOpacity); + + uint32_t nStops = stops.Length(); + + // SVG specification says that no stops should be treated like + // the corresponding fill or stroke had "none" specified. + if (nStops == 0) { + return do_AddRef(new gfxPattern(DeviceColor())); + } + + if (nStops == 1 || GradientVectorLengthIsZero()) { + // The gradient paints a single colour, using the stop-color of the last + // gradient step if there are more than one. + return do_AddRef(new gfxPattern(ToDeviceColor(stops.LastElement().mColor))); + } + + // Get the transform list (if there is one). We do this after the returns + // above since this call can be expensive when "gradientUnits" is set to + // "objectBoundingBox" (since that requiring a GetBBox() call). + gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds); + + if (patternMatrix.IsSingular()) { + return nullptr; + } + + // revert any vector effect transform so that the gradient appears unchanged + if (aFillOrStroke == &nsStyleSVG::mStroke) { + gfxMatrix userToOuterSVG; + if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) { + patternMatrix *= userToOuterSVG; + } + } + + if (!patternMatrix.Invert()) { + return nullptr; + } + + RefPtr<gfxPattern> gradient = CreateGradient(); + if (!gradient) { + return nullptr; + } + + uint16_t aSpread = GetSpreadMethod(); + if (aSpread == SVG_SPREADMETHOD_PAD) + gradient->SetExtend(ExtendMode::CLAMP); + else if (aSpread == SVG_SPREADMETHOD_REFLECT) + gradient->SetExtend(ExtendMode::REFLECT); + else if (aSpread == SVG_SPREADMETHOD_REPEAT) + gradient->SetExtend(ExtendMode::REPEAT); + + gradient->SetMatrix(patternMatrix); + + if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) { + static constexpr auto interpolationMethod = StyleColorInterpolationMethod{ + StyleColorSpace::SrgbLinear, StyleHueInterpolationMethod::Shorter}; + SVGColorStopInterpolator interpolator(gradient, stops, interpolationMethod); + interpolator.CreateStops(); + } else { + // setup standard sRGB stops + for (const auto& stop : stops) { + gradient->AddColorStop(stop.mPosition, ToDeviceColor(stop.mColor)); + } + } + + return gradient.forget(); +} + +// Private (helper) methods + +SVGGradientFrame* SVGGradientFrame::GetReferencedGradient() { + if (mNoHRefURI) { + return nullptr; + } + + auto GetHref = [this](nsAString& aHref) { + dom::SVGGradientElement* grad = + static_cast<dom::SVGGradientElement*>(this->GetContent()); + if (grad->mStringAttributes[dom::SVGGradientElement::HREF] + .IsExplicitlySet()) { + grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(aHref, + grad); + } else { + grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF].GetAnimValue( + aHref, grad); + } + this->mNoHRefURI = aHref.IsEmpty(); + }; + + // We don't call SVGObserverUtils::RemoveTemplateObserver and set + // `mNoHRefURI = false` on failure since we want to be invalidated if the ID + // specified by our href starts resolving to a different/valid element. + + return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref)); +} + +void SVGGradientFrame::GetStops(nsTArray<ColorStop>* aStops, + float aGraphicOpacity) { + float lastPosition = 0.0f; + for (const auto* stopFrame : mFrames) { + if (stopFrame->IsSVGStopFrame()) { + aStops->AppendElement( + GetStopInformation(stopFrame, aGraphicOpacity, lastPosition)); + } + } + if (!aStops->IsEmpty()) { + return; + } + + // Our gradient element doesn't have stops - try to "inherit" them + + // Before we recurse, make sure we'll break reference loops and over long + // reference chains: + static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; + AutoReferenceChainGuard refChainGuard(this, &mLoopFlag, + &sRefChainLengthCounter); + if (MOZ_UNLIKELY(!refChainGuard.Reference())) { + // Break reference chain + return; + } + + SVGGradientFrame* next = GetReferencedGradient(); + if (next) { + next->GetStops(aStops, aGraphicOpacity); + } +} + +// ------------------------------------------------------------------------- +// Linear Gradients +// ------------------------------------------------------------------------- + +NS_QUERYFRAME_HEAD(SVGLinearGradientFrame) + NS_QUERYFRAME_ENTRY(SVGLinearGradientFrame) +NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame) + +#ifdef DEBUG +void SVGLinearGradientFrame::Init(nsIContent* aContent, + nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) { + NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::linearGradient), + "Content is not an SVG linearGradient"); + + SVGGradientFrame::Init(aContent, aParent, aPrevInFlow); +} +#endif /* DEBUG */ + +nsresult SVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID, + nsAtom* aAttribute, + int32_t aModType) { + if (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::x1 || aAttribute == nsGkAtoms::y1 || + aAttribute == nsGkAtoms::x2 || aAttribute == nsGkAtoms::y2)) { + SVGObserverUtils::InvalidateRenderingObservers(this); + } + + return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); +} + +//---------------------------------------------------------------------- + +float SVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) { + dom::SVGLinearGradientElement* lengthElement = GetLinearGradientWithLength( + aIndex, static_cast<dom::SVGLinearGradientElement*>(GetContent())); + // We passed in mContent as a fallback, so, assuming mContent is non-null, the + // return value should also be non-null. + MOZ_ASSERT(lengthElement, + "Got unexpected null element from GetLinearGradientWithLength"); + const SVGAnimatedLength& length = lengthElement->mLengthAttributes[aIndex]; + + // Object bounding box units are handled by setting the appropriate + // transform in GetGradientTransform, but we need to handle user + // space units as part of the individual Get* routines. Fixes 323669. + + uint16_t gradientUnits = GetGradientUnits(); + if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { + return SVGUtils::UserSpace(mSource, &length); + } + + NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); + + return length.GetAnimValue(static_cast<SVGViewportElement*>(nullptr)); +} + +dom::SVGLinearGradientElement* +SVGLinearGradientFrame::GetLinearGradientWithLength( + uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) { + dom::SVGLinearGradientElement* thisElement = + static_cast<dom::SVGLinearGradientElement*>(GetContent()); + const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex]; + + if (length.IsExplicitlySet()) { + return thisElement; + } + + return SVGGradientFrame::GetLinearGradientWithLength(aIndex, aDefault); +} + +bool SVGLinearGradientFrame::GradientVectorLengthIsZero() { + return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) == + GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) && + GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) == + GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); +} + +already_AddRefed<gfxPattern> SVGLinearGradientFrame::CreateGradient() { + float x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1); + float y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1); + float x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2); + float y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2); + + return do_AddRef(new gfxPattern(x1, y1, x2, y2)); +} + +// ------------------------------------------------------------------------- +// Radial Gradients +// ------------------------------------------------------------------------- + +NS_QUERYFRAME_HEAD(SVGRadialGradientFrame) + NS_QUERYFRAME_ENTRY(SVGRadialGradientFrame) +NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame) + +#ifdef DEBUG +void SVGRadialGradientFrame::Init(nsIContent* aContent, + nsContainerFrame* aParent, + nsIFrame* aPrevInFlow) { + NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::radialGradient), + "Content is not an SVG radialGradient"); + + SVGGradientFrame::Init(aContent, aParent, aPrevInFlow); +} +#endif /* DEBUG */ + +nsresult SVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID, + nsAtom* aAttribute, + int32_t aModType) { + if (aNameSpaceID == kNameSpaceID_None && + (aAttribute == nsGkAtoms::r || aAttribute == nsGkAtoms::cx || + aAttribute == nsGkAtoms::cy || aAttribute == nsGkAtoms::fx || + aAttribute == nsGkAtoms::fy)) { + SVGObserverUtils::InvalidateRenderingObservers(this); + } + + return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); +} + +//---------------------------------------------------------------------- + +float SVGRadialGradientFrame::GetLengthValue(uint32_t aIndex) { + dom::SVGRadialGradientElement* lengthElement = GetRadialGradientWithLength( + aIndex, static_cast<dom::SVGRadialGradientElement*>(GetContent())); + // We passed in mContent as a fallback, so, assuming mContent is non-null, + // the return value should also be non-null. + MOZ_ASSERT(lengthElement, + "Got unexpected null element from GetRadialGradientWithLength"); + return GetLengthValueFromElement(aIndex, *lengthElement); +} + +float SVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, + float aDefaultValue) { + dom::SVGRadialGradientElement* lengthElement = + GetRadialGradientWithLength(aIndex, nullptr); + + return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement) + : aDefaultValue; +} + +float SVGRadialGradientFrame::GetLengthValueFromElement( + uint32_t aIndex, dom::SVGRadialGradientElement& aElement) { + const SVGAnimatedLength& length = aElement.mLengthAttributes[aIndex]; + + // Object bounding box units are handled by setting the appropriate + // transform in GetGradientTransform, but we need to handle user + // space units as part of the individual Get* routines. Fixes 323669. + + uint16_t gradientUnits = GetGradientUnits(); + if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) { + return SVGUtils::UserSpace(mSource, &length); + } + + NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); + + return length.GetAnimValue(static_cast<SVGViewportElement*>(nullptr)); +} + +dom::SVGRadialGradientElement* +SVGRadialGradientFrame::GetRadialGradientWithLength( + uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) { + dom::SVGRadialGradientElement* thisElement = + static_cast<dom::SVGRadialGradientElement*>(GetContent()); + const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex]; + + if (length.IsExplicitlySet()) { + return thisElement; + } + + return SVGGradientFrame::GetRadialGradientWithLength(aIndex, aDefault); +} + +bool SVGRadialGradientFrame::GradientVectorLengthIsZero() { + float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX); + float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY); + float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R); + // If fx or fy are not set, use cx/cy instead + float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx); + float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy); + float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR); + return cx == fx && cy == fy && r == fr; +} + +already_AddRefed<gfxPattern> SVGRadialGradientFrame::CreateGradient() { + float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX); + float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY); + float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R); + // If fx or fy are not set, use cx/cy instead + float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx); + float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy); + float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR); + + return do_AddRef(new gfxPattern(fx, fy, fr, cx, cy, r)); +} + +} // namespace mozilla + +// ------------------------------------------------------------------------- +// Public functions +// ------------------------------------------------------------------------- + +nsIFrame* NS_NewSVGLinearGradientFrame(mozilla::PresShell* aPresShell, + mozilla::ComputedStyle* aStyle) { + return new (aPresShell) + mozilla::SVGLinearGradientFrame(aStyle, aPresShell->GetPresContext()); +} + +nsIFrame* NS_NewSVGRadialGradientFrame(mozilla::PresShell* aPresShell, + mozilla::ComputedStyle* aStyle) { + return new (aPresShell) + mozilla::SVGRadialGradientFrame(aStyle, aPresShell->GetPresContext()); +} + +namespace mozilla { + +NS_IMPL_FRAMEARENA_HELPERS(SVGLinearGradientFrame) +NS_IMPL_FRAMEARENA_HELPERS(SVGRadialGradientFrame) + +} // namespace mozilla |