1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 // Main header first:
7 #include "nsSVGGradientFrame.h"
8 #include <algorithm>
9 
10 // Keep others in (case-insensitive) order:
11 #include "gfxPattern.h"
12 #include "mozilla/dom/SVGGradientElement.h"
13 #include "mozilla/dom/SVGStopElement.h"
14 #include "nsContentUtils.h"
15 #include "nsSVGEffects.h"
16 #include "nsSVGAnimatedTransformList.h"
17 
18 // XXX Tight coupling with content classes ahead!
19 
20 using namespace mozilla;
21 using namespace mozilla::dom;
22 using namespace mozilla::gfx;
23 
24 //----------------------------------------------------------------------
25 // Helper classes
26 
27 class MOZ_RAII nsSVGGradientFrame::AutoGradientReferencer
28 {
29 public:
AutoGradientReferencer(nsSVGGradientFrame * aFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM)30   explicit AutoGradientReferencer(nsSVGGradientFrame *aFrame
31                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
32     : mFrame(aFrame)
33   {
34     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
35     // Reference loops should normally be detected in advance and handled, so
36     // we're not expecting to encounter them here
37     MOZ_ASSERT(!mFrame->mLoopFlag, "Undetected reference loop!");
38     mFrame->mLoopFlag = true;
39   }
~AutoGradientReferencer()40   ~AutoGradientReferencer() {
41     mFrame->mLoopFlag = false;
42   }
43 private:
44   nsSVGGradientFrame *mFrame;
45   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
46 };
47 
48 //----------------------------------------------------------------------
49 // Implementation
50 
nsSVGGradientFrame(nsStyleContext * aContext)51 nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext)
52   : nsSVGPaintServerFrame(aContext)
53   , mLoopFlag(false)
54   , mNoHRefURI(false)
55 {
56 }
57 
58 //----------------------------------------------------------------------
59 // nsIFrame methods:
60 
61 nsresult
AttributeChanged(int32_t aNameSpaceID,nsIAtom * aAttribute,int32_t aModType)62 nsSVGGradientFrame::AttributeChanged(int32_t         aNameSpaceID,
63                                      nsIAtom*        aAttribute,
64                                      int32_t         aModType)
65 {
66   if (aNameSpaceID == kNameSpaceID_None &&
67       (aAttribute == nsGkAtoms::gradientUnits ||
68        aAttribute == nsGkAtoms::gradientTransform ||
69        aAttribute == nsGkAtoms::spreadMethod)) {
70     nsSVGEffects::InvalidateDirectRenderingObservers(this);
71   } else if ((aNameSpaceID == kNameSpaceID_XLink ||
72               aNameSpaceID == kNameSpaceID_None) &&
73              aAttribute == nsGkAtoms::href) {
74     // Blow away our reference, if any
75     Properties().Delete(nsSVGEffects::HrefAsPaintingProperty());
76     mNoHRefURI = false;
77     // And update whoever references us
78     nsSVGEffects::InvalidateDirectRenderingObservers(this);
79   }
80 
81   return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID,
82                                                  aAttribute, aModType);
83 }
84 
85 //----------------------------------------------------------------------
86 
87 uint16_t
GetEnumValue(uint32_t aIndex,nsIContent * aDefault)88 nsSVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
89 {
90   const nsSVGEnum& thisEnum =
91     static_cast<dom::SVGGradientElement*>(mContent)->mEnumAttributes[aIndex];
92 
93   if (thisEnum.IsExplicitlySet())
94     return thisEnum.GetAnimValue();
95 
96   AutoGradientReferencer gradientRef(this);
97 
98   nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
99   return next ? next->GetEnumValue(aIndex, aDefault) :
100     static_cast<dom::SVGGradientElement*>(aDefault)->
101       mEnumAttributes[aIndex].GetAnimValue();
102 }
103 
104 uint16_t
GetGradientUnits()105 nsSVGGradientFrame::GetGradientUnits()
106 {
107   // This getter is called every time the others are called - maybe cache it?
108   return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS);
109 }
110 
111 uint16_t
GetSpreadMethod()112 nsSVGGradientFrame::GetSpreadMethod()
113 {
114   return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD);
115 }
116 
117 const nsSVGAnimatedTransformList*
GetGradientTransformList(nsIContent * aDefault)118 nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault)
119 {
120   nsSVGAnimatedTransformList *thisTransformList =
121     static_cast<dom::SVGGradientElement*>(mContent)->GetAnimatedTransformList();
122 
123   if (thisTransformList && thisTransformList->IsExplicitlySet())
124     return thisTransformList;
125 
126   AutoGradientReferencer gradientRef(this);
127 
128   nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
129   return next ? next->GetGradientTransformList(aDefault) :
130     static_cast<const dom::SVGGradientElement*>(aDefault)
131       ->mGradientTransform.get();
132 }
133 
134 gfxMatrix
GetGradientTransform(nsIFrame * aSource,const gfxRect * aOverrideBounds)135 nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource,
136                                          const gfxRect *aOverrideBounds)
137 {
138   gfxMatrix bboxMatrix;
139 
140   uint16_t gradientUnits = GetGradientUnits();
141   if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) {
142     NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
143                  "Unknown gradientUnits type");
144     // objectBoundingBox is the default anyway
145 
146     gfxRect bbox =
147       aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource);
148     bboxMatrix =
149       gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
150   }
151 
152   const nsSVGAnimatedTransformList* animTransformList =
153     GetGradientTransformList(mContent);
154   if (!animTransformList)
155     return bboxMatrix;
156 
157   gfxMatrix gradientTransform =
158     animTransformList->GetAnimValue().GetConsolidationMatrix();
159   return bboxMatrix.PreMultiply(gradientTransform);
160 }
161 
162 dom::SVGLinearGradientElement*
GetLinearGradientWithLength(uint32_t aIndex,dom::SVGLinearGradientElement * aDefault)163 nsSVGGradientFrame::GetLinearGradientWithLength(uint32_t aIndex,
164   dom::SVGLinearGradientElement* aDefault)
165 {
166   // If this was a linear gradient with the required length, we would have
167   // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength.
168   // Since we didn't find the length, continue looking down the chain.
169 
170   AutoGradientReferencer gradientRef(this);
171 
172   nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
173   return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault;
174 }
175 
176 dom::SVGRadialGradientElement*
GetRadialGradientWithLength(uint32_t aIndex,dom::SVGRadialGradientElement * aDefault)177 nsSVGGradientFrame::GetRadialGradientWithLength(uint32_t aIndex,
178   dom::SVGRadialGradientElement* aDefault)
179 {
180   // If this was a radial gradient with the required length, we would have
181   // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength.
182   // Since we didn't find the length, continue looking down the chain.
183 
184   AutoGradientReferencer gradientRef(this);
185 
186   nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
187   return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault;
188 }
189 
190 //----------------------------------------------------------------------
191 // nsSVGPaintServerFrame methods:
192 
193 //helper
GetStopInformation(nsIFrame * aStopFrame,float * aOffset,nscolor * aStopColor,float * aStopOpacity)194 static void GetStopInformation(nsIFrame* aStopFrame,
195                                float *aOffset,
196                                nscolor *aStopColor,
197                                float *aStopOpacity)
198 {
199   nsIContent* stopContent = aStopFrame->GetContent();
200   MOZ_ASSERT(stopContent && stopContent->IsSVGElement(nsGkAtoms::stop));
201 
202   static_cast<SVGStopElement*>(stopContent)->
203     GetAnimatedNumberValues(aOffset, nullptr);
204 
205   *aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f);
206   *aStopColor = aStopFrame->StyleSVGReset()->mStopColor;
207   *aStopOpacity = aStopFrame->StyleSVGReset()->mStopOpacity;
208 }
209 
210 already_AddRefed<gfxPattern>
GetPaintServerPattern(nsIFrame * aSource,const DrawTarget * aDrawTarget,const gfxMatrix & aContextMatrix,nsStyleSVGPaint nsStyleSVG::* aFillOrStroke,float aGraphicOpacity,const gfxRect * aOverrideBounds)211 nsSVGGradientFrame::GetPaintServerPattern(nsIFrame* aSource,
212                                           const DrawTarget* aDrawTarget,
213                                           const gfxMatrix& aContextMatrix,
214                                           nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
215                                           float aGraphicOpacity,
216                                           const gfxRect* aOverrideBounds)
217 {
218   uint16_t gradientUnits = GetGradientUnits();
219   MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX ||
220              gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE);
221   if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
222     // Set mSource for this consumer.
223     // If this gradient is applied to text, our caller will be the glyph, which
224     // is not an element, so we need to get the parent
225     mSource = aSource->GetContent()->IsNodeOfType(nsINode::eTEXT) ?
226                 aSource->GetParent() : aSource;
227   }
228 
229   AutoTArray<nsIFrame*,8> stopFrames;
230   GetStopFrames(&stopFrames);
231 
232   uint32_t nStops = stopFrames.Length();
233 
234   // SVG specification says that no stops should be treated like
235   // the corresponding fill or stroke had "none" specified.
236   if (nStops == 0) {
237     RefPtr<gfxPattern> pattern = new gfxPattern(Color());
238     return pattern.forget();
239   }
240 
241   if (nStops == 1 || GradientVectorLengthIsZero()) {
242     // The gradient paints a single colour, using the stop-color of the last
243     // gradient step if there are more than one.
244     float stopOpacity = stopFrames[nStops-1]->StyleSVGReset()->mStopOpacity;
245     nscolor stopColor = stopFrames[nStops-1]->StyleSVGReset()->mStopColor;
246 
247     Color stopColor2 = Color::FromABGR(stopColor);
248     stopColor2.a *= stopOpacity * aGraphicOpacity;
249     RefPtr<gfxPattern> pattern = new gfxPattern(stopColor2);
250     return pattern.forget();
251   }
252 
253   // Get the transform list (if there is one). We do this after the returns
254   // above since this call can be expensive when "gradientUnits" is set to
255   // "objectBoundingBox" (since that requiring a GetBBox() call).
256   gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
257 
258   if (patternMatrix.IsSingular()) {
259     return nullptr;
260   }
261 
262   // revert any vector effect transform so that the gradient appears unchanged
263   if (aFillOrStroke == &nsStyleSVG::mStroke) {
264     gfxMatrix userToOuterSVG;
265     if (nsSVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
266       patternMatrix *= userToOuterSVG;
267     }
268   }
269 
270   if (!patternMatrix.Invert()) {
271     return nullptr;
272   }
273 
274   RefPtr<gfxPattern> gradient = CreateGradient();
275   if (!gradient || gradient->CairoStatus())
276     return nullptr;
277 
278   uint16_t aSpread = GetSpreadMethod();
279   if (aSpread == SVG_SPREADMETHOD_PAD)
280     gradient->SetExtend(ExtendMode::CLAMP);
281   else if (aSpread == SVG_SPREADMETHOD_REFLECT)
282     gradient->SetExtend(ExtendMode::REFLECT);
283   else if (aSpread == SVG_SPREADMETHOD_REPEAT)
284     gradient->SetExtend(ExtendMode::REPEAT);
285 
286   gradient->SetMatrix(patternMatrix);
287 
288   // setup stops
289   float lastOffset = 0.0f;
290 
291   for (uint32_t i = 0; i < nStops; i++) {
292     float offset, stopOpacity;
293     nscolor stopColor;
294 
295     GetStopInformation(stopFrames[i], &offset, &stopColor, &stopOpacity);
296 
297     if (offset < lastOffset)
298       offset = lastOffset;
299     else
300       lastOffset = offset;
301 
302     Color stopColor2 = Color::FromABGR(stopColor);
303     stopColor2.a *= stopOpacity * aGraphicOpacity;
304     gradient->AddColorStop(offset, stopColor2);
305   }
306 
307   return gradient.forget();
308 }
309 
310 // Private (helper) methods
311 
312 nsSVGGradientFrame *
GetReferencedGradient()313 nsSVGGradientFrame::GetReferencedGradient()
314 {
315   if (mNoHRefURI)
316     return nullptr;
317 
318   nsSVGPaintingProperty *property =
319     Properties().Get(nsSVGEffects::HrefAsPaintingProperty());
320 
321   if (!property) {
322     // Fetch our gradient element's href or xlink:href attribute
323     dom::SVGGradientElement* grad =
324       static_cast<dom::SVGGradientElement*>(mContent);
325     nsAutoString href;
326     if (grad->mStringAttributes[dom::SVGGradientElement::HREF]
327           .IsExplicitlySet()) {
328       grad->mStringAttributes[dom::SVGGradientElement::HREF]
329         .GetAnimValue(href, grad);
330     } else {
331       grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF]
332         .GetAnimValue(href, grad);
333     }
334 
335     if (href.IsEmpty()) {
336       mNoHRefURI = true;
337       return nullptr; // no URL
338     }
339 
340     // Convert href to an nsIURI
341     nsCOMPtr<nsIURI> targetURI;
342     nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
343     nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
344                                               mContent->GetUncomposedDoc(), base);
345 
346     property =
347       nsSVGEffects::GetPaintingProperty(targetURI, this,
348                                         nsSVGEffects::HrefAsPaintingProperty());
349     if (!property)
350       return nullptr;
351   }
352 
353   nsIFrame *result = property->GetReferencedFrame();
354   if (!result)
355     return nullptr;
356 
357   nsIAtom* frameType = result->GetType();
358   if (frameType != nsGkAtoms::svgLinearGradientFrame &&
359       frameType != nsGkAtoms::svgRadialGradientFrame)
360     return nullptr;
361 
362   return static_cast<nsSVGGradientFrame*>(result);
363 }
364 
365 nsSVGGradientFrame *
GetReferencedGradientIfNotInUse()366 nsSVGGradientFrame::GetReferencedGradientIfNotInUse()
367 {
368   nsSVGGradientFrame *referenced = GetReferencedGradient();
369   if (!referenced)
370     return nullptr;
371 
372   if (referenced->mLoopFlag) {
373     // XXXjwatt: we should really send an error to the JavaScript Console here:
374     NS_WARNING("gradient reference loop detected while inheriting attribute!");
375     return nullptr;
376   }
377 
378   return referenced;
379 }
380 
381 void
GetStopFrames(nsTArray<nsIFrame * > * aStopFrames)382 nsSVGGradientFrame::GetStopFrames(nsTArray<nsIFrame*>* aStopFrames)
383 {
384   nsIFrame *stopFrame = nullptr;
385   for (stopFrame = mFrames.FirstChild(); stopFrame;
386        stopFrame = stopFrame->GetNextSibling()) {
387     if (stopFrame->GetType() == nsGkAtoms::svgStopFrame) {
388       aStopFrames->AppendElement(stopFrame);
389     }
390   }
391   if (aStopFrames->Length() > 0) {
392     return;
393   }
394 
395   // Our gradient element doesn't have stops - try to "inherit" them
396 
397   AutoGradientReferencer gradientRef(this);
398   nsSVGGradientFrame* next = GetReferencedGradientIfNotInUse();
399   if (!next) {
400     return;
401   }
402 
403   return next->GetStopFrames(aStopFrames);
404 }
405 
406 // -------------------------------------------------------------------------
407 // Linear Gradients
408 // -------------------------------------------------------------------------
409 
410 #ifdef DEBUG
411 void
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)412 nsSVGLinearGradientFrame::Init(nsIContent*       aContent,
413                                nsContainerFrame* aParent,
414                                nsIFrame*         aPrevInFlow)
415 {
416   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::linearGradient),
417                "Content is not an SVG linearGradient");
418 
419   nsSVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
420 }
421 #endif /* DEBUG */
422 
423 nsIAtom*
GetType() const424 nsSVGLinearGradientFrame::GetType() const
425 {
426   return nsGkAtoms::svgLinearGradientFrame;
427 }
428 
429 nsresult
AttributeChanged(int32_t aNameSpaceID,nsIAtom * aAttribute,int32_t aModType)430 nsSVGLinearGradientFrame::AttributeChanged(int32_t         aNameSpaceID,
431                                            nsIAtom*        aAttribute,
432                                            int32_t         aModType)
433 {
434   if (aNameSpaceID == kNameSpaceID_None &&
435       (aAttribute == nsGkAtoms::x1 ||
436        aAttribute == nsGkAtoms::y1 ||
437        aAttribute == nsGkAtoms::x2 ||
438        aAttribute == nsGkAtoms::y2)) {
439     nsSVGEffects::InvalidateDirectRenderingObservers(this);
440   }
441 
442   return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
443                                               aAttribute, aModType);
444 }
445 
446 //----------------------------------------------------------------------
447 
448 float
GetLengthValue(uint32_t aIndex)449 nsSVGLinearGradientFrame::GetLengthValue(uint32_t aIndex)
450 {
451   dom::SVGLinearGradientElement* lengthElement =
452     GetLinearGradientWithLength(aIndex,
453       static_cast<dom::SVGLinearGradientElement*>(mContent));
454   // We passed in mContent as a fallback, so, assuming mContent is non-null, the
455   // return value should also be non-null.
456   MOZ_ASSERT(lengthElement,
457     "Got unexpected null element from GetLinearGradientWithLength");
458   const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex];
459 
460   // Object bounding box units are handled by setting the appropriate
461   // transform in GetGradientTransform, but we need to handle user
462   // space units as part of the individual Get* routines.  Fixes 323669.
463 
464   uint16_t gradientUnits = GetGradientUnits();
465   if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
466     return nsSVGUtils::UserSpace(mSource, &length);
467   }
468 
469   NS_ASSERTION(
470     gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
471     "Unknown gradientUnits type");
472 
473   return length.GetAnimValue(static_cast<SVGSVGElement*>(nullptr));
474 }
475 
476 dom::SVGLinearGradientElement*
GetLinearGradientWithLength(uint32_t aIndex,dom::SVGLinearGradientElement * aDefault)477 nsSVGLinearGradientFrame::GetLinearGradientWithLength(uint32_t aIndex,
478   dom::SVGLinearGradientElement* aDefault)
479 {
480   dom::SVGLinearGradientElement* thisElement =
481     static_cast<dom::SVGLinearGradientElement*>(mContent);
482   const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
483 
484   if (length.IsExplicitlySet()) {
485     return thisElement;
486   }
487 
488   return nsSVGGradientFrame::GetLinearGradientWithLength(aIndex, aDefault);
489 }
490 
491 bool
GradientVectorLengthIsZero()492 nsSVGLinearGradientFrame::GradientVectorLengthIsZero()
493 {
494   return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) ==
495          GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) &&
496          GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) ==
497          GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
498 }
499 
500 already_AddRefed<gfxPattern>
CreateGradient()501 nsSVGLinearGradientFrame::CreateGradient()
502 {
503   float x1, y1, x2, y2;
504 
505   x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1);
506   y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1);
507   x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2);
508   y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
509 
510   RefPtr<gfxPattern> pattern = new gfxPattern(x1, y1, x2, y2);
511   return pattern.forget();
512 }
513 
514 // -------------------------------------------------------------------------
515 // Radial Gradients
516 // -------------------------------------------------------------------------
517 
518 #ifdef DEBUG
519 void
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)520 nsSVGRadialGradientFrame::Init(nsIContent*       aContent,
521                                nsContainerFrame* aParent,
522                                nsIFrame*         aPrevInFlow)
523 {
524   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::radialGradient),
525                "Content is not an SVG radialGradient");
526 
527   nsSVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
528 }
529 #endif /* DEBUG */
530 
531 nsIAtom*
GetType() const532 nsSVGRadialGradientFrame::GetType() const
533 {
534   return nsGkAtoms::svgRadialGradientFrame;
535 }
536 
537 nsresult
AttributeChanged(int32_t aNameSpaceID,nsIAtom * aAttribute,int32_t aModType)538 nsSVGRadialGradientFrame::AttributeChanged(int32_t         aNameSpaceID,
539                                            nsIAtom*        aAttribute,
540                                            int32_t         aModType)
541 {
542   if (aNameSpaceID == kNameSpaceID_None &&
543       (aAttribute == nsGkAtoms::r ||
544        aAttribute == nsGkAtoms::cx ||
545        aAttribute == nsGkAtoms::cy ||
546        aAttribute == nsGkAtoms::fx ||
547        aAttribute == nsGkAtoms::fy)) {
548     nsSVGEffects::InvalidateDirectRenderingObservers(this);
549   }
550 
551   return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
552                                               aAttribute, aModType);
553 }
554 
555 //----------------------------------------------------------------------
556 
557 float
GetLengthValue(uint32_t aIndex)558 nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex)
559 {
560   dom::SVGRadialGradientElement* lengthElement =
561     GetRadialGradientWithLength(aIndex,
562       static_cast<dom::SVGRadialGradientElement*>(mContent));
563   // We passed in mContent as a fallback, so, assuming mContent is non-null,
564   // the return value should also be non-null.
565   MOZ_ASSERT(lengthElement,
566     "Got unexpected null element from GetRadialGradientWithLength");
567   return GetLengthValueFromElement(aIndex, *lengthElement);
568 }
569 
570 float
GetLengthValue(uint32_t aIndex,float aDefaultValue)571 nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, float aDefaultValue)
572 {
573   dom::SVGRadialGradientElement* lengthElement =
574     GetRadialGradientWithLength(aIndex, nullptr);
575 
576   return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement)
577                        : aDefaultValue;
578 }
579 
580 float
GetLengthValueFromElement(uint32_t aIndex,dom::SVGRadialGradientElement & aElement)581 nsSVGRadialGradientFrame::GetLengthValueFromElement(uint32_t aIndex,
582   dom::SVGRadialGradientElement& aElement)
583 {
584   const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex];
585 
586   // Object bounding box units are handled by setting the appropriate
587   // transform in GetGradientTransform, but we need to handle user
588   // space units as part of the individual Get* routines.  Fixes 323669.
589 
590   uint16_t gradientUnits = GetGradientUnits();
591   if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
592     return nsSVGUtils::UserSpace(mSource, &length);
593   }
594 
595   NS_ASSERTION(
596     gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
597     "Unknown gradientUnits type");
598 
599   return length.GetAnimValue(static_cast<SVGSVGElement*>(nullptr));
600 }
601 
602 dom::SVGRadialGradientElement*
GetRadialGradientWithLength(uint32_t aIndex,dom::SVGRadialGradientElement * aDefault)603 nsSVGRadialGradientFrame::GetRadialGradientWithLength(uint32_t aIndex,
604   dom::SVGRadialGradientElement* aDefault)
605 {
606   dom::SVGRadialGradientElement* thisElement =
607     static_cast<dom::SVGRadialGradientElement*>(mContent);
608   const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
609 
610   if (length.IsExplicitlySet()) {
611     return thisElement;
612   }
613 
614   return nsSVGGradientFrame::GetRadialGradientWithLength(aIndex, aDefault);
615 }
616 
617 bool
GradientVectorLengthIsZero()618 nsSVGRadialGradientFrame::GradientVectorLengthIsZero()
619 {
620   return GetLengthValue(dom::SVGRadialGradientElement::ATTR_R) == 0;
621 }
622 
623 already_AddRefed<gfxPattern>
CreateGradient()624 nsSVGRadialGradientFrame::CreateGradient()
625 {
626   float cx, cy, r, fx, fy;
627 
628   cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
629   cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
630   r  = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
631   // If fx or fy are not set, use cx/cy instead
632   fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
633   fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
634 
635   if (fx != cx || fy != cy) {
636     // The focal point (fFx and fFy) must be clamped to be *inside* - not on -
637     // the circumference of the gradient or we'll get rendering anomalies. We
638     // calculate the distance from the focal point to the gradient center and
639     // make sure it is *less* than the gradient radius.
640     // 1/128 is the limit of the fractional part of cairo's 24.8 fixed point
641     // representation divided by 2 to ensure that we get different cairo
642     // fractions
643     double dMax = std::max(0.0, r - 1.0/128);
644     float dx = fx - cx;
645     float dy = fy - cy;
646     double d = sqrt((dx * dx) + (dy * dy));
647     if (d > dMax) {
648       double angle = atan2(dy, dx);
649       fx = (float)(dMax * cos(angle)) + cx;
650       fy = (float)(dMax * sin(angle)) + cy;
651     }
652   }
653 
654   RefPtr<gfxPattern> pattern = new gfxPattern(fx, fy, 0, cx, cy, r);
655   return pattern.forget();
656 }
657 
658 // -------------------------------------------------------------------------
659 // Public functions
660 // -------------------------------------------------------------------------
661 
662 nsIFrame*
NS_NewSVGLinearGradientFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)663 NS_NewSVGLinearGradientFrame(nsIPresShell*   aPresShell,
664                              nsStyleContext* aContext)
665 {
666   return new (aPresShell) nsSVGLinearGradientFrame(aContext);
667 }
668 
NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame)669 NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame)
670 
671 nsIFrame*
672 NS_NewSVGRadialGradientFrame(nsIPresShell*   aPresShell,
673                              nsStyleContext* aContext)
674 {
675   return new (aPresShell) nsSVGRadialGradientFrame(aContext);
676 }
677 
678 NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame)
679