Backed out changeset 2960ea3e50ca (bug 1881157) for causing crashtest assertion failu...
[gecko.git] / layout / svg / SVGUtils.h
blob809f75e3449bef630d53af3f314641e7e0e5b0d8
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/. */
7 #ifndef LAYOUT_SVG_SVGUTILS_H_
8 #define LAYOUT_SVG_SVGUTILS_H_
10 // include math.h to pick up definition of M_ maths defines e.g. M_PI
11 #include <math.h>
13 #include "DrawMode.h"
14 #include "ImgDrawResult.h"
15 #include "gfx2DGlue.h"
16 #include "gfxMatrix.h"
17 #include "gfxPoint.h"
18 #include "gfxRect.h"
19 #include "mozilla/gfx/Rect.h"
20 #include "nsAlgorithm.h"
21 #include "nsChangeHint.h"
22 #include "nsColor.h"
23 #include "nsCOMPtr.h"
24 #include "nsID.h"
25 #include "nsIFrame.h"
26 #include "nsISupports.h"
27 #include "nsMathUtils.h"
28 #include "nsStyleStruct.h"
29 #include <algorithm>
31 class gfxContext;
32 class nsFrameList;
33 class nsIContent;
35 class nsPresContext;
36 class nsTextFrame;
38 struct nsStyleSVG;
39 struct nsRect;
41 namespace mozilla {
42 class SVGAnimatedEnumeration;
43 class SVGAnimatedLength;
44 class SVGContextPaint;
45 struct SVGContextPaintImpl;
46 class SVGDisplayContainerFrame;
47 class SVGGeometryFrame;
48 class SVGOuterSVGFrame;
49 namespace dom {
50 class Element;
51 class SVGElement;
52 class UserSpaceMetrics;
53 } // namespace dom
54 namespace gfx {
55 class DrawTarget;
56 class GeneralPattern;
57 } // namespace gfx
58 } // namespace mozilla
60 // maximum dimension of an offscreen surface - choose so that
61 // the surface size doesn't overflow a 32-bit signed int using
62 // 4 bytes per pixel; in line with Factory::CheckSurfaceSize
63 // In fact Macs can't even manage that
64 #define NS_SVG_OFFSCREEN_MAX_DIMENSION 4096
66 #define SVG_HIT_TEST_FILL 0x01
67 #define SVG_HIT_TEST_STROKE 0x02
69 bool NS_SVGNewGetBBoxEnabled();
71 namespace mozilla {
73 /**
74 * Sometimes we need to distinguish between an empty box and a box
75 * that contains an element that has no size e.g. a point at the origin.
77 class SVGBBox final {
78 using Rect = gfx::Rect;
80 public:
81 SVGBBox() : mIsEmpty(true) {}
83 MOZ_IMPLICIT SVGBBox(const Rect& aRect) : mBBox(aRect), mIsEmpty(false) {}
85 MOZ_IMPLICIT SVGBBox(const gfxRect& aRect)
86 : mBBox(ToRect(aRect)), mIsEmpty(false) {}
88 operator const Rect&() { return mBBox; }
90 gfxRect ToThebesRect() const { return ThebesRect(mBBox); }
92 bool IsEmpty() const { return mIsEmpty; }
94 bool IsFinite() const { return mBBox.IsFinite(); }
96 void Scale(float aScale) { mBBox.Scale(aScale); }
98 void UnionEdges(const SVGBBox& aSVGBBox) {
99 if (aSVGBBox.mIsEmpty) {
100 return;
102 mBBox = mIsEmpty ? aSVGBBox.mBBox : mBBox.UnionEdges(aSVGBBox.mBBox);
103 mIsEmpty = false;
106 void Intersect(const SVGBBox& aSVGBBox) {
107 if (!mIsEmpty && !aSVGBBox.mIsEmpty) {
108 mBBox = mBBox.Intersect(aSVGBBox.mBBox);
109 if (mBBox.IsEmpty()) {
110 mIsEmpty = true;
111 mBBox = Rect(0, 0, 0, 0);
113 } else {
114 mIsEmpty = true;
115 mBBox = Rect(0, 0, 0, 0);
119 private:
120 Rect mBBox;
121 bool mIsEmpty;
124 // GRRR WINDOWS HATE HATE HATE
125 #undef CLIP_MASK
127 class MOZ_RAII SVGAutoRenderState final {
128 using DrawTarget = gfx::DrawTarget;
130 public:
131 explicit SVGAutoRenderState(DrawTarget* aDrawTarget);
132 ~SVGAutoRenderState();
134 void SetPaintingToWindow(bool aPaintingToWindow);
136 static bool IsPaintingToWindow(DrawTarget* aDrawTarget);
138 private:
139 DrawTarget* mDrawTarget;
140 void* mOriginalRenderState;
141 bool mPaintingToWindow;
145 * General functions used by all of SVG layout and possibly content code.
146 * If a method is used by content and depends only on other content methods
147 * it should go in SVGContentUtils instead.
149 class SVGUtils final {
150 public:
151 using Element = dom::Element;
152 using SVGElement = dom::SVGElement;
153 using AntialiasMode = gfx::AntialiasMode;
154 using DrawTarget = gfx::DrawTarget;
155 using FillRule = gfx::FillRule;
156 using GeneralPattern = gfx::GeneralPattern;
157 using Size = gfx::Size;
158 using imgDrawingParams = image::imgDrawingParams;
160 NS_DECLARE_FRAME_PROPERTY_DELETABLE(ObjectBoundingBoxProperty, gfxRect)
163 * Returns the frame's post-filter ink overflow rect when passed the
164 * frame's pre-filter ink overflow rect. If the frame is not currently
165 * being filtered, this function simply returns aUnfilteredRect.
167 static nsRect GetPostFilterInkOverflowRect(nsIFrame* aFrame,
168 const nsRect& aPreFilterRect);
171 * Schedules an update of the frame's bounds (which will in turn invalidate
172 * the new area that the frame should paint to).
174 * This does nothing when passed an NS_FRAME_IS_NONDISPLAY frame.
175 * In future we may want to allow ReflowSVG to be called on such frames,
176 * but that would be better implemented as a ForceReflowSVG function to
177 * be called synchronously while painting them without marking or paying
178 * attention to dirty bits like this function.
180 * This is very similar to PresShell::FrameNeedsReflow. The main reason that
181 * we have this function instead of using FrameNeedsReflow is because we need
182 * to be able to call it under SVGOuterSVGFrame::NotifyViewportChange when
183 * that function is called by SVGOuterSVGFrame::Reflow. FrameNeedsReflow
184 * is not suitable for calling during reflow though, and it asserts as much.
185 * The reason that we want to be callable under NotifyViewportChange is
186 * because we want to synchronously notify and dirty the SVGOuterSVGFrame's
187 * children so that when SVGOuterSVGFrame::DidReflow is called its children
188 * will be updated for the new size as appropriate. Otherwise we'd have to
189 * post an event to the event loop to mark dirty flags and request an update.
191 * Another reason that we don't currently want to call
192 * PresShell::FrameNeedsReflow is because passing eRestyle to it to get it to
193 * mark descendants dirty would cause it to descend through
194 * SVGForeignObjectFrame frames to mark their children dirty, but we want to
195 * handle SVGForeignObjectFrame specially. It would also do unnecessary work
196 * descending into NS_FRAME_IS_NONDISPLAY frames.
198 static void ScheduleReflowSVG(nsIFrame* aFrame);
201 * Returns true if the frame or any of its children need ReflowSVG
202 * to be called on them.
204 static bool NeedsReflowSVG(const nsIFrame* aFrame);
207 * Percentage lengths in SVG are resolved against the width/height of the
208 * nearest viewport (or its viewBox, if set). This helper returns the size
209 * of this "context" for the given frame so that percentage values can be
210 * resolved.
212 static Size GetContextSize(const nsIFrame* aFrame);
214 /* Computes the input length in terms of object space coordinates.
215 Input: rect - bounding box
216 length - length to be converted
218 static float ObjectSpace(const gfxRect& aRect,
219 const SVGAnimatedLength* aLength);
221 /* Computes the input length in terms of user space coordinates.
222 Input: content - object to be used for determining user space
223 Input: length - length to be converted
225 static float UserSpace(nsIFrame* aNonSVGContext,
226 const SVGAnimatedLength* aLength);
227 static float UserSpace(const dom::UserSpaceMetrics& aMetrics,
228 const SVGAnimatedLength* aLength);
230 /* Find the outermost SVG frame of the passed frame */
231 static SVGOuterSVGFrame* GetOuterSVGFrame(nsIFrame* aFrame);
234 * Get the covered region for a frame. Return null if it's not an SVG frame.
235 * @param aRect gets a rectangle in app units
236 * @return the outer SVG frame which aRect is relative to
238 static nsIFrame* GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame,
239 nsRect* aRect);
241 /* Paint SVG frame with SVG effects
243 static void PaintFrameWithEffects(nsIFrame* aFrame, gfxContext& aContext,
244 const gfxMatrix& aTransform,
245 imgDrawingParams& aImgParams);
247 /* Hit testing - check if point hits the clipPath of indicated
248 * frame. Returns true if no clipPath set. */
249 static bool HitTestClip(nsIFrame* aFrame, const gfxPoint& aPoint);
252 * Returns the CanvasTM of the indicated frame, whether it's a
253 * child SVG frame, container SVG frame, or a regular frame.
254 * For regular frames, we just return an identity matrix.
256 static gfxMatrix GetCanvasTM(nsIFrame* aFrame);
259 * Returns whether the frame is transformed and what those transforms are.
261 static bool IsSVGTransformed(const nsIFrame* aFrame,
262 gfx::Matrix* aOwnTransform,
263 gfx::Matrix* aFromParentTransform);
266 * Notify the descendants of aFrame of a change to one of their ancestors
267 * that might affect them.
269 static void NotifyChildrenOfSVGChange(nsIFrame* aFrame, uint32_t aFlags);
272 * Convert a surface size to an integer for use by thebes
273 * possibly making it smaller in the process so the surface does not
274 * use excessive memory.
276 * @param aSize the desired surface size
277 * @param aResultOverflows true if the desired surface size is too big
278 * @return the surface size to use
280 static gfx::IntSize ConvertToSurfaceSize(const gfxSize& aSize,
281 bool* aResultOverflows);
284 * Hit test a given rectangle/matrix.
286 static bool HitTestRect(const gfx::Matrix& aMatrix, float aRX, float aRY,
287 float aRWidth, float aRHeight, float aX, float aY);
290 * Get the clip rect for the given frame, taking into account the CSS 'clip'
291 * property. See:
292 * http://www.w3.org/TR/SVG11/masking.html#OverflowAndClipProperties
293 * The arguments for aX, aY, aWidth and aHeight should be the dimensions of
294 * the viewport established by aFrame.
296 static gfxRect GetClipRectForFrame(const nsIFrame* aFrame, float aX, float aY,
297 float aWidth, float aHeight);
299 /* Using group opacity instead of fill or stroke opacity on a
300 * geometry object seems to be a common authoring mistake. If we're
301 * not applying filters and not both stroking and filling, we can
302 * generate the same result without going through the overhead of a
303 * push/pop group. */
304 static bool CanOptimizeOpacity(const nsIFrame* aFrame);
307 * Take the CTM to userspace for an element, and adjust it to a CTM to its
308 * object bounding box space if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX.
309 * (I.e. so that [0,0] is at the top left of its bbox, and [1,1] is at the
310 * bottom right of its bbox).
312 * If the bbox is empty, this will return a singular matrix.
314 * @param aFlags One or more of the BBoxFlags values defined below.
316 static gfxMatrix AdjustMatrixForUnits(const gfxMatrix& aMatrix,
317 const SVGAnimatedEnumeration* aUnits,
318 nsIFrame* aFrame, uint32_t aFlags);
320 enum BBoxFlags {
321 eBBoxIncludeFill = 1 << 0,
322 // Include the geometry of the fill even when the fill does not
323 // actually render (e.g. when fill="none" or fill-opacity="0")
324 eBBoxIncludeFillGeometry = 1 << 1,
325 eBBoxIncludeStroke = 1 << 2,
326 // Include the geometry of the stroke even when the stroke does not
327 // actually render (e.g. when stroke="none" or stroke-opacity="0")
328 eBBoxIncludeStrokeGeometry = 1 << 3,
329 eBBoxIncludeMarkers = 1 << 4,
330 eBBoxIncludeClipped = 1 << 5,
331 // Normally a getBBox call on outer-<svg> should only return the
332 // bounds of the elements children. This flag will cause the
333 // element's bounds to be returned instead.
334 eUseFrameBoundsForOuterSVG = 1 << 6,
335 // https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
336 eForGetClientRects = 1 << 7,
337 // If the given frame is an HTML element, only include the region of the
338 // given frame, instead of all continuations of it, while computing bbox if
339 // this flag is set.
340 eIncludeOnlyCurrentFrameForNonSVGElement = 1 << 8,
341 // This flag is only has an effect when the target is a <use> element.
342 // getBBox returns the bounds of the elements children in user space if
343 // this flag is set; Otherwise, getBBox returns the union bounds in
344 // the coordinate system formed by the <use> element.
345 eUseUserSpaceOfUseElement = 1 << 9,
346 // For a frame with a clip-path, if this flag is set then the result
347 // will not be clipped to the bbox of the content inside the clip-path.
348 eDoNotClipToBBoxOfContentInsideClipPath = 1 << 10,
349 // For some cases, e.g. when using transform-box: stroke-box, we may have
350 // the cyclical dependency if any of the elements in the subtree has
351 // non-scaling-stroke. In this case, we should break it and use
352 // transform-box:fill-box instead.
353 // https://github.com/w3c/csswg-drafts/issues/9640
354 eAvoidCycleIfNonScalingStroke = 1 << 11,
357 * This function in primarily for implementing the SVG DOM function getBBox()
358 * and the SVG attribute value 'objectBoundingBox'. However, it has been
359 * extended with various extra parameters in order to become more of a
360 * general purpose getter of all sorts of bounds that we might need to obtain
361 * for SVG elements, or even for other elements that have SVG effects applied
362 * to them.
364 * @param aFrame The frame of the element for which the bounds are to be
365 * obtained.
366 * @param aFlags One or more of the BBoxFlags values defined above.
367 * @param aToBoundsSpace If not specified the returned rect is in aFrame's
368 * element's "user space". A matrix can optionally be pass to specify a
369 * transform from aFrame's user space to the bounds space of interest
370 * (typically this will be the ancestor SVGOuterSVGFrame, but it could be
371 * to any other coordinate space).
373 static gfxRect GetBBox(nsIFrame* aFrame,
374 // If the default arg changes, update the handling for
375 // ObjectBoundingBoxProperty() in the implementation.
376 uint32_t aFlags = eBBoxIncludeFillGeometry,
377 const gfxMatrix* aToBoundsSpace = nullptr);
380 * "User space" is the space that the frame's BBox (as calculated by
381 * SVGUtils::GetBBox) is in. "Frame space" is the space that has its origin
382 * at the top left of the union of the frame's border-box rects over all
383 * continuations.
384 * This function returns the offset one needs to add to something in frame
385 * space in order to get its coordinates in user space.
387 static gfxPoint FrameSpaceInCSSPxToUserSpaceOffset(const nsIFrame* aFrame);
390 * Convert a userSpaceOnUse/objectBoundingBoxUnits rectangle that's specified
391 * using four SVGAnimatedLength values into a user unit rectangle in user
392 * space.
394 * @param aXYWH pointer to 4 consecutive SVGAnimatedLength objects containing
395 * the x, y, width and height values in that order
396 * @param aBBox the bounding box of the object the rect is relative to;
397 * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
398 * @param aFrame the object in which to interpret user-space units;
399 * may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
401 static gfxRect GetRelativeRect(uint16_t aUnits,
402 const SVGAnimatedLength* aXYWH,
403 const gfxRect& aBBox, nsIFrame* aFrame);
405 static gfxRect GetRelativeRect(uint16_t aUnits,
406 const SVGAnimatedLength* aXYWH,
407 const gfxRect& aBBox,
408 const dom::UserSpaceMetrics& aMetrics);
410 static bool OuterSVGIsCallingReflowSVG(nsIFrame* aFrame);
411 static bool AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame);
414 * See https://svgwg.org/svg2-draft/painting.html#NonScalingStroke
416 * If the computed value of the 'vector-effect' property on aFrame is
417 * 'non-scaling-stroke', then this function will set aUserToOuterSVG to the
418 * transform from aFrame's SVG user space to the initial coordinate system
419 * established by the viewport of aFrame's outer-<svg>'s (the coordinate
420 * system in which the stroke is fixed). If aUserToOuterSVG is set to a
421 * non-identity matrix this function returns true, else it returns false.
423 static bool GetNonScalingStrokeTransform(const nsIFrame* aFrame,
424 gfxMatrix* aUserToOuterSVG);
427 * Compute the maximum possible device space stroke extents of a path given
428 * the path's device space path extents, its stroke style and its ctm.
430 * This is a workaround for the lack of suitable cairo API for getting the
431 * tight device space stroke extents of a path. This basically gives us the
432 * tightest extents that we can guarantee fully enclose the inked stroke
433 * without doing the calculations for the actual tight extents. We exploit
434 * the fact that cairo does have an API for getting the tight device space
435 * fill/path extents.
437 * This should die once bug 478152 is fixed.
439 static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
440 const nsTextFrame* aFrame,
441 const gfxMatrix& aMatrix);
442 static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
443 const SVGGeometryFrame* aFrame,
444 const gfxMatrix& aMatrix);
447 * Convert a floating-point value to a 32-bit integer value, clamping to
448 * the range of valid integers.
450 static int32_t ClampToInt(double aVal) {
451 return NS_lround(
452 std::max(double(INT32_MIN), std::min(double(INT32_MAX), aVal)));
456 * Convert a floating-point value to a 64-bit integer value, clamping to
457 * the lowest and highest integers that can be safely compared to a double.
459 static int64_t ClampToInt64(double aVal) {
460 return static_cast<int64_t>(
461 std::clamp<double>(aVal, INT64_MIN, std::nexttoward(INT64_MAX, 0)));
464 static nscolor GetFallbackOrPaintColor(
465 const ComputedStyle&, StyleSVGPaint nsStyleSVG::*aFillOrStroke,
466 nscolor aDefaultContextFallbackColor);
468 static void MakeFillPatternFor(nsIFrame* aFrame, gfxContext* aContext,
469 GeneralPattern* aOutPattern,
470 imgDrawingParams& aImgParams,
471 SVGContextPaint* aContextPaint = nullptr);
473 static void MakeStrokePatternFor(nsIFrame* aFrame, gfxContext* aContext,
474 GeneralPattern* aOutPattern,
475 imgDrawingParams& aImgParams,
476 SVGContextPaint* aContextPaint = nullptr);
478 static float GetOpacity(const StyleSVGOpacity&, const SVGContextPaint*);
481 * @return false if there is no stroke
483 static bool HasStroke(const nsIFrame* aFrame,
484 const SVGContextPaint* aContextPaint = nullptr);
486 static float GetStrokeWidth(const nsIFrame* aFrame,
487 const SVGContextPaint* aContextPaint = nullptr);
490 * Set up a context for a stroked path (including any dashing that applies).
492 static void SetupStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext,
493 SVGContextPaint* aContextPaint = nullptr);
496 * This function returns a set of bit flags indicating which parts of the
497 * element (fill, stroke, bounds) should intercept pointer events. It takes
498 * into account the type of element and the value of the 'pointer-events'
499 * property on the element.
501 static uint16_t GetGeometryHitTestFlags(const nsIFrame* aFrame);
503 static FillRule ToFillRule(StyleFillRule aFillRule) {
504 return aFillRule == StyleFillRule::Evenodd ? FillRule::FILL_EVEN_ODD
505 : FillRule::FILL_WINDING;
508 static AntialiasMode ToAntialiasMode(StyleTextRendering aTextRendering) {
509 return aTextRendering == StyleTextRendering::Optimizespeed
510 ? AntialiasMode::NONE
511 : AntialiasMode::SUBPIXEL;
515 * Render a SVG glyph.
516 * @param aElement the SVG glyph element to render
517 * @param aContext the thebes aContext to draw to
518 * @return true if rendering succeeded
520 static void PaintSVGGlyph(Element* aElement, gfxContext* aContext);
523 * Get the extents of a SVG glyph.
524 * @param aElement the SVG glyph element
525 * @param aSVGToAppSpace the matrix mapping the SVG glyph space to the
526 * target context space
527 * @param aResult the result (valid when true is returned)
528 * @return true if calculating the extents succeeded
530 static bool GetSVGGlyphExtents(const Element* aElement,
531 const gfxMatrix& aSVGToAppSpace,
532 gfxRect* aResult);
535 * Returns the app unit canvas bounds of a userspace rect.
537 * @param aToCanvas Transform from userspace to canvas device space.
539 static nsRect ToCanvasBounds(const gfxRect& aUserspaceRect,
540 const gfxMatrix& aToCanvas,
541 const nsPresContext* presContext);
543 struct MaskUsage;
544 static MaskUsage DetermineMaskUsage(const nsIFrame* aFrame,
545 bool aHandleOpacity);
547 struct MOZ_STACK_CLASS MaskUsage {
548 friend MaskUsage SVGUtils::DetermineMaskUsage(const nsIFrame* aFrame,
549 bool aHandleOpacity);
551 bool ShouldGenerateMaskLayer() const { return mShouldGenerateMaskLayer; }
553 bool ShouldGenerateClipMaskLayer() const {
554 return mShouldGenerateClipMaskLayer;
557 bool ShouldGenerateLayer() const {
558 return mShouldGenerateMaskLayer || mShouldGenerateClipMaskLayer;
561 bool ShouldGenerateMask() const {
562 return mShouldGenerateMaskLayer || mShouldGenerateClipMaskLayer ||
563 !IsOpaque();
566 bool ShouldApplyClipPath() const { return mShouldApplyClipPath; }
568 bool HasSVGClip() const {
569 return mShouldGenerateClipMaskLayer || mShouldApplyClipPath;
572 bool ShouldApplyBasicShapeOrPath() const {
573 return mShouldApplyBasicShapeOrPath;
576 bool IsSimpleClipShape() const { return mIsSimpleClipShape; }
578 bool IsOpaque() const { return mOpacity == 1.0f; }
580 bool IsTransparent() const { return mOpacity == 0.0f; }
582 float Opacity() const { return mOpacity; }
584 bool UsingMaskOrClipPath() const {
585 return mShouldGenerateMaskLayer || mShouldGenerateClipMaskLayer ||
586 mShouldApplyClipPath || mShouldApplyBasicShapeOrPath;
589 bool ShouldDoSomething() const {
590 return mShouldGenerateMaskLayer || mShouldGenerateClipMaskLayer ||
591 mShouldApplyClipPath || mShouldApplyBasicShapeOrPath ||
592 mOpacity != 1.0f;
595 private:
596 MaskUsage() = default;
598 float mOpacity = 0.0f;
599 bool mShouldGenerateMaskLayer = false;
600 bool mShouldGenerateClipMaskLayer = false;
601 bool mShouldApplyClipPath = false;
602 bool mShouldApplyBasicShapeOrPath = false;
603 bool mIsSimpleClipShape = false;
606 static float ComputeOpacity(const nsIFrame* aFrame, bool aHandleOpacity);
609 * SVG frames expect to paint in SVG user units, which are equal to CSS px
610 * units. This method provides a transform matrix to multiply onto a
611 * gfxContext's current transform to convert the context's current units from
612 * its usual dev pixels to SVG user units/CSS px to keep the SVG code happy.
614 static gfxMatrix GetCSSPxToDevPxMatrix(const nsIFrame* aNonSVGFrame);
617 * It is a replacement of
618 * SVGElement::PrependLocalTransformsTo(eUserSpaceToParent).
619 * If no CSS transform is involved, they should behave exactly the same;
620 * if there are CSS transforms, this one will take them into account
621 * while SVGElement::PrependLocalTransformsTo won't.
623 static gfxMatrix GetTransformMatrixInUserSpace(const nsIFrame* aFrame);
626 } // namespace mozilla
628 #endif // LAYOUT_SVG_SVGUTILS_H_