summaryrefslogtreecommitdiffstats
path: root/layout/svg/SVGGradientFrame.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /layout/svg/SVGGradientFrame.cpp
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/svg/SVGGradientFrame.cpp')
-rw-r--r--layout/svg/SVGGradientFrame.cpp619
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