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