tor-browser

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

SVGPatternFrame.cpp (26105B)


      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 "SVGPatternFrame.h"
      9 
     10 // Keep others in (case-insensitive) order:
     11 #include "AutoReferenceChainGuard.h"
     12 #include "SVGAnimatedTransformList.h"
     13 #include "gfx2DGlue.h"
     14 #include "gfxContext.h"
     15 #include "gfxMatrix.h"
     16 #include "gfxPattern.h"
     17 #include "gfxPlatform.h"
     18 #include "mozilla/ComputedStyle.h"
     19 #include "mozilla/ISVGDisplayableFrame.h"
     20 #include "mozilla/PresShell.h"
     21 #include "mozilla/SVGContentUtils.h"
     22 #include "mozilla/SVGGeometryFrame.h"
     23 #include "mozilla/SVGObserverUtils.h"
     24 #include "mozilla/SVGUtils.h"
     25 #include "mozilla/dom/SVGPatternElement.h"
     26 #include "mozilla/dom/SVGUnitTypesBinding.h"
     27 #include "mozilla/gfx/2D.h"
     28 #include "nsGkAtoms.h"
     29 #include "nsIFrameInlines.h"
     30 
     31 using namespace mozilla::dom;
     32 using namespace mozilla::dom::SVGUnitTypes_Binding;
     33 using namespace mozilla::gfx;
     34 using namespace mozilla::image;
     35 
     36 namespace mozilla {
     37 
     38 //----------------------------------------------------------------------
     39 // Implementation
     40 
     41 SVGPatternFrame::SVGPatternFrame(ComputedStyle* aStyle,
     42                                 nsPresContext* aPresContext)
     43    : SVGPaintServerFrame(aStyle, aPresContext, kClassID),
     44      mSource(nullptr),
     45      mLoopFlag(false),
     46      mNoHRefURI(false) {}
     47 
     48 NS_IMPL_FRAMEARENA_HELPERS(SVGPatternFrame)
     49 
     50 NS_QUERYFRAME_HEAD(SVGPatternFrame)
     51  NS_QUERYFRAME_ENTRY(SVGPatternFrame)
     52 NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame)
     53 
     54 //----------------------------------------------------------------------
     55 // nsIFrame methods:
     56 
     57 nsresult SVGPatternFrame::AttributeChanged(int32_t aNameSpaceID,
     58                                           nsAtom* aAttribute,
     59                                           AttrModType aModType) {
     60  if (aNameSpaceID == kNameSpaceID_None &&
     61      (aAttribute == nsGkAtoms::patternUnits ||
     62       aAttribute == nsGkAtoms::patternContentUnits ||
     63       aAttribute == nsGkAtoms::patternTransform ||
     64       aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
     65       aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
     66       aAttribute == nsGkAtoms::preserveAspectRatio ||
     67       aAttribute == nsGkAtoms::viewBox)) {
     68    SVGObserverUtils::InvalidateRenderingObservers(this);
     69  }
     70 
     71  if ((aNameSpaceID == kNameSpaceID_XLink ||
     72       aNameSpaceID == kNameSpaceID_None) &&
     73      aAttribute == nsGkAtoms::href) {
     74    // Blow away our reference, if any
     75    SVGObserverUtils::RemoveTemplateObserver(this);
     76    mNoHRefURI = false;
     77    // And update whoever references us
     78    SVGObserverUtils::InvalidateRenderingObservers(this);
     79  }
     80 
     81  return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
     82                                               aModType);
     83 }
     84 
     85 #ifdef DEBUG
     86 void SVGPatternFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
     87                           nsIFrame* aPrevInFlow) {
     88  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::pattern),
     89               "Content is not an SVG pattern");
     90 
     91  SVGPaintServerFrame::Init(aContent, aParent, aPrevInFlow);
     92 }
     93 #endif /* DEBUG */
     94 
     95 //----------------------------------------------------------------------
     96 // SVGContainerFrame methods:
     97 
     98 // If our GetCanvasTM is getting called, we
     99 // need to return *our current* transformation
    100 // matrix, which depends on our units parameters
    101 // and X, Y, Width, and Height
    102 gfxMatrix SVGPatternFrame::GetCanvasTM() {
    103  if (mCTM) {
    104    return *mCTM;
    105  }
    106 
    107  // Do we know our rendering parent?
    108  if (mSource) {
    109    // Yes, use it!
    110    return mSource->GetCanvasTM();
    111  }
    112 
    113  // We get here when geometry in the <pattern> container is updated
    114  return gfxMatrix();
    115 }
    116 
    117 // -------------------------------------------------------------------------
    118 // Helper functions
    119 // -------------------------------------------------------------------------
    120 
    121 /** Calculate the maximum expansion of a matrix */
    122 static float MaxExpansion(const Matrix& aMatrix) {
    123  // maximum expansion derivation from
    124  // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
    125  // and also implemented in cairo_matrix_transformed_circle_major_axis
    126  double a = aMatrix._11;
    127  double b = aMatrix._12;
    128  double c = aMatrix._21;
    129  double d = aMatrix._22;
    130  double f = (a * a + b * b + c * c + d * d) / 2;
    131  double g = (a * a + b * b - c * c - d * d) / 2;
    132  double h = a * c + b * d;
    133  return sqrt(f + sqrt(g * g + h * h));
    134 }
    135 
    136 // The SVG specification says that the 'patternContentUnits' attribute "has no
    137 // effect if attribute ‘viewBox’ is specified". We still need to include a bbox
    138 // scale if the viewBox is specified and _patternUnits_ is set to or defaults to
    139 // objectBoundingBox though, since in that case the viewBox is relative to the
    140 // bbox
    141 static bool IncludeBBoxScale(const SVGAnimatedViewBox& aViewBox,
    142                             uint32_t aPatternContentUnits,
    143                             uint32_t aPatternUnits) {
    144  return (!aViewBox.IsExplicitlySet() &&
    145          aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) ||
    146         (aViewBox.IsExplicitlySet() &&
    147          aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
    148 }
    149 
    150 // Given the matrix for the pattern element's own transform, this returns a
    151 // combined matrix including the transforms applicable to its target.
    152 static Matrix GetPatternMatrix(nsIFrame* aSource,
    153                               const StyleSVGPaint nsStyleSVG::* aFillOrStroke,
    154                               uint16_t aPatternUnits,
    155                               const gfxMatrix& patternTransform,
    156                               const gfxRect& bbox, const gfxRect& callerBBox,
    157                               const Matrix& callerCTM) {
    158  // We really want the pattern matrix to handle translations
    159  gfxFloat minx = bbox.X();
    160  gfxFloat miny = bbox.Y();
    161 
    162  if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    163    minx += callerBBox.X();
    164    miny += callerBBox.Y();
    165  }
    166 
    167  double scale = 1.0 / MaxExpansion(callerCTM);
    168  auto patternMatrix = patternTransform;
    169  patternMatrix.PreScale(scale, scale);
    170  patternMatrix.PreTranslate(minx, miny);
    171 
    172  // revert the vector effect transform so that the pattern appears unchanged
    173  if (aFillOrStroke == &nsStyleSVG::mStroke) {
    174    gfxMatrix userToOuterSVG;
    175    if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
    176      patternMatrix *= userToOuterSVG;
    177    }
    178  }
    179 
    180  return ToMatrix(patternMatrix);
    181 }
    182 
    183 static nsresult GetTargetGeometry(gfxRect* aBBox,
    184                                  const SVGAnimatedViewBox& aViewBox,
    185                                  uint16_t aPatternContentUnits,
    186                                  uint16_t aPatternUnits, nsIFrame* aTarget,
    187                                  const Matrix& aContextMatrix,
    188                                  const gfxRect* aOverrideBounds) {
    189  *aBBox =
    190      aOverrideBounds
    191          ? *aOverrideBounds
    192          : SVGUtils::GetBBox(aTarget, SVGUtils::eUseFrameBoundsForOuterSVG |
    193                                           SVGUtils::eBBoxIncludeFillGeometry);
    194 
    195  // Sanity check
    196  if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
    197      aBBox->IsEmpty()) {
    198    return NS_ERROR_FAILURE;
    199  }
    200 
    201  // OK, now fix up the bounding box to reflect user coordinates
    202  // We handle device unit scaling in pattern matrix
    203  float scale = MaxExpansion(aContextMatrix);
    204  if (scale <= 0) {
    205    return NS_ERROR_FAILURE;
    206  }
    207  aBBox->Scale(scale);
    208  return NS_OK;
    209 }
    210 
    211 void SVGPatternFrame::PaintChildren(DrawTarget* aDrawTarget,
    212                                    SVGPatternFrame* aPatternWithChildren,
    213                                    nsIFrame* aSource, float aGraphicOpacity,
    214                                    imgDrawingParams& aImgParams) {
    215  gfxContext ctx(aDrawTarget);
    216  gfxGroupForBlendAutoSaveRestore autoGroupForBlend(&ctx);
    217 
    218  if (aGraphicOpacity != 1.0f) {
    219    autoGroupForBlend.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
    220                                            aGraphicOpacity);
    221  }
    222 
    223  // OK, now render -- note that we use "firstKid", which
    224  // we got at the beginning because it takes care of the
    225  // referenced pattern situation for us
    226 
    227  if (aSource->IsSVGGeometryFrame()) {
    228    // Set the geometrical parent of the pattern we are rendering
    229    aPatternWithChildren->mSource = static_cast<SVGGeometryFrame*>(aSource);
    230  }
    231 
    232  // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
    233  // give back a clear surface if there's a loop
    234  if (!aPatternWithChildren->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER)) {
    235    AutoSetRestorePaintServerState paintServer(aPatternWithChildren);
    236    for (auto* kid : aPatternWithChildren->mFrames) {
    237      gfxMatrix tm = *(aPatternWithChildren->mCTM);
    238 
    239      // The CTM of each frame referencing us can be different
    240      ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
    241      if (SVGFrame) {
    242        SVGFrame->NotifySVGChanged(
    243            ISVGDisplayableFrame::ChangeFlag::TransformChanged);
    244        tm = SVGUtils::GetTransformMatrixInUserSpace(kid) * tm;
    245      }
    246 
    247      SVGUtils::PaintFrameWithEffects(kid, ctx, tm, aImgParams);
    248    }
    249  }
    250 
    251  aPatternWithChildren->mSource = nullptr;
    252 }
    253 
    254 already_AddRefed<SourceSurface> SVGPatternFrame::PaintPattern(
    255    const DrawTarget* aDrawTarget, Matrix* patternMatrix,
    256    const Matrix& aContextMatrix, nsIFrame* aSource,
    257    StyleSVGPaint nsStyleSVG::* aFillOrStroke, float aGraphicOpacity,
    258    const gfxRect* aOverrideBounds, imgDrawingParams& aImgParams) {
    259  /*
    260   * General approach:
    261   *    Set the content geometry stuff
    262   *    Calculate our bbox (using x,y,width,height & patternUnits &
    263   *                        patternTransform)
    264   *    Create the surface
    265   *    Calculate the content transformation matrix
    266   *    Get our children (we may need to get them from another Pattern)
    267   *    Call SVGPaint on all of our children
    268   *    Return
    269   */
    270 
    271  SVGPatternFrame* patternWithChildren = GetPatternWithChildren();
    272  if (!patternWithChildren) {
    273    // Either no kids or a bad reference
    274    return nullptr;
    275  }
    276 
    277  const SVGAnimatedViewBox& viewBox = GetViewBox();
    278 
    279  uint16_t patternContentUnits =
    280      GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
    281  uint16_t patternUnits = GetEnumValue(SVGPatternElement::PATTERNUNITS);
    282 
    283  /*
    284   * Get the content geometry information.  This is a little tricky --
    285   * our parent is probably a <defs>, but we are rendering in the context
    286   * of some geometry source.  Our content geometry information needs to
    287   * come from our rendering parent as opposed to our content parent.  We
    288   * get that information from aSource, which is passed to us from the
    289   * backend renderer.
    290   *
    291   * There are three "geometries" that we need:
    292   *   1) The bounding box for the pattern.  We use this to get the
    293   *      width and height for the surface, and as the return to
    294   *      GetBBox.
    295   *   2) The transformation matrix for the pattern.  This is not *quite*
    296   *      the same as the canvas transformation matrix that we will
    297   *      provide to our rendering children since we "fudge" it a little
    298   *      to get the renderer to handle the translations correctly for us.
    299   *   3) The CTM that we return to our children who make up the pattern.
    300   */
    301 
    302  // Get all of the information we need from our "caller" -- i.e.
    303  // the geometry that is being rendered with a pattern
    304  gfxRect callerBBox;
    305  if (NS_FAILED(GetTargetGeometry(&callerBBox, viewBox, patternContentUnits,
    306                                  patternUnits, aSource, aContextMatrix,
    307                                  aOverrideBounds))) {
    308    return nullptr;
    309  }
    310 
    311  // Construct the CTM that we will provide to our children when we
    312  // render them into the tile.
    313  gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
    314                               callerBBox, aContextMatrix, aSource);
    315  if (ctm.IsSingular()) {
    316    return nullptr;
    317  }
    318 
    319  if (patternWithChildren->mCTM) {
    320    *patternWithChildren->mCTM = ctm;
    321  } else {
    322    patternWithChildren->mCTM = MakeUnique<gfxMatrix>(ctm);
    323  }
    324 
    325  // Get the bounding box of the pattern.  This will be used to determine
    326  // the size of the surface, and will also be used to define the bounding
    327  // box for the pattern tile.
    328  gfxRect bbox =
    329      GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource);
    330  if (bbox.IsEmpty()) {
    331    return nullptr;
    332  }
    333 
    334  // Get the pattern transform
    335  auto patternTransform = GetPatternTransform();
    336 
    337  // Get the transformation matrix that we will hand to the renderer's pattern
    338  // routine.
    339  *patternMatrix =
    340      GetPatternMatrix(aSource, aFillOrStroke, patternUnits, patternTransform,
    341                       bbox, callerBBox, aContextMatrix);
    342  if (patternMatrix->IsSingular()) {
    343    return nullptr;
    344  }
    345 
    346  // Now that we have all of the necessary geometries, we can
    347  // create our surface.
    348  gfxSize scaledSize = bbox.Size() * MaxExpansion(ToMatrix(patternTransform));
    349 
    350  bool resultOverflows;
    351  IntSize surfaceSize =
    352      SVGUtils::ConvertToSurfaceSize(scaledSize, &resultOverflows);
    353 
    354  // 0 disables rendering, < 0 is an error
    355  if (surfaceSize.width <= 0 || surfaceSize.height <= 0) {
    356    return nullptr;
    357  }
    358 
    359  gfxFloat patternWidth = bbox.Width();
    360  gfxFloat patternHeight = bbox.Height();
    361 
    362  if (resultOverflows || patternWidth != surfaceSize.width ||
    363      patternHeight != surfaceSize.height) {
    364    // scale drawing to pattern surface size
    365    patternWithChildren->mCTM->PostScale(surfaceSize.width / patternWidth,
    366                                         surfaceSize.height / patternHeight);
    367 
    368    // and rescale pattern to compensate
    369    patternMatrix->PreScale(patternWidth / surfaceSize.width,
    370                            patternHeight / surfaceSize.height);
    371  }
    372 
    373  RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTargetWithBacking(
    374      surfaceSize, SurfaceFormat::B8G8R8A8);
    375  if (!dt || !dt->IsValid()) {
    376    return nullptr;
    377  }
    378  dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height));
    379 
    380  PaintChildren(dt, patternWithChildren, aSource, aGraphicOpacity, aImgParams);
    381 
    382  // caller now owns the surface
    383  return dt->GetBackingSurface();
    384 }
    385 
    386 /* Will probably need something like this... */
    387 // How do we handle the insertion of a new frame?
    388 // We really don't want to rerender this every time,
    389 // do we?
    390 SVGPatternFrame* SVGPatternFrame::GetPatternWithChildren() {
    391  // Do we have any children ourselves?
    392  if (!mFrames.IsEmpty()) {
    393    return this;
    394  }
    395 
    396  // No, see if we chain to someone who does
    397 
    398  // Before we recurse, make sure we'll break reference loops and over long
    399  // reference chains:
    400  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    401  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    402                                        &sRefChainLengthCounter);
    403  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    404    // Break reference chain
    405    return nullptr;
    406  }
    407 
    408  SVGPatternFrame* next = GetReferencedPattern();
    409  if (!next) {
    410    return nullptr;
    411  }
    412 
    413  return next->GetPatternWithChildren();
    414 }
    415 
    416 uint16_t SVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
    417  SVGAnimatedEnumeration& thisEnum =
    418      static_cast<SVGPatternElement*>(GetContent())->mEnumAttributes[aIndex];
    419 
    420  if (thisEnum.IsExplicitlySet()) {
    421    return thisEnum.GetAnimValue();
    422  }
    423 
    424  // Before we recurse, make sure we'll break reference loops and over long
    425  // reference chains:
    426  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    427  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    428                                        &sRefChainLengthCounter);
    429  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    430    // Break reference chain
    431    return static_cast<SVGPatternElement*>(aDefault)
    432        ->mEnumAttributes[aIndex]
    433        .GetAnimValue();
    434  }
    435 
    436  SVGPatternFrame* next = GetReferencedPattern();
    437  return next ? next->GetEnumValue(aIndex, aDefault)
    438              : static_cast<SVGPatternElement*>(aDefault)
    439                    ->mEnumAttributes[aIndex]
    440                    .GetAnimValue();
    441 }
    442 
    443 SVGPatternFrame* SVGPatternFrame::GetPatternTransformFrame(
    444    SVGPatternFrame* aDefault) {
    445  if (!StyleDisplay()->mTransform.IsNone()) {
    446    return this;
    447  }
    448 
    449  // Before we recurse, make sure we'll break reference loops and over long
    450  // reference chains:
    451  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    452  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    453                                        &sRefChainLengthCounter);
    454  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    455    // Break reference chain
    456    return aDefault;
    457  }
    458 
    459  if (SVGPatternFrame* next = GetReferencedPattern()) {
    460    return next->GetPatternTransformFrame(aDefault);
    461  }
    462  return aDefault;
    463 }
    464 
    465 gfxMatrix SVGPatternFrame::GetPatternTransform() {
    466  return SVGUtils::GetTransformMatrixInUserSpace(
    467      GetPatternTransformFrame(this));
    468 }
    469 
    470 const SVGAnimatedViewBox& SVGPatternFrame::GetViewBox(nsIContent* aDefault) {
    471  const SVGAnimatedViewBox& thisViewBox =
    472      static_cast<SVGPatternElement*>(GetContent())->mViewBox;
    473 
    474  if (thisViewBox.IsExplicitlySet()) {
    475    return thisViewBox;
    476  }
    477 
    478  // Before we recurse, make sure we'll break reference loops and over long
    479  // reference chains:
    480  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    481  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    482                                        &sRefChainLengthCounter);
    483  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    484    // Break reference chain
    485    return static_cast<SVGPatternElement*>(aDefault)->mViewBox;
    486  }
    487 
    488  SVGPatternFrame* next = GetReferencedPattern();
    489  return next ? next->GetViewBox(aDefault)
    490              : static_cast<SVGPatternElement*>(aDefault)->mViewBox;
    491 }
    492 
    493 const SVGAnimatedPreserveAspectRatio& SVGPatternFrame::GetPreserveAspectRatio(
    494    nsIContent* aDefault) {
    495  const SVGAnimatedPreserveAspectRatio& thisPar =
    496      static_cast<SVGPatternElement*>(GetContent())->mPreserveAspectRatio;
    497 
    498  if (thisPar.IsExplicitlySet()) {
    499    return thisPar;
    500  }
    501 
    502  // Before we recurse, make sure we'll break reference loops and over long
    503  // reference chains:
    504  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    505  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    506                                        &sRefChainLengthCounter);
    507  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    508    // Break reference chain
    509    return static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
    510  }
    511 
    512  SVGPatternFrame* next = GetReferencedPattern();
    513  return next ? next->GetPreserveAspectRatio(aDefault)
    514              : static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
    515 }
    516 
    517 const SVGAnimatedLength* SVGPatternFrame::GetLengthValue(uint32_t aIndex,
    518                                                         nsIContent* aDefault) {
    519  const SVGAnimatedLength* thisLength =
    520      &static_cast<SVGPatternElement*>(GetContent())->mLengthAttributes[aIndex];
    521 
    522  if (thisLength->IsExplicitlySet()) {
    523    return thisLength;
    524  }
    525 
    526  // Before we recurse, make sure we'll break reference loops and over long
    527  // reference chains:
    528  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
    529  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
    530                                        &sRefChainLengthCounter);
    531  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    532    // Break reference chain
    533    return &static_cast<SVGPatternElement*>(aDefault)
    534                ->mLengthAttributes[aIndex];
    535  }
    536 
    537  SVGPatternFrame* next = GetReferencedPattern();
    538  return next ? next->GetLengthValue(aIndex, aDefault)
    539              : &static_cast<SVGPatternElement*>(aDefault)
    540                     ->mLengthAttributes[aIndex];
    541 }
    542 
    543 // Private (helper) methods
    544 
    545 SVGPatternFrame* SVGPatternFrame::GetReferencedPattern() {
    546  if (mNoHRefURI) {
    547    return nullptr;
    548  }
    549 
    550  auto GetHref = [this](nsAString& aHref) {
    551    SVGPatternElement* pattern =
    552        static_cast<SVGPatternElement*>(this->GetContent());
    553    if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
    554      pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(aHref,
    555                                                                       pattern);
    556    } else {
    557      pattern->mStringAttributes[SVGPatternElement::XLINK_HREF].GetAnimValue(
    558          aHref, pattern);
    559    }
    560    this->mNoHRefURI = aHref.IsEmpty();
    561  };
    562 
    563  // We don't call SVGObserverUtils::RemoveTemplateObserver and set
    564  // `mNoHRefURI = false` on failure since we want to be invalidated if the ID
    565  // specified by our href starts resolving to a different/valid element.
    566 
    567  return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref));
    568 }
    569 
    570 gfxRect SVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
    571                                        const gfxRect& aTargetBBox,
    572                                        const Matrix& aTargetCTM,
    573                                        nsIFrame* aTarget) {
    574  // We need to initialize our box
    575  float x, y, width, height;
    576 
    577  // Get the pattern x,y,width, and height
    578  const SVGAnimatedLength *tmpX, *tmpY, *tmpHeight, *tmpWidth;
    579  tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
    580  tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
    581  tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
    582  tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
    583 
    584  if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    585    SVGElementMetrics metrics(SVGElement::FromNode(GetContent()));
    586    x = SVGUtils::ObjectSpace(aTargetBBox, metrics, tmpX);
    587    y = SVGUtils::ObjectSpace(aTargetBBox, metrics, tmpY);
    588    width = SVGUtils::ObjectSpace(aTargetBBox, metrics, tmpWidth);
    589    height = SVGUtils::ObjectSpace(aTargetBBox, metrics, tmpHeight);
    590  } else {
    591    if (aTarget->IsTextFrame()) {
    592      aTarget = aTarget->GetParent();
    593    }
    594    float scale = MaxExpansion(aTargetCTM);
    595    x = SVGUtils::UserSpace(aTarget, tmpX) * scale;
    596    y = SVGUtils::UserSpace(aTarget, tmpY) * scale;
    597    width = SVGUtils::UserSpace(aTarget, tmpWidth) * scale;
    598    height = SVGUtils::UserSpace(aTarget, tmpHeight) * scale;
    599  }
    600 
    601  return gfxRect(x, y, width, height);
    602 }
    603 
    604 gfxMatrix SVGPatternFrame::ConstructCTM(const SVGAnimatedViewBox& aViewBox,
    605                                        uint16_t aPatternContentUnits,
    606                                        uint16_t aPatternUnits,
    607                                        const gfxRect& callerBBox,
    608                                        const Matrix& callerCTM,
    609                                        nsIFrame* aTarget) {
    610  if (aTarget->IsTextFrame()) {
    611    aTarget = aTarget->GetParent();
    612  }
    613  nsIContent* targetContent = aTarget->GetContent();
    614  SVGViewportElement* ctx = nullptr;
    615  gfxFloat scaleX, scaleY;
    616 
    617  // The objectBoundingBox conversion must be handled in the CTM:
    618  if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
    619    scaleX = callerBBox.Width();
    620    scaleY = callerBBox.Height();
    621  } else {
    622    if (targetContent->IsSVGElement()) {
    623      ctx = static_cast<SVGElement*>(targetContent)->GetCtx();
    624    }
    625    scaleX = scaleY = MaxExpansion(callerCTM);
    626  }
    627 
    628  if (!aViewBox.IsExplicitlySet()) {
    629    return gfxMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0);
    630  }
    631  const SVGViewBox& viewBox =
    632      aViewBox.GetAnimValue() * Style()->EffectiveZoom().ToFloat();
    633 
    634  if (!viewBox.IsValid()) {
    635    return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);  // singular
    636  }
    637 
    638  float viewportWidth, viewportHeight;
    639  if (targetContent->IsSVGElement()) {
    640    // If we're dealing with an SVG target only retrieve the context once.
    641    // Calling the nsIFrame* variant of GetAnimValue would look it up on
    642    // every call.
    643    viewportWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH)
    644                        ->GetAnimValueWithZoom(ctx);
    645    viewportHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT)
    646                         ->GetAnimValueWithZoom(ctx);
    647  } else {
    648    // No SVG target, call the nsIFrame* variant of GetAnimValue.
    649    viewportWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH)
    650                        ->GetAnimValueWithZoom(aTarget);
    651    viewportHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT)
    652                         ->GetAnimValueWithZoom(aTarget);
    653  }
    654 
    655  if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
    656    return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);  // singular
    657  }
    658 
    659  Matrix tm = SVGContentUtils::GetViewBoxTransform(
    660      viewportWidth * scaleX, viewportHeight * scaleY, viewBox.x, viewBox.y,
    661      viewBox.width, viewBox.height, GetPreserveAspectRatio());
    662 
    663  return ThebesMatrix(tm);
    664 }
    665 
    666 //----------------------------------------------------------------------
    667 // SVGPaintServerFrame methods:
    668 already_AddRefed<gfxPattern> SVGPatternFrame::GetPaintServerPattern(
    669    nsIFrame* aSource, const DrawTarget* aDrawTarget,
    670    const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::* aFillOrStroke,
    671    float aGraphicOpacity, imgDrawingParams& aImgParams,
    672    const gfxRect* aOverrideBounds) {
    673  if (aGraphicOpacity == 0.0f) {
    674    return do_AddRef(new gfxPattern(DeviceColor()));
    675  }
    676 
    677  // Paint it!
    678  Matrix pMatrix;
    679  RefPtr<SourceSurface> surface =
    680      PaintPattern(aDrawTarget, &pMatrix, ToMatrix(aContextMatrix), aSource,
    681                   aFillOrStroke, aGraphicOpacity, aOverrideBounds, aImgParams);
    682 
    683  if (!surface) {
    684    return nullptr;
    685  }
    686 
    687  auto pattern = MakeRefPtr<gfxPattern>(surface, pMatrix);
    688  pattern->SetExtend(ExtendMode::REPEAT);
    689 
    690  return pattern.forget();
    691 }
    692 
    693 }  // namespace mozilla
    694 
    695 // -------------------------------------------------------------------------
    696 // Public functions
    697 // -------------------------------------------------------------------------
    698 
    699 nsIFrame* NS_NewSVGPatternFrame(mozilla::PresShell* aPresShell,
    700                                mozilla::ComputedStyle* aStyle) {
    701  return new (aPresShell)
    702      mozilla::SVGPatternFrame(aStyle, aPresShell->GetPresContext());
    703 }