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 "gfx2DGlue.h"
13 #include "gfxContext.h"
14 #include "gfxMatrix.h"
15 #include "gfxPattern.h"
16 #include "gfxPlatform.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/ISVGDisplayableFrame.h"
19 #include "mozilla/PresShell.h"
20 #include "mozilla/SVGContentUtils.h"
21 #include "mozilla/SVGGeometryFrame.h"
22 #include "mozilla/SVGObserverUtils.h"
23 #include "mozilla/SVGUtils.h"
24 #include "mozilla/dom/SVGPatternElement.h"
25 #include "mozilla/dom/SVGUnitTypesBinding.h"
26 #include "mozilla/gfx/2D.h"
27 #include "nsGkAtoms.h"
28 #include "nsIFrameInlines.h"
29 #include "SVGAnimatedTransformList.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 
SVGPatternFrame(ComputedStyle * aStyle,nsPresContext * aPresContext)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 
NS_QUERYFRAME_HEAD(SVGPatternFrame)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                                            int32_t 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::InvalidateDirectRenderingObservers(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::InvalidateDirectRenderingObservers(this);
79   }
80 
81   return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
82                                                aModType);
83 }
84 
85 #ifdef DEBUG
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)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
GetCanvasTM()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 */
MaxExpansion(const Matrix & aMatrix)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
IncludeBBoxScale(const SVGAnimatedViewBox & aViewBox,uint32_t aPatternContentUnits,uint32_t aPatternUnits)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.
GetPatternMatrix(uint16_t aPatternUnits,const Matrix & patternTransform,const gfxRect & bbox,const gfxRect & callerBBox,const Matrix & callerCTM)152 static Matrix GetPatternMatrix(uint16_t aPatternUnits,
153                                const Matrix& patternTransform,
154                                const gfxRect& bbox, const gfxRect& callerBBox,
155                                const Matrix& callerCTM) {
156   // We really want the pattern matrix to handle translations
157   gfxFloat minx = bbox.X();
158   gfxFloat miny = bbox.Y();
159 
160   if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
161     minx += callerBBox.X();
162     miny += callerBBox.Y();
163   }
164 
165   float scale = 1.0f / MaxExpansion(callerCTM);
166   Matrix patternMatrix = patternTransform;
167   patternMatrix.PreScale(scale, scale);
168   patternMatrix.PreTranslate(minx, miny);
169 
170   return patternMatrix;
171 }
172 
GetTargetGeometry(gfxRect * aBBox,const SVGAnimatedViewBox & aViewBox,uint16_t aPatternContentUnits,uint16_t aPatternUnits,nsIFrame * aTarget,const Matrix & aContextMatrix,const gfxRect * aOverrideBounds)173 static nsresult GetTargetGeometry(gfxRect* aBBox,
174                                   const SVGAnimatedViewBox& aViewBox,
175                                   uint16_t aPatternContentUnits,
176                                   uint16_t aPatternUnits, nsIFrame* aTarget,
177                                   const Matrix& aContextMatrix,
178                                   const gfxRect* aOverrideBounds) {
179   *aBBox =
180       aOverrideBounds
181           ? *aOverrideBounds
182           : SVGUtils::GetBBox(aTarget, SVGUtils::eUseFrameBoundsForOuterSVG |
183                                            SVGUtils::eBBoxIncludeFillGeometry);
184 
185   // Sanity check
186   if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
187       (aBBox->Width() <= 0 || aBBox->Height() <= 0)) {
188     return NS_ERROR_FAILURE;
189   }
190 
191   // OK, now fix up the bounding box to reflect user coordinates
192   // We handle device unit scaling in pattern matrix
193   float scale = MaxExpansion(aContextMatrix);
194   if (scale <= 0) {
195     return NS_ERROR_FAILURE;
196   }
197   aBBox->Scale(scale);
198   return NS_OK;
199 }
200 
PaintPattern(const DrawTarget * aDrawTarget,Matrix * patternMatrix,const Matrix & aContextMatrix,nsIFrame * aSource,StyleSVGPaint nsStyleSVG::* aFillOrStroke,float aGraphicOpacity,const gfxRect * aOverrideBounds,imgDrawingParams & aImgParams)201 already_AddRefed<SourceSurface> SVGPatternFrame::PaintPattern(
202     const DrawTarget* aDrawTarget, Matrix* patternMatrix,
203     const Matrix& aContextMatrix, nsIFrame* aSource,
204     StyleSVGPaint nsStyleSVG::*aFillOrStroke, float aGraphicOpacity,
205     const gfxRect* aOverrideBounds, imgDrawingParams& aImgParams) {
206   /*
207    * General approach:
208    *    Set the content geometry stuff
209    *    Calculate our bbox (using x,y,width,height & patternUnits &
210    *                        patternTransform)
211    *    Create the surface
212    *    Calculate the content transformation matrix
213    *    Get our children (we may need to get them from another Pattern)
214    *    Call SVGPaint on all of our children
215    *    Return
216    */
217 
218   SVGPatternFrame* patternWithChildren = GetPatternWithChildren();
219   if (!patternWithChildren) {
220     // Either no kids or a bad reference
221     return nullptr;
222   }
223   nsIFrame* firstKid = patternWithChildren->mFrames.FirstChild();
224 
225   const SVGAnimatedViewBox& viewBox = GetViewBox();
226 
227   uint16_t patternContentUnits =
228       GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
229   uint16_t patternUnits = GetEnumValue(SVGPatternElement::PATTERNUNITS);
230 
231   /*
232    * Get the content geometry information.  This is a little tricky --
233    * our parent is probably a <defs>, but we are rendering in the context
234    * of some geometry source.  Our content geometry information needs to
235    * come from our rendering parent as opposed to our content parent.  We
236    * get that information from aSource, which is passed to us from the
237    * backend renderer.
238    *
239    * There are three "geometries" that we need:
240    *   1) The bounding box for the pattern.  We use this to get the
241    *      width and height for the surface, and as the return to
242    *      GetBBox.
243    *   2) The transformation matrix for the pattern.  This is not *quite*
244    *      the same as the canvas transformation matrix that we will
245    *      provide to our rendering children since we "fudge" it a little
246    *      to get the renderer to handle the translations correctly for us.
247    *   3) The CTM that we return to our children who make up the pattern.
248    */
249 
250   // Get all of the information we need from our "caller" -- i.e.
251   // the geometry that is being rendered with a pattern
252   gfxRect callerBBox;
253   if (NS_FAILED(GetTargetGeometry(&callerBBox, viewBox, patternContentUnits,
254                                   patternUnits, aSource, aContextMatrix,
255                                   aOverrideBounds))) {
256     return nullptr;
257   }
258 
259   // Construct the CTM that we will provide to our children when we
260   // render them into the tile.
261   gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
262                                callerBBox, aContextMatrix, aSource);
263   if (ctm.IsSingular()) {
264     return nullptr;
265   }
266 
267   if (patternWithChildren->mCTM) {
268     *patternWithChildren->mCTM = ctm;
269   } else {
270     patternWithChildren->mCTM = MakeUnique<gfxMatrix>(ctm);
271   }
272 
273   // Get the bounding box of the pattern.  This will be used to determine
274   // the size of the surface, and will also be used to define the bounding
275   // box for the pattern tile.
276   gfxRect bbox =
277       GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource);
278   if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
279     return nullptr;
280   }
281 
282   // Get the pattern transform
283   Matrix patternTransform = ToMatrix(GetPatternTransform());
284 
285   // revert the vector effect transform so that the pattern appears unchanged
286   if (aFillOrStroke == &nsStyleSVG::mStroke) {
287     gfxMatrix userToOuterSVG;
288     if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
289       patternTransform *= ToMatrix(userToOuterSVG);
290       if (patternTransform.IsSingular()) {
291         NS_WARNING("Singular matrix painting non-scaling-stroke");
292         return nullptr;
293       }
294     }
295   }
296 
297   // Get the transformation matrix that we will hand to the renderer's pattern
298   // routine.
299   *patternMatrix = GetPatternMatrix(patternUnits, patternTransform, bbox,
300                                     callerBBox, aContextMatrix);
301   if (patternMatrix->IsSingular()) {
302     return nullptr;
303   }
304 
305   // Now that we have all of the necessary geometries, we can
306   // create our surface.
307   gfxRect transformedBBox =
308       ThebesRect(patternTransform.TransformBounds(ToRect(bbox)));
309 
310   bool resultOverflows;
311   IntSize surfaceSize =
312       SVGUtils::ConvertToSurfaceSize(transformedBBox.Size(), &resultOverflows);
313 
314   // 0 disables rendering, < 0 is an error
315   if (surfaceSize.width <= 0 || surfaceSize.height <= 0) {
316     return nullptr;
317   }
318 
319   gfxFloat patternWidth = bbox.Width();
320   gfxFloat patternHeight = bbox.Height();
321 
322   if (resultOverflows || patternWidth != surfaceSize.width ||
323       patternHeight != surfaceSize.height) {
324     // scale drawing to pattern surface size
325     gfxMatrix tempTM = gfxMatrix(surfaceSize.width / patternWidth, 0.0, 0.0,
326                                  surfaceSize.height / patternHeight, 0.0, 0.0);
327     patternWithChildren->mCTM->PreMultiply(tempTM);
328 
329     // and rescale pattern to compensate
330     patternMatrix->PreScale(patternWidth / surfaceSize.width,
331                             patternHeight / surfaceSize.height);
332   }
333 
334   RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTargetWithBacking(
335       surfaceSize, SurfaceFormat::B8G8R8A8);
336   if (!dt || !dt->IsValid()) {
337     return nullptr;
338   }
339   dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height));
340 
341   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
342   MOZ_ASSERT(ctx);  // already checked the draw target above
343 
344   if (aGraphicOpacity != 1.0f) {
345     ctx->Save();
346     ctx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aGraphicOpacity);
347   }
348 
349   // OK, now render -- note that we use "firstKid", which
350   // we got at the beginning because it takes care of the
351   // referenced pattern situation for us
352 
353   if (aSource->IsSVGGeometryFrameOrSubclass()) {
354     // Set the geometrical parent of the pattern we are rendering
355     patternWithChildren->mSource = static_cast<SVGGeometryFrame*>(aSource);
356   }
357 
358   // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
359   // give back a clear surface if there's a loop
360   if (!patternWithChildren->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER)) {
361     AutoSetRestorePaintServerState paintServer(patternWithChildren);
362     for (nsIFrame* kid = firstKid; kid; kid = kid->GetNextSibling()) {
363       gfxMatrix tm = *(patternWithChildren->mCTM);
364 
365       // The CTM of each frame referencing us can be different
366       ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
367       if (SVGFrame) {
368         SVGFrame->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED);
369         tm = SVGUtils::GetTransformMatrixInUserSpace(kid) * tm;
370       }
371 
372       SVGUtils::PaintFrameWithEffects(kid, *ctx, tm, aImgParams);
373     }
374   }
375 
376   patternWithChildren->mSource = nullptr;
377 
378   if (aGraphicOpacity != 1.0f) {
379     ctx->PopGroupAndBlend();
380     ctx->Restore();
381   }
382 
383   // caller now owns the surface
384   return dt->GetBackingSurface();
385 }
386 
387 /* Will probably need something like this... */
388 // How do we handle the insertion of a new frame?
389 // We really don't want to rerender this every time,
390 // do we?
GetPatternWithChildren()391 SVGPatternFrame* SVGPatternFrame::GetPatternWithChildren() {
392   // Do we have any children ourselves?
393   if (!mFrames.IsEmpty()) {
394     return this;
395   }
396 
397   // No, see if we chain to someone who does
398 
399   // Before we recurse, make sure we'll break reference loops and over long
400   // reference chains:
401   static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
402   AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
403                                         &sRefChainLengthCounter);
404   if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
405     // Break reference chain
406     return nullptr;
407   }
408 
409   SVGPatternFrame* next = GetReferencedPattern();
410   if (!next) {
411     return nullptr;
412   }
413 
414   return next->GetPatternWithChildren();
415 }
416 
GetEnumValue(uint32_t aIndex,nsIContent * aDefault)417 uint16_t SVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
418   SVGAnimatedEnumeration& thisEnum =
419       static_cast<SVGPatternElement*>(GetContent())->mEnumAttributes[aIndex];
420 
421   if (thisEnum.IsExplicitlySet()) {
422     return thisEnum.GetAnimValue();
423   }
424 
425   // Before we recurse, make sure we'll break reference loops and over long
426   // reference chains:
427   static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
428   AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
429                                         &sRefChainLengthCounter);
430   if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
431     // Break reference chain
432     return static_cast<SVGPatternElement*>(aDefault)
433         ->mEnumAttributes[aIndex]
434         .GetAnimValue();
435   }
436 
437   SVGPatternFrame* next = GetReferencedPattern();
438   return next ? next->GetEnumValue(aIndex, aDefault)
439               : static_cast<SVGPatternElement*>(aDefault)
440                     ->mEnumAttributes[aIndex]
441                     .GetAnimValue();
442 }
443 
GetPatternTransformList(nsIContent * aDefault)444 SVGAnimatedTransformList* SVGPatternFrame::GetPatternTransformList(
445     nsIContent* aDefault) {
446   SVGAnimatedTransformList* thisTransformList =
447       static_cast<SVGPatternElement*>(GetContent())->GetAnimatedTransformList();
448 
449   if (thisTransformList && thisTransformList->IsExplicitlySet())
450     return thisTransformList;
451 
452   // Before we recurse, make sure we'll break reference loops and over long
453   // reference chains:
454   static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
455   AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
456                                         &sRefChainLengthCounter);
457   if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
458     // Break reference chain
459     return static_cast<SVGPatternElement*>(aDefault)->mPatternTransform.get();
460   }
461 
462   SVGPatternFrame* next = GetReferencedPattern();
463   return next ? next->GetPatternTransformList(aDefault)
464               : static_cast<SVGPatternElement*>(aDefault)
465                     ->mPatternTransform.get();
466 }
467 
GetPatternTransform()468 gfxMatrix SVGPatternFrame::GetPatternTransform() {
469   SVGAnimatedTransformList* animTransformList =
470       GetPatternTransformList(GetContent());
471   if (!animTransformList) {
472     return gfxMatrix();
473   }
474 
475   return animTransformList->GetAnimValue().GetConsolidationMatrix();
476 }
477 
GetViewBox(nsIContent * aDefault)478 const SVGAnimatedViewBox& SVGPatternFrame::GetViewBox(nsIContent* aDefault) {
479   const SVGAnimatedViewBox& thisViewBox =
480       static_cast<SVGPatternElement*>(GetContent())->mViewBox;
481 
482   if (thisViewBox.IsExplicitlySet()) {
483     return thisViewBox;
484   }
485 
486   // Before we recurse, make sure we'll break reference loops and over long
487   // reference chains:
488   static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
489   AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
490                                         &sRefChainLengthCounter);
491   if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
492     // Break reference chain
493     return static_cast<SVGPatternElement*>(aDefault)->mViewBox;
494   }
495 
496   SVGPatternFrame* next = GetReferencedPattern();
497   return next ? next->GetViewBox(aDefault)
498               : static_cast<SVGPatternElement*>(aDefault)->mViewBox;
499 }
500 
GetPreserveAspectRatio(nsIContent * aDefault)501 const SVGAnimatedPreserveAspectRatio& SVGPatternFrame::GetPreserveAspectRatio(
502     nsIContent* aDefault) {
503   const SVGAnimatedPreserveAspectRatio& thisPar =
504       static_cast<SVGPatternElement*>(GetContent())->mPreserveAspectRatio;
505 
506   if (thisPar.IsExplicitlySet()) {
507     return thisPar;
508   }
509 
510   // Before we recurse, make sure we'll break reference loops and over long
511   // reference chains:
512   static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
513   AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
514                                         &sRefChainLengthCounter);
515   if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
516     // Break reference chain
517     return static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
518   }
519 
520   SVGPatternFrame* next = GetReferencedPattern();
521   return next ? next->GetPreserveAspectRatio(aDefault)
522               : static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
523 }
524 
GetLengthValue(uint32_t aIndex,nsIContent * aDefault)525 const SVGAnimatedLength* SVGPatternFrame::GetLengthValue(uint32_t aIndex,
526                                                          nsIContent* aDefault) {
527   const SVGAnimatedLength* thisLength =
528       &static_cast<SVGPatternElement*>(GetContent())->mLengthAttributes[aIndex];
529 
530   if (thisLength->IsExplicitlySet()) {
531     return thisLength;
532   }
533 
534   // Before we recurse, make sure we'll break reference loops and over long
535   // reference chains:
536   static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
537   AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
538                                         &sRefChainLengthCounter);
539   if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
540     // Break reference chain
541     return &static_cast<SVGPatternElement*>(aDefault)
542                 ->mLengthAttributes[aIndex];
543   }
544 
545   SVGPatternFrame* next = GetReferencedPattern();
546   return next ? next->GetLengthValue(aIndex, aDefault)
547               : &static_cast<SVGPatternElement*>(aDefault)
548                      ->mLengthAttributes[aIndex];
549 }
550 
551 // Private (helper) methods
552 
GetReferencedPattern()553 SVGPatternFrame* SVGPatternFrame::GetReferencedPattern() {
554   if (mNoHRefURI) {
555     return nullptr;
556   }
557 
558   auto GetHref = [this](nsAString& aHref) {
559     SVGPatternElement* pattern =
560         static_cast<SVGPatternElement*>(this->GetContent());
561     if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
562       pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(aHref,
563                                                                        pattern);
564     } else {
565       pattern->mStringAttributes[SVGPatternElement::XLINK_HREF].GetAnimValue(
566           aHref, pattern);
567     }
568     this->mNoHRefURI = aHref.IsEmpty();
569   };
570 
571   nsIFrame* tframe = SVGObserverUtils::GetAndObserveTemplate(this, GetHref);
572   if (tframe) {
573     LayoutFrameType frameType = tframe->Type();
574     if (frameType == LayoutFrameType::SVGPattern) {
575       return static_cast<SVGPatternFrame*>(tframe);
576     }
577     // We don't call SVGObserverUtils::RemoveTemplateObserver and set
578     // `mNoHRefURI = false` here since we want to be invalidated if the ID
579     // specified by our href starts resolving to a different/valid element.
580   }
581 
582   return nullptr;
583 }
584 
GetPatternRect(uint16_t aPatternUnits,const gfxRect & aTargetBBox,const Matrix & aTargetCTM,nsIFrame * aTarget)585 gfxRect SVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
586                                         const gfxRect& aTargetBBox,
587                                         const Matrix& aTargetCTM,
588                                         nsIFrame* aTarget) {
589   // We need to initialize our box
590   float x, y, width, height;
591 
592   // Get the pattern x,y,width, and height
593   const SVGAnimatedLength *tmpX, *tmpY, *tmpHeight, *tmpWidth;
594   tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
595   tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
596   tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
597   tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
598 
599   if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
600     x = SVGUtils::ObjectSpace(aTargetBBox, tmpX);
601     y = SVGUtils::ObjectSpace(aTargetBBox, tmpY);
602     width = SVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
603     height = SVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
604   } else {
605     float scale = MaxExpansion(aTargetCTM);
606     x = SVGUtils::UserSpace(aTarget, tmpX) * scale;
607     y = SVGUtils::UserSpace(aTarget, tmpY) * scale;
608     width = SVGUtils::UserSpace(aTarget, tmpWidth) * scale;
609     height = SVGUtils::UserSpace(aTarget, tmpHeight) * scale;
610   }
611 
612   return gfxRect(x, y, width, height);
613 }
614 
ConstructCTM(const SVGAnimatedViewBox & aViewBox,uint16_t aPatternContentUnits,uint16_t aPatternUnits,const gfxRect & callerBBox,const Matrix & callerCTM,nsIFrame * aTarget)615 gfxMatrix SVGPatternFrame::ConstructCTM(const SVGAnimatedViewBox& aViewBox,
616                                         uint16_t aPatternContentUnits,
617                                         uint16_t aPatternUnits,
618                                         const gfxRect& callerBBox,
619                                         const Matrix& callerCTM,
620                                         nsIFrame* aTarget) {
621   SVGViewportElement* ctx = nullptr;
622   nsIContent* targetContent = aTarget->GetContent();
623   gfxFloat scaleX, scaleY;
624 
625   // The objectBoundingBox conversion must be handled in the CTM:
626   if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
627     scaleX = callerBBox.Width();
628     scaleY = callerBBox.Height();
629   } else {
630     if (targetContent->IsSVGElement()) {
631       ctx = static_cast<SVGElement*>(targetContent)->GetCtx();
632     }
633     scaleX = scaleY = MaxExpansion(callerCTM);
634   }
635 
636   if (!aViewBox.IsExplicitlySet()) {
637     return gfxMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0);
638   }
639   const SVGViewBox& viewBox = aViewBox.GetAnimValue();
640 
641   if (viewBox.height <= 0.0f || viewBox.width <= 0.0f) {
642     return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);  // singular
643   }
644 
645   float viewportWidth, viewportHeight;
646   if (targetContent->IsSVGElement()) {
647     // If we're dealing with an SVG target only retrieve the context once.
648     // Calling the nsIFrame* variant of GetAnimValue would look it up on
649     // every call.
650     viewportWidth =
651         GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx);
652     viewportHeight =
653         GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx);
654   } else {
655     // No SVG target, call the nsIFrame* variant of GetAnimValue.
656     viewportWidth =
657         GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget);
658     viewportHeight =
659         GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget);
660   }
661 
662   if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
663     return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);  // singular
664   }
665 
666   Matrix tm = SVGContentUtils::GetViewBoxTransform(
667       viewportWidth * scaleX, viewportHeight * scaleY, viewBox.x, viewBox.y,
668       viewBox.width, viewBox.height, GetPreserveAspectRatio());
669 
670   return ThebesMatrix(tm);
671 }
672 
673 //----------------------------------------------------------------------
674 // SVGPaintServerFrame methods:
GetPaintServerPattern(nsIFrame * aSource,const DrawTarget * aDrawTarget,const gfxMatrix & aContextMatrix,StyleSVGPaint nsStyleSVG::* aFillOrStroke,float aGraphicOpacity,imgDrawingParams & aImgParams,const gfxRect * aOverrideBounds)675 already_AddRefed<gfxPattern> SVGPatternFrame::GetPaintServerPattern(
676     nsIFrame* aSource, const DrawTarget* aDrawTarget,
677     const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::*aFillOrStroke,
678     float aGraphicOpacity, imgDrawingParams& aImgParams,
679     const gfxRect* aOverrideBounds) {
680   if (aGraphicOpacity == 0.0f) {
681     return do_AddRef(new gfxPattern(DeviceColor()));
682   }
683 
684   // Paint it!
685   Matrix pMatrix;
686   RefPtr<SourceSurface> surface =
687       PaintPattern(aDrawTarget, &pMatrix, ToMatrix(aContextMatrix), aSource,
688                    aFillOrStroke, aGraphicOpacity, aOverrideBounds, aImgParams);
689 
690   if (!surface) {
691     return nullptr;
692   }
693 
694   RefPtr<gfxPattern> pattern = new gfxPattern(surface, pMatrix);
695 
696   if (!pattern) {
697     return nullptr;
698   }
699 
700   pattern->SetExtend(ExtendMode::REPEAT);
701   return pattern.forget();
702 }
703 
704 }  // namespace mozilla
705 
706 // -------------------------------------------------------------------------
707 // Public functions
708 // -------------------------------------------------------------------------
709 
NS_NewSVGPatternFrame(mozilla::PresShell * aPresShell,mozilla::ComputedStyle * aStyle)710 nsIFrame* NS_NewSVGPatternFrame(mozilla::PresShell* aPresShell,
711                                 mozilla::ComputedStyle* aStyle) {
712   return new (aPresShell)
713       mozilla::SVGPatternFrame(aStyle, aPresShell->GetPresContext());
714 }
715