Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / layout / svg / SVGGradientFrame.cpp
blob865e113003ac6f3242bddfed38ca94dc108b05a0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // Main header first:
8 #include "SVGGradientFrame.h"
9 #include <algorithm>
11 // Keep others in (case-insensitive) order:
12 #include "AutoReferenceChainGuard.h"
13 #include "gfxPattern.h"
14 #include "gfxUtils.h"
15 #include "mozilla/PresShell.h"
16 #include "mozilla/SVGObserverUtils.h"
17 #include "mozilla/SVGUtils.h"
18 #include "mozilla/dom/SVGGradientElement.h"
19 #include "mozilla/dom/SVGGradientElementBinding.h"
20 #include "mozilla/dom/SVGStopElement.h"
21 #include "mozilla/dom/SVGUnitTypesBinding.h"
22 #include "nsContentUtils.h"
23 #include "SVGAnimatedTransformList.h"
25 using namespace mozilla::dom;
26 using namespace mozilla::dom::SVGGradientElement_Binding;
27 using namespace mozilla::dom::SVGUnitTypes_Binding;
28 using namespace mozilla::gfx;
30 namespace mozilla {
32 //----------------------------------------------------------------------
33 // Implementation
35 SVGGradientFrame::SVGGradientFrame(ComputedStyle* aStyle,
36 nsPresContext* aPresContext, ClassID aID)
37 : SVGPaintServerFrame(aStyle, aPresContext, aID),
38 mSource(nullptr),
39 mLoopFlag(false),
40 mNoHRefURI(false) {}
42 NS_QUERYFRAME_HEAD(SVGGradientFrame)
43 NS_QUERYFRAME_ENTRY(SVGGradientFrame)
44 NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame)
46 //----------------------------------------------------------------------
47 // nsIFrame methods:
49 nsresult SVGGradientFrame::AttributeChanged(int32_t aNameSpaceID,
50 nsAtom* aAttribute,
51 int32_t aModType) {
52 if (aNameSpaceID == kNameSpaceID_None &&
53 (aAttribute == nsGkAtoms::gradientUnits ||
54 aAttribute == nsGkAtoms::gradientTransform ||
55 aAttribute == nsGkAtoms::spreadMethod)) {
56 SVGObserverUtils::InvalidateRenderingObservers(this);
57 } else if ((aNameSpaceID == kNameSpaceID_XLink ||
58 aNameSpaceID == kNameSpaceID_None) &&
59 aAttribute == nsGkAtoms::href) {
60 // Blow away our reference, if any
61 SVGObserverUtils::RemoveTemplateObserver(this);
62 mNoHRefURI = false;
63 // And update whoever references us
64 SVGObserverUtils::InvalidateRenderingObservers(this);
67 return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
68 aModType);
71 //----------------------------------------------------------------------
73 uint16_t SVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
74 const SVGAnimatedEnumeration& thisEnum =
75 static_cast<dom::SVGGradientElement*>(GetContent())
76 ->mEnumAttributes[aIndex];
78 if (thisEnum.IsExplicitlySet()) {
79 return thisEnum.GetAnimValue();
82 // Before we recurse, make sure we'll break reference loops and over long
83 // reference chains:
84 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
85 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
86 &sRefChainLengthCounter);
87 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
88 // Break reference chain
89 return static_cast<dom::SVGGradientElement*>(aDefault)
90 ->mEnumAttributes[aIndex]
91 .GetAnimValue();
94 SVGGradientFrame* next = GetReferencedGradient();
96 return next ? next->GetEnumValue(aIndex, aDefault)
97 : static_cast<dom::SVGGradientElement*>(aDefault)
98 ->mEnumAttributes[aIndex]
99 .GetAnimValue();
102 uint16_t SVGGradientFrame::GetGradientUnits() {
103 // This getter is called every time the others are called - maybe cache it?
104 return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS);
107 uint16_t SVGGradientFrame::GetSpreadMethod() {
108 return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD);
111 SVGGradientFrame* SVGGradientFrame::GetGradientTransformFrame(
112 SVGGradientFrame* aDefault) {
113 if (!StyleDisplay()->mTransform.IsNone()) {
114 return this;
116 // Before we recurse, make sure we'll break reference loops and over long
117 // reference chains:
118 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
119 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
120 &sRefChainLengthCounter);
121 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
122 // Break reference chain
123 return aDefault;
126 if (SVGGradientFrame* next = GetReferencedGradient()) {
127 return next->GetGradientTransformFrame(aDefault);
129 return aDefault;
132 gfxMatrix SVGGradientFrame::GetGradientTransform(
133 nsIFrame* aSource, const gfxRect* aOverrideBounds) {
134 gfxMatrix bboxMatrix;
135 uint16_t gradientUnits = GetGradientUnits();
136 if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) {
137 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
138 "Unknown gradientUnits type");
139 // objectBoundingBox is the default anyway
141 gfxRect bbox = aOverrideBounds
142 ? *aOverrideBounds
143 : SVGUtils::GetBBox(
144 aSource, SVGUtils::eUseFrameBoundsForOuterSVG |
145 SVGUtils::eBBoxIncludeFillGeometry);
146 bboxMatrix =
147 gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
150 return bboxMatrix.PreMultiply(
151 SVGUtils::GetTransformMatrixInUserSpace(GetGradientTransformFrame(this)));
154 dom::SVGLinearGradientElement* SVGGradientFrame::GetLinearGradientWithLength(
155 uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) {
156 // If this was a linear gradient with the required length, we would have
157 // already found it in SVGLinearGradientFrame::GetLinearGradientWithLength.
158 // Since we didn't find the length, continue looking down the chain.
160 // Before we recurse, make sure we'll break reference loops and over long
161 // reference chains:
162 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
163 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
164 &sRefChainLengthCounter);
165 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
166 // Break reference chain
167 return aDefault;
170 SVGGradientFrame* next = GetReferencedGradient();
171 return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault;
174 dom::SVGRadialGradientElement* SVGGradientFrame::GetRadialGradientWithLength(
175 uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) {
176 // If this was a radial gradient with the required length, we would have
177 // already found it in SVGRadialGradientFrame::GetRadialGradientWithLength.
178 // Since we didn't find the length, continue looking down the chain.
180 // Before we recurse, make sure we'll break reference loops and over long
181 // reference chains:
182 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
183 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
184 &sRefChainLengthCounter);
185 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
186 // Break reference chain
187 return aDefault;
190 SVGGradientFrame* next = GetReferencedGradient();
191 return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault;
194 //----------------------------------------------------------------------
195 // SVGPaintServerFrame methods:
197 // helpers
199 static ColorStop GetStopInformation(const nsIFrame* aStopFrame,
200 float aGraphicOpacity,
201 float& aLastPosition) {
202 nsIContent* stopContent = aStopFrame->GetContent();
203 MOZ_ASSERT(stopContent && stopContent->IsSVGElement(nsGkAtoms::stop));
205 float position;
206 static_cast<SVGStopElement*>(stopContent)
207 ->GetAnimatedNumberValues(&position, nullptr);
209 position = std::clamp(position, 0.0f, 1.0f);
211 if (position < aLastPosition) {
212 position = aLastPosition;
213 } else {
214 aLastPosition = position;
217 const auto* svgReset = aStopFrame->StyleSVGReset();
219 sRGBColor stopColor =
220 sRGBColor::FromABGR(svgReset->mStopColor.CalcColor(aStopFrame));
221 stopColor.a *= svgReset->mStopOpacity * aGraphicOpacity;
223 return ColorStop(position, false,
224 StyleAbsoluteColor::FromColor(stopColor.ToABGR()));
227 class MOZ_STACK_CLASS SVGColorStopInterpolator
228 : public ColorStopInterpolator<SVGColorStopInterpolator> {
229 public:
230 SVGColorStopInterpolator(
231 gfxPattern* aGradient, const nsTArray<ColorStop>& aStops,
232 const StyleColorInterpolationMethod& aStyleColorInterpolationMethod,
233 bool aExtend)
234 : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod, aExtend),
235 mGradient(aGradient) {}
237 void CreateStop(float aPosition, DeviceColor aColor) {
238 mGradient->AddColorStop(aPosition, aColor);
241 private:
242 gfxPattern* mGradient;
245 already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern(
246 nsIFrame* aSource, const DrawTarget* aDrawTarget,
247 const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::* aFillOrStroke,
248 float aGraphicOpacity, imgDrawingParams& aImgParams,
249 const gfxRect* aOverrideBounds) {
250 uint16_t gradientUnits = GetGradientUnits();
251 MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX ||
252 gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE);
253 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
254 // Set mSource for this consumer.
255 // If this gradient is applied to text, our caller will be the glyph, which
256 // is not an element, so we need to get the parent
257 mSource = aSource->IsTextFrame() ? aSource->GetParent() : aSource;
260 AutoTArray<ColorStop, 8> stops;
261 GetStops(&stops, aGraphicOpacity);
263 uint32_t nStops = stops.Length();
265 // SVG specification says that no stops should be treated like
266 // the corresponding fill or stroke had "none" specified.
267 if (nStops == 0) {
268 return do_AddRef(new gfxPattern(DeviceColor()));
271 if (nStops == 1 || GradientVectorLengthIsZero()) {
272 // The gradient paints a single colour, using the stop-color of the last
273 // gradient step if there are more than one.
274 return do_AddRef(new gfxPattern(ToDeviceColor(stops.LastElement().mColor)));
277 // Get the transform list (if there is one). We do this after the returns
278 // above since this call can be expensive when "gradientUnits" is set to
279 // "objectBoundingBox" (since that requiring a GetBBox() call).
280 gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
281 if (patternMatrix.IsSingular()) {
282 return nullptr;
285 // revert any vector effect transform so that the gradient appears unchanged
286 if (aFillOrStroke == &nsStyleSVG::mStroke) {
287 gfxMatrix userToOuterSVG;
288 if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
289 patternMatrix *= userToOuterSVG;
293 if (!patternMatrix.Invert()) {
294 return nullptr;
297 RefPtr<gfxPattern> gradient = CreateGradient();
298 if (!gradient) {
299 return nullptr;
302 uint16_t aSpread = GetSpreadMethod();
303 if (aSpread == SVG_SPREADMETHOD_PAD) {
304 gradient->SetExtend(ExtendMode::CLAMP);
305 } else if (aSpread == SVG_SPREADMETHOD_REFLECT) {
306 gradient->SetExtend(ExtendMode::REFLECT);
307 } else if (aSpread == SVG_SPREADMETHOD_REPEAT) {
308 gradient->SetExtend(ExtendMode::REPEAT);
311 gradient->SetMatrix(patternMatrix);
313 if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) {
314 static constexpr auto interpolationMethod = StyleColorInterpolationMethod{
315 StyleColorSpace::SrgbLinear, StyleHueInterpolationMethod::Shorter};
316 SVGColorStopInterpolator interpolator(gradient, stops, interpolationMethod,
317 false);
318 interpolator.CreateStops();
319 } else {
320 // setup standard sRGB stops
321 for (const auto& stop : stops) {
322 gradient->AddColorStop(stop.mPosition, ToDeviceColor(stop.mColor));
326 return gradient.forget();
329 // Private (helper) methods
331 SVGGradientFrame* SVGGradientFrame::GetReferencedGradient() {
332 if (mNoHRefURI) {
333 return nullptr;
336 auto GetHref = [this](nsAString& aHref) {
337 dom::SVGGradientElement* grad =
338 static_cast<dom::SVGGradientElement*>(this->GetContent());
339 if (grad->mStringAttributes[dom::SVGGradientElement::HREF]
340 .IsExplicitlySet()) {
341 grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(aHref,
342 grad);
343 } else {
344 grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF].GetAnimValue(
345 aHref, grad);
347 this->mNoHRefURI = aHref.IsEmpty();
350 // We don't call SVGObserverUtils::RemoveTemplateObserver and set
351 // `mNoHRefURI = false` on failure since we want to be invalidated if the ID
352 // specified by our href starts resolving to a different/valid element.
354 return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref));
357 void SVGGradientFrame::GetStops(nsTArray<ColorStop>* aStops,
358 float aGraphicOpacity) {
359 float lastPosition = 0.0f;
360 for (const auto* stopFrame : mFrames) {
361 if (stopFrame->IsSVGStopFrame()) {
362 aStops->AppendElement(
363 GetStopInformation(stopFrame, aGraphicOpacity, lastPosition));
366 if (!aStops->IsEmpty()) {
367 return;
370 // Our gradient element doesn't have stops - try to "inherit" them
372 // Before we recurse, make sure we'll break reference loops and over long
373 // reference chains:
374 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
375 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
376 &sRefChainLengthCounter);
377 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
378 // Break reference chain
379 return;
382 SVGGradientFrame* next = GetReferencedGradient();
383 if (next) {
384 next->GetStops(aStops, aGraphicOpacity);
388 // -------------------------------------------------------------------------
389 // Linear Gradients
390 // -------------------------------------------------------------------------
392 NS_QUERYFRAME_HEAD(SVGLinearGradientFrame)
393 NS_QUERYFRAME_ENTRY(SVGLinearGradientFrame)
394 NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame)
396 #ifdef DEBUG
397 void SVGLinearGradientFrame::Init(nsIContent* aContent,
398 nsContainerFrame* aParent,
399 nsIFrame* aPrevInFlow) {
400 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::linearGradient),
401 "Content is not an SVG linearGradient");
403 SVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
405 #endif /* DEBUG */
407 nsresult SVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID,
408 nsAtom* aAttribute,
409 int32_t aModType) {
410 if (aNameSpaceID == kNameSpaceID_None &&
411 (aAttribute == nsGkAtoms::x1 || aAttribute == nsGkAtoms::y1 ||
412 aAttribute == nsGkAtoms::x2 || aAttribute == nsGkAtoms::y2)) {
413 SVGObserverUtils::InvalidateRenderingObservers(this);
416 return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
419 //----------------------------------------------------------------------
421 float SVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) {
422 dom::SVGLinearGradientElement* lengthElement = GetLinearGradientWithLength(
423 aIndex, static_cast<dom::SVGLinearGradientElement*>(GetContent()));
424 // We passed in mContent as a fallback, so, assuming mContent is non-null, the
425 // return value should also be non-null.
426 MOZ_ASSERT(lengthElement,
427 "Got unexpected null element from GetLinearGradientWithLength");
428 const SVGAnimatedLength& length = lengthElement->mLengthAttributes[aIndex];
430 // Object bounding box units are handled by setting the appropriate
431 // transform in GetGradientTransform, but we need to handle user
432 // space units as part of the individual Get* routines. Fixes 323669.
434 uint16_t gradientUnits = GetGradientUnits();
435 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
436 return SVGUtils::UserSpace(mSource, &length);
439 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
440 "Unknown gradientUnits type");
442 return length.GetAnimValueWithZoom(static_cast<SVGViewportElement*>(nullptr));
445 dom::SVGLinearGradientElement*
446 SVGLinearGradientFrame::GetLinearGradientWithLength(
447 uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) {
448 dom::SVGLinearGradientElement* thisElement =
449 static_cast<dom::SVGLinearGradientElement*>(GetContent());
450 const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex];
452 if (length.IsExplicitlySet()) {
453 return thisElement;
456 return SVGGradientFrame::GetLinearGradientWithLength(aIndex, aDefault);
459 bool SVGLinearGradientFrame::GradientVectorLengthIsZero() {
460 return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) ==
461 GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) &&
462 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) ==
463 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
466 already_AddRefed<gfxPattern> SVGLinearGradientFrame::CreateGradient() {
467 float x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1);
468 float y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1);
469 float x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2);
470 float y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
472 return do_AddRef(new gfxPattern(x1, y1, x2, y2));
475 // -------------------------------------------------------------------------
476 // Radial Gradients
477 // -------------------------------------------------------------------------
479 NS_QUERYFRAME_HEAD(SVGRadialGradientFrame)
480 NS_QUERYFRAME_ENTRY(SVGRadialGradientFrame)
481 NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame)
483 #ifdef DEBUG
484 void SVGRadialGradientFrame::Init(nsIContent* aContent,
485 nsContainerFrame* aParent,
486 nsIFrame* aPrevInFlow) {
487 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::radialGradient),
488 "Content is not an SVG radialGradient");
490 SVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
492 #endif /* DEBUG */
494 nsresult SVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID,
495 nsAtom* aAttribute,
496 int32_t aModType) {
497 if (aNameSpaceID == kNameSpaceID_None &&
498 (aAttribute == nsGkAtoms::r || aAttribute == nsGkAtoms::cx ||
499 aAttribute == nsGkAtoms::cy || aAttribute == nsGkAtoms::fx ||
500 aAttribute == nsGkAtoms::fy)) {
501 SVGObserverUtils::InvalidateRenderingObservers(this);
504 return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
507 //----------------------------------------------------------------------
509 float SVGRadialGradientFrame::GetLengthValue(uint32_t aIndex) {
510 dom::SVGRadialGradientElement* lengthElement = GetRadialGradientWithLength(
511 aIndex, static_cast<dom::SVGRadialGradientElement*>(GetContent()));
512 // We passed in mContent as a fallback, so, assuming mContent is non-null,
513 // the return value should also be non-null.
514 MOZ_ASSERT(lengthElement,
515 "Got unexpected null element from GetRadialGradientWithLength");
516 return GetLengthValueFromElement(aIndex, *lengthElement);
519 float SVGRadialGradientFrame::GetLengthValue(uint32_t aIndex,
520 float aDefaultValue) {
521 dom::SVGRadialGradientElement* lengthElement =
522 GetRadialGradientWithLength(aIndex, nullptr);
524 return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement)
525 : aDefaultValue;
528 float SVGRadialGradientFrame::GetLengthValueFromElement(
529 uint32_t aIndex, dom::SVGRadialGradientElement& aElement) {
530 const SVGAnimatedLength& length = aElement.mLengthAttributes[aIndex];
532 // Object bounding box units are handled by setting the appropriate
533 // transform in GetGradientTransform, but we need to handle user
534 // space units as part of the individual Get* routines. Fixes 323669.
536 uint16_t gradientUnits = GetGradientUnits();
537 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
538 return SVGUtils::UserSpace(mSource, &length);
541 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
542 "Unknown gradientUnits type");
544 return length.GetAnimValueWithZoom(static_cast<SVGViewportElement*>(nullptr));
547 dom::SVGRadialGradientElement*
548 SVGRadialGradientFrame::GetRadialGradientWithLength(
549 uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) {
550 dom::SVGRadialGradientElement* thisElement =
551 static_cast<dom::SVGRadialGradientElement*>(GetContent());
552 const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex];
554 if (length.IsExplicitlySet()) {
555 return thisElement;
558 return SVGGradientFrame::GetRadialGradientWithLength(aIndex, aDefault);
561 bool SVGRadialGradientFrame::GradientVectorLengthIsZero() {
562 float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
563 float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
564 float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
565 // If fx or fy are not set, use cx/cy instead
566 float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
567 float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
568 float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR);
569 return cx == fx && cy == fy && r == fr;
572 already_AddRefed<gfxPattern> SVGRadialGradientFrame::CreateGradient() {
573 float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
574 float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
575 float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
576 // If fx or fy are not set, use cx/cy instead
577 float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
578 float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
579 float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR);
581 return do_AddRef(new gfxPattern(fx, fy, fr, cx, cy, r));
584 } // namespace mozilla
586 // -------------------------------------------------------------------------
587 // Public functions
588 // -------------------------------------------------------------------------
590 nsIFrame* NS_NewSVGLinearGradientFrame(mozilla::PresShell* aPresShell,
591 mozilla::ComputedStyle* aStyle) {
592 return new (aPresShell)
593 mozilla::SVGLinearGradientFrame(aStyle, aPresShell->GetPresContext());
596 nsIFrame* NS_NewSVGRadialGradientFrame(mozilla::PresShell* aPresShell,
597 mozilla::ComputedStyle* aStyle) {
598 return new (aPresShell)
599 mozilla::SVGRadialGradientFrame(aStyle, aPresShell->GetPresContext());
602 namespace mozilla {
604 NS_IMPL_FRAMEARENA_HELPERS(SVGLinearGradientFrame)
605 NS_IMPL_FRAMEARENA_HELPERS(SVGRadialGradientFrame)
607 } // namespace mozilla