LCOV - code coverage report
Current view: top level - layout/svg - SVGGeometryFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 207 380 54.5 %
Date: 2017-07-14 16:53:18 Functions: 20 30 66.7 %
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             : #include "SVGGeometryFrame.h"
       8             : 
       9             : // Keep others in (case-insensitive) order:
      10             : #include "gfx2DGlue.h"
      11             : #include "gfxContext.h"
      12             : #include "gfxPlatform.h"
      13             : #include "gfxUtils.h"
      14             : #include "mozilla/gfx/2D.h"
      15             : #include "mozilla/gfx/Helpers.h"
      16             : #include "mozilla/RefPtr.h"
      17             : #include "mozilla/SVGContextPaint.h"
      18             : #include "nsDisplayList.h"
      19             : #include "nsGkAtoms.h"
      20             : #include "nsLayoutUtils.h"
      21             : #include "nsSVGEffects.h"
      22             : #include "nsSVGIntegrationUtils.h"
      23             : #include "nsSVGMarkerFrame.h"
      24             : #include "SVGGeometryElement.h"
      25             : #include "nsSVGUtils.h"
      26             : #include "mozilla/ArrayUtils.h"
      27             : #include "SVGAnimatedTransformList.h"
      28             : #include "SVGContentUtils.h"
      29             : #include "SVGGraphicsElement.h"
      30             : 
      31             : using namespace mozilla;
      32             : using namespace mozilla::dom;
      33             : using namespace mozilla::gfx;
      34             : using namespace mozilla::image;
      35             : 
      36             : //----------------------------------------------------------------------
      37             : // Implementation
      38             : 
      39             : nsIFrame*
      40          73 : NS_NewSVGGeometryFrame(nsIPresShell* aPresShell,
      41             :                        nsStyleContext* aContext)
      42             : {
      43          73 :   return new (aPresShell) SVGGeometryFrame(aContext);
      44             : }
      45             : 
      46          73 : NS_IMPL_FRAMEARENA_HELPERS(SVGGeometryFrame)
      47             : 
      48             : //----------------------------------------------------------------------
      49             : // nsQueryFrame methods
      50             : 
      51         230 : NS_QUERYFRAME_HEAD(SVGGeometryFrame)
      52         138 :   NS_QUERYFRAME_ENTRY(nsSVGDisplayableFrame)
      53           2 :   NS_QUERYFRAME_ENTRY(SVGGeometryFrame)
      54          90 : NS_QUERYFRAME_TAIL_INHERITING(nsFrame)
      55             : 
      56             : //----------------------------------------------------------------------
      57             : // Display list item:
      58             : 
      59             : class nsDisplaySVGGeometry : public nsDisplayItem {
      60             :   typedef mozilla::image::imgDrawingParams imgDrawingParams;
      61             : 
      62             : public:
      63          34 :   nsDisplaySVGGeometry(nsDisplayListBuilder* aBuilder,
      64             :                        SVGGeometryFrame* aFrame)
      65          34 :     : nsDisplayItem(aBuilder, aFrame)
      66             :   {
      67          34 :     MOZ_COUNT_CTOR(nsDisplaySVGGeometry);
      68          34 :     MOZ_ASSERT(aFrame, "Must have a frame!");
      69          34 :   }
      70             : #ifdef NS_BUILD_REFCNT_LOGGING
      71          68 :   virtual ~nsDisplaySVGGeometry() {
      72          34 :     MOZ_COUNT_DTOR(nsDisplaySVGGeometry);
      73          34 :   }
      74             : #endif
      75             : 
      76         314 :   NS_DISPLAY_DECL_NAME("nsDisplaySVGGeometry", TYPE_SVG_GEOMETRY)
      77             : 
      78             :   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
      79             :                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
      80             :   virtual void Paint(nsDisplayListBuilder* aBuilder,
      81             :                      gfxContext* aCtx) override;
      82             : 
      83           0 :   nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
      84             :   {
      85           0 :     return new nsDisplayItemGenericImageGeometry(this, aBuilder);
      86             :   }
      87             : 
      88             :   void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
      89             :                                  const nsDisplayItemGeometry* aGeometry,
      90             :                                  nsRegion *aInvalidRegion) override;
      91             : };
      92             : 
      93             : void
      94           0 : nsDisplaySVGGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
      95             :                               HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
      96             : {
      97           0 :   SVGGeometryFrame *frame = static_cast<SVGGeometryFrame*>(mFrame);
      98           0 :   nsPoint pointRelativeToReferenceFrame = aRect.Center();
      99             :   // ToReferenceFrame() includes frame->GetPosition(), our user space position.
     100             :   nsPoint userSpacePtInAppUnits = pointRelativeToReferenceFrame -
     101           0 :                                    (ToReferenceFrame() - frame->GetPosition());
     102             :   gfxPoint userSpacePt =
     103           0 :     gfxPoint(userSpacePtInAppUnits.x, userSpacePtInAppUnits.y) /
     104           0 :       frame->PresContext()->AppUnitsPerCSSPixel();
     105           0 :   if (frame->GetFrameForPoint(userSpacePt)) {
     106           0 :     aOutFrames->AppendElement(frame);
     107             :   }
     108           0 : }
     109             : 
     110             : void
     111          34 : nsDisplaySVGGeometry::Paint(nsDisplayListBuilder* aBuilder,
     112             :                             gfxContext* aCtx)
     113             : {
     114          34 :   uint32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
     115             : 
     116             :   // ToReferenceFrame includes our mRect offset, but painting takes
     117             :   // account of that too. To avoid double counting, we subtract that
     118             :   // here.
     119          34 :   nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
     120             : 
     121             :   gfxPoint devPixelOffset =
     122          34 :     nsLayoutUtils::PointToGfxPoint(offset, appUnitsPerDevPixel);
     123             : 
     124          68 :   gfxMatrix tm = nsSVGUtils::GetCSSPxToDevPxMatrix(mFrame) *
     125         102 :                    gfxMatrix::Translation(devPixelOffset);
     126          34 :   imgDrawingParams imgParams(aBuilder->ShouldSyncDecodeImages()
     127             :                              ? imgIContainer::FLAG_SYNC_DECODE
     128          34 :                              : imgIContainer::FLAG_SYNC_DECODE_IF_FAST);
     129             : 
     130          68 :   static_cast<SVGGeometryFrame*>(mFrame)->PaintSVG(*aCtx,
     131          68 :                                                    tm, imgParams);
     132             : 
     133          34 :   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, imgParams.result);
     134          34 : }
     135             : 
     136             : void
     137           0 : nsDisplaySVGGeometry::ComputeInvalidationRegion(
     138             :   nsDisplayListBuilder* aBuilder,
     139             :   const nsDisplayItemGeometry* aGeometry,
     140             :   nsRegion* aInvalidRegion)
     141             : {
     142             :   auto geometry =
     143           0 :     static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
     144             : 
     145           0 :   if (aBuilder->ShouldSyncDecodeImages() &&
     146           0 :       geometry->ShouldInvalidateToSyncDecodeImages()) {
     147             :     bool snap;
     148           0 :     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
     149             :   }
     150             : 
     151           0 :   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
     152           0 : }
     153             : 
     154             : namespace mozilla {
     155             : 
     156             : //----------------------------------------------------------------------
     157             : // nsIFrame methods
     158             : 
     159             : void
     160          73 : SVGGeometryFrame::Init(nsIContent*       aContent,
     161             :                        nsContainerFrame* aParent,
     162             :                        nsIFrame*         aPrevInFlow)
     163             : {
     164          73 :   AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
     165          73 :   nsFrame::Init(aContent, aParent, aPrevInFlow);
     166          73 : }
     167             : 
     168             : nsresult
     169           0 : SVGGeometryFrame::AttributeChanged(int32_t         aNameSpaceID,
     170             :                                    nsIAtom*        aAttribute,
     171             :                                    int32_t         aModType)
     172             : {
     173             :   // We don't invalidate for transform changes (the layers code does that).
     174             :   // Also note that SVGTransformableElement::GetAttributeChangeHint will
     175             :   // return nsChangeHint_UpdateOverflow for "transform" attribute changes
     176             :   // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
     177             : 
     178           0 :   if (aNameSpaceID == kNameSpaceID_None &&
     179             :       (static_cast<SVGGeometryElement*>
     180           0 :                   (mContent)->AttributeDefinesGeometry(aAttribute))) {
     181           0 :     nsLayoutUtils::PostRestyleEvent(
     182           0 :       mContent->AsElement(), nsRestyleHint(0),
     183           0 :       nsChangeHint_InvalidateRenderingObservers);
     184           0 :     nsSVGUtils::ScheduleReflowSVG(this);
     185             :   }
     186           0 :   return NS_OK;
     187             : }
     188             : 
     189             : /* virtual */ void
     190          89 : SVGGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
     191             : {
     192          89 :   nsFrame::DidSetStyleContext(aOldStyleContext);
     193             : 
     194          89 :   if (aOldStyleContext) {
     195          16 :     auto oldStyleEffects = aOldStyleContext->PeekStyleEffects();
     196          30 :     if (oldStyleEffects &&
     197          16 :         StyleEffects()->mOpacity != oldStyleEffects->mOpacity &&
     198           0 :         nsSVGUtils::CanOptimizeOpacity(this)) {
     199             :       // nsIFrame::BuildDisplayListForStackingContext() is not going to create an
     200             :       // nsDisplayOpacity display list item, so DLBI won't invalidate for us.
     201           0 :       InvalidateFrame();
     202             :     }
     203             : 
     204             :     SVGGeometryElement* element =
     205          16 :       static_cast<SVGGeometryElement*>(mContent);
     206             : 
     207          16 :     auto oldStyleSVG = aOldStyleContext->PeekStyleSVG();
     208          16 :     if (oldStyleSVG && !SVGContentUtils::ShapeTypeHasNoCorners(mContent)) {
     209           2 :       if (StyleSVG()->mStrokeLinecap != oldStyleSVG->mStrokeLinecap &&
     210           0 :           element->IsSVGElement(nsGkAtoms::path)) {
     211             :         // If the stroke-linecap changes to or from "butt" then our element
     212             :         // needs to update its cached Moz2D Path, since SVGPathData::BuildPath
     213             :         // decides whether or not to insert little lines into the path for zero
     214             :         // length subpaths base on that property.
     215           0 :         element->ClearAnyCachedPath();
     216           2 :       } else if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
     217           2 :         if (StyleSVG()->mClipRule != oldStyleSVG->mClipRule) {
     218             :           // Moz2D Path objects are fill-rule specific.
     219             :           // For clipPath we use clip-rule as the path's fill-rule.
     220           0 :           element->ClearAnyCachedPath();
     221             :         }
     222             :       } else {
     223           0 :         if (StyleSVG()->mFillRule != oldStyleSVG->mFillRule) {
     224             :           // Moz2D Path objects are fill-rule specific.
     225           0 :           element->ClearAnyCachedPath();
     226             :         }
     227             :       }
     228             :     }
     229             :   }
     230          89 : }
     231             : 
     232             : bool
     233         150 : SVGGeometryFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform,
     234             :                                    gfx::Matrix *aFromParentTransform) const
     235             : {
     236         150 :   bool foundTransform = false;
     237             : 
     238             :   // Check if our parent has children-only transforms:
     239         150 :   nsIFrame *parent = GetParent();
     240         300 :   if (parent &&
     241         150 :       parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
     242             :     foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
     243         150 :                        HasChildrenOnlyTransform(aFromParentTransform);
     244             :   }
     245             : 
     246         150 :   nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
     247             :   nsSVGAnimatedTransformList* transformList =
     248         150 :     content->GetAnimatedTransformList();
     249         300 :   if ((transformList && transformList->HasTransform()) ||
     250         150 :       content->GetAnimateMotionTransform()) {
     251           0 :     if (aOwnTransform) {
     252             :       *aOwnTransform = gfx::ToMatrix(
     253           0 :                          content->PrependLocalTransformsTo(
     254           0 :                            gfxMatrix(),
     255           0 :                            eUserSpaceToParent));
     256             :     }
     257           0 :     foundTransform = true;
     258             :   }
     259         150 :   return foundTransform;
     260             : }
     261             : 
     262             : void
     263          34 : SVGGeometryFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
     264             :                                    const nsRect&           aDirtyRect,
     265             :                                    const nsDisplayListSet& aLists)
     266             : {
     267          68 :   if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions() ||
     268          34 :       (!IsVisibleForPainting(aBuilder) && aBuilder->IsForPainting())) {
     269           0 :     return;
     270             :   }
     271          34 :   DisplayOutline(aBuilder, aLists);
     272          34 :   aLists.Content()->AppendNewToTop(
     273          68 :     new (aBuilder) nsDisplaySVGGeometry(aBuilder, this));
     274             : }
     275             : 
     276             : //----------------------------------------------------------------------
     277             : // nsSVGDisplayableFrame methods
     278             : 
     279             : void
     280          34 : SVGGeometryFrame::PaintSVG(gfxContext& aContext,
     281             :                            const gfxMatrix& aTransform,
     282             :                            imgDrawingParams& aImgParams,
     283             :                            const nsIntRect* aDirtyRect)
     284             : {
     285          34 :   if (!StyleVisibility()->IsVisible())
     286           0 :     return;
     287             : 
     288             :   // Matrix to the geometry's user space:
     289             :   gfxMatrix newMatrix =
     290          34 :     aContext.CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers();
     291          34 :   if (newMatrix.IsSingular()) {
     292           0 :     return;
     293             :   }
     294             : 
     295          34 :   uint32_t paintOrder = StyleSVG()->mPaintOrder;
     296             : 
     297          34 :   if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
     298          34 :     Render(&aContext, eRenderFill | eRenderStroke, newMatrix, aImgParams);
     299          34 :     PaintMarkers(aContext, aTransform, aImgParams);
     300             :   } else {
     301           0 :     while (paintOrder) {
     302             :       uint32_t component =
     303           0 :         paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
     304           0 :       switch (component) {
     305             :         case NS_STYLE_PAINT_ORDER_FILL:
     306           0 :           Render(&aContext, eRenderFill, newMatrix, aImgParams);
     307           0 :           break;
     308             :         case NS_STYLE_PAINT_ORDER_STROKE:
     309           0 :           Render(&aContext, eRenderStroke, newMatrix, aImgParams);
     310           0 :           break;
     311             :         case NS_STYLE_PAINT_ORDER_MARKERS:
     312           0 :           PaintMarkers(aContext, aTransform, aImgParams);
     313           0 :           break;
     314             :       }
     315           0 :       paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
     316             :     }
     317             :   }
     318             : }
     319             : 
     320             : nsIFrame*
     321           0 : SVGGeometryFrame::GetFrameForPoint(const gfxPoint& aPoint)
     322             : {
     323             :   FillRule fillRule;
     324             :   uint16_t hitTestFlags;
     325           0 :   if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
     326           0 :     hitTestFlags = SVG_HIT_TEST_FILL;
     327           0 :     fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mClipRule);
     328             :   } else {
     329           0 :     hitTestFlags = GetHitTestFlags();
     330           0 :     if (!hitTestFlags) {
     331           0 :       return nullptr;
     332             :     }
     333           0 :     if (hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) {
     334             :       gfxRect rect =
     335           0 :         nsLayoutUtils::RectToGfxRect(mRect, PresContext()->AppUnitsPerCSSPixel());
     336           0 :       if (!rect.Contains(aPoint)) {
     337           0 :         return nullptr;
     338             :       }
     339             :     }
     340           0 :     fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mFillRule);
     341             :   }
     342             : 
     343           0 :   bool isHit = false;
     344             : 
     345             :   SVGGeometryElement* content =
     346           0 :     static_cast<SVGGeometryElement*>(mContent);
     347             : 
     348             :   // Using ScreenReferenceDrawTarget() opens us to Moz2D backend specific hit-
     349             :   // testing bugs. Maybe we should use a BackendType::CAIRO DT for hit-testing
     350             :   // so that we get more consistent/backwards compatible results?
     351             :   RefPtr<DrawTarget> drawTarget =
     352           0 :     gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
     353           0 :   RefPtr<Path> path = content->GetOrBuildPath(*drawTarget, fillRule);
     354           0 :   if (!path) {
     355           0 :     return nullptr; // no path, so we don't paint anything that can be hit
     356             :   }
     357             : 
     358           0 :   if (hitTestFlags & SVG_HIT_TEST_FILL) {
     359           0 :     isHit = path->ContainsPoint(ToPoint(aPoint), Matrix());
     360             :   }
     361           0 :   if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) {
     362           0 :     Point point = ToPoint(aPoint);
     363           0 :     SVGContentUtils::AutoStrokeOptions stroke;
     364           0 :     SVGContentUtils::GetStrokeOptions(&stroke, content, StyleContext(), nullptr);
     365           0 :     gfxMatrix userToOuterSVG;
     366           0 :     if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
     367             :       // We need to transform the path back into the appropriate ancestor
     368             :       // coordinate system in order for non-scaled stroke to be correct.
     369             :       // Naturally we also need to transform the point into the same
     370             :       // coordinate system in order to hit-test against the path.
     371           0 :       point = ToMatrix(userToOuterSVG).TransformPoint(point);
     372             :       RefPtr<PathBuilder> builder =
     373           0 :         path->TransformedCopyToBuilder(ToMatrix(userToOuterSVG), fillRule);
     374           0 :       path = builder->Finish();
     375             :     }
     376           0 :     isHit = path->StrokeContainsPoint(stroke, point, Matrix());
     377             :   }
     378             : 
     379           0 :   if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
     380           0 :     return this;
     381             : 
     382           0 :   return nullptr;
     383             : }
     384             : 
     385             : void
     386          82 : SVGGeometryFrame::ReflowSVG()
     387             : {
     388          82 :   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
     389             :                "This call is probably a wasteful mistake");
     390             : 
     391          82 :   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
     392             :              "ReflowSVG mechanism not designed for this");
     393             : 
     394          82 :   if (!nsSVGUtils::NeedsReflowSVG(this)) {
     395           0 :     return;
     396             :   }
     397             : 
     398             :   uint32_t flags = nsSVGUtils::eBBoxIncludeFill |
     399             :                    nsSVGUtils::eBBoxIncludeStroke |
     400          82 :                    nsSVGUtils::eBBoxIncludeMarkers;
     401             :   // Our "visual" overflow rect needs to be valid for building display lists
     402             :   // for hit testing, which means that for certain values of 'pointer-events'
     403             :   // it needs to include the geometry of the fill or stroke even when the fill/
     404             :   // stroke don't actually render (e.g. when stroke="none" or
     405             :   // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'.
     406          82 :   uint16_t hitTestFlags = GetHitTestFlags();
     407          82 :   if ((hitTestFlags & SVG_HIT_TEST_FILL)) {
     408          78 :    flags |= nsSVGUtils::eBBoxIncludeFillGeometry;
     409             :   }
     410          82 :   if ((hitTestFlags & SVG_HIT_TEST_STROKE)) {
     411           4 :    flags |= nsSVGUtils::eBBoxIncludeStrokeGeometry;
     412             :   }
     413             : 
     414          82 :   gfxRect extent = GetBBoxContribution(Matrix(), flags).ToThebesRect();
     415          82 :   mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
     416          82 :             PresContext()->AppUnitsPerCSSPixel());
     417             : 
     418          82 :   if (mState & NS_FRAME_FIRST_REFLOW) {
     419             :     // Make sure we have our filter property (if any) before calling
     420             :     // FinishAndStoreOverflow (subsequent filter changes are handled off
     421             :     // nsChangeHint_UpdateEffects):
     422          48 :     nsSVGEffects::UpdateEffects(this);
     423             :   }
     424             : 
     425         164 :   nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
     426         164 :   nsOverflowAreas overflowAreas(overflow, overflow);
     427          82 :   FinishAndStoreOverflow(overflowAreas, mRect.Size());
     428             : 
     429             :   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
     430          82 :               NS_FRAME_HAS_DIRTY_CHILDREN);
     431             : 
     432             :   // Invalidate, but only if this is not our first reflow (since if it is our
     433             :   // first reflow then we haven't had our first paint yet).
     434          82 :   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     435          34 :     InvalidateFrame();
     436             :   }
     437             : }
     438             : 
     439             : void
     440          50 : SVGGeometryFrame::NotifySVGChanged(uint32_t aFlags)
     441             : {
     442          50 :   MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
     443             :              "Invalidation logic may need adjusting");
     444             : 
     445             :   // Changes to our ancestors may affect how we render when we are rendered as
     446             :   // part of our ancestor (specifically, if our coordinate context changes size
     447             :   // and we have percentage lengths defining our geometry, then we need to be
     448             :   // reflowed). However, ancestor changes cannot affect how we render when we
     449             :   // are rendered as part of any rendering observers that we may have.
     450             :   // Therefore no need to notify rendering observers here.
     451             : 
     452             :   // Don't try to be too smart trying to avoid the ScheduleReflowSVG calls
     453             :   // for the stroke properties examined below. Checking HasStroke() is not
     454             :   // enough, since what we care about is whether we include the stroke in our
     455             :   // overflow rects or not, and we sometimes deliberately include stroke
     456             :   // when it's not visible. See the complexities of GetBBoxContribution.
     457             : 
     458          50 :   if (aFlags & COORD_CONTEXT_CHANGED) {
     459             :     // Stroke currently contributes to our mRect, which is why we have to take
     460             :     // account of stroke-width here. Note that we do not need to take account
     461             :     // of stroke-dashoffset since, although that can have a percentage value
     462             :     // that is resolved against our coordinate context, it does not affect our
     463             :     // mRect.
     464          34 :     if (static_cast<SVGGeometryElement*>(mContent)->GeometryDependsOnCoordCtx() ||
     465          17 :         StyleSVG()->mStrokeWidth.HasPercent()) {
     466           0 :       static_cast<SVGGeometryElement*>(mContent)->ClearAnyCachedPath();
     467           0 :       nsSVGUtils::ScheduleReflowSVG(this);
     468             :     }
     469             :   }
     470             : 
     471          50 :   if ((aFlags & TRANSFORM_CHANGED) && StyleSVGReset()->HasNonScalingStroke()) {
     472             :     // Stroke currently contributes to our mRect, and our stroke depends on
     473             :     // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
     474           0 :     nsSVGUtils::ScheduleReflowSVG(this);
     475             :   }
     476          50 : }
     477             : 
     478             : SVGBBox
     479          82 : SVGGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
     480             :                                       uint32_t aFlags)
     481             : {
     482          82 :   SVGBBox bbox;
     483             : 
     484          82 :   if (aToBBoxUserspace.IsSingular()) {
     485             :     // XXX ReportToConsole
     486           0 :     return bbox;
     487             :   }
     488             : 
     489          82 :   if ((aFlags & nsSVGUtils::eForGetClientRects) &&
     490           0 :       aToBBoxUserspace.PreservesAxisAlignedRectangles()) {
     491           0 :     Rect rect = NSRectToRect(mRect, PresContext()->AppUnitsPerCSSPixel());
     492           0 :     bbox = aToBBoxUserspace.TransformBounds(rect);
     493           0 :     return bbox;
     494             :   }
     495             : 
     496             :   SVGGeometryElement* element =
     497          82 :     static_cast<SVGGeometryElement*>(mContent);
     498             : 
     499         164 :   bool getFill = (aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
     500           8 :                  ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
     501          86 :                   StyleSVG()->mFill.Type() != eStyleSVGPaintType_None);
     502             : 
     503         164 :   bool getStroke = (aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
     504         156 :                    ((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
     505         160 :                     nsSVGUtils::HasStroke(this));
     506             : 
     507         164 :   SVGContentUtils::AutoStrokeOptions strokeOptions;
     508          82 :   if (getStroke) {
     509           4 :     SVGContentUtils::GetStrokeOptions(&strokeOptions, element,
     510             :                                       StyleContext(), nullptr,
     511           4 :                                       SVGContentUtils::eIgnoreStrokeDashing);
     512             :   } else {
     513             :     // Override the default line width of 1.f so that when we call
     514             :     // GetGeometryBounds below the result doesn't include stroke bounds.
     515          78 :     strokeOptions.mLineWidth = 0.f;
     516             :   }
     517             : 
     518          82 :   Rect simpleBounds;
     519          82 :   bool gotSimpleBounds = false;
     520          82 :   gfxMatrix userToOuterSVG;
     521          86 :   if (getStroke &&
     522           4 :       nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
     523           0 :     Matrix moz2dUserToOuterSVG = ToMatrix(userToOuterSVG);
     524           0 :     if (moz2dUserToOuterSVG.IsSingular()) {
     525           0 :       return bbox;
     526             :     }
     527             :     gotSimpleBounds = element->GetGeometryBounds(&simpleBounds,
     528             :                                                  strokeOptions,
     529             :                                                  aToBBoxUserspace,
     530           0 :                                                  &moz2dUserToOuterSVG);
     531             :   } else {
     532             :     gotSimpleBounds = element->GetGeometryBounds(&simpleBounds,
     533             :                                                  strokeOptions,
     534          82 :                                                  aToBBoxUserspace);
     535             :   }
     536             : 
     537          82 :   if (gotSimpleBounds) {
     538          42 :     bbox = simpleBounds;
     539             :   } else {
     540             :     // Get the bounds using a Moz2D Path object (more expensive):
     541          80 :     RefPtr<DrawTarget> tmpDT;
     542             : #ifdef XP_WIN
     543             :     // Unfortunately D2D backed DrawTarget produces bounds with rounding errors
     544             :     // when whole number results are expected, even in the case of trivial
     545             :     // calculations. To avoid that and meet the expectations of web content we
     546             :     // have to use a CAIRO DrawTarget. The most efficient way to do that is to
     547             :     // wrap the cached cairo_surface_t from ScreenReferenceSurface():
     548             :     RefPtr<gfxASurface> refSurf =
     549             :       gfxPlatform::GetPlatform()->ScreenReferenceSurface();
     550             :     tmpDT = gfxPlatform::GetPlatform()->
     551             :       CreateDrawTargetForSurface(refSurf, IntSize(1, 1));
     552             : #else
     553          40 :     tmpDT = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
     554             : #endif
     555             : 
     556          40 :     FillRule fillRule = nsSVGUtils::ToFillRule(StyleSVG()->mFillRule);
     557          80 :     RefPtr<Path> pathInUserSpace = element->GetOrBuildPath(*tmpDT, fillRule);
     558          40 :     if (!pathInUserSpace) {
     559           0 :       return bbox;
     560             :     }
     561          80 :     RefPtr<Path> pathInBBoxSpace;
     562          40 :     if (aToBBoxUserspace.IsIdentity()) {
     563          40 :       pathInBBoxSpace = pathInUserSpace;
     564             :     } else {
     565             :       RefPtr<PathBuilder> builder =
     566           0 :         pathInUserSpace->TransformedCopyToBuilder(aToBBoxUserspace, fillRule);
     567           0 :       pathInBBoxSpace = builder->Finish();
     568           0 :       if (!pathInBBoxSpace) {
     569           0 :         return bbox;
     570             :       }
     571             :     }
     572             : 
     573             :     // Be careful when replacing the following logic to get the fill and stroke
     574             :     // extents independently (instead of computing the stroke extents from the
     575             :     // path extents). You may think that you can just use the stroke extents if
     576             :     // there is both a fill and a stroke. In reality it's necessary to
     577             :     // calculate both the fill and stroke extents, and take the union of the
     578             :     // two. There are two reasons for this:
     579             :     //
     580             :     // # Due to stroke dashing, in certain cases the fill extents could
     581             :     //   actually extend outside the stroke extents.
     582             :     // # If the stroke is very thin, cairo won't paint any stroke, and so the
     583             :     //   stroke bounds that it will return will be empty.
     584             : 
     585          40 :     Rect pathBBoxExtents = pathInBBoxSpace->GetBounds();
     586          40 :     if (!pathBBoxExtents.IsFinite()) {
     587             :       // This can happen in the case that we only have a move-to command in the
     588             :       // path commands, in which case we know nothing gets rendered.
     589           0 :       return bbox;
     590             :     }
     591             : 
     592             :     // Account for fill:
     593          40 :     if (getFill) {
     594          40 :       bbox = pathBBoxExtents;
     595             :     }
     596             : 
     597             :     // Account for stroke:
     598          40 :     if (getStroke) {
     599             : #if 0
     600             :       // This disabled code is how we would calculate the stroke bounds using
     601             :       // Moz2D Path::GetStrokedBounds(). Unfortunately at the time of writing
     602             :       // it there are two problems that prevent us from using it.
     603             :       //
     604             :       // First, it seems that some of the Moz2D backends are really dumb. Not
     605             :       // only do some GetStrokeOptions() implementations sometimes
     606             :       // significantly overestimate the stroke bounds, but if an argument is
     607             :       // passed for the aTransform parameter then they just return bounds-of-
     608             :       // transformed-bounds.  These two things combined can lead the bounds to
     609             :       // be unacceptably oversized, leading to massive over-invalidation.
     610             :       //
     611             :       // Second, the way we account for non-scaling-stroke by transforming the
     612             :       // path using the transform to the outer-<svg> element is not compatible
     613             :       // with the way that SVGGeometryFrame::Reflow() inserts a scale
     614             :       // into aToBBoxUserspace and then scales the bounds that we return.
     615             :       SVGContentUtils::AutoStrokeOptions strokeOptions;
     616             :       SVGContentUtils::GetStrokeOptions(&strokeOptions, element,
     617             :                                         StyleContext(), nullptr,
     618             :                                         SVGContentUtils::eIgnoreStrokeDashing);
     619             :       Rect strokeBBoxExtents;
     620             :       gfxMatrix userToOuterSVG;
     621             :       if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
     622             :         Matrix outerSVGToUser = ToMatrix(userToOuterSVG);
     623             :         outerSVGToUser.Invert();
     624             :         Matrix outerSVGToBBox = aToBBoxUserspace * outerSVGToUser;
     625             :         RefPtr<PathBuilder> builder =
     626             :           pathInUserSpace->TransformedCopyToBuilder(ToMatrix(userToOuterSVG));
     627             :         RefPtr<Path> pathInOuterSVGSpace = builder->Finish();
     628             :         strokeBBoxExtents =
     629             :           pathInOuterSVGSpace->GetStrokedBounds(strokeOptions, outerSVGToBBox);
     630             :       } else {
     631             :         strokeBBoxExtents =
     632             :           pathInUserSpace->GetStrokedBounds(strokeOptions, aToBBoxUserspace);
     633             :       }
     634             :       MOZ_ASSERT(strokeBBoxExtents.IsFinite(), "bbox is about to go bad");
     635             :       bbox.UnionEdges(strokeBBoxExtents);
     636             : #else
     637             :     // For now we just use nsSVGUtils::PathExtentsToMaxStrokeExtents:
     638             :       gfxRect strokeBBoxExtents =
     639           0 :         nsSVGUtils::PathExtentsToMaxStrokeExtents(ThebesRect(pathBBoxExtents),
     640             :                                                   this,
     641           0 :                                                   ThebesMatrix(aToBBoxUserspace));
     642           0 :       MOZ_ASSERT(ToRect(strokeBBoxExtents).IsFinite(), "bbox is about to go bad");
     643           0 :       bbox.UnionEdges(strokeBBoxExtents);
     644             : #endif
     645             :     }
     646             :   }
     647             : 
     648             :   // Account for markers:
     649         164 :   if ((aFlags & nsSVGUtils::eBBoxIncludeMarkers) != 0 &&
     650          82 :       static_cast<SVGGeometryElement*>(mContent)->IsMarkable()) {
     651             : 
     652          64 :     float strokeWidth = nsSVGUtils::GetStrokeWidth(this);
     653          64 :     MarkerProperties properties = GetMarkerProperties(this);
     654             : 
     655          64 :     if (properties.MarkersExist()) {
     656           0 :       nsTArray<nsSVGMark> marks;
     657           0 :       static_cast<SVGGeometryElement*>(mContent)->GetMarkPoints(&marks);
     658           0 :       uint32_t num = marks.Length();
     659             : 
     660             :       // These are in the same order as the nsSVGMark::Type constants.
     661             :       nsSVGMarkerFrame* markerFrames[] = {
     662           0 :         properties.GetMarkerStartFrame(),
     663           0 :         properties.GetMarkerMidFrame(),
     664           0 :         properties.GetMarkerEndFrame(),
     665           0 :       };
     666             :       static_assert(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount,
     667             :                     "Number of Marker frames should be equal to eTypeCount");
     668             : 
     669           0 :       for (uint32_t i = 0; i < num; i++) {
     670           0 :         const nsSVGMark& mark = marks[i];
     671           0 :         nsSVGMarkerFrame* frame = markerFrames[mark.type];
     672           0 :         if (frame) {
     673             :           SVGBBox mbbox =
     674             :             frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
     675           0 :                                            mark, strokeWidth);
     676           0 :           MOZ_ASSERT(mbbox.IsFinite(), "bbox is about to go bad");
     677           0 :           bbox.UnionEdges(mbbox);
     678             :         }
     679             :       }
     680             :     }
     681             :   }
     682             : 
     683          82 :   return bbox;
     684             : }
     685             : 
     686             : //----------------------------------------------------------------------
     687             : // SVGGeometryFrame methods:
     688             : 
     689             : gfxMatrix
     690           0 : SVGGeometryFrame::GetCanvasTM()
     691             : {
     692           0 :   NS_ASSERTION(GetParent(), "null parent");
     693             : 
     694           0 :   nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
     695           0 :   SVGGraphicsElement *content = static_cast<SVGGraphicsElement*>(mContent);
     696             : 
     697           0 :   return content->PrependLocalTransformsTo(parent->GetCanvasTM());
     698             : }
     699             : 
     700             : SVGGeometryFrame::MarkerProperties
     701          90 : SVGGeometryFrame::GetMarkerProperties(SVGGeometryFrame *aFrame)
     702             : {
     703          90 :   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
     704             : 
     705             :   MarkerProperties result;
     706             :   nsCOMPtr<nsIURI> markerURL =
     707         180 :     nsSVGEffects::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerStart);
     708          90 :   result.mMarkerStart =
     709          90 :     nsSVGEffects::GetMarkerProperty(markerURL, aFrame,
     710             :                                     nsSVGEffects::MarkerBeginProperty());
     711             : 
     712          90 :   markerURL = nsSVGEffects::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid);
     713          90 :   result.mMarkerMid =
     714          90 :     nsSVGEffects::GetMarkerProperty(markerURL, aFrame,
     715             :                                     nsSVGEffects::MarkerMiddleProperty());
     716             : 
     717          90 :   markerURL = nsSVGEffects::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd);
     718          90 :   result.mMarkerEnd =
     719          90 :     nsSVGEffects::GetMarkerProperty(markerURL, aFrame,
     720             :                                     nsSVGEffects::MarkerEndProperty());
     721         180 :   return result;
     722             : }
     723             : 
     724             : nsSVGMarkerFrame *
     725           0 : SVGGeometryFrame::MarkerProperties::GetMarkerStartFrame()
     726             : {
     727           0 :   if (!mMarkerStart)
     728           0 :     return nullptr;
     729             :   return static_cast<nsSVGMarkerFrame*>(
     730           0 :     mMarkerStart->GetReferencedFrame(LayoutFrameType::SVGMarker, nullptr));
     731             : }
     732             : 
     733             : nsSVGMarkerFrame *
     734           0 : SVGGeometryFrame::MarkerProperties::GetMarkerMidFrame()
     735             : {
     736           0 :   if (!mMarkerMid)
     737           0 :     return nullptr;
     738             :   return static_cast<nsSVGMarkerFrame*>(
     739           0 :     mMarkerMid->GetReferencedFrame(LayoutFrameType::SVGMarker, nullptr));
     740             : }
     741             : 
     742             : nsSVGMarkerFrame *
     743           0 : SVGGeometryFrame::MarkerProperties::GetMarkerEndFrame()
     744             : {
     745           0 :   if (!mMarkerEnd)
     746           0 :     return nullptr;
     747             :   return static_cast<nsSVGMarkerFrame*>(
     748           0 :     mMarkerEnd->GetReferencedFrame(LayoutFrameType::SVGMarker, nullptr));
     749             : }
     750             : 
     751             : void
     752          34 : SVGGeometryFrame::Render(gfxContext* aContext,
     753             :                          uint32_t aRenderComponents,
     754             :                          const gfxMatrix& aNewTransform,
     755             :                          imgDrawingParams& aImgParams)
     756             : {
     757          34 :   MOZ_ASSERT(!aNewTransform.IsSingular());
     758             : 
     759          34 :   DrawTarget* drawTarget = aContext->GetDrawTarget();
     760             : 
     761             :   FillRule fillRule =
     762          68 :     nsSVGUtils::ToFillRule((GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) ?
     763          68 :                              StyleSVG()->mClipRule : StyleSVG()->mFillRule);
     764             : 
     765             :   SVGGeometryElement* element =
     766          34 :     static_cast<SVGGeometryElement*>(mContent);
     767             : 
     768             :   AntialiasMode aaMode =
     769          68 :     (StyleSVG()->mShapeRendering == NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED ||
     770          34 :      StyleSVG()->mShapeRendering == NS_STYLE_SHAPE_RENDERING_CRISPEDGES) ?
     771          34 :     AntialiasMode::NONE : AntialiasMode::SUBPIXEL;
     772             : 
     773             :   // We wait as late as possible before setting the transform so that we don't
     774             :   // set it unnecessarily if we return early (it's an expensive operation for
     775             :   // some backends).
     776          68 :   gfxContextMatrixAutoSaveRestore autoRestoreTransform(aContext);
     777          34 :   aContext->SetMatrix(aNewTransform);
     778             : 
     779          34 :   if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
     780             :     // We don't complicate this code with GetAsSimplePath since the cost of
     781             :     // masking will dwarf Path creation overhead anyway.
     782           0 :     RefPtr<Path> path = element->GetOrBuildPath(*drawTarget, fillRule);
     783           0 :     if (path) {
     784           0 :       ColorPattern white(ToDeviceColor(Color(1.0f, 1.0f, 1.0f, 1.0f)));
     785             :       drawTarget->Fill(path, white,
     786           0 :                        DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
     787             :     }
     788           0 :     return;
     789             :   }
     790             : 
     791          34 :   SVGGeometryElement::SimplePath simplePath;
     792          68 :   RefPtr<Path> path;
     793             : 
     794          34 :   element->GetAsSimplePath(&simplePath);
     795          34 :   if (!simplePath.IsPath()) {
     796          30 :     path = element->GetOrBuildPath(*drawTarget, fillRule);
     797          30 :     if (!path) {
     798           0 :       return;
     799             :     }
     800             :   }
     801             : 
     802          34 :   SVGContextPaint* contextPaint = SVGContextPaint::GetContextPaint(mContent);
     803             : 
     804          34 :   if (aRenderComponents & eRenderFill) {
     805          68 :     GeneralPattern fillPattern;
     806          34 :     nsSVGUtils::MakeFillPatternFor(this, aContext, &fillPattern, aImgParams,
     807          34 :                                    contextPaint);
     808             : 
     809          34 :     if (fillPattern.GetPattern()) {
     810          32 :       DrawOptions drawOptions(1.0f, CompositionOp::OP_OVER, aaMode);
     811          32 :       if (simplePath.IsRect()) {
     812           4 :         drawTarget->FillRect(simplePath.AsRect(), fillPattern, drawOptions);
     813          28 :       } else if (path) {
     814          28 :         drawTarget->Fill(path, fillPattern, drawOptions);
     815             :       }
     816             :     }
     817             :   }
     818             : 
     819          68 :   if ((aRenderComponents & eRenderStroke) &&
     820          34 :       nsSVGUtils::HasStroke(this, contextPaint)) {
     821             :     // Account for vector-effect:non-scaling-stroke:
     822           2 :     gfxMatrix userToOuterSVG;
     823           2 :     if (nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) {
     824             :       // A simple Rect can't be transformed with rotate/skew, so let's switch
     825             :       // to using a real path:
     826           0 :       if (!path) {
     827           0 :         path = element->GetOrBuildPath(*drawTarget, fillRule);
     828           0 :         if (!path) {
     829           0 :           return;
     830             :         }
     831           0 :         simplePath.Reset();
     832             :       }
     833             :       // We need to transform the path back into the appropriate ancestor
     834             :       // coordinate system, and paint it it that coordinate system, in order
     835             :       // for non-scaled stroke to paint correctly.
     836           0 :       gfxMatrix outerSVGToUser = userToOuterSVG;
     837           0 :       outerSVGToUser.Invert();
     838           0 :       aContext->Multiply(outerSVGToUser);
     839             :       RefPtr<PathBuilder> builder =
     840           0 :         path->TransformedCopyToBuilder(ToMatrix(userToOuterSVG), fillRule);
     841           0 :       path = builder->Finish();
     842             :     }
     843           4 :     GeneralPattern strokePattern;
     844           2 :     nsSVGUtils::MakeStrokePatternFor(this, aContext, &strokePattern,
     845           2 :                                      aImgParams, contextPaint);
     846             : 
     847           2 :     if (strokePattern.GetPattern()) {
     848           4 :       SVGContentUtils::AutoStrokeOptions strokeOptions;
     849           2 :       SVGContentUtils::GetStrokeOptions(&strokeOptions,
     850           2 :                                         static_cast<nsSVGElement*>(mContent),
     851           2 :                                         StyleContext(), contextPaint);
     852             :       // GetStrokeOptions may set the line width to zero as an optimization
     853           2 :       if (strokeOptions.mLineWidth <= 0) {
     854           0 :         return;
     855             :       }
     856           2 :       DrawOptions drawOptions(1.0f, CompositionOp::OP_OVER, aaMode);
     857           2 :       if (simplePath.IsRect()) {
     858           0 :         drawTarget->StrokeRect(simplePath.AsRect(), strokePattern,
     859           0 :                                strokeOptions, drawOptions);
     860           2 :       } else if (simplePath.IsLine()) {
     861           0 :         drawTarget->StrokeLine(simplePath.Point1(), simplePath.Point2(),
     862           0 :                                strokePattern, strokeOptions, drawOptions);
     863             :       } else {
     864           2 :         drawTarget->Stroke(path, strokePattern, strokeOptions, drawOptions);
     865             :       }
     866             :     }
     867             :   }
     868             : }
     869             : 
     870             : void
     871          34 : SVGGeometryFrame::PaintMarkers(gfxContext& aContext,
     872             :                                const gfxMatrix& aTransform,
     873             :                                imgDrawingParams& aImgParams)
     874             : {
     875          34 :   SVGContextPaint* contextPaint = SVGContextPaint::GetContextPaint(mContent);
     876          34 :   if (static_cast<SVGGeometryElement*>(mContent)->IsMarkable()) {
     877          26 :     MarkerProperties properties = GetMarkerProperties(this);
     878             : 
     879          26 :     if (properties.MarkersExist()) {
     880           0 :       float strokeWidth = nsSVGUtils::GetStrokeWidth(this, contextPaint);
     881             : 
     882           0 :       nsTArray<nsSVGMark> marks;
     883             :       static_cast<SVGGeometryElement*>
     884           0 :                  (mContent)->GetMarkPoints(&marks);
     885             : 
     886           0 :       uint32_t num = marks.Length();
     887           0 :       if (num) {
     888             :         // These are in the same order as the nsSVGMark::Type constants.
     889             :         nsSVGMarkerFrame* markerFrames[] = {
     890           0 :           properties.GetMarkerStartFrame(),
     891           0 :           properties.GetMarkerMidFrame(),
     892           0 :           properties.GetMarkerEndFrame(),
     893           0 :         };
     894             :         static_assert(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount,
     895             :                       "Number of Marker frames should be equal to eTypeCount");
     896             : 
     897           0 :         for (uint32_t i = 0; i < num; i++) {
     898           0 :           const nsSVGMark& mark = marks[i];
     899           0 :           nsSVGMarkerFrame* frame = markerFrames[mark.type];
     900           0 :           if (frame) {
     901             :             frame->PaintMark(aContext, aTransform, this, mark, strokeWidth,
     902           0 :                              aImgParams);
     903             :           }
     904             :         }
     905             :       }
     906             :     }
     907             :   }
     908          34 : }
     909             : 
     910             : uint16_t
     911          82 : SVGGeometryFrame::GetHitTestFlags()
     912             : {
     913          82 :   return nsSVGUtils::GetGeometryHitTestFlags(this);
     914             : }
     915             : } // namespace mozilla

Generated by: LCOV version 1.13