layout/svg/nsSVGGradientFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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)

mercurial