LCOV - code coverage report
Current view: top level - layout/svg - nsSVGUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 205 877 23.4 %
Date: 2017-07-14 16:53:18 Functions: 25 67 37.3 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
       8             : #include "nsSVGUtils.h"
       9             : #include <algorithm>
      10             : 
      11             : // Keep others in (case-insensitive) order:
      12             : #include "gfx2DGlue.h"
      13             : #include "gfxContext.h"
      14             : #include "gfxMatrix.h"
      15             : #include "gfxPlatform.h"
      16             : #include "gfxRect.h"
      17             : #include "gfxUtils.h"
      18             : #include "mozilla/gfx/2D.h"
      19             : #include "mozilla/gfx/PatternHelpers.h"
      20             : #include "mozilla/Preferences.h"
      21             : #include "mozilla/SVGContextPaint.h"
      22             : #include "nsCSSClipPathInstance.h"
      23             : #include "nsCSSFrameConstructor.h"
      24             : #include "nsDisplayList.h"
      25             : #include "nsFilterInstance.h"
      26             : #include "nsFrameList.h"
      27             : #include "nsGkAtoms.h"
      28             : #include "nsIContent.h"
      29             : #include "nsIDocument.h"
      30             : #include "nsIFrame.h"
      31             : #include "nsIPresShell.h"
      32             : #include "nsSVGDisplayableFrame.h"
      33             : #include "nsLayoutUtils.h"
      34             : #include "nsPresContext.h"
      35             : #include "nsStyleCoord.h"
      36             : #include "nsStyleStruct.h"
      37             : #include "nsSVGClipPathFrame.h"
      38             : #include "nsSVGContainerFrame.h"
      39             : #include "nsSVGEffects.h"
      40             : #include "nsSVGFilterPaintCallback.h"
      41             : #include "nsSVGForeignObjectFrame.h"
      42             : #include "nsSVGInnerSVGFrame.h"
      43             : #include "nsSVGIntegrationUtils.h"
      44             : #include "nsSVGLength2.h"
      45             : #include "nsSVGMaskFrame.h"
      46             : #include "nsSVGOuterSVGFrame.h"
      47             : #include "mozilla/dom/SVGClipPathElement.h"
      48             : #include "mozilla/dom/SVGPathElement.h"
      49             : #include "SVGGeometryElement.h"
      50             : #include "SVGGeometryFrame.h"
      51             : #include "nsSVGPaintServerFrame.h"
      52             : #include "mozilla/dom/SVGSVGElement.h"
      53             : #include "nsTextFrame.h"
      54             : #include "SVGContentUtils.h"
      55             : #include "SVGTextFrame.h"
      56             : #include "mozilla/Unused.h"
      57             : 
      58             : using namespace mozilla;
      59             : using namespace mozilla::dom;
      60             : using namespace mozilla::gfx;
      61             : using namespace mozilla::image;
      62             : 
      63             : static bool sSVGPathCachingEnabled;
      64             : static bool sSVGDisplayListHitTestingEnabled;
      65             : static bool sSVGDisplayListPaintingEnabled;
      66             : static bool sSVGNewGetBBoxEnabled;
      67             : 
      68             : bool
      69          44 : NS_SVGPathCachingEnabled()
      70             : {
      71          44 :   return sSVGPathCachingEnabled;
      72             : }
      73             : 
      74             : bool
      75           0 : NS_SVGDisplayListHitTestingEnabled()
      76             : {
      77           0 :   return sSVGDisplayListHitTestingEnabled;
      78             : }
      79             : 
      80             : bool
      81          20 : NS_SVGDisplayListPaintingEnabled()
      82             : {
      83          20 :   return sSVGDisplayListPaintingEnabled;
      84             : }
      85             : 
      86             : bool
      87           0 : NS_SVGNewGetBBoxEnabled()
      88             : {
      89           0 :   return sSVGNewGetBBoxEnabled;
      90             : }
      91             : 
      92             : 
      93             : // we only take the address of this:
      94             : static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
      95             : 
      96           0 : SVGAutoRenderState::SVGAutoRenderState(DrawTarget* aDrawTarget
      97           0 :                                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
      98             :   : mDrawTarget(aDrawTarget)
      99             :   , mOriginalRenderState(nullptr)
     100           0 :   , mPaintingToWindow(false)
     101             : {
     102           0 :   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     103           0 :   mOriginalRenderState =
     104           0 :     aDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
     105             :   // We always remove ourselves from aContext before it dies, so
     106             :   // passing nullptr as the destroy function is okay.
     107           0 :   aDrawTarget->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
     108           0 : }
     109             : 
     110           0 : SVGAutoRenderState::~SVGAutoRenderState()
     111             : {
     112           0 :   mDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
     113           0 :   if (mOriginalRenderState) {
     114           0 :     mDrawTarget->AddUserData(&sSVGAutoRenderStateKey,
     115           0 :                              mOriginalRenderState, nullptr);
     116             :   }
     117           0 : }
     118             : 
     119             : void
     120           0 : SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
     121             : {
     122           0 :   mPaintingToWindow = aPaintingToWindow;
     123           0 : }
     124             : 
     125             : /* static */ bool
     126           0 : SVGAutoRenderState::IsPaintingToWindow(DrawTarget* aDrawTarget)
     127             : {
     128           0 :   void *state = aDrawTarget->GetUserData(&sSVGAutoRenderStateKey);
     129           0 :   if (state) {
     130           0 :     return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
     131             :   }
     132           0 :   return false;
     133             : }
     134             : 
     135             : void
     136           3 : nsSVGUtils::Init()
     137             : {
     138             :   Preferences::AddBoolVarCache(&sSVGPathCachingEnabled,
     139           3 :                                "svg.path-caching.enabled");
     140             : 
     141             :   Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
     142           3 :                                "svg.display-lists.hit-testing.enabled");
     143             : 
     144             :   Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
     145           3 :                                "svg.display-lists.painting.enabled");
     146             : 
     147             :   Preferences::AddBoolVarCache(&sSVGNewGetBBoxEnabled,
     148           3 :                                "svg.new-getBBox.enabled");
     149           3 : }
     150             : 
     151             : nsRect
     152           0 : nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
     153             :                                             const nsRect &aPreFilterRect)
     154             : {
     155           0 :   MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
     156             :              "Called on invalid frame type");
     157             : 
     158           0 :   nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
     159           0 :   if (!property || !property->ReferencesValidResources()) {
     160           0 :     return aPreFilterRect;
     161             :   }
     162             : 
     163           0 :   return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
     164             : }
     165             : 
     166             : bool
     167         182 : nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
     168             : {
     169         182 :   return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
     170             : }
     171             : 
     172             : bool
     173           0 : nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame)
     174             : {
     175           0 :   nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
     176           0 :   do {
     177           0 :     if (outer->IsCallingReflowSVG()) {
     178           0 :       return true;
     179             :     }
     180           0 :     outer = GetOuterSVGFrame(outer->GetParent());
     181           0 :   } while (outer);
     182           0 :   return false;
     183             : }
     184             : 
     185             : void
     186           0 : nsSVGUtils::ScheduleReflowSVG(nsIFrame *aFrame)
     187             : {
     188           0 :   MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG),
     189             :              "Passed bad frame!");
     190             : 
     191             :   // If this is triggered, the callers should be fixed to call us before
     192             :   // ReflowSVG is called. If we try to mark dirty bits on frames while we're
     193             :   // in the process of removing them, things will get messed up.
     194           0 :   NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
     195             :                "Do not call under nsSVGDisplayableFrame::ReflowSVG!");
     196             : 
     197             :   // We don't call nsSVGEffects::InvalidateRenderingObservers here because
     198             :   // we should only be called under InvalidateAndScheduleReflowSVG (which
     199             :   // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
     200             :   // (at which point the frame has no observers).
     201             : 
     202           0 :   if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
     203           0 :     return;
     204             :   }
     205             : 
     206           0 :   if (aFrame->GetStateBits() &
     207             :       (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
     208             :     // Nothing to do if we're already dirty, or if the outer-<svg>
     209             :     // hasn't yet had its initial reflow.
     210           0 :     return;
     211             :   }
     212             : 
     213           0 :   nsSVGOuterSVGFrame *outerSVGFrame = nullptr;
     214             : 
     215             :   // We must not add dirty bits to the nsSVGOuterSVGFrame or else
     216             :   // PresShell::FrameNeedsReflow won't work when we pass it in below.
     217           0 :   if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
     218           0 :     outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame);
     219             :   } else {
     220           0 :     aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
     221             : 
     222           0 :     nsIFrame *f = aFrame->GetParent();
     223           0 :     while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
     224           0 :       if (f->GetStateBits() &
     225             :           (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
     226           0 :         return;
     227             :       }
     228           0 :       f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
     229           0 :       f = f->GetParent();
     230           0 :       MOZ_ASSERT(f->IsFrameOfType(nsIFrame::eSVG),
     231             :                  "NS_STATE_IS_OUTER_SVG check above not valid!");
     232             :     }
     233             : 
     234           0 :     outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
     235             : 
     236           0 :     MOZ_ASSERT(outerSVGFrame && outerSVGFrame->IsSVGOuterSVGFrame(),
     237             :                "Did not find nsSVGOuterSVGFrame!");
     238             :   }
     239             : 
     240           0 :   if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
     241             :     // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
     242             :     // need to call PresShell::FrameNeedsReflow, since we have an
     243             :     // nsSVGOuterSVGFrame::DidReflow call pending.
     244           0 :     return;
     245             :   }
     246             : 
     247             :   nsFrameState dirtyBit =
     248           0 :     (outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY : NS_FRAME_HAS_DIRTY_CHILDREN);
     249             : 
     250           0 :   aFrame->PresContext()->PresShell()->FrameNeedsReflow(
     251           0 :     outerSVGFrame, nsIPresShell::eResize, dirtyBit);
     252             : }
     253             : 
     254             : bool
     255         182 : nsSVGUtils::NeedsReflowSVG(nsIFrame *aFrame)
     256             : {
     257         182 :   MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG),
     258             :              "SVG uses bits differently!");
     259             : 
     260             :   // The flags we test here may change, hence why we have this separate
     261             :   // function.
     262         182 :   return NS_SUBTREE_DIRTY(aFrame);
     263             : }
     264             : 
     265             : void
     266           0 : nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
     267             : {
     268           0 :   MOZ_ASSERT(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG),
     269             :              "Not expecting to be called on the outer SVG Frame");
     270             : 
     271           0 :   aFrame = aFrame->GetParent();
     272             : 
     273           0 :   while (aFrame) {
     274           0 :     if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
     275           0 :       return;
     276             : 
     277           0 :     nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
     278           0 :     if (property) {
     279           0 :       property->Invalidate();
     280             :     }
     281           0 :     aFrame = aFrame->GetParent();
     282             :   }
     283             : }
     284             : 
     285             : Size
     286         144 : nsSVGUtils::GetContextSize(const nsIFrame* aFrame)
     287             : {
     288         144 :   Size size;
     289             : 
     290         144 :   MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "bad cast");
     291         144 :   const nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
     292             : 
     293         144 :   SVGSVGElement* ctx = element->GetCtx();
     294         144 :   if (ctx) {
     295          42 :     size.width = ctx->GetLength(SVGContentUtils::X);
     296          42 :     size.height = ctx->GetLength(SVGContentUtils::Y);
     297             :   }
     298         144 :   return size;
     299             : }
     300             : 
     301             : float
     302           0 : nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
     303             : {
     304             :   float axis;
     305             : 
     306           0 :   switch (aLength->GetCtxType()) {
     307             :   case SVGContentUtils::X:
     308           0 :     axis = aRect.Width();
     309           0 :     break;
     310             :   case SVGContentUtils::Y:
     311           0 :     axis = aRect.Height();
     312           0 :     break;
     313             :   case SVGContentUtils::XY:
     314           0 :     axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
     315             :                    aRect.Width(), aRect.Height()));
     316           0 :     break;
     317             :   default:
     318           0 :     NS_NOTREACHED("unexpected ctx type");
     319           0 :     axis = 0.0f;
     320           0 :     break;
     321             :   }
     322           0 :   if (aLength->IsPercentage()) {
     323             :     // Multiply first to avoid precision errors:
     324           0 :     return axis * aLength->GetAnimValInSpecifiedUnits() / 100;
     325             :   }
     326           0 :   return aLength->GetAnimValue(static_cast<SVGSVGElement*>(nullptr)) * axis;
     327             : }
     328             : 
     329             : float
     330           0 : nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
     331             : {
     332           0 :   return aLength->GetAnimValue(aSVGElement);
     333             : }
     334             : 
     335             : float
     336           0 : nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
     337             : {
     338           0 :   return aLength->GetAnimValue(aNonSVGContext);
     339             : }
     340             : 
     341             : float
     342           0 : nsSVGUtils::UserSpace(const UserSpaceMetrics& aMetrics, const nsSVGLength2 *aLength)
     343             : {
     344           0 :   return aLength->GetAnimValue(aMetrics);
     345             : }
     346             : 
     347             : nsSVGOuterSVGFrame *
     348         619 : nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
     349             : {
     350        1054 :   while (aFrame) {
     351         619 :     if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
     352         184 :       return static_cast<nsSVGOuterSVGFrame*>(aFrame);
     353             :     }
     354         435 :     aFrame = aFrame->GetParent();
     355             :   }
     356             : 
     357           0 :   return nullptr;
     358             : }
     359             : 
     360             : nsIFrame*
     361          20 : nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
     362             : {
     363          20 :   nsSVGDisplayableFrame* svg = do_QueryFrame(aFrame);
     364          20 :   if (!svg)
     365          20 :     return nullptr;
     366           0 :   nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
     367           0 :   if (outer == svg) {
     368           0 :     return nullptr;
     369             :   }
     370             : 
     371           0 :   if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
     372           0 :     *aRect = nsRect(0, 0, 0, 0);
     373             :   } else {
     374             :     uint32_t flags = nsSVGUtils::eForGetClientRects |
     375             :                      nsSVGUtils::eBBoxIncludeFill |
     376             :                      nsSVGUtils::eBBoxIncludeStroke |
     377           0 :                      nsSVGUtils::eBBoxIncludeMarkers;
     378           0 :     gfxMatrix m = nsSVGUtils::GetUserToCanvasTM(aFrame);
     379           0 :     SVGBBox bbox = nsSVGUtils::GetBBox(aFrame, flags, &m);
     380             :     nsRect bounds =
     381             :       nsLayoutUtils::RoundGfxRectToAppRect(bbox,
     382           0 :                        aFrame->PresContext()->AppUnitsPerDevPixel());
     383           0 :     nsMargin bp = outer->GetUsedBorderAndPadding();
     384           0 :     *aRect = bounds + nsPoint(bp.left, bp.top);
     385             :   }
     386             : 
     387           0 :   return outer;
     388             : }
     389             : 
     390             : gfxMatrix
     391           0 : nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
     392             : {
     393             :   // XXX yuck, we really need a common interface for GetCanvasTM
     394             : 
     395           0 :   if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
     396           0 :     return GetCSSPxToDevPxMatrix(aFrame);
     397             :   }
     398             : 
     399           0 :   LayoutFrameType type = aFrame->Type();
     400           0 :   if (type == LayoutFrameType::SVGForeignObject) {
     401           0 :     return static_cast<nsSVGForeignObjectFrame*>(aFrame)->GetCanvasTM();
     402             :   }
     403           0 :   if (type == LayoutFrameType::SVGOuterSVG) {
     404           0 :     return GetCSSPxToDevPxMatrix(aFrame);
     405             :   }
     406             : 
     407           0 :   nsSVGContainerFrame *containerFrame = do_QueryFrame(aFrame);
     408           0 :   if (containerFrame) {
     409           0 :     return containerFrame->GetCanvasTM();
     410             :   }
     411             : 
     412           0 :   return static_cast<SVGGeometryFrame*>(aFrame)->GetCanvasTM();
     413             : }
     414             : 
     415             : gfxMatrix
     416           0 : nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame)
     417             : {
     418           0 :   nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
     419           0 :   NS_ASSERTION(svgFrame, "bad frame");
     420             : 
     421           0 :   gfxMatrix tm;
     422           0 :   if (svgFrame) {
     423           0 :     nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
     424             :     tm = content->PrependLocalTransformsTo(
     425           0 :                     GetCanvasTM(aFrame->GetParent()),
     426           0 :                     eUserSpaceToParent);
     427             :   }
     428           0 :   return tm;
     429             : }
     430             : 
     431             : void
     432          60 : nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags)
     433             : {
     434         151 :   for (nsIFrame* kid : aFrame->PrincipalChildList()) {
     435          91 :     nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
     436          91 :     if (SVGFrame) {
     437          77 :       SVGFrame->NotifySVGChanged(aFlags);
     438             :     } else {
     439          14 :       NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) ||
     440             :                    nsSVGUtils::IsInSVGTextSubtree(kid),
     441             :                    "SVG frame expected");
     442             :       // recurse into the children of container frames e.g. <clipPath>, <mask>
     443             :       // in case they have child frames with transformation matrices
     444          14 :       if (kid->IsFrameOfType(nsIFrame::eSVG)) {
     445          14 :         NotifyChildrenOfSVGChange(kid, aFlags);
     446             :       }
     447             :     }
     448             :   }
     449          60 : }
     450             : 
     451             : // ************************************************************
     452             : 
     453             : class SVGPaintCallback : public nsSVGFilterPaintCallback
     454             : {
     455             : public:
     456           0 :   virtual void Paint(gfxContext& aContext, nsIFrame *aTarget,
     457             :                            const gfxMatrix& aTransform,
     458             :                            const nsIntRect* aDirtyRect,
     459             :                            imgDrawingParams& aImgParams) override
     460             :   {
     461           0 :     nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aTarget);
     462           0 :     NS_ASSERTION(svgFrame, "Expected SVG frame here");
     463             : 
     464           0 :     nsIntRect* dirtyRect = nullptr;
     465           0 :     nsIntRect tmpDirtyRect;
     466             : 
     467             :     // aDirtyRect is in user-space pixels, we need to convert to
     468             :     // outer-SVG-frame-relative device pixels.
     469           0 :     if (aDirtyRect) {
     470           0 :       gfxMatrix userToDeviceSpace = aTransform;
     471           0 :       if (userToDeviceSpace.IsSingular()) {
     472           0 :         return;
     473             :       }
     474             :       gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
     475           0 :         gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
     476           0 :       dirtyBounds.RoundOut();
     477           0 :       if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
     478           0 :         dirtyRect = &tmpDirtyRect;
     479             :       }
     480             :     }
     481             : 
     482           0 :     svgFrame->PaintSVG(aContext, nsSVGUtils::GetCSSPxToDevPxMatrix(aTarget),
     483           0 :                        aImgParams, dirtyRect);
     484             :   }
     485             : };
     486             : 
     487             : float
     488           5 : nsSVGUtils::ComputeOpacity(nsIFrame* aFrame, bool aHandleOpacity)
     489             : {
     490           5 :   float opacity = aFrame->StyleEffects()->mOpacity;
     491             : 
     492           5 :   if (opacity != 1.0f &&
     493           0 :       (nsSVGUtils::CanOptimizeOpacity(aFrame) || !aHandleOpacity)) {
     494           0 :     return 1.0f;
     495             :   }
     496             : 
     497           5 :   return opacity;
     498             : }
     499             : 
     500             : void
     501           5 : nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity,
     502             :                                MaskUsage& aUsage)
     503             : {
     504           5 :   aUsage.opacity = ComputeOpacity(aFrame, aHandleOpacity);
     505             : 
     506             :   nsIFrame* firstFrame =
     507           5 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
     508             : 
     509             :   nsSVGEffects::EffectProperties effectProperties =
     510           5 :     nsSVGEffects::GetEffectProperties(firstFrame);
     511           5 :   const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
     512             : 
     513          10 :   nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
     514             : 
     515             : #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
     516           5 :   aUsage.shouldGenerateMaskLayer = (maskFrames.Length() > 0);
     517             : #else
     518             :   // Since we do not support image mask so far, we should treat any
     519             :   // unresolvable mask as no mask. Otherwise, any object with a valid image
     520             :   // mask, e.g. url("xxx.png"), will become invisible just because we can not
     521             :   // handle image mask correctly. (See bug 1294171)
     522             :   aUsage.shouldGenerateMaskLayer = maskFrames.Length() == 1 && maskFrames[0];
     523             : #endif
     524             : 
     525           5 :   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
     526           5 :   MOZ_ASSERT(!clipPathFrame ||
     527             :              svgReset->mClipPath.GetType() == StyleShapeSourceType::URL);
     528             : 
     529           5 :   switch (svgReset->mClipPath.GetType()) {
     530             :     case StyleShapeSourceType::URL:
     531           2 :       if (clipPathFrame) {
     532           2 :         if (clipPathFrame->IsTrivial()) {
     533           2 :           aUsage.shouldApplyClipPath = true;
     534             :         } else {
     535           0 :           aUsage.shouldGenerateClipMaskLayer = true;
     536             :         }
     537             :       }
     538           2 :       break;
     539             :     case StyleShapeSourceType::Shape:
     540             :     case StyleShapeSourceType::Box:
     541           0 :       aUsage.shouldApplyBasicShape = true;
     542           0 :       break;
     543             :     case StyleShapeSourceType::None:
     544           3 :       MOZ_ASSERT(!aUsage.shouldGenerateClipMaskLayer &&
     545             :                  !aUsage.shouldApplyClipPath && !aUsage.shouldApplyBasicShape);
     546           3 :       break;
     547             :     default:
     548           0 :       MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type.");
     549             :       break;
     550             :   }
     551           5 : }
     552             : 
     553           0 : class MixModeBlender {
     554             : public:
     555             :   typedef mozilla::gfx::Factory Factory;
     556             : 
     557           0 :   MixModeBlender(nsIFrame *aFrame, gfxContext* aContext)
     558           0 :     : mFrame(aFrame), mSourceCtx(aContext)
     559             :   {
     560           0 :     MOZ_ASSERT(mFrame && mSourceCtx);
     561           0 :   }
     562             : 
     563           0 :   bool ShouldCreateDrawTargetForBlend() const
     564             :   {
     565           0 :     return mFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
     566             :   }
     567             : 
     568           0 :   gfxContext* CreateBlendTarget(const gfxMatrix& aTransform)
     569             :   {
     570           0 :     MOZ_ASSERT(ShouldCreateDrawTargetForBlend());
     571             : 
     572             :     // Create a temporary context to draw to so we can blend it back with
     573             :     // another operator.
     574           0 :     IntRect drawRect = ComputeClipExtsInDeviceSpace(aTransform);
     575             : 
     576             :     RefPtr<DrawTarget> targetDT =
     577           0 :       mSourceCtx->GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(),
     578           0 :                                                            SurfaceFormat::B8G8R8A8);
     579           0 :     if (!targetDT || !targetDT->IsValid()) {
     580           0 :       return nullptr;
     581             :     }
     582             : 
     583           0 :     MOZ_ASSERT(!mTargetCtx,
     584             :                "CreateBlendTarget is designed to be used once only.");
     585             : 
     586           0 :     mTargetCtx = gfxContext::CreateOrNull(targetDT);
     587           0 :     MOZ_ASSERT(mTargetCtx); // already checked the draw target above
     588           0 :     mTargetCtx->SetMatrix(mSourceCtx->CurrentMatrix() *
     589           0 :                           gfxMatrix::Translation(-drawRect.TopLeft()));
     590             : 
     591           0 :     mTargetOffset = drawRect.TopLeft();
     592             : 
     593           0 :     return mTargetCtx;
     594             :   }
     595             : 
     596           0 :   void BlendToTarget()
     597             :   {
     598           0 :     MOZ_ASSERT(ShouldCreateDrawTargetForBlend());
     599           0 :     MOZ_ASSERT(mTargetCtx,
     600             :                "BlendToTarget should be used after CreateBlendTarget.");
     601             : 
     602           0 :     RefPtr<SourceSurface> targetSurf = mTargetCtx->GetDrawTarget()->Snapshot();
     603             : 
     604           0 :     gfxContextAutoSaveRestore save(mSourceCtx);
     605           0 :     mSourceCtx->SetMatrix(gfxMatrix()); // This will be restored right after.
     606             :     RefPtr<gfxPattern> pattern =
     607             :       new gfxPattern(targetSurf,
     608           0 :                      Matrix::Translation(mTargetOffset.x, mTargetOffset.y));
     609           0 :     mSourceCtx->SetPattern(pattern);
     610           0 :     mSourceCtx->Paint();
     611           0 :   }
     612             : 
     613             : private:
     614             :   MixModeBlender() = delete;
     615             : 
     616           0 :   IntRect ComputeClipExtsInDeviceSpace(const gfxMatrix& aTransform)
     617             :   {
     618             :     // These are used if we require a temporary surface for a custom blend
     619             :     // mode. Clip the source context first, so that we can generate a smaller
     620             :     // temporary surface. (Since we will clip this context in
     621             :     // SetupContextMatrix, a pair of save/restore is needed.)
     622           0 :     gfxContextAutoSaveRestore saver(mSourceCtx);
     623             : 
     624           0 :     if (!(mFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
     625             :       // aFrame has a valid visual overflow rect, so clip to it before calling
     626             :       // PushGroup() to minimize the size of the surfaces we'll composite:
     627           0 :       gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(mSourceCtx);
     628           0 :       mSourceCtx->Multiply(aTransform);
     629           0 :       nsRect overflowRect = mFrame->GetVisualOverflowRectRelativeToSelf();
     630           0 :       if (mFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
     631           0 :           nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
     632             :         // Unlike containers, leaf frames do not include GetPosition() in
     633             :         // GetCanvasTM().
     634           0 :         overflowRect = overflowRect + mFrame->GetPosition();
     635             :       }
     636           0 :       mSourceCtx->Clip(NSRectToSnappedRect(overflowRect,
     637           0 :                                            mFrame->PresContext()->AppUnitsPerDevPixel(),
     638           0 :                                            *mSourceCtx->GetDrawTarget()));
     639             :     }
     640             : 
     641             :     // Get the clip extents in device space.
     642           0 :     mSourceCtx->SetMatrix(gfxMatrix());
     643           0 :     gfxRect clippedFrameSurfaceRect = mSourceCtx->GetClipExtents();
     644           0 :     clippedFrameSurfaceRect.RoundOut();
     645             : 
     646           0 :     IntRect result;
     647           0 :     ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
     648             : 
     649           0 :     return Factory::CheckSurfaceSize(result.Size()) ? result : IntRect();
     650             :   }
     651             : 
     652             :   nsIFrame* mFrame;
     653             :   gfxContext* mSourceCtx;
     654             :   RefPtr<gfxContext> mTargetCtx;
     655             :   IntPoint mTargetOffset;
     656             : };
     657             : 
     658             : void
     659           0 : nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
     660             :                                   gfxContext& aContext,
     661             :                                   const gfxMatrix& aTransform,
     662             :                                   imgDrawingParams& aImgParams,
     663             :                                   const nsIntRect *aDirtyRect)
     664             : {
     665           0 :   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
     666             :                (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
     667             :                aFrame->PresContext()->IsGlyph(),
     668             :                "If display lists are enabled, only painting of non-display "
     669             :                "SVG should take this code path");
     670             : 
     671           0 :   nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
     672           0 :   if (!svgFrame)
     673           0 :     return;
     674             : 
     675           0 :   MaskUsage maskUsage;
     676           0 :   DetermineMaskUsage(aFrame, true, maskUsage);
     677           0 :   if (maskUsage.opacity == 0.0f) {
     678           0 :     return;
     679             :   }
     680             : 
     681           0 :   const nsIContent* content = aFrame->GetContent();
     682           0 :   if (content->IsSVGElement() &&
     683           0 :       !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
     684           0 :     return;
     685             :   }
     686             : 
     687           0 :   if (aDirtyRect &&
     688           0 :       !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
     689             :     // Here we convert aFrame's paint bounds to outer-<svg> device space,
     690             :     // compare it to aDirtyRect, and return early if they don't intersect.
     691             :     // We don't do this optimization for nondisplay SVG since nondisplay
     692             :     // SVG doesn't maintain bounds/overflow rects.
     693           0 :     nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
     694           0 :     if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
     695           0 :         nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
     696             :       // Unlike containers, leaf frames do not include GetPosition() in
     697             :       // GetCanvasTM().
     698           0 :       overflowRect = overflowRect + aFrame->GetPosition();
     699             :     }
     700           0 :     int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
     701           0 :     gfxMatrix tm = aTransform;
     702           0 :     if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
     703           0 :       gfx::Matrix childrenOnlyTM;
     704           0 :       if (static_cast<nsSVGContainerFrame*>(aFrame)->
     705           0 :             HasChildrenOnlyTransform(&childrenOnlyTM)) {
     706             :         // Undo the children-only transform:
     707           0 :         if (!childrenOnlyTM.Invert()) {
     708           0 :           return;
     709             :         }
     710           0 :         tm = ThebesMatrix(childrenOnlyTM) * tm;
     711             :       }
     712             :     }
     713           0 :     nsIntRect bounds = TransformFrameRectToOuterSVG(overflowRect,
     714             :                          tm, aFrame->PresContext()).
     715           0 :                            ToOutsidePixels(appUnitsPerDevPx);
     716           0 :     if (!aDirtyRect->Intersects(bounds)) {
     717           0 :       return;
     718             :     }
     719             :   }
     720             : 
     721             :   /* SVG defines the following rendering model:
     722             :    *
     723             :    *  1. Render fill
     724             :    *  2. Render stroke
     725             :    *  3. Render markers
     726             :    *  4. Apply filter
     727             :    *  5. Apply clipping, masking, group opacity
     728             :    *
     729             :    * We follow this, but perform a couple of optimizations:
     730             :    *
     731             :    * + Use cairo's clipPath when representable natively (single object
     732             :    *   clip region).
     733             :    *f
     734             :    * + Merge opacity and masking if both used together.
     735             :    */
     736             : 
     737             :   /* Properties are added lazily and may have been removed by a restyle,
     738             :      so make sure all applicable ones are set again. */
     739             :   nsSVGEffects::EffectProperties effectProperties =
     740           0 :     nsSVGEffects::GetEffectProperties(aFrame);
     741           0 :   if (effectProperties.HasInvalidEffects()) {
     742             :     // Some resource is invalid. We shouldn't paint anything.
     743           0 :     return;
     744             :   }
     745             : 
     746           0 :   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
     747           0 :   nsTArray<nsSVGMaskFrame*> masks = effectProperties.GetMaskFrames();
     748           0 :   nsSVGMaskFrame *maskFrame = masks.IsEmpty() ? nullptr : masks[0];
     749             : 
     750           0 :   MixModeBlender blender(aFrame, &aContext);
     751           0 :   gfxContext* target = blender.ShouldCreateDrawTargetForBlend()
     752           0 :                        ? blender.CreateBlendTarget(aTransform) : &aContext;
     753             : 
     754           0 :   if (!target) {
     755           0 :     return;
     756             :   }
     757             : 
     758             :   /* Check if we need to do additional operations on this child's
     759             :    * rendering, which necessitates rendering into another surface. */
     760           0 :   bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
     761           0 :                              maskUsage.shouldGenerateClipMaskLayer ||
     762           0 :                              maskUsage.shouldGenerateMaskLayer);
     763           0 :   bool shouldPushMask = false;
     764             : 
     765           0 :   if (shouldGenerateMask) {
     766           0 :     Matrix maskTransform;
     767           0 :     RefPtr<SourceSurface> maskSurface;
     768             : 
     769             :     // maskFrame can be nullptr even if maskUsage.shouldGenerateMaskLayer is
     770             :     // true. That happens when a user gives an unresolvable mask-id, such as
     771             :     //   mask:url()
     772             :     //   mask:url(#id-which-does-not-exist)
     773             :     // Since we only uses nsSVGUtils with SVG elements, not like mask on an
     774             :     // HTML element, we should treat an unresolvable mask as no-mask here.
     775           0 :     if (maskUsage.shouldGenerateMaskLayer && maskFrame) {
     776             :       uint8_t maskMode =
     777           0 :         aFrame->StyleSVGReset()->mMask.mLayers[0].mMaskMode;
     778             :       nsSVGMaskFrame::MaskParams params(&aContext, aFrame, aTransform,
     779             :                                         maskUsage.opacity, &maskTransform,
     780           0 :                                         maskMode, aImgParams);
     781           0 :       maskSurface = maskFrame->GetMaskForMaskedFrame(params);
     782             : 
     783           0 :       if (!maskSurface) {
     784             :         // Either entire surface is clipped out, or gfx buffer allocation
     785             :         // failure in nsSVGMaskFrame::GetMaskForMaskedFrame.
     786           0 :         return;
     787             :       }
     788           0 :       shouldPushMask = true;
     789             :     }
     790             : 
     791           0 :     if (maskUsage.shouldGenerateClipMaskLayer) {
     792           0 :       Matrix clippedMaskTransform;
     793             :       RefPtr<SourceSurface> clipMaskSurface =
     794           0 :         clipPathFrame->GetClipMask(aContext, aFrame, aTransform,
     795             :                                    &clippedMaskTransform, maskSurface,
     796           0 :                                    maskTransform);
     797           0 :       if (clipMaskSurface) {
     798           0 :         maskSurface = clipMaskSurface;
     799           0 :         maskTransform = clippedMaskTransform;
     800             :       } else {
     801             :         // Either entire surface is clipped out, or gfx buffer allocation
     802             :         // failure in nsSVGClipPathFrame::GetClipMask.
     803           0 :         return;
     804             :       }
     805           0 :       shouldPushMask = true;
     806             :     }
     807             : 
     808           0 :     if (!maskUsage.shouldGenerateClipMaskLayer &&
     809           0 :         !maskUsage.shouldGenerateMaskLayer) {
     810           0 :       shouldPushMask = true;
     811             :     }
     812             : 
     813             :     // SVG mask multiply opacity into maskSurface already, so we do not bother
     814             :     // to apply opacity again.
     815           0 :     if (shouldPushMask) {
     816           0 :       target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
     817             :                                     maskFrame ? 1.0 : maskUsage.opacity,
     818           0 :                                     maskSurface, maskTransform);
     819             :     }
     820             :   }
     821             : 
     822             :   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
     823             :    * we can just do normal painting and get it clipped appropriately.
     824             :    */
     825           0 :   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     826           0 :     if (maskUsage.shouldApplyClipPath) {
     827           0 :       clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform);
     828             :     } else {
     829           0 :       nsCSSClipPathInstance::ApplyBasicShapeClip(aContext, aFrame);
     830             :     }
     831             :   }
     832             : 
     833             :   /* Paint the child */
     834           0 :   if (effectProperties.HasValidFilter()) {
     835           0 :     nsRegion* dirtyRegion = nullptr;
     836           0 :     nsRegion tmpDirtyRegion;
     837           0 :     if (aDirtyRect) {
     838             :       // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
     839             :       // it in frame space.
     840           0 :       gfxMatrix userToDeviceSpace = aTransform;
     841           0 :       if (userToDeviceSpace.IsSingular()) {
     842           0 :         return;
     843             :       }
     844           0 :       gfxMatrix deviceToUserSpace = userToDeviceSpace;
     845           0 :       deviceToUserSpace.Invert();
     846             :       gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
     847           0 :                               gfxRect(aDirtyRect->x, aDirtyRect->y,
     848           0 :                                       aDirtyRect->width, aDirtyRect->height));
     849             :       tmpDirtyRegion =
     850           0 :         nsLayoutUtils::RoundGfxRectToAppRect(
     851           0 :           dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
     852           0 :         aFrame->GetPosition();
     853           0 :       dirtyRegion = &tmpDirtyRegion;
     854             :     }
     855             : 
     856           0 :     SVGPaintCallback paintCallback;
     857           0 :     nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
     858             :                                          aTransform, &paintCallback,
     859           0 :                                          dirtyRegion, aImgParams);
     860             :   } else {
     861           0 :      svgFrame->PaintSVG(*target, aTransform, aImgParams, aDirtyRect);
     862             :   }
     863             : 
     864           0 :   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     865           0 :     aContext.PopClip();
     866             :   }
     867             : 
     868           0 :   if (shouldPushMask) {
     869           0 :     target->PopGroupAndBlend();
     870             :   }
     871             : 
     872           0 :   if (blender.ShouldCreateDrawTargetForBlend()) {
     873           0 :     MOZ_ASSERT(target != &aContext);
     874           0 :     blender.BlendToTarget();
     875             :   }
     876             : }
     877             : 
     878             : bool
     879           0 : nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
     880             : {
     881             :   nsSVGEffects::EffectProperties props =
     882           0 :     nsSVGEffects::GetEffectProperties(aFrame);
     883           0 :   if (!props.mClipPath) {
     884           0 :     const nsStyleSVGReset *style = aFrame->StyleSVGReset();
     885           0 :     if (style->HasClipPath()) {
     886           0 :       return nsCSSClipPathInstance::HitTestBasicShapeClip(aFrame, aPoint);
     887             :     }
     888           0 :     return true;
     889             :   }
     890             : 
     891           0 :   if (props.HasInvalidClipPath()) {
     892             :     // clipPath is not a valid resource, so nothing gets painted, so
     893             :     // hit-testing must fail.
     894           0 :     return false;
     895             :   }
     896           0 :   nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame();
     897             : 
     898           0 :   if (!clipPathFrame) {
     899             :     // clipPath doesn't exist, ignore it.
     900           0 :     return true;
     901             :   }
     902             : 
     903           0 :   return clipPathFrame->PointIsInsideClipPath(aFrame, aPoint);
     904             : }
     905             : 
     906             : nsIFrame *
     907           0 : nsSVGUtils::HitTestChildren(nsSVGDisplayContainerFrame* aFrame,
     908             :                             const gfxPoint& aPoint)
     909             : {
     910             :   // First we transform aPoint into the coordinate space established by aFrame
     911             :   // for its children (e.g. take account of any 'viewBox' attribute):
     912           0 :   gfxPoint point = aPoint;
     913           0 :   if (aFrame->GetContent()->IsSVGElement()) { // must check before cast
     914           0 :     gfxMatrix m = static_cast<const nsSVGElement*>(aFrame->GetContent())->
     915           0 :                     PrependLocalTransformsTo(gfxMatrix(),
     916           0 :                                              eChildToUserSpace);
     917           0 :     if (!m.IsIdentity()) {
     918           0 :       if (!m.Invert()) {
     919           0 :         return nullptr;
     920             :       }
     921           0 :       point = m.TransformPoint(point);
     922             :     }
     923             :   }
     924             : 
     925             :   // Traverse the list in reverse order, so that if we get a hit we know that's
     926             :   // the topmost frame that intersects the point; then we can just return it.
     927           0 :   nsIFrame* result = nullptr;
     928           0 :   for (nsIFrame* current = aFrame->PrincipalChildList().LastChild();
     929           0 :        current;
     930             :        current = current->GetPrevSibling()) {
     931           0 :     nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(current);
     932           0 :     if (SVGFrame) {
     933           0 :       const nsIContent* content = current->GetContent();
     934           0 :       if (content->IsSVGElement() &&
     935           0 :           !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
     936           0 :         continue;
     937             :       }
     938             :       // GetFrameForPoint() expects a point in its frame's SVG user space, so
     939             :       // we need to convert to that space:
     940           0 :       gfxPoint p = point;
     941           0 :       if (content->IsSVGElement()) { // must check before cast
     942             :         gfxMatrix m = static_cast<const nsSVGElement*>(content)->
     943           0 :                         PrependLocalTransformsTo(gfxMatrix(),
     944           0 :                                                  eUserSpaceToParent);
     945           0 :         if (!m.IsIdentity()) {
     946           0 :           if (!m.Invert()) {
     947           0 :             continue;
     948             :           }
     949           0 :           p = m.TransformPoint(p);
     950             :         }
     951             :       }
     952           0 :       result = SVGFrame->GetFrameForPoint(p);
     953           0 :       if (result)
     954           0 :         break;
     955             :     }
     956             :   }
     957             : 
     958           0 :   if (result && !HitTestClip(aFrame, aPoint))
     959           0 :     result = nullptr;
     960             : 
     961           0 :   return result;
     962             : }
     963             : 
     964             : nsRect
     965           0 : nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
     966             :                                          const gfxMatrix& aMatrix,
     967             :                                          nsPresContext* aPresContext)
     968             : {
     969           0 :   gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
     970           0 :   r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
     971             :   return nsLayoutUtils::RoundGfxRectToAppRect(
     972           0 :     aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
     973             : }
     974             : 
     975             : IntSize
     976           0 : nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
     977             :                                  bool *aResultOverflows)
     978             : {
     979           0 :   IntSize surfaceSize(ClampToInt(ceil(aSize.width)), ClampToInt(ceil(aSize.height)));
     980             : 
     981           0 :   *aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
     982           0 :     surfaceSize.height != ceil(aSize.height);
     983             : 
     984           0 :   if (!Factory::AllowedSurfaceSize(surfaceSize)) {
     985           0 :     surfaceSize.width = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
     986           0 :                                surfaceSize.width);
     987           0 :     surfaceSize.height = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
     988           0 :                                 surfaceSize.height);
     989           0 :     *aResultOverflows = true;
     990             :   }
     991             : 
     992           0 :   return surfaceSize;
     993             : }
     994             : 
     995             : bool
     996           0 : nsSVGUtils::HitTestRect(const gfx::Matrix &aMatrix,
     997             :                         float aRX, float aRY, float aRWidth, float aRHeight,
     998             :                         float aX, float aY)
     999             : {
    1000           0 :   gfx::Rect rect(aRX, aRY, aRWidth, aRHeight);
    1001           0 :   if (rect.IsEmpty() || aMatrix.IsSingular()) {
    1002           0 :     return false;
    1003             :   }
    1004           0 :   gfx::Matrix toRectSpace = aMatrix;
    1005           0 :   toRectSpace.Invert();
    1006           0 :   gfx::Point p = toRectSpace.TransformPoint(gfx::Point(aX, aY));
    1007           0 :   return rect.x <= p.x && p.x <= rect.XMost() &&
    1008           0 :          rect.y <= p.y && p.y <= rect.YMost();
    1009             : }
    1010             : 
    1011             : gfxRect
    1012           0 : nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame,
    1013             :                                 float aX, float aY, float aWidth, float aHeight)
    1014             : {
    1015           0 :   const nsStyleDisplay* disp = aFrame->StyleDisplay();
    1016           0 :   const nsStyleEffects* effects = aFrame->StyleEffects();
    1017             : 
    1018           0 :   if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) {
    1019           0 :     NS_ASSERTION(effects->mClipFlags == NS_STYLE_CLIP_AUTO,
    1020             :                  "We don't know about this type of clip.");
    1021           0 :     return gfxRect(aX, aY, aWidth, aHeight);
    1022             :   }
    1023             : 
    1024           0 :   if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
    1025           0 :       disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
    1026             : 
    1027             :     nsIntRect clipPxRect =
    1028           0 :       effects->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel());
    1029             :     gfxRect clipRect =
    1030           0 :       gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height);
    1031             : 
    1032           0 :     if (NS_STYLE_CLIP_RIGHT_AUTO & effects->mClipFlags) {
    1033           0 :       clipRect.width = aWidth - clipRect.X();
    1034             :     }
    1035           0 :     if (NS_STYLE_CLIP_BOTTOM_AUTO & effects->mClipFlags) {
    1036           0 :       clipRect.height = aHeight - clipRect.Y();
    1037             :     }
    1038             : 
    1039           0 :     if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
    1040           0 :       clipRect.x = aX;
    1041           0 :       clipRect.width = aWidth;
    1042             :     }
    1043           0 :     if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
    1044           0 :       clipRect.y = aY;
    1045           0 :       clipRect.height = aHeight;
    1046             :     }
    1047             : 
    1048           0 :     return clipRect;
    1049             :   }
    1050           0 :   return gfxRect(aX, aY, aWidth, aHeight);
    1051             : }
    1052             : 
    1053             : void
    1054           0 : nsSVGUtils::SetClipRect(gfxContext *aContext,
    1055             :                         const gfxMatrix &aCTM,
    1056             :                         const gfxRect &aRect)
    1057             : {
    1058           0 :   if (aCTM.IsSingular())
    1059           0 :     return;
    1060             : 
    1061           0 :   gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
    1062           0 :   aContext->Multiply(aCTM);
    1063           0 :   aContext->Clip(aRect);
    1064             : }
    1065             : 
    1066             : gfxRect
    1067           7 : nsSVGUtils::GetBBox(nsIFrame* aFrame, uint32_t aFlags,
    1068             :                     const gfxMatrix* aToBoundsSpace)
    1069             : {
    1070           7 :   if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
    1071           0 :     aFrame = aFrame->GetParent();
    1072             :   }
    1073             : 
    1074           7 :   if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
    1075             :     // It is possible to apply a gradient, pattern, clipping path, mask or
    1076             :     // filter to text. When one of these facilities is applied to text
    1077             :     // the bounding box is the entire text element in all
    1078             :     // cases.
    1079           0 :     nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame);
    1080           0 :     if (ancestor && nsSVGUtils::IsInSVGTextSubtree(ancestor)) {
    1081           0 :       while (!ancestor->IsSVGTextFrame()) {
    1082           0 :         ancestor = ancestor->GetParent();
    1083             :       }
    1084             :     }
    1085           0 :     aFrame = ancestor;
    1086             :   }
    1087             : 
    1088           7 :   nsSVGDisplayableFrame* svg = do_QueryFrame(aFrame);
    1089           7 :   const bool hasSVGLayout = aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT;
    1090           7 :   if (hasSVGLayout && !svg) {
    1091             :     // An SVG frame, but not one that can be displayed directly (for
    1092             :     // example, nsGradientFrame). These can't contribute to the bbox.
    1093           0 :     return gfxRect();
    1094             :   }
    1095             : 
    1096           7 :   const bool isOuterSVG = svg && !hasSVGLayout;
    1097           7 :   MOZ_ASSERT(!isOuterSVG || aFrame->IsSVGOuterSVGFrame());
    1098           7 :   if (!svg ||
    1099           0 :       (isOuterSVG && (aFlags & eUseFrameBoundsForOuterSVG))) {
    1100             :     // An HTML element or an SVG outer frame.
    1101           5 :     MOZ_ASSERT(!hasSVGLayout);
    1102           5 :     return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
    1103             :   }
    1104             : 
    1105           2 :   MOZ_ASSERT(svg);
    1106             : 
    1107           2 :   nsIContent* content = aFrame->GetContent();
    1108           4 :   if (content->IsSVGElement() &&
    1109           2 :       !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
    1110           0 :     return gfxRect();
    1111             :   }
    1112             : 
    1113           2 :   if (aFlags == eBBoxIncludeFillGeometry &&
    1114             :       // We only cache bbox in element's own user space
    1115             :       !aToBoundsSpace) {
    1116           2 :     gfxRect* prop = aFrame->GetProperty(ObjectBoundingBoxProperty());
    1117           2 :     if (prop) {
    1118           0 :       return *prop;
    1119             :     }
    1120             :   }
    1121             : 
    1122           2 :   gfxMatrix matrix;
    1123           2 :   if (aToBoundsSpace) {
    1124           0 :     matrix = *aToBoundsSpace;
    1125             :   }
    1126             : 
    1127           2 :   if (aFrame->IsSVGForeignObjectFrame()) {
    1128             :     // The spec says getBBox "Returns the tight bounding box in *current user
    1129             :     // space*". So we should really be doing this for all elements, but that
    1130             :     // needs investigation to check that we won't break too much content.
    1131             :     // NOTE: When changing this to apply to other frame types, make sure to
    1132             :     // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
    1133           2 :     MOZ_ASSERT(content->IsSVGElement(), "bad cast");
    1134           2 :     nsSVGElement *element = static_cast<nsSVGElement*>(content);
    1135           2 :     matrix = element->PrependLocalTransformsTo(matrix, eChildToUserSpace);
    1136             :   }
    1137             :   gfxRect bbox =
    1138           2 :     svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
    1139             :   // Account for 'clipped'.
    1140           2 :   if (aFlags & nsSVGUtils::eBBoxIncludeClipped) {
    1141           0 :     gfxRect clipRect(0, 0, 0, 0);
    1142             :     float x, y, width, height;
    1143           0 :     gfxMatrix tm;
    1144             :     gfxRect fillBBox =
    1145           0 :       svg->GetBBoxContribution(ToMatrix(tm),
    1146           0 :                                nsSVGUtils::eBBoxIncludeFill).ToThebesRect();
    1147           0 :     x = fillBBox.x;
    1148           0 :     y = fillBBox.y;
    1149           0 :     width = fillBBox.width;
    1150           0 :     height = fillBBox.height;
    1151           0 :     bool hasClip = aFrame->StyleDisplay()->IsScrollableOverflow();
    1152           0 :     if (hasClip) {
    1153             :       clipRect =
    1154           0 :         nsSVGUtils::GetClipRectForFrame(aFrame, x, y, width, height);
    1155           0 :       if (aFrame->IsSVGForeignObjectFrame() || aFrame->IsSVGUseFrame()) {
    1156           0 :         clipRect = matrix.TransformBounds(clipRect);
    1157             :       }
    1158             :     }
    1159             :     nsSVGEffects::EffectProperties effectProperties =
    1160           0 :       nsSVGEffects::GetEffectProperties(aFrame);
    1161           0 :     if (effectProperties.HasInvalidClipPath()) {
    1162           0 :       bbox = gfxRect(0, 0, 0, 0);
    1163             :     } else {
    1164             :       nsSVGClipPathFrame *clipPathFrame =
    1165           0 :         effectProperties.GetClipPathFrame();
    1166           0 :       if (clipPathFrame) {
    1167             :         SVGClipPathElement *clipContent =
    1168           0 :           static_cast<SVGClipPathElement*>(clipPathFrame->GetContent());
    1169           0 :         RefPtr<SVGAnimatedEnumeration> units = clipContent->ClipPathUnits();
    1170           0 :         if (units->AnimVal() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    1171           0 :           matrix.PreTranslate(gfxPoint(x, y));
    1172           0 :           matrix.PreScale(width, height);
    1173           0 :         } else if (aFrame->IsSVGForeignObjectFrame()) {
    1174           0 :           matrix = gfxMatrix();
    1175             :         }
    1176             :         bbox =
    1177           0 :           clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix).ToThebesRect();
    1178             :       }
    1179             : 
    1180           0 :       if (hasClip) {
    1181           0 :         bbox = bbox.Intersect(clipRect);
    1182             :       }
    1183             : 
    1184           0 :       if (bbox.IsEmpty()) {
    1185           0 :         bbox = gfxRect(0, 0, 0, 0);
    1186             :       }
    1187             :     }
    1188             :   }
    1189             : 
    1190           2 :   if (aFlags == eBBoxIncludeFillGeometry &&
    1191             :       // We only cache bbox in element's own user space
    1192             :       !aToBoundsSpace) {
    1193             :     // Obtaining the bbox for objectBoundingBox calculations is common so we
    1194             :     // cache the result for future calls, since calculation can be expensive:
    1195           2 :     aFrame->SetProperty(ObjectBoundingBoxProperty(), new gfxRect(bbox));
    1196             :   }
    1197             : 
    1198           2 :   return bbox;
    1199             : }
    1200             : 
    1201             : gfxPoint
    1202          13 : nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
    1203             : {
    1204          13 :   if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
    1205             :     // The user space for non-SVG frames is defined as the bounding box of the
    1206             :     // frame's border-box rects over all continuations.
    1207          11 :     return gfxPoint();
    1208             :   }
    1209             : 
    1210             :   // Leaf frames apply their own offset inside their user space.
    1211           4 :   if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
    1212           2 :       nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
    1213           0 :     return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
    1214           0 :                                          nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
    1215             :   }
    1216             : 
    1217             :   // For foreignObject frames, nsSVGUtils::GetBBox applies their local
    1218             :   // transform, so we need to do the same here.
    1219           2 :   if (aFrame->IsSVGForeignObjectFrame()) {
    1220           2 :     gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
    1221           2 :         PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
    1222           2 :     NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform");
    1223           2 :     return transform.GetTranslation();
    1224             :   }
    1225             : 
    1226           0 :   return gfxPoint();
    1227             : }
    1228             : 
    1229             : static gfxRect
    1230           0 : GetBoundingBoxRelativeRect(const nsSVGLength2 *aXYWH,
    1231             :                            const gfxRect& aBBox)
    1232             : {
    1233           0 :   return gfxRect(aBBox.x + nsSVGUtils::ObjectSpace(aBBox, &aXYWH[0]),
    1234           0 :                  aBBox.y + nsSVGUtils::ObjectSpace(aBBox, &aXYWH[1]),
    1235           0 :                  nsSVGUtils::ObjectSpace(aBBox, &aXYWH[2]),
    1236           0 :                  nsSVGUtils::ObjectSpace(aBBox, &aXYWH[3]));
    1237             : }
    1238             : 
    1239             : gfxRect
    1240           0 : nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
    1241             :                             const gfxRect& aBBox,
    1242             :                             const UserSpaceMetrics& aMetrics)
    1243             : {
    1244           0 :   if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    1245           0 :     return GetBoundingBoxRelativeRect(aXYWH, aBBox);
    1246             :   }
    1247           0 :   return gfxRect(UserSpace(aMetrics, &aXYWH[0]),
    1248           0 :                  UserSpace(aMetrics, &aXYWH[1]),
    1249           0 :                  UserSpace(aMetrics, &aXYWH[2]),
    1250           0 :                  UserSpace(aMetrics, &aXYWH[3]));
    1251             : }
    1252             : 
    1253             : gfxRect
    1254           0 : nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
    1255             :                             const gfxRect& aBBox, nsIFrame *aFrame)
    1256             : {
    1257           0 :   if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    1258           0 :     return GetBoundingBoxRelativeRect(aXYWH, aBBox);
    1259             :   }
    1260           0 :   nsIContent* content = aFrame->GetContent();
    1261           0 :   if (content->IsSVGElement()) {
    1262           0 :     nsSVGElement* svgElement = static_cast<nsSVGElement*>(content);
    1263           0 :     return GetRelativeRect(aUnits, aXYWH, aBBox, SVGElementMetrics(svgElement));
    1264             :   }
    1265           0 :   return GetRelativeRect(aUnits, aXYWH, aBBox, NonSVGFrameUserSpaceMetrics(aFrame));
    1266             : }
    1267             : 
    1268             : bool
    1269         173 : nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
    1270             : {
    1271         173 :   if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
    1272         173 :     return false;
    1273             :   }
    1274           0 :   LayoutFrameType type = aFrame->Type();
    1275           0 :   if (type != LayoutFrameType::SVGImage &&
    1276             :       type != LayoutFrameType::SVGGeometry) {
    1277           0 :     return false;
    1278             :   }
    1279           0 :   if (aFrame->StyleEffects()->HasFilters()) {
    1280           0 :     return false;
    1281             :   }
    1282             :   // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
    1283           0 :   if (type == LayoutFrameType::SVGImage) {
    1284           0 :     return true;
    1285             :   }
    1286           0 :   const nsStyleSVG *style = aFrame->StyleSVG();
    1287           0 :   if (style->HasMarker()) {
    1288           0 :     return false;
    1289             :   }
    1290           0 :   if (!style->HasFill() || !HasStroke(aFrame)) {
    1291           0 :     return true;
    1292             :   }
    1293           0 :   return false;
    1294             : }
    1295             : 
    1296             : gfxMatrix
    1297           2 : nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
    1298             :                                  nsSVGEnum *aUnits,
    1299             :                                  nsIFrame *aFrame)
    1300             : {
    1301           4 :   if (aFrame &&
    1302           2 :       aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    1303           2 :     gfxRect bbox = GetBBox(aFrame);
    1304           2 :     gfxMatrix tm = aMatrix;
    1305           2 :     tm.PreTranslate(gfxPoint(bbox.X(), bbox.Y()));
    1306           2 :     tm.PreScale(bbox.Width(), bbox.Height());
    1307           2 :     return tm;
    1308             :   }
    1309           0 :   return aMatrix;
    1310             : }
    1311             : 
    1312             : nsIFrame*
    1313         158 : nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
    1314             : {
    1315         158 :   for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame;
    1316             :        ancestorFrame = ancestorFrame->GetParent()) {
    1317         158 :     if (!ancestorFrame->IsSVGAFrame()) {
    1318         158 :       return ancestorFrame;
    1319             :     }
    1320             :   }
    1321           0 :   return nullptr;
    1322             : }
    1323             : 
    1324             : bool
    1325           6 : nsSVGUtils::GetNonScalingStrokeTransform(nsIFrame *aFrame,
    1326             :                                          gfxMatrix* aUserToOuterSVG)
    1327             : {
    1328           6 :   if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
    1329           0 :     aFrame = aFrame->GetParent();
    1330             :   }
    1331             : 
    1332           6 :   if (!aFrame->StyleSVGReset()->HasNonScalingStroke()) {
    1333           6 :     return false;
    1334             :   }
    1335             : 
    1336           0 :   nsIContent *content = aFrame->GetContent();
    1337           0 :   MOZ_ASSERT(content->IsSVGElement(), "bad cast");
    1338             : 
    1339           0 :   *aUserToOuterSVG = ThebesMatrix(SVGContentUtils::GetCTM(
    1340           0 :                        static_cast<nsSVGElement*>(content), true));
    1341             : 
    1342           0 :   return !aUserToOuterSVG->IsIdentity();
    1343             : }
    1344             : 
    1345             : // The logic here comes from _cairo_stroke_style_max_distance_from_path
    1346             : static gfxRect
    1347           0 : PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
    1348             :                               nsIFrame* aFrame,
    1349             :                               double aStyleExpansionFactor,
    1350             :                               const gfxMatrix& aMatrix)
    1351             : {
    1352             :   double style_expansion =
    1353           0 :     aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
    1354             : 
    1355           0 :   gfxMatrix matrix = aMatrix;
    1356             : 
    1357           0 :   gfxMatrix outerSVGToUser;
    1358           0 :   if (nsSVGUtils::GetNonScalingStrokeTransform(aFrame, &outerSVGToUser)) {
    1359           0 :     outerSVGToUser.Invert();
    1360           0 :     matrix.PreMultiply(outerSVGToUser);
    1361             :   }
    1362             : 
    1363           0 :   double dx = style_expansion * (fabs(matrix._11) + fabs(matrix._21));
    1364           0 :   double dy = style_expansion * (fabs(matrix._22) + fabs(matrix._12));
    1365             : 
    1366           0 :   gfxRect strokeExtents = aPathExtents;
    1367           0 :   strokeExtents.Inflate(dx, dy);
    1368           0 :   return strokeExtents;
    1369             : }
    1370             : 
    1371             : /*static*/ gfxRect
    1372           0 : nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
    1373             :                                           nsTextFrame* aFrame,
    1374             :                                           const gfxMatrix& aMatrix)
    1375             : {
    1376           0 :   NS_ASSERTION(nsSVGUtils::IsInSVGTextSubtree(aFrame),
    1377             :                "expected an nsTextFrame for SVG text");
    1378           0 :   return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
    1379             : }
    1380             : 
    1381             : /*static*/ gfxRect
    1382           0 : nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
    1383             :                                           SVGGeometryFrame* aFrame,
    1384             :                                           const gfxMatrix& aMatrix)
    1385             : {
    1386             :   bool strokeMayHaveCorners =
    1387           0 :     !SVGContentUtils::ShapeTypeHasNoCorners(aFrame->GetContent());
    1388             : 
    1389             :   // For a shape without corners the stroke can only extend half the stroke
    1390             :   // width from the path in the x/y-axis directions. For shapes with corners
    1391             :   // the stroke can extend by sqrt(1/2) (think 45 degree rotated rect, or line
    1392             :   // with stroke-linecaps="square").
    1393           0 :   double styleExpansionFactor = strokeMayHaveCorners ? M_SQRT1_2 : 0.5;
    1394             : 
    1395             :   // The stroke can extend even further for paths that can be affected by
    1396             :   // stroke-miterlimit.
    1397             :   bool affectedByMiterlimit =
    1398           0 :     aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::path,
    1399             :                                              nsGkAtoms::polyline,
    1400           0 :                                              nsGkAtoms::polygon);
    1401             : 
    1402           0 :   if (affectedByMiterlimit) {
    1403           0 :     const nsStyleSVG* style = aFrame->StyleSVG();
    1404           0 :     if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
    1405           0 :         styleExpansionFactor < style->mStrokeMiterlimit / 2.0) {
    1406           0 :       styleExpansionFactor = style->mStrokeMiterlimit / 2.0;
    1407             :     }
    1408             :   }
    1409             : 
    1410             :   return ::PathExtentsToMaxStrokeExtents(aPathExtents,
    1411             :                                          aFrame,
    1412             :                                          styleExpansionFactor,
    1413           0 :                                          aMatrix);
    1414             : }
    1415             : 
    1416             : // ----------------------------------------------------------------------
    1417             : 
    1418             : /* static */ nscolor
    1419          20 : nsSVGUtils::GetFallbackOrPaintColor(nsStyleContext *aStyleContext,
    1420             :                                     nsStyleSVGPaint nsStyleSVG::*aFillOrStroke)
    1421             : {
    1422          20 :   const nsStyleSVGPaint &paint = aStyleContext->StyleSVG()->*aFillOrStroke;
    1423          20 :   nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
    1424             :   nscolor color;
    1425          20 :   switch (paint.Type()) {
    1426             :     case eStyleSVGPaintType_Server:
    1427             :     case eStyleSVGPaintType_ContextStroke:
    1428           0 :       color = paint.GetFallbackType() == eStyleSVGFallbackType_Color ?
    1429             :                 paint.GetFallbackColor() : NS_RGBA(0, 0, 0, 0);
    1430           0 :       break;
    1431             :     case eStyleSVGPaintType_ContextFill:
    1432           0 :       color = paint.GetFallbackType() == eStyleSVGFallbackType_Color ?
    1433             :                 paint.GetFallbackColor() : NS_RGB(0, 0, 0);
    1434           0 :       break;
    1435             :     default:
    1436          20 :       color = paint.GetColor();
    1437          20 :       break;
    1438             :   }
    1439          20 :   if (styleIfVisited) {
    1440             :     const nsStyleSVGPaint &paintIfVisited =
    1441           0 :       styleIfVisited->StyleSVG()->*aFillOrStroke;
    1442             :     // To prevent Web content from detecting if a user has visited a URL
    1443             :     // (via URL loading triggered by paint servers or performance
    1444             :     // differences between paint servers or between a paint server and a
    1445             :     // color), we do not allow whether links are visited to change which
    1446             :     // paint server is used or switch between paint servers and simple
    1447             :     // colors.  A :visited style may only override a simple color with
    1448             :     // another simple color.
    1449           0 :     if (paintIfVisited.Type() == eStyleSVGPaintType_Color &&
    1450           0 :         paint.Type() == eStyleSVGPaintType_Color) {
    1451           0 :       nscolor colors[2] = { color, paintIfVisited.GetColor() };
    1452           0 :       return nsStyleContext::CombineVisitedColors(
    1453           0 :                colors, aStyleContext->RelevantLinkVisited());
    1454             :     }
    1455             :   }
    1456          20 :   return color;
    1457             : }
    1458             : 
    1459             : /* static */ void
    1460          34 : nsSVGUtils::MakeFillPatternFor(nsIFrame* aFrame,
    1461             :                                gfxContext* aContext,
    1462             :                                GeneralPattern* aOutPattern,
    1463             :                                imgDrawingParams& aImgParams,
    1464             :                                SVGContextPaint* aContextPaint)
    1465             : {
    1466          34 :   const nsStyleSVG* style = aFrame->StyleSVG();
    1467          34 :   if (style->mFill.Type() == eStyleSVGPaintType_None) {
    1468          18 :     return;
    1469             :   }
    1470             : 
    1471          32 :   const float opacity = aFrame->StyleEffects()->mOpacity;
    1472             : 
    1473          32 :   float fillOpacity = GetOpacity(style->FillOpacitySource(),
    1474             :                                  style->mFillOpacity,
    1475          32 :                                  aContextPaint);
    1476          32 :   if (opacity < 1.0f &&
    1477           0 :       nsSVGUtils::CanOptimizeOpacity(aFrame)) {
    1478             :     // Combine the group opacity into the fill opacity (we will have skipped
    1479             :     // creating an offscreen surface to apply the group opacity).
    1480           0 :     fillOpacity *= opacity;
    1481             :   }
    1482             : 
    1483          32 :   const DrawTarget* dt = aContext->GetDrawTarget();
    1484             : 
    1485             :   nsSVGPaintServerFrame *ps =
    1486          32 :     nsSVGEffects::GetPaintServer(aFrame, &nsStyleSVG::mFill,
    1487          32 :                                  nsSVGEffects::FillProperty());
    1488             : 
    1489          32 :   if (ps) {
    1490             :     RefPtr<gfxPattern> pattern =
    1491           0 :       ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrix(),
    1492           0 :                                 &nsStyleSVG::mFill, fillOpacity, aImgParams);
    1493           0 :     if (pattern) {
    1494           0 :       pattern->CacheColorStops(dt);
    1495           0 :       aOutPattern->Init(*pattern->GetPattern(dt));
    1496           0 :       return;
    1497             :     }
    1498             :   }
    1499             : 
    1500          32 :   if (aContextPaint) {
    1501          14 :     RefPtr<gfxPattern> pattern;
    1502          14 :     switch (style->mFill.Type()) {
    1503             :     case eStyleSVGPaintType_ContextFill:
    1504             :       pattern =
    1505          28 :         aContextPaint->GetFillPattern(dt, fillOpacity,
    1506          42 :                                       aContext->CurrentMatrix(), aImgParams);
    1507          14 :       break;
    1508             :     case eStyleSVGPaintType_ContextStroke:
    1509             :       pattern =
    1510           0 :         aContextPaint->GetStrokePattern(dt, fillOpacity,
    1511           0 :                                         aContext->CurrentMatrix(), aImgParams);
    1512           0 :       break;
    1513             :     default:
    1514             :       ;
    1515             :     }
    1516          14 :     if (pattern) {
    1517          14 :       aOutPattern->Init(*pattern->GetPattern(dt));
    1518          14 :       return;
    1519             :     }
    1520             :   }
    1521             : 
    1522          18 :   if (style->mFill.GetFallbackType() == eStyleSVGFallbackType_None) {
    1523           0 :     return;
    1524             :   }
    1525             : 
    1526             :   // On failure, use the fallback colour in case we have an
    1527             :   // objectBoundingBox where the width or height of the object is zero.
    1528             :   // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
    1529             :   Color color(Color::FromABGR(GetFallbackOrPaintColor(aFrame->StyleContext(),
    1530          18 :                                                       &nsStyleSVG::mFill)));
    1531          18 :   color.a *= fillOpacity;
    1532          18 :   aOutPattern->InitColorPattern(ToDeviceColor(color));
    1533             : }
    1534             : 
    1535             : /* static */ void
    1536           2 : nsSVGUtils::MakeStrokePatternFor(nsIFrame* aFrame,
    1537             :                                  gfxContext* aContext,
    1538             :                                  GeneralPattern* aOutPattern,
    1539             :                                  imgDrawingParams& aImgParams,
    1540             :                                  SVGContextPaint* aContextPaint)
    1541             : {
    1542           2 :   const nsStyleSVG* style = aFrame->StyleSVG();
    1543           2 :   if (style->mStroke.Type() == eStyleSVGPaintType_None) {
    1544           0 :     return;
    1545             :   }
    1546             : 
    1547           2 :   const float opacity = aFrame->StyleEffects()->mOpacity;
    1548             : 
    1549           2 :   float strokeOpacity = GetOpacity(style->StrokeOpacitySource(),
    1550             :                                    style->mStrokeOpacity,
    1551           2 :                                    aContextPaint);
    1552           2 :   if (opacity < 1.0f &&
    1553           0 :       nsSVGUtils::CanOptimizeOpacity(aFrame)) {
    1554             :     // Combine the group opacity into the stroke opacity (we will have skipped
    1555             :     // creating an offscreen surface to apply the group opacity).
    1556           0 :     strokeOpacity *= opacity;
    1557             :   }
    1558             : 
    1559           2 :   const DrawTarget* dt = aContext->GetDrawTarget();
    1560             : 
    1561             :   nsSVGPaintServerFrame *ps =
    1562           2 :     nsSVGEffects::GetPaintServer(aFrame, &nsStyleSVG::mStroke,
    1563           2 :                                  nsSVGEffects::StrokeProperty());
    1564             : 
    1565           2 :   if (ps) {
    1566             :     RefPtr<gfxPattern> pattern =
    1567           0 :       ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrix(),
    1568           0 :                                 &nsStyleSVG::mStroke, strokeOpacity, aImgParams);
    1569           0 :     if (pattern) {
    1570           0 :       pattern->CacheColorStops(dt);
    1571           0 :       aOutPattern->Init(*pattern->GetPattern(dt));
    1572           0 :       return;
    1573             :     }
    1574             :   }
    1575             : 
    1576           2 :   if (aContextPaint) {
    1577           0 :     RefPtr<gfxPattern> pattern;
    1578           0 :     switch (style->mStroke.Type()) {
    1579             :     case eStyleSVGPaintType_ContextFill:
    1580             :       pattern =
    1581           0 :         aContextPaint->GetFillPattern(dt, strokeOpacity,
    1582           0 :                                       aContext->CurrentMatrix(), aImgParams);
    1583           0 :       break;
    1584             :     case eStyleSVGPaintType_ContextStroke:
    1585             :       pattern =
    1586           0 :         aContextPaint->GetStrokePattern(dt, strokeOpacity,
    1587           0 :                                         aContext->CurrentMatrix(), aImgParams);
    1588           0 :       break;
    1589             :     default:
    1590             :       ;
    1591             :     }
    1592           0 :     if (pattern) {
    1593           0 :       aOutPattern->Init(*pattern->GetPattern(dt));
    1594           0 :       return;
    1595             :     }
    1596             :   }
    1597             : 
    1598           2 :   if (style->mStroke.GetFallbackType() == eStyleSVGFallbackType_None) {
    1599           0 :     return;
    1600             :   }
    1601             : 
    1602             :   // On failure, use the fallback colour in case we have an
    1603             :   // objectBoundingBox where the width or height of the object is zero.
    1604             :   // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
    1605             :   Color color(Color::FromABGR(GetFallbackOrPaintColor(aFrame->StyleContext(),
    1606           2 :                                                       &nsStyleSVG::mStroke)));
    1607           2 :   color.a *= strokeOpacity;
    1608           2 :   aOutPattern->InitColorPattern(ToDeviceColor(color));
    1609             : }
    1610             : 
    1611             : /* static */ float
    1612          34 : nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
    1613             :                        const float& aOpacity,
    1614             :                        SVGContextPaint *aContextPaint)
    1615             : {
    1616          34 :   float opacity = 1.0f;
    1617          34 :   switch (aOpacityType) {
    1618             :   case eStyleSVGOpacitySource_Normal:
    1619          31 :     opacity = aOpacity;
    1620          31 :     break;
    1621             :   case eStyleSVGOpacitySource_ContextFillOpacity:
    1622           3 :     if (aContextPaint) {
    1623           3 :       opacity = aContextPaint->GetFillOpacity();
    1624             :     } else {
    1625           0 :       NS_WARNING("Content used context-fill-opacity when not in a context element");
    1626             :     }
    1627           3 :     break;
    1628             :   case eStyleSVGOpacitySource_ContextStrokeOpacity:
    1629           0 :     if (aContextPaint) {
    1630           0 :       opacity = aContextPaint->GetStrokeOpacity();
    1631             :     } else {
    1632           0 :       NS_WARNING("Content used context-stroke-opacity when not in a context element");
    1633             :     }
    1634           0 :     break;
    1635             :   default:
    1636           0 :     NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
    1637             :   }
    1638          34 :   return opacity;
    1639             : }
    1640             : 
    1641             : bool
    1642         112 : nsSVGUtils::HasStroke(nsIFrame* aFrame, SVGContextPaint* aContextPaint)
    1643             : {
    1644         112 :   const nsStyleSVG *style = aFrame->StyleSVG();
    1645         112 :   return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
    1646             : }
    1647             : 
    1648             : float
    1649          66 : nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, SVGContextPaint* aContextPaint)
    1650             : {
    1651          66 :   const nsStyleSVG *style = aFrame->StyleSVG();
    1652          66 :   if (aContextPaint && style->StrokeWidthFromObject()) {
    1653           0 :     return aContextPaint->GetStrokeWidth();
    1654             :   }
    1655             : 
    1656          66 :   nsIContent* content = aFrame->GetContent();
    1657          66 :   if (content->IsNodeOfType(nsINode::eTEXT)) {
    1658           0 :     content = content->GetParent();
    1659             :   }
    1660             : 
    1661          66 :   nsSVGElement *ctx = static_cast<nsSVGElement*>(content);
    1662             : 
    1663          66 :   return SVGContentUtils::CoordToFloat(ctx, style->mStrokeWidth);
    1664             : }
    1665             : 
    1666             : static bool
    1667           0 : GetStrokeDashData(nsIFrame* aFrame,
    1668             :                   nsTArray<gfxFloat>& aDashes,
    1669             :                   gfxFloat* aDashOffset,
    1670             :                   SVGContextPaint* aContextPaint)
    1671             : {
    1672           0 :   const nsStyleSVG* style = aFrame->StyleSVG();
    1673           0 :   nsIContent *content = aFrame->GetContent();
    1674             :   nsSVGElement *ctx = static_cast<nsSVGElement*>
    1675           0 :     (content->IsNodeOfType(nsINode::eTEXT) ?
    1676           0 :      content->GetParent() : content);
    1677             : 
    1678           0 :   gfxFloat totalLength = 0.0;
    1679           0 :   if (aContextPaint && style->StrokeDasharrayFromObject()) {
    1680           0 :     aDashes = aContextPaint->GetStrokeDashArray();
    1681             : 
    1682           0 :     for (uint32_t i = 0; i < aDashes.Length(); i++) {
    1683           0 :       if (aDashes[i] < 0.0) {
    1684           0 :         return false;
    1685             :       }
    1686           0 :       totalLength += aDashes[i];
    1687             :     }
    1688             : 
    1689             :   } else {
    1690           0 :     uint32_t count = style->mStrokeDasharray.Length();
    1691           0 :     if (!count || !aDashes.SetLength(count, fallible)) {
    1692           0 :       return false;
    1693             :     }
    1694             : 
    1695           0 :     gfxFloat pathScale = 1.0;
    1696             : 
    1697           0 :     if (content->IsSVGElement(nsGkAtoms::path)) {
    1698           0 :       pathScale = static_cast<SVGPathElement*>(content)->
    1699           0 :         GetPathLengthScale(SVGPathElement::eForStroking);
    1700           0 :       if (pathScale <= 0) {
    1701           0 :         return false;
    1702             :       }
    1703             :     }
    1704             : 
    1705           0 :     const nsTArray<nsStyleCoord>& dasharray = style->mStrokeDasharray;
    1706             : 
    1707           0 :     for (uint32_t i = 0; i < count; i++) {
    1708           0 :       aDashes[i] = SVGContentUtils::CoordToFloat(ctx,
    1709           0 :                                                  dasharray[i]) * pathScale;
    1710           0 :       if (aDashes[i] < 0.0) {
    1711           0 :         return false;
    1712             :       }
    1713           0 :       totalLength += aDashes[i];
    1714             :     }
    1715             :   }
    1716             : 
    1717           0 :   if (aContextPaint && style->StrokeDashoffsetFromObject()) {
    1718           0 :     *aDashOffset = aContextPaint->GetStrokeDashOffset();
    1719             :   } else {
    1720           0 :     *aDashOffset = SVGContentUtils::CoordToFloat(ctx,
    1721             :                                                  style->mStrokeDashoffset);
    1722             :   }
    1723             : 
    1724           0 :   return (totalLength > 0.0);
    1725             : }
    1726             : 
    1727             : void
    1728           0 : nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame* aFrame,
    1729             :                                      gfxContext *aContext,
    1730             :                                      SVGContextPaint* aContextPaint)
    1731             : {
    1732           0 :   float width = GetStrokeWidth(aFrame, aContextPaint);
    1733           0 :   if (width <= 0)
    1734           0 :     return;
    1735           0 :   aContext->SetLineWidth(width);
    1736             : 
    1737             :   // Apply any stroke-specific transform
    1738           0 :   gfxMatrix outerSVGToUser;
    1739           0 :   if (GetNonScalingStrokeTransform(aFrame, &outerSVGToUser) &&
    1740           0 :       outerSVGToUser.Invert()) {
    1741           0 :     aContext->Multiply(outerSVGToUser);
    1742             :   }
    1743             : 
    1744           0 :   const nsStyleSVG* style = aFrame->StyleSVG();
    1745             : 
    1746           0 :   switch (style->mStrokeLinecap) {
    1747             :   case NS_STYLE_STROKE_LINECAP_BUTT:
    1748           0 :     aContext->SetLineCap(CapStyle::BUTT);
    1749           0 :     break;
    1750             :   case NS_STYLE_STROKE_LINECAP_ROUND:
    1751           0 :     aContext->SetLineCap(CapStyle::ROUND);
    1752           0 :     break;
    1753             :   case NS_STYLE_STROKE_LINECAP_SQUARE:
    1754           0 :     aContext->SetLineCap(CapStyle::SQUARE);
    1755           0 :     break;
    1756             :   }
    1757             : 
    1758           0 :   aContext->SetMiterLimit(style->mStrokeMiterlimit);
    1759             : 
    1760           0 :   switch (style->mStrokeLinejoin) {
    1761             :   case NS_STYLE_STROKE_LINEJOIN_MITER:
    1762           0 :     aContext->SetLineJoin(JoinStyle::MITER_OR_BEVEL);
    1763           0 :     break;
    1764             :   case NS_STYLE_STROKE_LINEJOIN_ROUND:
    1765           0 :     aContext->SetLineJoin(JoinStyle::ROUND);
    1766           0 :     break;
    1767             :   case NS_STYLE_STROKE_LINEJOIN_BEVEL:
    1768           0 :     aContext->SetLineJoin(JoinStyle::BEVEL);
    1769           0 :     break;
    1770             :   }
    1771             : 
    1772           0 :   AutoTArray<gfxFloat, 10> dashes;
    1773             :   gfxFloat dashOffset;
    1774           0 :   if (GetStrokeDashData(aFrame, dashes, &dashOffset, aContextPaint)) {
    1775           0 :     aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset);
    1776             :   }
    1777             : }
    1778             : 
    1779             : uint16_t
    1780          82 : nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame)
    1781             : {
    1782          82 :   uint16_t flags = 0;
    1783             : 
    1784          82 :   switch (aFrame->StyleUserInterface()->mPointerEvents) {
    1785             :   case NS_STYLE_POINTER_EVENTS_NONE:
    1786           0 :     break;
    1787             :   case NS_STYLE_POINTER_EVENTS_AUTO:
    1788             :   case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
    1789          82 :     if (aFrame->StyleVisibility()->IsVisible()) {
    1790          82 :       if (aFrame->StyleSVG()->mFill.Type() != eStyleSVGPaintType_None)
    1791          78 :         flags |= SVG_HIT_TEST_FILL;
    1792          82 :       if (aFrame->StyleSVG()->mStroke.Type() != eStyleSVGPaintType_None)
    1793           4 :         flags |= SVG_HIT_TEST_STROKE;
    1794          82 :       if (aFrame->StyleSVG()->mStrokeOpacity > 0)
    1795          82 :         flags |= SVG_HIT_TEST_CHECK_MRECT;
    1796             :     }
    1797          82 :     break;
    1798             :   case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
    1799           0 :     if (aFrame->StyleVisibility()->IsVisible()) {
    1800           0 :       flags |= SVG_HIT_TEST_FILL;
    1801             :     }
    1802           0 :     break;
    1803             :   case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
    1804           0 :     if (aFrame->StyleVisibility()->IsVisible()) {
    1805           0 :       flags |= SVG_HIT_TEST_STROKE;
    1806             :     }
    1807           0 :     break;
    1808             :   case NS_STYLE_POINTER_EVENTS_VISIBLE:
    1809           0 :     if (aFrame->StyleVisibility()->IsVisible()) {
    1810           0 :       flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
    1811             :     }
    1812           0 :     break;
    1813             :   case NS_STYLE_POINTER_EVENTS_PAINTED:
    1814           0 :     if (aFrame->StyleSVG()->mFill.Type() != eStyleSVGPaintType_None)
    1815           0 :       flags |= SVG_HIT_TEST_FILL;
    1816           0 :     if (aFrame->StyleSVG()->mStroke.Type() != eStyleSVGPaintType_None)
    1817           0 :       flags |= SVG_HIT_TEST_STROKE;
    1818           0 :     if (aFrame->StyleSVG()->mStrokeOpacity)
    1819           0 :       flags |= SVG_HIT_TEST_CHECK_MRECT;
    1820           0 :     break;
    1821             :   case NS_STYLE_POINTER_EVENTS_FILL:
    1822           0 :     flags |= SVG_HIT_TEST_FILL;
    1823           0 :     break;
    1824             :   case NS_STYLE_POINTER_EVENTS_STROKE:
    1825           0 :     flags |= SVG_HIT_TEST_STROKE;
    1826           0 :     break;
    1827             :   case NS_STYLE_POINTER_EVENTS_ALL:
    1828           0 :     flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
    1829           0 :     break;
    1830             :   default:
    1831           0 :     NS_ERROR("not reached");
    1832           0 :     break;
    1833             :   }
    1834             : 
    1835          82 :   return flags;
    1836             : }
    1837             : 
    1838             : void
    1839           0 : nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext)
    1840             : {
    1841           0 :   nsIFrame* frame = aElement->GetPrimaryFrame();
    1842           0 :   nsSVGDisplayableFrame* svgFrame = do_QueryFrame(frame);
    1843           0 :   if (!svgFrame) {
    1844           0 :     return;
    1845             :   }
    1846           0 :   gfxMatrix m;
    1847           0 :   if (frame->GetContent()->IsSVGElement()) {
    1848             :     // PaintSVG() expects the passed transform to be the transform to its own
    1849             :     // SVG user space, so we need to account for any 'transform' attribute:
    1850           0 :     m = static_cast<nsSVGElement*>(frame->GetContent())->
    1851           0 :           PrependLocalTransformsTo(gfxMatrix(), eUserSpaceToParent);
    1852             :   }
    1853             : 
    1854             :   // SVG-in-OpenType is not allowed to paint exteral resources, so we can
    1855             :   // just pass a dummy params into PatintSVG.
    1856           0 :   imgDrawingParams dummy;
    1857           0 :   svgFrame->PaintSVG(*aContext, m, dummy);
    1858             : }
    1859             : 
    1860             : bool
    1861           0 : nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
    1862             :                                const gfxMatrix& aSVGToAppSpace,
    1863             :                                gfxRect* aResult)
    1864             : {
    1865           0 :   nsIFrame* frame = aElement->GetPrimaryFrame();
    1866           0 :   nsSVGDisplayableFrame* svgFrame = do_QueryFrame(frame);
    1867           0 :   if (!svgFrame) {
    1868           0 :     return false;
    1869             :   }
    1870             : 
    1871           0 :   gfxMatrix transform(aSVGToAppSpace);
    1872           0 :   nsIContent* content = frame->GetContent();
    1873           0 :   if (content->IsSVGElement()) {
    1874             :     transform = static_cast<nsSVGElement*>(content)->
    1875           0 :                   PrependLocalTransformsTo(aSVGToAppSpace);
    1876             :   }
    1877             : 
    1878           0 :   *aResult = svgFrame->GetBBoxContribution(gfx::ToMatrix(transform),
    1879             :     nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry |
    1880             :     nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry |
    1881           0 :     nsSVGUtils::eBBoxIncludeMarkers).ToThebesRect();
    1882           0 :   return true;
    1883             : }
    1884             : 
    1885             : nsRect
    1886           0 : nsSVGUtils::ToCanvasBounds(const gfxRect &aUserspaceRect,
    1887             :                            const gfxMatrix &aToCanvas,
    1888             :                            const nsPresContext *presContext)
    1889             : {
    1890             :   return nsLayoutUtils::RoundGfxRectToAppRect(
    1891           0 :                           aToCanvas.TransformBounds(aUserspaceRect),
    1892           0 :                           presContext->AppUnitsPerDevPixel());
    1893             : }
    1894             : 
    1895             : gfxMatrix
    1896          42 : nsSVGUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
    1897             : {
    1898          42 :   int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
    1899             :   float devPxPerCSSPx =
    1900          42 :     1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
    1901             : 
    1902             :   return gfxMatrix(devPxPerCSSPx, 0.0,
    1903             :                    0.0, devPxPerCSSPx,
    1904          42 :                    0.0, 0.0);
    1905             : }

Generated by: LCOV version 1.13