Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
6 // Main header first:
7 #include "nsSVGGradientFrame.h"
8 #include <algorithm>
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 #include "gfxColor.h"
19 // XXX Tight coupling with content classes ahead!
21 using namespace mozilla;
22 using namespace mozilla::dom;
24 //----------------------------------------------------------------------
25 // Helper classes
27 class MOZ_STACK_CLASS nsSVGGradientFrame::AutoGradientReferencer
28 {
29 public:
30 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 NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!");
38 mFrame->mLoopFlag = true;
39 }
40 ~AutoGradientReferencer() {
41 mFrame->mLoopFlag = false;
42 }
43 private:
44 nsSVGGradientFrame *mFrame;
45 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
46 };
48 //----------------------------------------------------------------------
49 // Implementation
51 nsSVGGradientFrame::nsSVGGradientFrame(nsStyleContext* aContext) :
52 nsSVGGradientFrameBase(aContext),
53 mLoopFlag(false),
54 mNoHRefURI(false)
55 {
56 }
58 NS_IMPL_FRAMEARENA_HELPERS(nsSVGGradientFrame)
60 //----------------------------------------------------------------------
61 // nsIFrame methods:
63 nsresult
64 nsSVGGradientFrame::AttributeChanged(int32_t aNameSpaceID,
65 nsIAtom* aAttribute,
66 int32_t aModType)
67 {
68 if (aNameSpaceID == kNameSpaceID_None &&
69 (aAttribute == nsGkAtoms::gradientUnits ||
70 aAttribute == nsGkAtoms::gradientTransform ||
71 aAttribute == nsGkAtoms::spreadMethod)) {
72 nsSVGEffects::InvalidateDirectRenderingObservers(this);
73 } else if (aNameSpaceID == kNameSpaceID_XLink &&
74 aAttribute == nsGkAtoms::href) {
75 // Blow away our reference, if any
76 Properties().Delete(nsSVGEffects::HrefProperty());
77 mNoHRefURI = false;
78 // And update whoever references us
79 nsSVGEffects::InvalidateDirectRenderingObservers(this);
80 }
82 return nsSVGGradientFrameBase::AttributeChanged(aNameSpaceID,
83 aAttribute, aModType);
84 }
86 //----------------------------------------------------------------------
88 uint16_t
89 nsSVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
90 {
91 const nsSVGEnum& thisEnum =
92 static_cast<dom::SVGGradientElement*>(mContent)->mEnumAttributes[aIndex];
94 if (thisEnum.IsExplicitlySet())
95 return thisEnum.GetAnimValue();
97 AutoGradientReferencer gradientRef(this);
99 nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
100 return next ? next->GetEnumValue(aIndex, aDefault) :
101 static_cast<dom::SVGGradientElement*>(aDefault)->
102 mEnumAttributes[aIndex].GetAnimValue();
103 }
105 uint16_t
106 nsSVGGradientFrame::GetGradientUnits()
107 {
108 // This getter is called every time the others are called - maybe cache it?
109 return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS);
110 }
112 uint16_t
113 nsSVGGradientFrame::GetSpreadMethod()
114 {
115 return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD);
116 }
118 const nsSVGAnimatedTransformList*
119 nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault)
120 {
121 nsSVGAnimatedTransformList *thisTransformList =
122 static_cast<dom::SVGGradientElement*>(mContent)->GetAnimatedTransformList();
124 if (thisTransformList && thisTransformList->IsExplicitlySet())
125 return thisTransformList;
127 AutoGradientReferencer gradientRef(this);
129 nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
130 return next ? next->GetGradientTransformList(aDefault) :
131 static_cast<const dom::SVGGradientElement*>(aDefault)
132 ->mGradientTransform.get();
133 }
135 gfxMatrix
136 nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource,
137 const gfxRect *aOverrideBounds)
138 {
139 gfxMatrix bboxMatrix;
141 uint16_t gradientUnits = GetGradientUnits();
142 if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) {
143 NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
144 "Unknown gradientUnits type");
145 // objectBoundingBox is the default anyway
147 gfxRect bbox =
148 aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource);
149 bboxMatrix =
150 gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
151 }
153 const nsSVGAnimatedTransformList* animTransformList =
154 GetGradientTransformList(mContent);
155 if (!animTransformList)
156 return bboxMatrix;
158 gfxMatrix gradientTransform =
159 animTransformList->GetAnimValue().GetConsolidationMatrix();
160 return bboxMatrix.PreMultiply(gradientTransform);
161 }
163 dom::SVGLinearGradientElement*
164 nsSVGGradientFrame::GetLinearGradientWithLength(uint32_t aIndex,
165 dom::SVGLinearGradientElement* aDefault)
166 {
167 // If this was a linear gradient with the required length, we would have
168 // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength.
169 // Since we didn't find the length, continue looking down the chain.
171 AutoGradientReferencer gradientRef(this);
173 nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
174 return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault;
175 }
177 dom::SVGRadialGradientElement*
178 nsSVGGradientFrame::GetRadialGradientWithLength(uint32_t aIndex,
179 dom::SVGRadialGradientElement* aDefault)
180 {
181 // If this was a radial gradient with the required length, we would have
182 // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength.
183 // Since we didn't find the length, continue looking down the chain.
185 AutoGradientReferencer gradientRef(this);
187 nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse();
188 return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault;
189 }
191 //----------------------------------------------------------------------
192 // nsSVGPaintServerFrame methods:
194 //helper
195 static void GetStopInformation(nsIFrame* aStopFrame,
196 float *aOffset,
197 nscolor *aStopColor,
198 float *aStopOpacity)
199 {
200 nsIContent* stopContent = aStopFrame->GetContent();
201 MOZ_ASSERT(stopContent && stopContent->IsSVG(nsGkAtoms::stop));
203 static_cast<SVGStopElement*>(stopContent)->
204 GetAnimatedNumberValues(aOffset, nullptr);
206 *aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f);
207 *aStopColor = aStopFrame->StyleSVGReset()->mStopColor;
208 *aStopOpacity = aStopFrame->StyleSVGReset()->mStopOpacity;
209 }
211 already_AddRefed<gfxPattern>
212 nsSVGGradientFrame::GetPaintServerPattern(nsIFrame *aSource,
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 }
229 nsAutoTArray<nsIFrame*,8> stopFrames;
230 GetStopFrames(&stopFrames);
232 uint32_t nStops = stopFrames.Length();
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 nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0));
238 return pattern.forget();
239 }
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;
247 nsRefPtr<gfxPattern> pattern = new gfxPattern(
248 gfxRGBA(NS_GET_R(stopColor)/255.0,
249 NS_GET_G(stopColor)/255.0,
250 NS_GET_B(stopColor)/255.0,
251 NS_GET_A(stopColor)/255.0 *
252 stopOpacity * aGraphicOpacity));
253 return pattern.forget();
254 }
256 // Get the transform list (if there is one). We do this after the returns
257 // above since this call can be expensive when "gradientUnits" is set to
258 // "objectBoundingBox" (since that requiring a GetBBox() call).
259 gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
261 if (patternMatrix.IsSingular()) {
262 return nullptr;
263 }
265 // revert the vector effect transform so that the gradient appears unchanged
266 if (aFillOrStroke == &nsStyleSVG::mStroke) {
267 patternMatrix.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert());
268 }
270 patternMatrix.Invert();
272 nsRefPtr<gfxPattern> gradient = CreateGradient();
273 if (!gradient || gradient->CairoStatus())
274 return nullptr;
276 uint16_t aSpread = GetSpreadMethod();
277 if (aSpread == SVG_SPREADMETHOD_PAD)
278 gradient->SetExtend(gfxPattern::EXTEND_PAD);
279 else if (aSpread == SVG_SPREADMETHOD_REFLECT)
280 gradient->SetExtend(gfxPattern::EXTEND_REFLECT);
281 else if (aSpread == SVG_SPREADMETHOD_REPEAT)
282 gradient->SetExtend(gfxPattern::EXTEND_REPEAT);
284 gradient->SetMatrix(patternMatrix);
286 // setup stops
287 float lastOffset = 0.0f;
289 for (uint32_t i = 0; i < nStops; i++) {
290 float offset, stopOpacity;
291 nscolor stopColor;
293 GetStopInformation(stopFrames[i], &offset, &stopColor, &stopOpacity);
295 if (offset < lastOffset)
296 offset = lastOffset;
297 else
298 lastOffset = offset;
300 gradient->AddColorStop(offset,
301 gfxRGBA(NS_GET_R(stopColor)/255.0,
302 NS_GET_G(stopColor)/255.0,
303 NS_GET_B(stopColor)/255.0,
304 NS_GET_A(stopColor)/255.0 *
305 stopOpacity * aGraphicOpacity));
306 }
308 return gradient.forget();
309 }
311 // Private (helper) methods
313 nsSVGGradientFrame *
314 nsSVGGradientFrame::GetReferencedGradient()
315 {
316 if (mNoHRefURI)
317 return nullptr;
319 nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*>
320 (Properties().Get(nsSVGEffects::HrefProperty()));
322 if (!property) {
323 // Fetch our gradient element's xlink:href attribute
324 dom::SVGGradientElement*grad = static_cast<dom::SVGGradientElement*>(mContent);
325 nsAutoString href;
326 grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(href, grad);
327 if (href.IsEmpty()) {
328 mNoHRefURI = true;
329 return nullptr; // no URL
330 }
332 // Convert href to an nsIURI
333 nsCOMPtr<nsIURI> targetURI;
334 nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
335 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
336 mContent->GetCurrentDoc(), base);
338 property =
339 nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty());
340 if (!property)
341 return nullptr;
342 }
344 nsIFrame *result = property->GetReferencedFrame();
345 if (!result)
346 return nullptr;
348 nsIAtom* frameType = result->GetType();
349 if (frameType != nsGkAtoms::svgLinearGradientFrame &&
350 frameType != nsGkAtoms::svgRadialGradientFrame)
351 return nullptr;
353 return static_cast<nsSVGGradientFrame*>(result);
354 }
356 nsSVGGradientFrame *
357 nsSVGGradientFrame::GetReferencedGradientIfNotInUse()
358 {
359 nsSVGGradientFrame *referenced = GetReferencedGradient();
360 if (!referenced)
361 return nullptr;
363 if (referenced->mLoopFlag) {
364 // XXXjwatt: we should really send an error to the JavaScript Console here:
365 NS_WARNING("gradient reference loop detected while inheriting attribute!");
366 return nullptr;
367 }
369 return referenced;
370 }
372 void
373 nsSVGGradientFrame::GetStopFrames(nsTArray<nsIFrame*>* aStopFrames)
374 {
375 nsIFrame *stopFrame = nullptr;
376 for (stopFrame = mFrames.FirstChild(); stopFrame;
377 stopFrame = stopFrame->GetNextSibling()) {
378 if (stopFrame->GetType() == nsGkAtoms::svgStopFrame) {
379 aStopFrames->AppendElement(stopFrame);
380 }
381 }
382 if (aStopFrames->Length() > 0) {
383 return;
384 }
386 // Our gradient element doesn't have stops - try to "inherit" them
388 AutoGradientReferencer gradientRef(this);
389 nsSVGGradientFrame* next = GetReferencedGradientIfNotInUse();
390 if (!next) {
391 return;
392 }
394 return next->GetStopFrames(aStopFrames);
395 }
397 // -------------------------------------------------------------------------
398 // Linear Gradients
399 // -------------------------------------------------------------------------
401 #ifdef DEBUG
402 void
403 nsSVGLinearGradientFrame::Init(nsIContent* aContent,
404 nsIFrame* aParent,
405 nsIFrame* aPrevInFlow)
406 {
407 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::linearGradient),
408 "Content is not an SVG linearGradient");
410 nsSVGLinearGradientFrameBase::Init(aContent, aParent, aPrevInFlow);
411 }
412 #endif /* DEBUG */
414 nsIAtom*
415 nsSVGLinearGradientFrame::GetType() const
416 {
417 return nsGkAtoms::svgLinearGradientFrame;
418 }
420 nsresult
421 nsSVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID,
422 nsIAtom* aAttribute,
423 int32_t aModType)
424 {
425 if (aNameSpaceID == kNameSpaceID_None &&
426 (aAttribute == nsGkAtoms::x1 ||
427 aAttribute == nsGkAtoms::y1 ||
428 aAttribute == nsGkAtoms::x2 ||
429 aAttribute == nsGkAtoms::y2)) {
430 nsSVGEffects::InvalidateDirectRenderingObservers(this);
431 }
433 return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
434 aAttribute, aModType);
435 }
437 //----------------------------------------------------------------------
439 float
440 nsSVGLinearGradientFrame::GetLengthValue(uint32_t aIndex)
441 {
442 dom::SVGLinearGradientElement* lengthElement =
443 GetLinearGradientWithLength(aIndex,
444 static_cast<dom::SVGLinearGradientElement*>(mContent));
445 // We passed in mContent as a fallback, so, assuming mContent is non-null, the
446 // return value should also be non-null.
447 NS_ABORT_IF_FALSE(lengthElement,
448 "Got unexpected null element from GetLinearGradientWithLength");
449 const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex];
451 // Object bounding box units are handled by setting the appropriate
452 // transform in GetGradientTransform, but we need to handle user
453 // space units as part of the individual Get* routines. Fixes 323669.
455 uint16_t gradientUnits = GetGradientUnits();
456 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
457 return nsSVGUtils::UserSpace(mSource, &length);
458 }
460 NS_ASSERTION(
461 gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
462 "Unknown gradientUnits type");
464 return length.GetAnimValue(static_cast<SVGSVGElement*>(nullptr));
465 }
467 dom::SVGLinearGradientElement*
468 nsSVGLinearGradientFrame::GetLinearGradientWithLength(uint32_t aIndex,
469 dom::SVGLinearGradientElement* aDefault)
470 {
471 dom::SVGLinearGradientElement* thisElement =
472 static_cast<dom::SVGLinearGradientElement*>(mContent);
473 const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
475 if (length.IsExplicitlySet()) {
476 return thisElement;
477 }
479 return nsSVGLinearGradientFrameBase::GetLinearGradientWithLength(aIndex,
480 aDefault);
481 }
483 bool
484 nsSVGLinearGradientFrame::GradientVectorLengthIsZero()
485 {
486 return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) ==
487 GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) &&
488 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) ==
489 GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
490 }
492 already_AddRefed<gfxPattern>
493 nsSVGLinearGradientFrame::CreateGradient()
494 {
495 float x1, y1, x2, y2;
497 x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1);
498 y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1);
499 x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2);
500 y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
502 nsRefPtr<gfxPattern> pattern = new gfxPattern(x1, y1, x2, y2);
503 return pattern.forget();
504 }
506 // -------------------------------------------------------------------------
507 // Radial Gradients
508 // -------------------------------------------------------------------------
510 #ifdef DEBUG
511 void
512 nsSVGRadialGradientFrame::Init(nsIContent* aContent,
513 nsIFrame* aParent,
514 nsIFrame* aPrevInFlow)
515 {
516 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::radialGradient),
517 "Content is not an SVG radialGradient");
519 nsSVGRadialGradientFrameBase::Init(aContent, aParent, aPrevInFlow);
520 }
521 #endif /* DEBUG */
523 nsIAtom*
524 nsSVGRadialGradientFrame::GetType() const
525 {
526 return nsGkAtoms::svgRadialGradientFrame;
527 }
529 nsresult
530 nsSVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID,
531 nsIAtom* aAttribute,
532 int32_t aModType)
533 {
534 if (aNameSpaceID == kNameSpaceID_None &&
535 (aAttribute == nsGkAtoms::r ||
536 aAttribute == nsGkAtoms::cx ||
537 aAttribute == nsGkAtoms::cy ||
538 aAttribute == nsGkAtoms::fx ||
539 aAttribute == nsGkAtoms::fy)) {
540 nsSVGEffects::InvalidateDirectRenderingObservers(this);
541 }
543 return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
544 aAttribute, aModType);
545 }
547 //----------------------------------------------------------------------
549 float
550 nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex)
551 {
552 dom::SVGRadialGradientElement* lengthElement =
553 GetRadialGradientWithLength(aIndex,
554 static_cast<dom::SVGRadialGradientElement*>(mContent));
555 // We passed in mContent as a fallback, so, assuming mContent is non-null,
556 // the return value should also be non-null.
557 NS_ABORT_IF_FALSE(lengthElement,
558 "Got unexpected null element from GetRadialGradientWithLength");
559 return GetLengthValueFromElement(aIndex, *lengthElement);
560 }
562 float
563 nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, float aDefaultValue)
564 {
565 dom::SVGRadialGradientElement* lengthElement =
566 GetRadialGradientWithLength(aIndex, nullptr);
568 return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement)
569 : aDefaultValue;
570 }
572 float
573 nsSVGRadialGradientFrame::GetLengthValueFromElement(uint32_t aIndex,
574 dom::SVGRadialGradientElement& aElement)
575 {
576 const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex];
578 // Object bounding box units are handled by setting the appropriate
579 // transform in GetGradientTransform, but we need to handle user
580 // space units as part of the individual Get* routines. Fixes 323669.
582 uint16_t gradientUnits = GetGradientUnits();
583 if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
584 return nsSVGUtils::UserSpace(mSource, &length);
585 }
587 NS_ASSERTION(
588 gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
589 "Unknown gradientUnits type");
591 return length.GetAnimValue(static_cast<SVGSVGElement*>(nullptr));
592 }
594 dom::SVGRadialGradientElement*
595 nsSVGRadialGradientFrame::GetRadialGradientWithLength(uint32_t aIndex,
596 dom::SVGRadialGradientElement* aDefault)
597 {
598 dom::SVGRadialGradientElement* thisElement =
599 static_cast<dom::SVGRadialGradientElement*>(mContent);
600 const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
602 if (length.IsExplicitlySet()) {
603 return thisElement;
604 }
606 return nsSVGRadialGradientFrameBase::GetRadialGradientWithLength(aIndex,
607 aDefault);
608 }
610 bool
611 nsSVGRadialGradientFrame::GradientVectorLengthIsZero()
612 {
613 return GetLengthValue(dom::SVGRadialGradientElement::ATTR_R) == 0;
614 }
616 already_AddRefed<gfxPattern>
617 nsSVGRadialGradientFrame::CreateGradient()
618 {
619 float cx, cy, r, fx, fy;
621 cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
622 cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
623 r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
624 // If fx or fy are not set, use cx/cy instead
625 fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
626 fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
628 if (fx != cx || fy != cy) {
629 // The focal point (fFx and fFy) must be clamped to be *inside* - not on -
630 // the circumference of the gradient or we'll get rendering anomalies. We
631 // calculate the distance from the focal point to the gradient center and
632 // make sure it is *less* than the gradient radius.
633 // 1/128 is the limit of the fractional part of cairo's 24.8 fixed point
634 // representation divided by 2 to ensure that we get different cairo
635 // fractions
636 double dMax = std::max(0.0, r - 1.0/128);
637 float dx = fx - cx;
638 float dy = fy - cy;
639 double d = sqrt((dx * dx) + (dy * dy));
640 if (d > dMax) {
641 double angle = atan2(dy, dx);
642 fx = (float)(dMax * cos(angle)) + cx;
643 fy = (float)(dMax * sin(angle)) + cy;
644 }
645 }
647 nsRefPtr<gfxPattern> pattern = new gfxPattern(fx, fy, 0, cx, cy, r);
648 return pattern.forget();
649 }
651 // -------------------------------------------------------------------------
652 // Public functions
653 // -------------------------------------------------------------------------
655 nsIFrame*
656 NS_NewSVGLinearGradientFrame(nsIPresShell* aPresShell,
657 nsStyleContext* aContext)
658 {
659 return new (aPresShell) nsSVGLinearGradientFrame(aContext);
660 }
662 NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame)
664 nsIFrame*
665 NS_NewSVGRadialGradientFrame(nsIPresShell* aPresShell,
666 nsStyleContext* aContext)
667 {
668 return new (aPresShell) nsSVGRadialGradientFrame(aContext);
669 }
671 NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame)