Bug 1858915 [wpt PR 42525] - Use taskcluster-run in infra tests, a=testonly
[gecko.git] / layout / svg / SVGGradientFrame.cpp
blob3465ba76a23fd716a1f452156276062f5635af9a
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 // XXX Tight coupling with content classes ahead!
27 using namespace mozilla::dom;
28 using namespace mozilla::dom::SVGGradientElement_Binding;
29 using namespace mozilla::dom::SVGUnitTypes_Binding;
30 using namespace mozilla::gfx;
32 namespace mozilla {
34 //----------------------------------------------------------------------
35 // Implementation
37 SVGGradientFrame::SVGGradientFrame(ComputedStyle* aStyle,
38 nsPresContext* aPresContext, ClassID aID)
39 : SVGPaintServerFrame(aStyle, aPresContext, aID),
40 mSource(nullptr),
41 mLoopFlag(false),
42 mNoHRefURI(false) {}
44 NS_QUERYFRAME_HEAD(SVGGradientFrame)
45 NS_QUERYFRAME_ENTRY(SVGGradientFrame)
46 NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame)
48 //----------------------------------------------------------------------
49 // nsIFrame methods:
51 nsresult SVGGradientFrame::AttributeChanged(int32_t aNameSpaceID,
52 nsAtom* aAttribute,
53 int32_t aModType) {
54 if (aNameSpaceID == kNameSpaceID_None &&
55 (aAttribute == nsGkAtoms::gradientUnits ||
56 aAttribute == nsGkAtoms::gradientTransform ||
57 aAttribute == nsGkAtoms::spreadMethod)) {
58 SVGObserverUtils::InvalidateRenderingObservers(this);
59 } else if ((aNameSpaceID == kNameSpaceID_XLink ||
60 aNameSpaceID == kNameSpaceID_None) &&
61 aAttribute == nsGkAtoms::href) {
62 // Blow away our reference, if any
63 SVGObserverUtils::RemoveTemplateObserver(this);
64 mNoHRefURI = false;
65 // And update whoever references us
66 SVGObserverUtils::InvalidateRenderingObservers(this);
69 return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
70 aModType);
73 //----------------------------------------------------------------------
75 uint16_t SVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
76 const SVGAnimatedEnumeration& thisEnum =
77 static_cast<dom::SVGGradientElement*>(GetContent())
78 ->mEnumAttributes[aIndex];
80 if (thisEnum.IsExplicitlySet()) {
81 return thisEnum.GetAnimValue();
84 // Before we recurse, make sure we'll break reference loops and over long
85 // reference chains:
86 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
87 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
88 &sRefChainLengthCounter);
89 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
90 // Break reference chain
91 return static_cast<dom::SVGGradientElement*>(aDefault)
92 ->mEnumAttributes[aIndex]
93 .GetAnimValue();
96 SVGGradientFrame* next = GetReferencedGradient();
98 return next ? next->GetEnumValue(aIndex, aDefault)
99 : static_cast<dom::SVGGradientElement*>(aDefault)
100 ->mEnumAttributes[aIndex]
101 .GetAnimValue();
104 uint16_t SVGGradientFrame::GetGradientUnits() {
105 // This getter is called every time the others are called - maybe cache it?
106 return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS);
109 uint16_t SVGGradientFrame::GetSpreadMethod() {
110 return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD);
113 const SVGAnimatedTransformList* SVGGradientFrame::GetGradientTransformList(
114 nsIContent* aDefault) {
115 SVGAnimatedTransformList* thisTransformList =
116 static_cast<dom::SVGGradientElement*>(GetContent())
117 ->GetAnimatedTransformList();
119 if (thisTransformList && thisTransformList->IsExplicitlySet())
120 return thisTransformList;
122 // Before we recurse, make sure we'll break reference loops and over long
123 // reference chains:
124 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
125 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
126 &sRefChainLengthCounter);
127 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
128 // Break reference chain
129 return static_cast<const dom::SVGGradientElement*>(aDefault)
130 ->mGradientTransform.get();
133 SVGGradientFrame* next = GetReferencedGradient();
135 return next ? next->GetGradientTransformList(aDefault)
136 : static_cast<const dom::SVGGradientElement*>(aDefault)
137 ->mGradientTransform.get();
140 gfxMatrix SVGGradientFrame::GetGradientTransform(
141 nsIFrame* aSource, const gfxRect* aOverrideBounds) {
142 gfxMatrix bboxMatrix;
144 uint16_t gradientUnits = GetGradientUnits();
145 if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) {
146 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
147 "Unknown gradientUnits type");
148 // objectBoundingBox is the default anyway
150 gfxRect bbox = aOverrideBounds
151 ? *aOverrideBounds
152 : SVGUtils::GetBBox(
153 aSource, SVGUtils::eUseFrameBoundsForOuterSVG |
154 SVGUtils::eBBoxIncludeFillGeometry);
155 bboxMatrix =
156 gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
159 const SVGAnimatedTransformList* animTransformList =
160 GetGradientTransformList(GetContent());
161 if (!animTransformList) {
162 return bboxMatrix;
165 gfxMatrix gradientTransform =
166 animTransformList->GetAnimValue().GetConsolidationMatrix();
167 return bboxMatrix.PreMultiply(gradientTransform);
170 dom::SVGLinearGradientElement* SVGGradientFrame::GetLinearGradientWithLength(
171 uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) {
172 // If this was a linear gradient with the required length, we would have
173 // already found it in SVGLinearGradientFrame::GetLinearGradientWithLength.
174 // Since we didn't find the length, continue looking down the chain.
176 // Before we recurse, make sure we'll break reference loops and over long
177 // reference chains:
178 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
179 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
180 &sRefChainLengthCounter);
181 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
182 // Break reference chain
183 return aDefault;
186 SVGGradientFrame* next = GetReferencedGradient();
187 return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault;
190 dom::SVGRadialGradientElement* SVGGradientFrame::GetRadialGradientWithLength(
191 uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) {
192 // If this was a radial gradient with the required length, we would have
193 // already found it in SVGRadialGradientFrame::GetRadialGradientWithLength.
194 // Since we didn't find the length, continue looking down the chain.
196 // Before we recurse, make sure we'll break reference loops and over long
197 // reference chains:
198 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
199 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
200 &sRefChainLengthCounter);
201 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
202 // Break reference chain
203 return aDefault;
206 SVGGradientFrame* next = GetReferencedGradient();
207 return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault;
210 //----------------------------------------------------------------------
211 // SVGPaintServerFrame methods:
213 // helper
214 static void GetStopInformation(nsIFrame* aStopFrame, float* aOffset,
215 nscolor* aStopColor, float* aStopOpacity) {
216 nsIContent* stopContent = aStopFrame->GetContent();
217 MOZ_ASSERT(stopContent && stopContent->IsSVGElement(nsGkAtoms::stop));
219 static_cast<SVGStopElement*>(stopContent)
220 ->GetAnimatedNumberValues(aOffset, nullptr);
222 const nsStyleSVGReset* styleSVGReset = aStopFrame->StyleSVGReset();
223 *aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f);
224 *aStopColor = styleSVGReset->mStopColor.CalcColor(aStopFrame);
225 *aStopOpacity = styleSVGReset->mStopOpacity;
228 already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern(
229 nsIFrame* aSource, const DrawTarget* aDrawTarget,
230 const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::*aFillOrStroke,
231 float aGraphicOpacity, imgDrawingParams& aImgParams,
232 const gfxRect* aOverrideBounds) {
233 uint16_t gradientUnits = GetGradientUnits();
234 MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX ||
235 gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE);
236 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
237 // Set mSource for this consumer.
238 // If this gradient is applied to text, our caller will be the glyph, which
239 // is not an element, so we need to get the parent
240 mSource = aSource->IsTextFrame() ? aSource->GetParent() : aSource;
243 AutoTArray<nsIFrame*, 8> stopFrames;
244 GetStopFrames(&stopFrames);
246 uint32_t nStops = stopFrames.Length();
248 // SVG specification says that no stops should be treated like
249 // the corresponding fill or stroke had "none" specified.
250 if (nStops == 0) {
251 RefPtr<gfxPattern> pattern = new gfxPattern(DeviceColor());
252 return do_AddRef(new gfxPattern(DeviceColor()));
255 if (nStops == 1 || GradientVectorLengthIsZero()) {
256 auto* lastStopFrame = stopFrames[nStops - 1];
257 const auto* svgReset = lastStopFrame->StyleSVGReset();
258 // The gradient paints a single colour, using the stop-color of the last
259 // gradient step if there are more than one.
260 float stopOpacity = svgReset->mStopOpacity;
261 nscolor stopColor = svgReset->mStopColor.CalcColor(lastStopFrame);
263 sRGBColor stopColor2 = sRGBColor::FromABGR(stopColor);
264 stopColor2.a *= stopOpacity * aGraphicOpacity;
265 return do_AddRef(new gfxPattern(ToDeviceColor(stopColor2)));
268 // Get the transform list (if there is one). We do this after the returns
269 // above since this call can be expensive when "gradientUnits" is set to
270 // "objectBoundingBox" (since that requiring a GetBBox() call).
271 gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
273 if (patternMatrix.IsSingular()) {
274 return nullptr;
277 // revert any vector effect transform so that the gradient appears unchanged
278 if (aFillOrStroke == &nsStyleSVG::mStroke) {
279 gfxMatrix userToOuterSVG;
280 if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
281 patternMatrix *= userToOuterSVG;
285 if (!patternMatrix.Invert()) {
286 return nullptr;
289 RefPtr<gfxPattern> gradient = CreateGradient();
290 if (!gradient) {
291 return nullptr;
294 uint16_t aSpread = GetSpreadMethod();
295 if (aSpread == SVG_SPREADMETHOD_PAD)
296 gradient->SetExtend(ExtendMode::CLAMP);
297 else if (aSpread == SVG_SPREADMETHOD_REFLECT)
298 gradient->SetExtend(ExtendMode::REFLECT);
299 else if (aSpread == SVG_SPREADMETHOD_REPEAT)
300 gradient->SetExtend(ExtendMode::REPEAT);
302 gradient->SetMatrix(patternMatrix);
304 // setup stops
305 float lastOffset = 0.0f;
307 for (uint32_t i = 0; i < nStops; i++) {
308 float offset, stopOpacity;
309 nscolor stopColor;
311 GetStopInformation(stopFrames[i], &offset, &stopColor, &stopOpacity);
313 if (offset < lastOffset)
314 offset = lastOffset;
315 else
316 lastOffset = offset;
318 sRGBColor stopColor2 = sRGBColor::FromABGR(stopColor);
319 stopColor2.a *= stopOpacity * aGraphicOpacity;
320 gradient->AddColorStop(offset, ToDeviceColor(stopColor2));
323 return gradient.forget();
326 // Private (helper) methods
328 SVGGradientFrame* SVGGradientFrame::GetReferencedGradient() {
329 if (mNoHRefURI) {
330 return nullptr;
333 auto GetHref = [this](nsAString& aHref) {
334 dom::SVGGradientElement* grad =
335 static_cast<dom::SVGGradientElement*>(this->GetContent());
336 if (grad->mStringAttributes[dom::SVGGradientElement::HREF]
337 .IsExplicitlySet()) {
338 grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(aHref,
339 grad);
340 } else {
341 grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF].GetAnimValue(
342 aHref, grad);
344 this->mNoHRefURI = aHref.IsEmpty();
347 // We don't call SVGObserverUtils::RemoveTemplateObserver and set
348 // `mNoHRefURI = false` on failure since we want to be invalidated if the ID
349 // specified by our href starts resolving to a different/valid element.
351 return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref));
354 void SVGGradientFrame::GetStopFrames(nsTArray<nsIFrame*>* aStopFrames) {
355 nsIFrame* stopFrame = nullptr;
356 for (stopFrame = mFrames.FirstChild(); stopFrame;
357 stopFrame = stopFrame->GetNextSibling()) {
358 if (stopFrame->IsSVGStopFrame()) {
359 aStopFrames->AppendElement(stopFrame);
362 if (aStopFrames->Length() > 0) {
363 return;
366 // Our gradient element doesn't have stops - try to "inherit" them
368 // Before we recurse, make sure we'll break reference loops and over long
369 // reference chains:
370 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
371 AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
372 &sRefChainLengthCounter);
373 if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
374 // Break reference chain
375 return;
378 SVGGradientFrame* next = GetReferencedGradient();
379 if (next) {
380 next->GetStopFrames(aStopFrames);
384 // -------------------------------------------------------------------------
385 // Linear Gradients
386 // -------------------------------------------------------------------------
388 NS_QUERYFRAME_HEAD(SVGLinearGradientFrame)
389 NS_QUERYFRAME_ENTRY(SVGLinearGradientFrame)
390 NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame)
392 #ifdef DEBUG
393 void SVGLinearGradientFrame::Init(nsIContent* aContent,
394 nsContainerFrame* aParent,
395 nsIFrame* aPrevInFlow) {
396 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::linearGradient),
397 "Content is not an SVG linearGradient");
399 SVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
401 #endif /* DEBUG */
403 nsresult SVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID,
404 nsAtom* aAttribute,
405 int32_t aModType) {
406 if (aNameSpaceID == kNameSpaceID_None &&
407 (aAttribute == nsGkAtoms::x1 || aAttribute == nsGkAtoms::y1 ||
408 aAttribute == nsGkAtoms::x2 || aAttribute == nsGkAtoms::y2)) {
409 SVGObserverUtils::InvalidateRenderingObservers(this);
412 return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
415 //----------------------------------------------------------------------
417 float SVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) {
418 dom::SVGLinearGradientElement* lengthElement = GetLinearGradientWithLength(
419 aIndex, static_cast<dom::SVGLinearGradientElement*>(GetContent()));
420 // We passed in mContent as a fallback, so, assuming mContent is non-null, the
421 // return value should also be non-null.
422 MOZ_ASSERT(lengthElement,
423 "Got unexpected null element from GetLinearGradientWithLength");
424 const SVGAnimatedLength& length = lengthElement->mLengthAttributes[aIndex];
426 // Object bounding box units are handled by setting the appropriate
427 // transform in GetGradientTransform, but we need to handle user
428 // space units as part of the individual Get* routines. Fixes 323669.
430 uint16_t gradientUnits = GetGradientUnits();
431 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
432 return SVGUtils::UserSpace(mSource, &length);
435 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
436 "Unknown gradientUnits type");
438 return length.GetAnimValue(static_cast<SVGViewportElement*>(nullptr));
441 dom::SVGLinearGradientElement*
442 SVGLinearGradientFrame::GetLinearGradientWithLength(
443 uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) {
444 dom::SVGLinearGradientElement* thisElement =
445 static_cast<dom::SVGLinearGradientElement*>(GetContent());
446 const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex];
448 if (length.IsExplicitlySet()) {
449 return thisElement;
452 return SVGGradientFrame::GetLinearGradientWithLength(aIndex, aDefault);
455 bool SVGLinearGradientFrame::GradientVectorLengthIsZero() {
456 return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) ==
457 GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) &&
458 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) ==
459 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
462 already_AddRefed<gfxPattern> SVGLinearGradientFrame::CreateGradient() {
463 float x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1);
464 float y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1);
465 float x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2);
466 float y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
468 RefPtr<gfxPattern> pattern = new gfxPattern(x1, y1, x2, y2);
469 return pattern.forget();
472 // -------------------------------------------------------------------------
473 // Radial Gradients
474 // -------------------------------------------------------------------------
476 NS_QUERYFRAME_HEAD(SVGRadialGradientFrame)
477 NS_QUERYFRAME_ENTRY(SVGRadialGradientFrame)
478 NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame)
480 #ifdef DEBUG
481 void SVGRadialGradientFrame::Init(nsIContent* aContent,
482 nsContainerFrame* aParent,
483 nsIFrame* aPrevInFlow) {
484 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::radialGradient),
485 "Content is not an SVG radialGradient");
487 SVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
489 #endif /* DEBUG */
491 nsresult SVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID,
492 nsAtom* aAttribute,
493 int32_t aModType) {
494 if (aNameSpaceID == kNameSpaceID_None &&
495 (aAttribute == nsGkAtoms::r || aAttribute == nsGkAtoms::cx ||
496 aAttribute == nsGkAtoms::cy || aAttribute == nsGkAtoms::fx ||
497 aAttribute == nsGkAtoms::fy)) {
498 SVGObserverUtils::InvalidateRenderingObservers(this);
501 return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
504 //----------------------------------------------------------------------
506 float SVGRadialGradientFrame::GetLengthValue(uint32_t aIndex) {
507 dom::SVGRadialGradientElement* lengthElement = GetRadialGradientWithLength(
508 aIndex, static_cast<dom::SVGRadialGradientElement*>(GetContent()));
509 // We passed in mContent as a fallback, so, assuming mContent is non-null,
510 // the return value should also be non-null.
511 MOZ_ASSERT(lengthElement,
512 "Got unexpected null element from GetRadialGradientWithLength");
513 return GetLengthValueFromElement(aIndex, *lengthElement);
516 float SVGRadialGradientFrame::GetLengthValue(uint32_t aIndex,
517 float aDefaultValue) {
518 dom::SVGRadialGradientElement* lengthElement =
519 GetRadialGradientWithLength(aIndex, nullptr);
521 return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement)
522 : aDefaultValue;
525 float SVGRadialGradientFrame::GetLengthValueFromElement(
526 uint32_t aIndex, dom::SVGRadialGradientElement& aElement) {
527 const SVGAnimatedLength& length = aElement.mLengthAttributes[aIndex];
529 // Object bounding box units are handled by setting the appropriate
530 // transform in GetGradientTransform, but we need to handle user
531 // space units as part of the individual Get* routines. Fixes 323669.
533 uint16_t gradientUnits = GetGradientUnits();
534 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
535 return SVGUtils::UserSpace(mSource, &length);
538 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
539 "Unknown gradientUnits type");
541 return length.GetAnimValue(static_cast<SVGViewportElement*>(nullptr));
544 dom::SVGRadialGradientElement*
545 SVGRadialGradientFrame::GetRadialGradientWithLength(
546 uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) {
547 dom::SVGRadialGradientElement* thisElement =
548 static_cast<dom::SVGRadialGradientElement*>(GetContent());
549 const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex];
551 if (length.IsExplicitlySet()) {
552 return thisElement;
555 return SVGGradientFrame::GetRadialGradientWithLength(aIndex, aDefault);
558 bool SVGRadialGradientFrame::GradientVectorLengthIsZero() {
559 float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
560 float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
561 float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
562 // If fx or fy are not set, use cx/cy instead
563 float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
564 float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
565 float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR);
566 return cx == fx && cy == fy && r == fr;
569 already_AddRefed<gfxPattern> SVGRadialGradientFrame::CreateGradient() {
570 float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
571 float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
572 float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
573 // If fx or fy are not set, use cx/cy instead
574 float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
575 float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
576 float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR);
578 RefPtr<gfxPattern> pattern = new gfxPattern(fx, fy, fr, cx, cy, r);
579 return pattern.forget();
582 } // namespace mozilla
584 // -------------------------------------------------------------------------
585 // Public functions
586 // -------------------------------------------------------------------------
588 nsIFrame* NS_NewSVGLinearGradientFrame(mozilla::PresShell* aPresShell,
589 mozilla::ComputedStyle* aStyle) {
590 return new (aPresShell)
591 mozilla::SVGLinearGradientFrame(aStyle, aPresShell->GetPresContext());
594 nsIFrame* NS_NewSVGRadialGradientFrame(mozilla::PresShell* aPresShell,
595 mozilla::ComputedStyle* aStyle) {
596 return new (aPresShell)
597 mozilla::SVGRadialGradientFrame(aStyle, aPresShell->GetPresContext());
600 namespace mozilla {
602 NS_IMPL_FRAMEARENA_HELPERS(SVGLinearGradientFrame)
603 NS_IMPL_FRAMEARENA_HELPERS(SVGRadialGradientFrame)
605 } // namespace mozilla