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