tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGGradientFrame.cpp (22646B)


      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/. */
      6 
      7 // Main header first:
      8 #include "SVGGradientFrame.h"
      9 
     10 #include <algorithm>
     11 
     12 // Keep others in (case-insensitive) order:
     13 #include "AutoReferenceChainGuard.h"
     14 #include "SVGAnimatedTransformList.h"
     15 #include "gfxPattern.h"
     16 #include "gfxUtils.h"
     17 #include "mozilla/PresShell.h"
     18 #include "mozilla/SVGObserverUtils.h"
     19 #include "mozilla/SVGUtils.h"
     20 #include "mozilla/dom/SVGGradientElement.h"
     21 #include "mozilla/dom/SVGGradientElementBinding.h"
     22 #include "mozilla/dom/SVGStopElement.h"
     23 #include "mozilla/dom/SVGUnitTypesBinding.h"
     24 #include "nsContentUtils.h"
     25 
     26 using namespace mozilla::dom;
     27 using namespace mozilla::dom::SVGGradientElement_Binding;
     28 using namespace mozilla::dom::SVGUnitTypes_Binding;
     29 using namespace mozilla::gfx;
     30 
     31 namespace mozilla {
     32 
     33 //----------------------------------------------------------------------
     34 // Implementation
     35 
     36 SVGGradientFrame::SVGGradientFrame(ComputedStyle* aStyle,
     37                                   nsPresContext* aPresContext, ClassID aID)
     38    : SVGPaintServerFrame(aStyle, aPresContext, aID),
     39      mSource(nullptr),
     40      mLoopFlag(false),
     41      mNoHRefURI(false) {}
     42 
     43 NS_QUERYFRAME_HEAD(SVGGradientFrame)
     44  NS_QUERYFRAME_ENTRY(SVGGradientFrame)
     45 NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame)
     46 
     47 //----------------------------------------------------------------------
     48 // nsIFrame methods:
     49 
     50 nsresult SVGGradientFrame::AttributeChanged(int32_t aNameSpaceID,
     51                                            nsAtom* aAttribute,
     52                                            AttrModType aModType) {
     53  if (aNameSpaceID == kNameSpaceID_None &&
     54      (aAttribute == nsGkAtoms::gradientUnits ||
     55       aAttribute == nsGkAtoms::gradientTransform ||
     56       aAttribute == nsGkAtoms::spreadMethod)) {
     57    SVGObserverUtils::InvalidateRenderingObservers(this);
     58  } else if ((aNameSpaceID == kNameSpaceID_XLink ||
     59              aNameSpaceID == kNameSpaceID_None) &&
     60             aAttribute == nsGkAtoms::href) {
     61    // Blow away our reference, if any
     62    SVGObserverUtils::RemoveTemplateObserver(this);
     63    mNoHRefURI = false;
     64    // And update whoever references us
     65    SVGObserverUtils::InvalidateRenderingObservers(this);
     66  }
     67 
     68  return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
     69                                               aModType);
     70 }
     71 
     72 //----------------------------------------------------------------------
     73 
     74 uint16_t SVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
     75  const SVGAnimatedEnumeration& thisEnum =
     76      static_cast<dom::SVGGradientElement*>(GetContent())
     77          ->mEnumAttributes[aIndex];
     78 
     79  if (thisEnum.IsExplicitlySet()) {
     80    return thisEnum.GetAnimValue();
     81  }
     82 
     83  // Before we recurse, make sure we'll break reference loops and over long
     84  // reference chains:
     85  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
     86  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
     87                                        &sRefChainLengthCounter);
     88  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
     89    // Break reference chain
     90    return static_cast<dom::SVGGradientElement*>(aDefault)
     91        ->mEnumAttributes[aIndex]
     92        .GetAnimValue();
     93  }
     94 
     95  SVGGradientFrame* next = GetReferencedGradient();
     96 
     97  return next ? next->GetEnumValue(aIndex, aDefault)
     98              : static_cast<dom::SVGGradientElement*>(aDefault)
     99                    ->mEnumAttributes[aIndex]
    100                    .GetAnimValue();
    101 }
    102 
    103 uint16_t SVGGradientFrame::GetGradientUnits() {
    104  // This getter is called every time the others are called - maybe cache it?
    105  return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS);
    106 }
    107 
    108 uint16_t SVGGradientFrame::GetSpreadMethod() {
    109  return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD);
    110 }
    111 
    112 SVGGradientFrame* SVGGradientFrame::GetGradientTransformFrame(
    113    SVGGradientFrame* aDefault) {
    114  if (!StyleDisplay()->mTransform.IsNone()) {
    115    return this;
    116  }
    117  // Before we recurse, make sure we'll break reference loops and over long
    118  // reference chains:
    119  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    120  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    121                                        &sRefChainLengthCounter);
    122  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    123    // Break reference chain
    124    return aDefault;
    125  }
    126 
    127  if (SVGGradientFrame* next = GetReferencedGradient()) {
    128    return next->GetGradientTransformFrame(aDefault);
    129  }
    130  return aDefault;
    131 }
    132 
    133 gfxMatrix SVGGradientFrame::GetGradientTransform(
    134    nsIFrame* aSource, const gfxRect* aOverrideBounds) {
    135  gfxMatrix bboxMatrix;
    136  uint16_t gradientUnits = GetGradientUnits();
    137  if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) {
    138    NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
    139                 "Unknown gradientUnits type");
    140    // objectBoundingBox is the default anyway
    141 
    142    gfxRect bbox = aOverrideBounds
    143                       ? *aOverrideBounds
    144                       : SVGUtils::GetBBox(
    145                             aSource, SVGUtils::eUseFrameBoundsForOuterSVG |
    146                                          SVGUtils::eBBoxIncludeFillGeometry);
    147    bboxMatrix =
    148        gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
    149  }
    150 
    151  return bboxMatrix.PreMultiply(
    152      SVGUtils::GetTransformMatrixInUserSpace(GetGradientTransformFrame(this)));
    153 }
    154 
    155 dom::SVGLinearGradientElement* SVGGradientFrame::GetLinearGradientWithLength(
    156    uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) {
    157  // If this was a linear gradient with the required length, we would have
    158  // already found it in SVGLinearGradientFrame::GetLinearGradientWithLength.
    159  // Since we didn't find the length, continue looking down the chain.
    160 
    161  // Before we recurse, make sure we'll break reference loops and over long
    162  // reference chains:
    163  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    164  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    165                                        &sRefChainLengthCounter);
    166  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    167    // Break reference chain
    168    return aDefault;
    169  }
    170 
    171  SVGGradientFrame* next = GetReferencedGradient();
    172  return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault;
    173 }
    174 
    175 dom::SVGRadialGradientElement* SVGGradientFrame::GetRadialGradientWithLength(
    176    uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) {
    177  // If this was a radial gradient with the required length, we would have
    178  // already found it in SVGRadialGradientFrame::GetRadialGradientWithLength.
    179  // Since we didn't find the length, continue looking down the chain.
    180 
    181  // Before we recurse, make sure we'll break reference loops and over long
    182  // reference chains:
    183  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    184  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    185                                        &sRefChainLengthCounter);
    186  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    187    // Break reference chain
    188    return aDefault;
    189  }
    190 
    191  SVGGradientFrame* next = GetReferencedGradient();
    192  return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault;
    193 }
    194 
    195 //----------------------------------------------------------------------
    196 // SVGPaintServerFrame methods:
    197 
    198 // helpers
    199 
    200 static ColorStop GetStopInformation(const nsIFrame* aStopFrame,
    201                                    float aGraphicOpacity,
    202                                    float& aLastPosition) {
    203  nsIContent* stopContent = aStopFrame->GetContent();
    204  MOZ_ASSERT(stopContent && stopContent->IsSVGElement(nsGkAtoms::stop));
    205 
    206  float position;
    207  static_cast<SVGStopElement*>(stopContent)
    208      ->GetAnimatedNumberValues(&position, nullptr);
    209 
    210  position = std::clamp(position, 0.0f, 1.0f);
    211 
    212  if (position < aLastPosition) {
    213    position = aLastPosition;
    214  } else {
    215    aLastPosition = position;
    216  }
    217 
    218  const auto* svgReset = aStopFrame->StyleSVGReset();
    219 
    220  sRGBColor stopColor =
    221      sRGBColor::FromABGR(svgReset->mStopColor.CalcColor(aStopFrame));
    222  stopColor.a *= svgReset->mStopOpacity * aGraphicOpacity;
    223 
    224  return ColorStop(position, false,
    225                   StyleAbsoluteColor::FromColor(stopColor.ToABGR()));
    226 }
    227 
    228 class MOZ_STACK_CLASS SVGColorStopInterpolator
    229    : public ColorStopInterpolator<SVGColorStopInterpolator> {
    230 public:
    231  SVGColorStopInterpolator(
    232      gfxPattern* aGradient, const nsTArray<ColorStop>& aStops,
    233      const StyleColorInterpolationMethod& aStyleColorInterpolationMethod,
    234      bool aExtend)
    235      : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod, aExtend),
    236        mGradient(aGradient) {}
    237 
    238  void CreateStop(float aPosition, DeviceColor aColor) {
    239    mGradient->AddColorStop(aPosition, aColor);
    240  }
    241 
    242 private:
    243  gfxPattern* mGradient;
    244 };
    245 
    246 already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern(
    247    nsIFrame* aSource, const DrawTarget* aDrawTarget,
    248    const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::* aFillOrStroke,
    249    float aGraphicOpacity, imgDrawingParams& aImgParams,
    250    const gfxRect* aOverrideBounds) {
    251  uint16_t gradientUnits = GetGradientUnits();
    252  MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX ||
    253             gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE);
    254  if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
    255    // Set mSource for this consumer.
    256    // If this gradient is applied to text, our caller will be the glyph, which
    257    // is not an element, so we need to get the parent
    258    mSource = aSource->IsTextFrame() ? aSource->GetParent() : aSource;
    259  }
    260 
    261  AutoTArray<ColorStop, 8> stops;
    262  GetStops(&stops, aGraphicOpacity);
    263 
    264  uint32_t nStops = stops.Length();
    265 
    266  // SVG specification says that no stops should be treated like
    267  // the corresponding fill or stroke had "none" specified.
    268  if (nStops == 0) {
    269    return do_AddRef(new gfxPattern(DeviceColor()));
    270  }
    271 
    272  if (nStops == 1 || GradientVectorLengthIsZero()) {
    273    // The gradient paints a single colour, using the stop-color of the last
    274    // gradient step if there are more than one.
    275    return do_AddRef(new gfxPattern(ToDeviceColor(stops.LastElement().mColor)));
    276  }
    277 
    278  // Get the transform list (if there is one). We do this after the returns
    279  // above since this call can be expensive when "gradientUnits" is set to
    280  // "objectBoundingBox" (since that requiring a GetBBox() call).
    281  gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
    282  if (patternMatrix.IsSingular()) {
    283    return nullptr;
    284  }
    285 
    286  // revert any vector effect transform so that the gradient appears unchanged
    287  if (aFillOrStroke == &nsStyleSVG::mStroke) {
    288    gfxMatrix userToOuterSVG;
    289    if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
    290      patternMatrix *= userToOuterSVG;
    291    }
    292  }
    293 
    294  if (!patternMatrix.Invert()) {
    295    return nullptr;
    296  }
    297 
    298  RefPtr<gfxPattern> gradient = CreateGradient();
    299  if (!gradient) {
    300    return nullptr;
    301  }
    302 
    303  uint16_t aSpread = GetSpreadMethod();
    304  if (aSpread == SVG_SPREADMETHOD_PAD) {
    305    gradient->SetExtend(ExtendMode::CLAMP);
    306  } else if (aSpread == SVG_SPREADMETHOD_REFLECT) {
    307    gradient->SetExtend(ExtendMode::REFLECT);
    308  } else if (aSpread == SVG_SPREADMETHOD_REPEAT) {
    309    gradient->SetExtend(ExtendMode::REPEAT);
    310  }
    311 
    312  gradient->SetMatrix(patternMatrix);
    313 
    314  if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) {
    315    static constexpr auto interpolationMethod = StyleColorInterpolationMethod{
    316        StyleColorSpace::SrgbLinear, StyleHueInterpolationMethod::Shorter};
    317    SVGColorStopInterpolator interpolator(gradient, stops, interpolationMethod,
    318                                          false);
    319    interpolator.CreateStops();
    320  } else {
    321    // setup standard sRGB stops
    322    for (const auto& stop : stops) {
    323      gradient->AddColorStop(stop.mPosition, ToDeviceColor(stop.mColor));
    324    }
    325  }
    326 
    327  return gradient.forget();
    328 }
    329 
    330 // Private (helper) methods
    331 
    332 float SVGGradientFrame::GetLengthValue(const SVGAnimatedLength& aLength) {
    333  // Object bounding box units are handled by setting the appropriate
    334  // transform in GetGradientTransform, but we need to handle user
    335  // space units as part of the individual Get* routines.  Fixes 323669.
    336 
    337  uint16_t gradientUnits = GetGradientUnits();
    338  if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
    339    return SVGUtils::UserSpace(mSource, &aLength);
    340  }
    341 
    342  NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
    343               "Unknown gradientUnits type");
    344 
    345  if (aLength.IsPercentage()) {
    346    // We want the percentage of the objectBoundingBox rather than
    347    // the percentage of the nearest viewport.
    348    return aLength.GetAnimValInSpecifiedUnits() / 100.0f;
    349  }
    350  return aLength.GetAnimValueWithZoom(
    351      static_cast<dom::SVGElement*>(GetContent()));
    352 }
    353 
    354 SVGGradientFrame* SVGGradientFrame::GetReferencedGradient() {
    355  if (mNoHRefURI) {
    356    return nullptr;
    357  }
    358 
    359  auto GetHref = [this](nsAString& aHref) {
    360    dom::SVGGradientElement* grad =
    361        static_cast<dom::SVGGradientElement*>(this->GetContent());
    362    if (grad->mStringAttributes[dom::SVGGradientElement::HREF]
    363            .IsExplicitlySet()) {
    364      grad->mStringAttributes[dom::SVGGradientElement::HREF].GetAnimValue(aHref,
    365                                                                          grad);
    366    } else {
    367      grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF].GetAnimValue(
    368          aHref, grad);
    369    }
    370    this->mNoHRefURI = aHref.IsEmpty();
    371  };
    372 
    373  // We don't call SVGObserverUtils::RemoveTemplateObserver and set
    374  // `mNoHRefURI = false` on failure since we want to be invalidated if the ID
    375  // specified by our href starts resolving to a different/valid element.
    376 
    377  return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref));
    378 }
    379 
    380 void SVGGradientFrame::GetStops(nsTArray<ColorStop>* aStops,
    381                                float aGraphicOpacity) {
    382  float lastPosition = 0.0f;
    383  for (const auto* stopFrame : mFrames) {
    384    if (stopFrame->IsSVGStopFrame()) {
    385      aStops->AppendElement(
    386          GetStopInformation(stopFrame, aGraphicOpacity, lastPosition));
    387    }
    388  }
    389  if (!aStops->IsEmpty()) {
    390    return;
    391  }
    392 
    393  // Our gradient element doesn't have stops - try to "inherit" them
    394 
    395  // Before we recurse, make sure we'll break reference loops and over long
    396  // reference chains:
    397  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    398  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    399                                        &sRefChainLengthCounter);
    400  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    401    // Break reference chain
    402    return;
    403  }
    404 
    405  SVGGradientFrame* next = GetReferencedGradient();
    406  if (next) {
    407    next->GetStops(aStops, aGraphicOpacity);
    408  }
    409 }
    410 
    411 // -------------------------------------------------------------------------
    412 // Linear Gradients
    413 // -------------------------------------------------------------------------
    414 
    415 NS_QUERYFRAME_HEAD(SVGLinearGradientFrame)
    416  NS_QUERYFRAME_ENTRY(SVGLinearGradientFrame)
    417 NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame)
    418 
    419 #ifdef DEBUG
    420 void SVGLinearGradientFrame::Init(nsIContent* aContent,
    421                                  nsContainerFrame* aParent,
    422                                  nsIFrame* aPrevInFlow) {
    423  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::linearGradient),
    424               "Content is not an SVG linearGradient");
    425 
    426  SVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
    427 }
    428 #endif /* DEBUG */
    429 
    430 nsresult SVGLinearGradientFrame::AttributeChanged(int32_t aNameSpaceID,
    431                                                  nsAtom* aAttribute,
    432                                                  AttrModType aModType) {
    433  if (aNameSpaceID == kNameSpaceID_None &&
    434      (aAttribute == nsGkAtoms::x1 || aAttribute == nsGkAtoms::y1 ||
    435       aAttribute == nsGkAtoms::x2 || aAttribute == nsGkAtoms::y2)) {
    436    SVGObserverUtils::InvalidateRenderingObservers(this);
    437  }
    438 
    439  return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
    440 }
    441 
    442 //----------------------------------------------------------------------
    443 
    444 float SVGLinearGradientFrame::GetLengthValue(uint32_t aIndex) {
    445  dom::SVGLinearGradientElement* lengthElement = GetLinearGradientWithLength(
    446      aIndex, static_cast<dom::SVGLinearGradientElement*>(GetContent()));
    447  // We passed in mContent as a fallback, so, assuming mContent is non-null, the
    448  // return value should also be non-null.
    449  MOZ_ASSERT(lengthElement,
    450             "Got unexpected null element from GetLinearGradientWithLength");
    451 
    452  return GetLengthValue(lengthElement->mLengthAttributes[aIndex]);
    453 }
    454 
    455 dom::SVGLinearGradientElement*
    456 SVGLinearGradientFrame::GetLinearGradientWithLength(
    457    uint32_t aIndex, dom::SVGLinearGradientElement* aDefault) {
    458  dom::SVGLinearGradientElement* thisElement =
    459      static_cast<dom::SVGLinearGradientElement*>(GetContent());
    460  const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex];
    461 
    462  if (length.IsExplicitlySet()) {
    463    return thisElement;
    464  }
    465 
    466  return SVGGradientFrame::GetLinearGradientWithLength(aIndex, aDefault);
    467 }
    468 
    469 bool SVGLinearGradientFrame::GradientVectorLengthIsZero() {
    470  return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) ==
    471             GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) &&
    472         GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) ==
    473             GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
    474 }
    475 
    476 already_AddRefed<gfxPattern> SVGLinearGradientFrame::CreateGradient() {
    477  float x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1);
    478  float y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1);
    479  float x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2);
    480  float y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
    481 
    482  return do_AddRef(new gfxPattern(x1, y1, x2, y2));
    483 }
    484 
    485 // -------------------------------------------------------------------------
    486 // Radial Gradients
    487 // -------------------------------------------------------------------------
    488 
    489 NS_QUERYFRAME_HEAD(SVGRadialGradientFrame)
    490  NS_QUERYFRAME_ENTRY(SVGRadialGradientFrame)
    491 NS_QUERYFRAME_TAIL_INHERITING(SVGGradientFrame)
    492 
    493 #ifdef DEBUG
    494 void SVGRadialGradientFrame::Init(nsIContent* aContent,
    495                                  nsContainerFrame* aParent,
    496                                  nsIFrame* aPrevInFlow) {
    497  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::radialGradient),
    498               "Content is not an SVG radialGradient");
    499 
    500  SVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
    501 }
    502 #endif /* DEBUG */
    503 
    504 nsresult SVGRadialGradientFrame::AttributeChanged(int32_t aNameSpaceID,
    505                                                  nsAtom* aAttribute,
    506                                                  AttrModType aModType) {
    507  if (aNameSpaceID == kNameSpaceID_None &&
    508      (aAttribute == nsGkAtoms::r || aAttribute == nsGkAtoms::cx ||
    509       aAttribute == nsGkAtoms::cy || aAttribute == nsGkAtoms::fx ||
    510       aAttribute == nsGkAtoms::fy)) {
    511    SVGObserverUtils::InvalidateRenderingObservers(this);
    512  }
    513 
    514  return SVGGradientFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
    515 }
    516 
    517 //----------------------------------------------------------------------
    518 
    519 float SVGRadialGradientFrame::GetLengthValue(uint32_t aIndex,
    520                                             Maybe<float> aDefaultValue) {
    521  dom::SVGRadialGradientElement* lengthElement = GetRadialGradientWithLength(
    522      aIndex, aDefaultValue.isNothing()
    523                  ? static_cast<dom::SVGRadialGradientElement*>(GetContent())
    524                  : nullptr);
    525 
    526  // If we passed our content as a fallback, then assuming that is non-null,
    527  // the return value should also be non-null.
    528  MOZ_ASSERT(aDefaultValue.isSome() || lengthElement,
    529             "Got unexpected null element from GetRadialGradientWithLength");
    530 
    531  return lengthElement
    532             ? GetLengthValue(lengthElement->mLengthAttributes[aIndex])
    533             : aDefaultValue.value();
    534 }
    535 
    536 dom::SVGRadialGradientElement*
    537 SVGRadialGradientFrame::GetRadialGradientWithLength(
    538    uint32_t aIndex, dom::SVGRadialGradientElement* aDefault) {
    539  dom::SVGRadialGradientElement* thisElement =
    540      static_cast<dom::SVGRadialGradientElement*>(GetContent());
    541  const SVGAnimatedLength& length = thisElement->mLengthAttributes[aIndex];
    542 
    543  if (length.IsExplicitlySet()) {
    544    return thisElement;
    545  }
    546 
    547  return SVGGradientFrame::GetRadialGradientWithLength(aIndex, aDefault);
    548 }
    549 
    550 bool SVGRadialGradientFrame::GradientVectorLengthIsZero() {
    551  float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
    552  float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
    553  float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
    554  // If fx or fy are not set, use cx/cy instead
    555  float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
    556  float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
    557  float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR);
    558  return cx == fx && cy == fy && r == fr;
    559 }
    560 
    561 already_AddRefed<gfxPattern> SVGRadialGradientFrame::CreateGradient() {
    562  float cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
    563  float cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
    564  float r = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
    565  // If fx or fy are not set, use cx/cy instead
    566  float fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
    567  float fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
    568  float fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR);
    569 
    570  return do_AddRef(new gfxPattern(fx, fy, fr, cx, cy, r));
    571 }
    572 
    573 }  // namespace mozilla
    574 
    575 // -------------------------------------------------------------------------
    576 // Public functions
    577 // -------------------------------------------------------------------------
    578 
    579 nsIFrame* NS_NewSVGLinearGradientFrame(mozilla::PresShell* aPresShell,
    580                                       mozilla::ComputedStyle* aStyle) {
    581  return new (aPresShell)
    582      mozilla::SVGLinearGradientFrame(aStyle, aPresShell->GetPresContext());
    583 }
    584 
    585 nsIFrame* NS_NewSVGRadialGradientFrame(mozilla::PresShell* aPresShell,
    586                                       mozilla::ComputedStyle* aStyle) {
    587  return new (aPresShell)
    588      mozilla::SVGRadialGradientFrame(aStyle, aPresShell->GetPresContext());
    589 }
    590 
    591 namespace mozilla {
    592 
    593 NS_IMPL_FRAMEARENA_HELPERS(SVGLinearGradientFrame)
    594 NS_IMPL_FRAMEARENA_HELPERS(SVGRadialGradientFrame)
    595 
    596 }  // namespace mozilla