Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
6 // Main header first:
7 #include "nsSVGPatternFrame.h"
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 "nsRenderingContext.h"
20 #include "nsStyleContext.h"
21 #include "nsSVGEffects.h"
22 #include "nsSVGPathGeometryFrame.h"
23 #include "mozilla/dom/SVGPatternElement.h"
24 #include "nsSVGUtils.h"
25 #include "nsSVGAnimatedTransformList.h"
26 #include "SVGContentUtils.h"
27 #include "gfxColor.h"
29 using namespace mozilla;
30 using namespace mozilla::dom;
31 using namespace mozilla::gfx;
33 //----------------------------------------------------------------------
34 // Helper classes
36 class MOZ_STACK_CLASS nsSVGPatternFrame::AutoPatternReferencer
37 {
38 public:
39 AutoPatternReferencer(nsSVGPatternFrame *aFrame
40 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
41 : mFrame(aFrame)
42 {
43 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
44 // Reference loops should normally be detected in advance and handled, so
45 // we're not expecting to encounter them here
46 NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!");
47 mFrame->mLoopFlag = true;
48 }
49 ~AutoPatternReferencer() {
50 mFrame->mLoopFlag = false;
51 }
52 private:
53 nsSVGPatternFrame *mFrame;
54 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
55 };
57 //----------------------------------------------------------------------
58 // Implementation
60 nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext) :
61 nsSVGPatternFrameBase(aContext),
62 mLoopFlag(false),
63 mNoHRefURI(false)
64 {
65 }
67 NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame)
69 //----------------------------------------------------------------------
70 // nsIFrame methods:
72 nsresult
73 nsSVGPatternFrame::AttributeChanged(int32_t aNameSpaceID,
74 nsIAtom* aAttribute,
75 int32_t aModType)
76 {
77 if (aNameSpaceID == kNameSpaceID_None &&
78 (aAttribute == nsGkAtoms::patternUnits ||
79 aAttribute == nsGkAtoms::patternContentUnits ||
80 aAttribute == nsGkAtoms::patternTransform ||
81 aAttribute == nsGkAtoms::x ||
82 aAttribute == nsGkAtoms::y ||
83 aAttribute == nsGkAtoms::width ||
84 aAttribute == nsGkAtoms::height ||
85 aAttribute == nsGkAtoms::preserveAspectRatio ||
86 aAttribute == nsGkAtoms::viewBox)) {
87 nsSVGEffects::InvalidateDirectRenderingObservers(this);
88 }
90 if (aNameSpaceID == kNameSpaceID_XLink &&
91 aAttribute == nsGkAtoms::href) {
92 // Blow away our reference, if any
93 Properties().Delete(nsSVGEffects::HrefProperty());
94 mNoHRefURI = false;
95 // And update whoever references us
96 nsSVGEffects::InvalidateDirectRenderingObservers(this);
97 }
99 return nsSVGPatternFrameBase::AttributeChanged(aNameSpaceID,
100 aAttribute, aModType);
101 }
103 #ifdef DEBUG
104 void
105 nsSVGPatternFrame::Init(nsIContent* aContent,
106 nsIFrame* aParent,
107 nsIFrame* aPrevInFlow)
108 {
109 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::pattern), "Content is not an SVG pattern");
111 nsSVGPatternFrameBase::Init(aContent, aParent, aPrevInFlow);
112 }
113 #endif /* DEBUG */
115 nsIAtom*
116 nsSVGPatternFrame::GetType() const
117 {
118 return nsGkAtoms::svgPatternFrame;
119 }
121 //----------------------------------------------------------------------
122 // nsSVGContainerFrame methods:
124 // If our GetCanvasTM is getting called, we
125 // need to return *our current* transformation
126 // matrix, which depends on our units parameters
127 // and X, Y, Width, and Height
128 gfxMatrix
129 nsSVGPatternFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
130 {
131 if (mCTM) {
132 return *mCTM;
133 }
135 // Do we know our rendering parent?
136 if (mSource) {
137 // Yes, use it!
138 return mSource->GetCanvasTM(aFor, aTransformRoot);
139 }
141 // We get here when geometry in the <pattern> container is updated
142 return gfxMatrix();
143 }
145 // -------------------------------------------------------------------------
146 // Helper functions
147 // -------------------------------------------------------------------------
149 /** Calculate the maximum expansion of a matrix */
150 static float
151 MaxExpansion(const Matrix &aMatrix)
152 {
153 // maximum expansion derivation from
154 // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
155 // and also implemented in cairo_matrix_transformed_circle_major_axis
156 double a = aMatrix._11;
157 double b = aMatrix._12;
158 double c = aMatrix._21;
159 double d = aMatrix._22;
160 double f = (a * a + b * b + c * c + d * d) / 2;
161 double g = (a * a + b * b - c * c - d * d) / 2;
162 double h = a * c + b * d;
163 return sqrt(f + sqrt(g * g + h * h));
164 }
166 // The SVG specification says that the 'patternContentUnits' attribute "has no effect if
167 // attribute ‘viewBox’ is specified". We still need to include a bbox scale
168 // if the viewBox is specified and _patternUnits_ is set to or defaults to
169 // objectBoundingBox though, since in that case the viewBox is relative to the bbox
170 static bool
171 IncludeBBoxScale(const nsSVGViewBox& aViewBox,
172 uint32_t aPatternContentUnits, uint32_t aPatternUnits)
173 {
174 return (!aViewBox.IsExplicitlySet() &&
175 aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) ||
176 (aViewBox.IsExplicitlySet() &&
177 aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
178 }
180 // Given the matrix for the pattern element's own transform, this returns a
181 // combined matrix including the transforms applicable to its target.
182 static gfxMatrix
183 GetPatternMatrix(uint16_t aPatternUnits,
184 const gfxMatrix &patternTransform,
185 const gfxRect &bbox,
186 const gfxRect &callerBBox,
187 const Matrix &callerCTM)
188 {
189 // We really want the pattern matrix to handle translations
190 gfxFloat minx = bbox.X();
191 gfxFloat miny = bbox.Y();
193 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
194 minx += callerBBox.X();
195 miny += callerBBox.Y();
196 }
198 float scale = 1.0f / MaxExpansion(callerCTM);
199 gfxMatrix patternMatrix = patternTransform;
200 patternMatrix.Scale(scale, scale);
201 patternMatrix.Translate(gfxPoint(minx, miny));
203 return patternMatrix;
204 }
206 static nsresult
207 GetTargetGeometry(gfxRect *aBBox,
208 const nsSVGViewBox &aViewBox,
209 uint16_t aPatternContentUnits,
210 uint16_t aPatternUnits,
211 nsIFrame *aTarget,
212 const Matrix &aContextMatrix,
213 const gfxRect *aOverrideBounds)
214 {
215 *aBBox = aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aTarget);
217 // Sanity check
218 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
219 (aBBox->Width() <= 0 || aBBox->Height() <= 0)) {
220 return NS_ERROR_FAILURE;
221 }
223 // OK, now fix up the bounding box to reflect user coordinates
224 // We handle device unit scaling in pattern matrix
225 float scale = MaxExpansion(aContextMatrix);
226 if (scale <= 0) {
227 return NS_ERROR_FAILURE;
228 }
229 aBBox->Scale(scale);
230 return NS_OK;
231 }
233 nsresult
234 nsSVGPatternFrame::PaintPattern(gfxASurface** surface,
235 gfxMatrix* patternMatrix,
236 const gfxMatrix &aContextMatrix,
237 nsIFrame *aSource,
238 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
239 float aGraphicOpacity,
240 const gfxRect *aOverrideBounds)
241 {
242 /*
243 * General approach:
244 * Set the content geometry stuff
245 * Calculate our bbox (using x,y,width,height & patternUnits &
246 * patternTransform)
247 * Create the surface
248 * Calculate the content transformation matrix
249 * Get our children (we may need to get them from another Pattern)
250 * Call SVGPaint on all of our children
251 * Return
252 */
253 *surface = nullptr;
255 // Get the first child of the pattern data we will render
256 nsIFrame* firstKid = GetPatternFirstChild();
257 if (!firstKid)
258 return NS_ERROR_FAILURE; // Either no kids or a bad reference
260 const nsSVGViewBox& viewBox = GetViewBox();
262 uint16_t patternContentUnits =
263 GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
264 uint16_t patternUnits =
265 GetEnumValue(SVGPatternElement::PATTERNUNITS);
267 /*
268 * Get the content geometry information. This is a little tricky --
269 * our parent is probably a <defs>, but we are rendering in the context
270 * of some geometry source. Our content geometry information needs to
271 * come from our rendering parent as opposed to our content parent. We
272 * get that information from aSource, which is passed to us from the
273 * backend renderer.
274 *
275 * There are three "geometries" that we need:
276 * 1) The bounding box for the pattern. We use this to get the
277 * width and height for the surface, and as the return to
278 * GetBBox.
279 * 2) The transformation matrix for the pattern. This is not *quite*
280 * the same as the canvas transformation matrix that we will
281 * provide to our rendering children since we "fudge" it a little
282 * to get the renderer to handle the translations correctly for us.
283 * 3) The CTM that we return to our children who make up the pattern.
284 */
286 // Get all of the information we need from our "caller" -- i.e.
287 // the geometry that is being rendered with a pattern
288 gfxRect callerBBox;
289 if (NS_FAILED(GetTargetGeometry(&callerBBox,
290 viewBox,
291 patternContentUnits, patternUnits,
292 aSource,
293 ToMatrix(aContextMatrix),
294 aOverrideBounds)))
295 return NS_ERROR_FAILURE;
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, ToMatrix(aContextMatrix), aSource);
301 if (ctm.IsSingular()) {
302 return NS_ERROR_FAILURE;
303 }
305 // Get the pattern we are going to render
306 nsSVGPatternFrame *patternFrame =
307 static_cast<nsSVGPatternFrame*>(firstKid->GetParent());
308 if (patternFrame->mCTM) {
309 *patternFrame->mCTM = ctm;
310 } else {
311 patternFrame->mCTM = new gfxMatrix(ctm);
312 }
314 // Get the bounding box of the pattern. This will be used to determine
315 // the size of the surface, and will also be used to define the bounding
316 // box for the pattern tile.
317 gfxRect bbox = GetPatternRect(patternUnits, callerBBox, ToMatrix(aContextMatrix), aSource);
318 if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
319 return NS_ERROR_FAILURE;
320 }
322 // Get the pattern transform
323 gfxMatrix patternTransform = GetPatternTransform();
325 // revert the vector effect transform so that the pattern appears unchanged
326 if (aFillOrStroke == &nsStyleSVG::mStroke) {
327 patternTransform.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert());
328 }
330 // Get the transformation matrix that we will hand to the renderer's pattern
331 // routine.
332 *patternMatrix = GetPatternMatrix(patternUnits, patternTransform,
333 bbox, callerBBox, ToMatrix(aContextMatrix));
334 if (patternMatrix->IsSingular()) {
335 return NS_ERROR_FAILURE;
336 }
338 // Now that we have all of the necessary geometries, we can
339 // create our surface.
340 gfxRect transformedBBox = patternTransform.TransformBounds(bbox);
342 bool resultOverflows;
343 IntSize surfaceSize =
344 nsSVGUtils::ConvertToSurfaceSize(
345 transformedBBox.Size(), &resultOverflows).ToIntSize();
347 // 0 disables rendering, < 0 is an error
348 if (surfaceSize.width <= 0 || surfaceSize.height <= 0)
349 return NS_ERROR_FAILURE;
351 gfxFloat patternWidth = bbox.Width();
352 gfxFloat patternHeight = bbox.Height();
354 if (resultOverflows ||
355 patternWidth != surfaceSize.width ||
356 patternHeight != surfaceSize.height) {
357 // scale drawing to pattern surface size
358 gfxMatrix tempTM =
359 gfxMatrix(surfaceSize.width / patternWidth, 0.0f,
360 0.0f, surfaceSize.height / patternHeight,
361 0.0f, 0.0f);
362 patternFrame->mCTM->PreMultiply(tempTM);
364 // and rescale pattern to compensate
365 patternMatrix->Scale(patternWidth / surfaceSize.width,
366 patternHeight / surfaceSize.height);
367 }
369 nsRefPtr<gfxASurface> tmpSurface =
370 gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize,
371 gfxContentType::COLOR_ALPHA);
372 if (!tmpSurface || tmpSurface->CairoStatus())
373 return NS_ERROR_FAILURE;
375 nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
376 context->Init(aSource->PresContext()->DeviceContext(), tmpSurface);
377 gfxContext* gfx = context->ThebesContext();
379 // Fill with transparent black
380 gfx->SetOperator(gfxContext::OPERATOR_CLEAR);
381 gfx->Paint();
382 gfx->SetOperator(gfxContext::OPERATOR_OVER);
384 if (aGraphicOpacity != 1.0f) {
385 gfx->Save();
386 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
387 }
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
393 if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
394 // Set the geometrical parent of the pattern we are rendering
395 patternFrame->mSource = static_cast<nsSVGPathGeometryFrame*>(aSource);
396 }
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 (!(patternFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) {
401 patternFrame->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 nsSVGUtils::PaintFrameWithEffects(context, nullptr, kid);
410 }
411 patternFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
412 }
414 patternFrame->mSource = nullptr;
416 if (aGraphicOpacity != 1.0f) {
417 gfx->PopGroupToSource();
418 gfx->Paint(aGraphicOpacity);
419 gfx->Restore();
420 }
422 // caller now owns the surface
423 tmpSurface.forget(surface);
424 return NS_OK;
425 }
427 /* Will probably need something like this... */
428 // How do we handle the insertion of a new frame?
429 // We really don't want to rerender this every time,
430 // do we?
431 nsIFrame*
432 nsSVGPatternFrame::GetPatternFirstChild()
433 {
434 // Do we have any children ourselves?
435 nsIFrame* kid = mFrames.FirstChild();
436 if (kid)
437 return kid;
439 // No, see if we chain to someone who does
440 AutoPatternReferencer patternRef(this);
442 nsSVGPatternFrame* next = GetReferencedPatternIfNotInUse();
443 if (!next)
444 return nullptr;
446 return next->GetPatternFirstChild();
447 }
449 uint16_t
450 nsSVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
451 {
452 nsSVGEnum& thisEnum =
453 static_cast<SVGPatternElement *>(mContent)->mEnumAttributes[aIndex];
455 if (thisEnum.IsExplicitlySet())
456 return thisEnum.GetAnimValue();
458 AutoPatternReferencer patternRef(this);
460 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
461 return next ? next->GetEnumValue(aIndex, aDefault) :
462 static_cast<SVGPatternElement *>(aDefault)->
463 mEnumAttributes[aIndex].GetAnimValue();
464 }
466 nsSVGAnimatedTransformList*
467 nsSVGPatternFrame::GetPatternTransformList(nsIContent* aDefault)
468 {
469 nsSVGAnimatedTransformList *thisTransformList =
470 static_cast<SVGPatternElement *>(mContent)->GetAnimatedTransformList();
472 if (thisTransformList && thisTransformList->IsExplicitlySet())
473 return thisTransformList;
475 AutoPatternReferencer patternRef(this);
477 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
478 return next ? next->GetPatternTransformList(aDefault) :
479 static_cast<SVGPatternElement *>(aDefault)->mPatternTransform.get();
480 }
482 gfxMatrix
483 nsSVGPatternFrame::GetPatternTransform()
484 {
485 nsSVGAnimatedTransformList* animTransformList =
486 GetPatternTransformList(mContent);
487 if (!animTransformList)
488 return gfxMatrix();
490 return animTransformList->GetAnimValue().GetConsolidationMatrix();
491 }
493 const nsSVGViewBox &
494 nsSVGPatternFrame::GetViewBox(nsIContent* aDefault)
495 {
496 const nsSVGViewBox &thisViewBox =
497 static_cast<SVGPatternElement *>(mContent)->mViewBox;
499 if (thisViewBox.IsExplicitlySet())
500 return thisViewBox;
502 AutoPatternReferencer patternRef(this);
504 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
505 return next ? next->GetViewBox(aDefault) :
506 static_cast<SVGPatternElement *>(aDefault)->mViewBox;
507 }
509 const SVGAnimatedPreserveAspectRatio &
510 nsSVGPatternFrame::GetPreserveAspectRatio(nsIContent *aDefault)
511 {
512 const SVGAnimatedPreserveAspectRatio &thisPar =
513 static_cast<SVGPatternElement *>(mContent)->mPreserveAspectRatio;
515 if (thisPar.IsExplicitlySet())
516 return thisPar;
518 AutoPatternReferencer patternRef(this);
520 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
521 return next ? next->GetPreserveAspectRatio(aDefault) :
522 static_cast<SVGPatternElement *>(aDefault)->mPreserveAspectRatio;
523 }
525 const nsSVGLength2 *
526 nsSVGPatternFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault)
527 {
528 const nsSVGLength2 *thisLength =
529 &static_cast<SVGPatternElement *>(mContent)->mLengthAttributes[aIndex];
531 if (thisLength->IsExplicitlySet())
532 return thisLength;
534 AutoPatternReferencer patternRef(this);
536 nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse();
537 return next ? next->GetLengthValue(aIndex, aDefault) :
538 &static_cast<SVGPatternElement *>(aDefault)->mLengthAttributes[aIndex];
539 }
541 // Private (helper) methods
542 nsSVGPatternFrame *
543 nsSVGPatternFrame::GetReferencedPattern()
544 {
545 if (mNoHRefURI)
546 return nullptr;
548 nsSVGPaintingProperty *property = static_cast<nsSVGPaintingProperty*>
549 (Properties().Get(nsSVGEffects::HrefProperty()));
551 if (!property) {
552 // Fetch our pattern element's xlink:href attribute
553 SVGPatternElement *pattern = static_cast<SVGPatternElement *>(mContent);
554 nsAutoString href;
555 pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(href, pattern);
556 if (href.IsEmpty()) {
557 mNoHRefURI = true;
558 return nullptr; // no URL
559 }
561 // Convert href to an nsIURI
562 nsCOMPtr<nsIURI> targetURI;
563 nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
564 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
565 mContent->GetCurrentDoc(), base);
567 property =
568 nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty());
569 if (!property)
570 return nullptr;
571 }
573 nsIFrame *result = property->GetReferencedFrame();
574 if (!result)
575 return nullptr;
577 nsIAtom* frameType = result->GetType();
578 if (frameType != nsGkAtoms::svgPatternFrame)
579 return nullptr;
581 return static_cast<nsSVGPatternFrame*>(result);
582 }
584 nsSVGPatternFrame *
585 nsSVGPatternFrame::GetReferencedPatternIfNotInUse()
586 {
587 nsSVGPatternFrame *referenced = GetReferencedPattern();
588 if (!referenced)
589 return nullptr;
591 if (referenced->mLoopFlag) {
592 // XXXjwatt: we should really send an error to the JavaScript Console here:
593 NS_WARNING("pattern reference loop detected while inheriting attribute!");
594 return nullptr;
595 }
597 return referenced;
598 }
600 gfxRect
601 nsSVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
602 const gfxRect &aTargetBBox,
603 const Matrix &aTargetCTM,
604 nsIFrame *aTarget)
605 {
606 // We need to initialize our box
607 float x,y,width,height;
609 // Get the pattern x,y,width, and height
610 const nsSVGLength2 *tmpX, *tmpY, *tmpHeight, *tmpWidth;
611 tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
612 tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
613 tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
614 tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
616 if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
617 x = nsSVGUtils::ObjectSpace(aTargetBBox, tmpX);
618 y = nsSVGUtils::ObjectSpace(aTargetBBox, tmpY);
619 width = nsSVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
620 height = nsSVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
621 } else {
622 float scale = MaxExpansion(aTargetCTM);
623 x = nsSVGUtils::UserSpace(aTarget, tmpX) * scale;
624 y = nsSVGUtils::UserSpace(aTarget, tmpY) * scale;
625 width = nsSVGUtils::UserSpace(aTarget, tmpWidth) * scale;
626 height = nsSVGUtils::UserSpace(aTarget, tmpHeight) * scale;
627 }
629 return gfxRect(x, y, width, height);
630 }
632 gfxMatrix
633 nsSVGPatternFrame::ConstructCTM(const nsSVGViewBox& aViewBox,
634 uint16_t aPatternContentUnits,
635 uint16_t aPatternUnits,
636 const gfxRect &callerBBox,
637 const Matrix &callerCTM,
638 nsIFrame *aTarget)
639 {
640 gfxMatrix tCTM;
641 SVGSVGElement *ctx = nullptr;
642 nsIContent* targetContent = aTarget->GetContent();
644 // The objectBoundingBox conversion must be handled in the CTM:
645 if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
646 tCTM.Scale(callerBBox.Width(), callerBBox.Height());
647 } else {
648 if (targetContent->IsSVG()) {
649 ctx = static_cast<nsSVGElement*>(targetContent)->GetCtx();
650 }
651 float scale = MaxExpansion(callerCTM);
652 tCTM.Scale(scale, scale);
653 }
655 if (!aViewBox.IsExplicitlySet()) {
656 return tCTM;
657 }
658 const nsSVGViewBoxRect viewBoxRect = aViewBox.GetAnimValue();
660 if (viewBoxRect.height <= 0.0f || viewBoxRect.width <= 0.0f) {
661 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
662 }
664 float viewportWidth, viewportHeight;
665 if (targetContent->IsSVG()) {
666 // If we're dealing with an SVG target only retrieve the context once.
667 // Calling the nsIFrame* variant of GetAnimValue would look it up on
668 // every call.
669 viewportWidth =
670 GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx);
671 viewportHeight =
672 GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx);
673 } else {
674 // No SVG target, call the nsIFrame* variant of GetAnimValue.
675 viewportWidth =
676 GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget);
677 viewportHeight =
678 GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget);
679 }
681 if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
682 return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
683 }
685 Matrix tm = SVGContentUtils::GetViewBoxTransform(
686 viewportWidth, viewportHeight,
687 viewBoxRect.x, viewBoxRect.y,
688 viewBoxRect.width, viewBoxRect.height,
689 GetPreserveAspectRatio());
691 return ThebesMatrix(tm) * tCTM;
692 }
694 //----------------------------------------------------------------------
695 // nsSVGPaintServerFrame methods:
697 already_AddRefed<gfxPattern>
698 nsSVGPatternFrame::GetPaintServerPattern(nsIFrame *aSource,
699 const gfxMatrix& aContextMatrix,
700 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
701 float aGraphicOpacity,
702 const gfxRect *aOverrideBounds)
703 {
704 if (aGraphicOpacity == 0.0f) {
705 nsRefPtr<gfxPattern> pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0));
706 return pattern.forget();
707 }
709 // Paint it!
710 nsRefPtr<gfxASurface> surface;
711 gfxMatrix pMatrix;
712 nsresult rv = PaintPattern(getter_AddRefs(surface), &pMatrix, aContextMatrix,
713 aSource, aFillOrStroke, aGraphicOpacity, aOverrideBounds);
715 if (NS_FAILED(rv)) {
716 return nullptr;
717 }
719 pMatrix.Invert();
721 nsRefPtr<gfxPattern> pattern = new gfxPattern(surface);
723 if (!pattern || pattern->CairoStatus())
724 return nullptr;
726 pattern->SetMatrix(pMatrix);
727 pattern->SetExtend(gfxPattern::EXTEND_REPEAT);
728 return pattern.forget();
729 }
731 // -------------------------------------------------------------------------
732 // Public functions
733 // -------------------------------------------------------------------------
735 nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell,
736 nsStyleContext* aContext)
737 {
738 return new (aPresShell) nsSVGPatternFrame(aContext);
739 }