LCOV - code coverage report
Current view: top level - layout/svg - nsSVGIntegrationUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 205 491 41.8 %
Date: 2017-07-14 16:53:18 Functions: 22 38 57.9 %
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 "nsSVGIntegrationUtils.h"
       8             : 
       9             : // Keep others in (case-insensitive) order:
      10             : #include "gfxDrawable.h"
      11             : #include "gfxPrefs.h"
      12             : #include "nsCSSAnonBoxes.h"
      13             : #include "nsCSSClipPathInstance.h"
      14             : #include "nsDisplayList.h"
      15             : #include "nsFilterInstance.h"
      16             : #include "nsLayoutUtils.h"
      17             : #include "gfxContext.h"
      18             : #include "nsSVGClipPathFrame.h"
      19             : #include "nsSVGEffects.h"
      20             : #include "nsSVGElement.h"
      21             : #include "nsSVGFilterPaintCallback.h"
      22             : #include "nsSVGMaskFrame.h"
      23             : #include "nsSVGPaintServerFrame.h"
      24             : #include "nsSVGUtils.h"
      25             : #include "FrameLayerBuilder.h"
      26             : #include "BasicLayers.h"
      27             : #include "mozilla/gfx/Point.h"
      28             : #include "nsCSSRendering.h"
      29             : #include "mozilla/Unused.h"
      30             : #include "mozilla/GeckoRestyleManager.h"
      31             : 
      32             : using namespace mozilla;
      33             : using namespace mozilla::layers;
      34             : using namespace mozilla::gfx;
      35             : using namespace mozilla::image;
      36             : 
      37             : // ----------------------------------------------------------------------
      38             : 
      39             : /**
      40             :  * This class is used to get the pre-effects visual overflow rect of a frame,
      41             :  * or, in the case of a frame with continuations, to collect the union of the
      42             :  * pre-effects visual overflow rects of all the continuations. The result is
      43             :  * relative to the origin (top left corner of the border box) of the frame, or,
      44             :  * if the frame has continuations, the origin of the  _first_ continuation.
      45             :  */
      46           5 : class PreEffectsVisualOverflowCollector : public nsLayoutUtils::BoxCallback
      47             : {
      48             : public:
      49             :   /**
      50             :    * If the pre-effects visual overflow rect of the frame being examined
      51             :    * happens to be known, it can be passed in as aCurrentFrame and its
      52             :    * pre-effects visual overflow rect can be passed in as
      53             :    * aCurrentFrameOverflowArea. This is just an optimization to save a
      54             :    * frame property lookup - these arguments are optional.
      55             :    */
      56           5 :   PreEffectsVisualOverflowCollector(nsIFrame* aFirstContinuation,
      57             :                                     nsIFrame* aCurrentFrame,
      58             :                                     const nsRect& aCurrentFrameOverflowArea,
      59             :                                     bool aInReflow)
      60           5 :     : mFirstContinuation(aFirstContinuation)
      61             :     , mCurrentFrame(aCurrentFrame)
      62             :     , mCurrentFrameOverflowArea(aCurrentFrameOverflowArea)
      63           5 :     , mInReflow(aInReflow)
      64             :   {
      65           5 :     NS_ASSERTION(!mFirstContinuation->GetPrevContinuation(),
      66             :                  "We want the first continuation here");
      67           5 :   }
      68             : 
      69           5 :   virtual void AddBox(nsIFrame* aFrame) override {
      70           5 :     nsRect overflow = (aFrame == mCurrentFrame)
      71             :       ? mCurrentFrameOverflowArea
      72          10 :       : GetPreEffectsVisualOverflowRect(aFrame, mInReflow);
      73           5 :     mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mFirstContinuation));
      74           5 :   }
      75             : 
      76           5 :   nsRect GetResult() const {
      77           5 :     return mResult;
      78             :   }
      79             : 
      80             : private:
      81             : 
      82           5 :   static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame,
      83             :                                                 bool aInReflow) {
      84           5 :     nsRect* r = aFrame->GetProperty(nsIFrame::PreEffectsBBoxProperty());
      85           5 :     if (r) {
      86           1 :       return *r;
      87             :     }
      88             : 
      89             : #ifdef DEBUG
      90             :     // Having PreTransformOverflowAreasProperty cached means
      91             :     // GetVisualOverflowRect() will return post-effect rect, which is not what
      92             :     // we want. This function intentional reports pre-effect rect. But it does
      93             :     // not matter if there is no SVG effect on this frame, since no effect
      94             :     // means post-effect rect matches pre-effect rect.
      95             :     //
      96             :     // This function may be called during reflow or painting. We should only
      97             :     // do this check in painting process since the PreEffectsBBoxProperty of
      98             :     // continuations are not set correctly while reflowing.
      99           4 :     if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame) && !aInReflow) {
     100             :       nsOverflowAreas* preTransformOverflows =
     101           4 :         aFrame->GetProperty(aFrame->PreTransformOverflowAreasProperty());
     102             : 
     103           4 :       MOZ_ASSERT(!preTransformOverflows,
     104             :                  "GetVisualOverflowRect() won't return the pre-effects rect!");
     105             :     }
     106             : #endif
     107           4 :     return aFrame->GetVisualOverflowRect();
     108             :   }
     109             : 
     110             :   nsIFrame*     mFirstContinuation;
     111             :   nsIFrame*     mCurrentFrame;
     112             :   const nsRect& mCurrentFrameOverflowArea;
     113             :   nsRect        mResult;
     114             :   bool          mInReflow;
     115             : };
     116             : 
     117             : /**
     118             :  * Gets the union of the pre-effects visual overflow rects of all of a frame's
     119             :  * continuations, in "user space".
     120             :  */
     121             : static nsRect
     122           5 : GetPreEffectsVisualOverflowUnion(nsIFrame* aFirstContinuation,
     123             :                                  nsIFrame* aCurrentFrame,
     124             :                                  const nsRect& aCurrentFramePreEffectsOverflow,
     125             :                                  const nsPoint& aFirstContinuationToUserSpace,
     126             :                                  bool aInReflow)
     127             : {
     128           5 :   NS_ASSERTION(!aFirstContinuation->GetPrevContinuation(),
     129             :                "Need first continuation here");
     130             :   PreEffectsVisualOverflowCollector collector(aFirstContinuation,
     131             :                                               aCurrentFrame,
     132             :                                               aCurrentFramePreEffectsOverflow,
     133          10 :                                               aInReflow);
     134             :   // Compute union of all overflow areas relative to aFirstContinuation:
     135           5 :   nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation, &collector);
     136             :   // Return the result in user space:
     137          10 :   return collector.GetResult() + aFirstContinuationToUserSpace;
     138             : }
     139             : 
     140             : 
     141             : bool
     142        2699 : nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
     143             : {
     144             :   // Even when SVG display lists are disabled, returning true for SVG frames
     145             :   // does not adversely affect any of our callers. Therefore we don't bother
     146             :   // checking the SDL prefs here, since we don't know if we're being called for
     147             :   // painting or hit-testing anyway.
     148        2699 :   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
     149        5398 :   return aFrame->StyleEffects()->HasFilters() ||
     150        5396 :          style->HasClipPath() || style->HasMask();
     151             : }
     152             : 
     153             : bool
     154         618 : nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(const nsIFrame* aFrame)
     155             : {
     156         618 :   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
     157         618 :   return style->HasClipPath() || style->HasMask();
     158             : }
     159             : 
     160             : nsPoint
     161          13 : nsSVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame* aFrame)
     162             : {
     163          13 :   if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
     164             :     // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
     165             :     // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
     166             :     // not what we want. SVG frames are always in user space, so they have
     167             :     // no offset adjustment to make.
     168           2 :     return nsPoint();
     169             :   }
     170             : 
     171             :   // The GetAllInFlowRectsUnion() call gets the union of the frame border-box
     172             :   // rects over all continuations, relative to the origin (top-left of the
     173             :   // border box) of its second argument (here, aFrame, the first continuation).
     174          11 :   return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame).TopLeft();
     175             : }
     176             : 
     177             : /* static */ nsSize
     178           0 : nsSVGIntegrationUtils::GetContinuationUnionSize(nsIFrame* aNonSVGFrame)
     179             : {
     180           0 :   NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
     181             :                "SVG frames should not get here");
     182             :   nsIFrame* firstFrame =
     183           0 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
     184           0 :   return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame).Size();
     185             : }
     186             : 
     187             : /* static */ gfx::Size
     188           0 : nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(nsIFrame* aNonSVGFrame)
     189             : {
     190           0 :   NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
     191             :                "SVG frames should not get here");
     192             :   nsIFrame* firstFrame =
     193           0 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
     194           0 :   nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame);
     195           0 :   nsPresContext* presContext = firstFrame->PresContext();
     196           0 :   return gfx::Size(presContext->AppUnitsToFloatCSSPixels(r.width),
     197           0 :                    presContext->AppUnitsToFloatCSSPixels(r.height));
     198             : }
     199             : 
     200             : gfxRect
     201           5 : nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
     202             : {
     203             :   // Except for nsSVGOuterSVGFrame, we shouldn't be getting here with SVG
     204             :   // frames at all. This function is for elements that are laid out using the
     205             :   // CSS box model rules.
     206           5 :   NS_ASSERTION(!(aNonSVGFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
     207             :                "Frames with SVG layout should not get here");
     208           5 :   MOZ_ASSERT(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG) ||
     209             :              aNonSVGFrame->IsSVGOuterSVGFrame());
     210             : 
     211             :   nsIFrame* firstFrame =
     212           5 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
     213             :   // 'r' is in "user space":
     214          10 :   nsRect r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(),
     215          10 :                                               GetOffsetToBoundingBox(firstFrame),
     216          10 :                                               false);
     217             :   return nsLayoutUtils::RectToGfxRect(r,
     218          10 :            aNonSVGFrame->PresContext()->AppUnitsPerCSSPixel());
     219             : }
     220             : 
     221             : // XXX Since we're called during reflow, this method is broken for frames with
     222             : // continuations. When we're called for a frame with continuations, we're
     223             : // called for each continuation in turn as it's reflowed. However, it isn't
     224             : // until the last continuation is reflowed that this method's
     225             : // GetOffsetToBoundingBox() and GetPreEffectsVisualOverflowUnion() calls will
     226             : // obtain valid border boxes for all the continuations. As a result, we'll
     227             : // end up returning bogus post-filter visual overflow rects for all the prior
     228             : // continuations. Unfortunately, by the time the last continuation is
     229             : // reflowed, it's too late to go back and set and propagate the overflow
     230             : // rects on the previous continuations.
     231             : //
     232             : // The reason that we need to pass an override bbox to
     233             : // GetPreEffectsVisualOverflowUnion rather than just letting it call into our
     234             : // GetSVGBBoxForNonSVGFrame method is because we get called by
     235             : // ComputeEffectsRect when it has been called with
     236             : // aStoreRectProperties set to false. In this case the pre-effects visual
     237             : // overflow rect that it has been passed may be different to that stored on
     238             : // aFrame, resulting in a different bbox.
     239             : //
     240             : // XXXjwatt The pre-effects visual overflow rect passed to
     241             : // ComputeEffectsRect won't include continuation overflows, so
     242             : // for frames with continuation the following filter analysis will likely end
     243             : // up being carried out with a bbox created as if the frame didn't have
     244             : // continuations.
     245             : //
     246             : // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right
     247             : // for SVG frames, since for SVG frames the SVG spec defines the bbox to be
     248             : // something quite different to the pre-effects visual overflow rect. However,
     249             : // we're essentially calculating an invalidation area here, and using the
     250             : // pre-effects overflow rect will actually overestimate that area which, while
     251             : // being a bit wasteful, isn't otherwise a problem.
     252             : //
     253             : nsRect
     254           1 :   nsSVGIntegrationUtils::
     255             :     ComputePostEffectsVisualOverflowRect(nsIFrame* aFrame,
     256             :                                          const nsRect& aPreEffectsOverflowRect)
     257             : {
     258           1 :   NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
     259             :                  "Don't call this on SVG child frames");
     260             : 
     261             :   nsIFrame* firstFrame =
     262           1 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
     263             :   nsSVGEffects::EffectProperties effectProperties =
     264           1 :     nsSVGEffects::GetEffectProperties(firstFrame);
     265           1 :   if (!effectProperties.HasValidFilter()) {
     266           1 :     return aPreEffectsOverflowRect;
     267             :   }
     268             : 
     269             :   // Create an override bbox - see comment above:
     270           0 :   nsPoint firstFrameToBoundingBox = GetOffsetToBoundingBox(firstFrame);
     271             :   // overrideBBox is in "user space", in _CSS_ pixels:
     272             :   // XXX Why are we rounding out to pixel boundaries? We don't do that in
     273             :   // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
     274             :   gfxRect overrideBBox =
     275             :     nsLayoutUtils::RectToGfxRect(
     276           0 :       GetPreEffectsVisualOverflowUnion(firstFrame, aFrame,
     277             :                                        aPreEffectsOverflowRect,
     278             :                                        firstFrameToBoundingBox,
     279             :                                        true),
     280           0 :       aFrame->PresContext()->AppUnitsPerCSSPixel());
     281           0 :   overrideBBox.RoundOut();
     282             : 
     283             :   nsRect overflowRect =
     284           0 :     nsFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox);
     285             : 
     286             :   // Return overflowRect relative to aFrame, rather than "user space":
     287           0 :   return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToBoundingBox);
     288             : }
     289             : 
     290             : nsIntRegion
     291           7 : nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
     292             :                                                       const nsPoint& aToReferenceFrame,
     293             :                                                       const nsIntRegion& aInvalidRegion)
     294             : {
     295           7 :   if (aInvalidRegion.IsEmpty()) {
     296           3 :     return nsIntRect();
     297             :   }
     298             : 
     299             :   // Don't bother calling GetEffectProperties; the filter property should
     300             :   // already have been set up during reflow/ComputeFrameEffectsRect
     301             :   nsIFrame* firstFrame =
     302           4 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
     303           4 :   nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
     304           4 :   if (!prop || !prop->IsInObserverLists()) {
     305           4 :     return aInvalidRegion;
     306             :   }
     307             : 
     308           0 :   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
     309             : 
     310           0 :   if (!prop || !prop->ReferencesValidResources()) {
     311             :     // The frame is either not there or not currently available,
     312             :     // perhaps because we're in the middle of tearing stuff down.
     313             :     // Be conservative, return our visual overflow rect relative
     314             :     // to the reference frame.
     315           0 :     nsRect overflow = aFrame->GetVisualOverflowRect() + aToReferenceFrame;
     316           0 :     return overflow.ToOutsidePixels(appUnitsPerDevPixel);
     317             :   }
     318             : 
     319             :   // Convert aInvalidRegion into bounding box frame space in app units:
     320             :   nsPoint toBoundingBox =
     321           0 :     aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
     322             :   // The initial rect was relative to the reference frame, so we need to
     323             :   // remove that offset to get a rect relative to the current frame.
     324           0 :   toBoundingBox -= aToReferenceFrame;
     325           0 :   nsRegion preEffectsRegion = aInvalidRegion.ToAppUnits(appUnitsPerDevPixel).MovedBy(toBoundingBox);
     326             : 
     327             :   // Adjust the dirty area for effects, and shift it back to being relative to
     328             :   // the reference frame.
     329           0 :   nsRegion result = nsFilterInstance::GetPostFilterDirtyArea(firstFrame,
     330           0 :     preEffectsRegion).MovedBy(-toBoundingBox);
     331             :   // Return the result, in pixels relative to the reference frame.
     332           0 :   return result.ToOutsidePixels(appUnitsPerDevPixel);
     333             : }
     334             : 
     335             : nsRect
     336           7 : nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
     337             :                                                        const nsRect& aDirtyRect)
     338             : {
     339             :   // Don't bother calling GetEffectProperties; the filter property should
     340             :   // already have been set up during reflow/ComputeFrameEffectsRect
     341             :   nsIFrame* firstFrame =
     342           7 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
     343           7 :   nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
     344           7 :   if (!prop || !prop->ReferencesValidResources()) {
     345           7 :     return aDirtyRect;
     346             :   }
     347             : 
     348             :   // Convert aDirtyRect into "user space" in app units:
     349             :   nsPoint toUserSpace =
     350           0 :     aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
     351           0 :   nsRect postEffectsRect = aDirtyRect + toUserSpace;
     352             : 
     353             :   // Return ther result, relative to aFrame, not in user space:
     354           0 :   return nsFilterInstance::GetPreFilterNeededArea(firstFrame, postEffectsRect).GetBounds()
     355           0 :     - toUserSpace;
     356             : }
     357             : 
     358             : bool
     359           0 : nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
     360             : {
     361             :   nsIFrame* firstFrame =
     362           0 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
     363             :   // Convert aPt to user space:
     364           0 :   nsPoint toUserSpace;
     365           0 :   if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
     366             :     // XXXmstange Isn't this wrong for svg:use and innerSVG frames?
     367           0 :     toUserSpace = aFrame->GetPosition();
     368             :   } else {
     369           0 :     toUserSpace =
     370           0 :       aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
     371             :   }
     372           0 :   nsPoint pt = aPt + toUserSpace;
     373             :   gfxPoint userSpacePt =
     374           0 :     gfxPoint(pt.x, pt.y) / aFrame->PresContext()->AppUnitsPerCSSPixel();
     375           0 :   return nsSVGUtils::HitTestClip(firstFrame, userSpacePt);
     376             : }
     377             : 
     378             : class RegularFramePaintCallback : public nsSVGFilterPaintCallback
     379             : {
     380             : public:
     381           0 :   RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
     382             :                             LayerManager* aManager,
     383             :                             const gfxPoint& aUserSpaceToFrameSpaceOffset)
     384           0 :     : mBuilder(aBuilder), mLayerManager(aManager),
     385           0 :       mUserSpaceToFrameSpaceOffset(aUserSpaceToFrameSpaceOffset) {}
     386             : 
     387           0 :   virtual void Paint(gfxContext& aContext, nsIFrame *aTarget,
     388             :                      const gfxMatrix& aTransform,
     389             :                      const nsIntRect* aDirtyRect,
     390             :                      imgDrawingParams& aImgParams) override
     391             :   {
     392           0 :     BasicLayerManager* basic = mLayerManager->AsBasicLayerManager();
     393           0 :     RefPtr<gfxContext> oldCtx = basic->GetTarget();
     394           0 :     basic->SetTarget(&aContext);
     395             : 
     396           0 :     gfxContextMatrixAutoSaveRestore autoSR(&aContext);
     397           0 :     aContext.SetMatrix(aContext.CurrentMatrix().PreTranslate(-mUserSpaceToFrameSpaceOffset));
     398             : 
     399           0 :     mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder);
     400           0 :     basic->SetTarget(oldCtx);
     401           0 :   }
     402             : 
     403             : private:
     404             :   nsDisplayListBuilder* mBuilder;
     405             :   LayerManager* mLayerManager;
     406             :   gfxPoint mUserSpaceToFrameSpaceOffset;
     407             : };
     408             : 
     409             : typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
     410             : 
     411             : /**
     412             :  * Paint css-positioned-mask onto a given target(aMaskDT).
     413             :  */
     414             : static void
     415           3 : PaintMaskSurface(const PaintFramesParams& aParams,
     416             :                  DrawTarget* aMaskDT, float aOpacity, nsStyleContext* aSC,
     417             :                  const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
     418             :                  const gfxMatrix& aMaskSurfaceMatrix,
     419             :                  const nsPoint& aOffsetToUserSpace)
     420             : {
     421           3 :   MOZ_ASSERT(aMaskFrames.Length() > 0);
     422           3 :   MOZ_ASSERT(aMaskDT->GetFormat() == SurfaceFormat::A8);
     423           3 :   MOZ_ASSERT(aOpacity == 1.0 || aMaskFrames.Length() == 1);
     424             : 
     425           3 :   const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
     426             :   gfxMatrix cssPxToDevPxMatrix =
     427           3 :     nsSVGUtils::GetCSSPxToDevPxMatrix(aParams.frame);
     428             : 
     429           3 :   nsPresContext* presContext = aParams.frame->PresContext();
     430             :   gfxPoint devPixelOffsetToUserSpace =
     431             :     nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
     432           3 :                                    presContext->AppUnitsPerDevPixel());
     433             : 
     434           6 :   RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(aMaskDT);
     435           3 :   MOZ_ASSERT(maskContext);
     436           3 :   maskContext->SetMatrix(aMaskSurfaceMatrix);
     437             : 
     438             :   // Multiple SVG masks interleave with image mask. Paint each layer onto
     439             :   // aMaskDT one at a time.
     440           6 :   for (int i = aMaskFrames.Length() - 1; i >= 0 ; i--) {
     441           3 :     nsSVGMaskFrame *maskFrame = aMaskFrames[i];
     442           3 :     CompositionOp compositionOp = (i == int(aMaskFrames.Length() - 1))
     443           3 :       ? CompositionOp::OP_OVER
     444           3 :       : nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite);
     445             : 
     446             :     // maskFrame != nullptr means we get a SVG mask.
     447             :     // maskFrame == nullptr means we get an image mask.
     448           3 :     if (maskFrame) {
     449           0 :       Matrix svgMaskMatrix;
     450           0 :       nsSVGMaskFrame::MaskParams params(maskContext, aParams.frame,
     451             :                                         cssPxToDevPxMatrix,
     452             :                                         aOpacity, &svgMaskMatrix,
     453           0 :                                         svgReset->mMask.mLayers[i].mMaskMode,
     454           0 :                                         aParams.imgParams);
     455           0 :       RefPtr<SourceSurface> svgMask = maskFrame->GetMaskForMaskedFrame(params);
     456           0 :       if (svgMask) {
     457           0 :         gfxContextMatrixAutoSaveRestore matRestore(maskContext);
     458             : 
     459           0 :         maskContext->Multiply(ThebesMatrix(svgMaskMatrix));
     460           0 :         aMaskDT->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), svgMask,
     461             :                              Point(0, 0),
     462           0 :                              DrawOptions(1.0, compositionOp));
     463             :       }
     464             :     } else {
     465           6 :       gfxContextMatrixAutoSaveRestore matRestore(maskContext);
     466             : 
     467           3 :       maskContext->Multiply(gfxMatrix::Translation(-devPixelOffsetToUserSpace));
     468             :       nsCSSRendering::PaintBGParams  params =
     469             :         nsCSSRendering::PaintBGParams::ForSingleLayer(*presContext,
     470             :                                                       aParams.dirtyRect,
     471             :                                                       aParams.borderArea,
     472           3 :                                                       aParams.frame,
     473           3 :                                                       aParams.builder->GetBackgroundPaintFlags() |
     474             :                                                       nsCSSRendering::PAINTBG_MASK_IMAGE,
     475             :                                                       i, compositionOp,
     476           9 :                                                       aOpacity);
     477             : 
     478           3 :       aParams.imgParams.result &=
     479             :         nsCSSRendering::PaintStyleImageLayerWithSC(params, *maskContext, aSC,
     480           6 :                                               *aParams.frame->StyleBorder());
     481             :     }
     482             :   }
     483           3 : }
     484             : 
     485           3 : struct MaskPaintResult {
     486             :   RefPtr<SourceSurface> maskSurface;
     487             :   Matrix maskTransform;
     488             :   bool transparentBlackMask;
     489             :   bool opacityApplied;
     490             : 
     491           3 :   MaskPaintResult()
     492           3 :     : transparentBlackMask(false)
     493           3 :     , opacityApplied(false)
     494           3 :   {}
     495             : };
     496             : 
     497             : static MaskPaintResult
     498           3 : CreateAndPaintMaskSurface(const PaintFramesParams& aParams,
     499             :                           float aOpacity, nsStyleContext* aSC,
     500             :                           const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
     501             :                           const nsPoint& aOffsetToUserSpace)
     502             : {
     503           3 :   const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
     504           3 :   MOZ_ASSERT(aMaskFrames.Length() > 0);
     505           3 :   MaskPaintResult paintResult;
     506             : 
     507           3 :   gfxContext& ctx = aParams.ctx;
     508             : 
     509             :   // Optimization for single SVG mask.
     510           3 :   if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) {
     511             :     gfxMatrix cssPxToDevPxMatrix =
     512           0 :       nsSVGUtils::GetCSSPxToDevPxMatrix(aParams.frame);
     513           0 :     paintResult.opacityApplied = true;
     514           0 :     nsSVGMaskFrame::MaskParams params(&ctx, aParams.frame, cssPxToDevPxMatrix,
     515             :                                       aOpacity, &paintResult.maskTransform,
     516           0 :                                       svgReset->mMask.mLayers[0].mMaskMode,
     517           0 :                                       aParams.imgParams);
     518             :     paintResult.maskSurface =
     519           0 :       aMaskFrames[0]->GetMaskForMaskedFrame(params);
     520             : 
     521           0 :     if (!paintResult.maskSurface) {
     522           0 :       paintResult.transparentBlackMask = true;
     523             :     }
     524             : 
     525           0 :     return paintResult;
     526             :   }
     527             : 
     528           3 :   const IntRect& maskSurfaceRect = aParams.maskRect;
     529           3 :   if (maskSurfaceRect.IsEmpty()) {
     530           0 :     paintResult.transparentBlackMask = true;
     531           0 :     return paintResult;
     532             :   }
     533             : 
     534             :   RefPtr<DrawTarget> maskDT =
     535           9 :       ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(),
     536           9 :                                                    SurfaceFormat::A8);
     537           3 :   if (!maskDT || !maskDT->IsValid()) {
     538           0 :     return paintResult;
     539             :   }
     540             : 
     541             :   // We can paint mask along with opacity only if
     542             :   // 1. There is only one mask, or
     543             :   // 2. No overlap among masks.
     544             :   // Collision detect in #2 is not that trivial, we only accept #1 here.
     545           3 :   paintResult.opacityApplied = (aMaskFrames.Length() == 1);
     546             : 
     547             :   // Set context's matrix on maskContext, offset by the maskSurfaceRect's
     548             :   // position. This makes sure that we combine the masks in device space.
     549             :   gfxMatrix maskSurfaceMatrix =
     550           3 :     ctx.CurrentMatrix() * gfxMatrix::Translation(-aParams.maskRect.TopLeft());
     551             : 
     552           3 :   PaintMaskSurface(aParams, maskDT,
     553           3 :                    paintResult.opacityApplied ? aOpacity : 1.0,
     554             :                    aSC, aMaskFrames, maskSurfaceMatrix,
     555           3 :                    aOffsetToUserSpace);
     556             : 
     557           3 :   if (aParams.imgParams.result != DrawResult::SUCCESS) {
     558             :     // Now we know the status of mask resource since we used it while painting.
     559             :     // According to the return value of PaintMaskSurface, we know whether mask
     560             :     // resource is resolvable or not.
     561             :     //
     562             :     // For a HTML doc:
     563             :     //   According to css-masking spec, always create a mask surface when
     564             :     //   we have any item in maskFrame even if all of those items are
     565             :     //   non-resolvable <mask-sources> or <images>.
     566             :     //   Set paintResult.transparentBlackMask as true,  the caller should stop
     567             :     //   painting masked content as if this mask is a transparent black one.
     568             :     // For a SVG doc:
     569             :     //   SVG 1.1 say that if we fail to resolve a mask, we should draw the
     570             :     //   object unmasked.
     571             :     //   Left patinResult.maskSurface empty, the caller should paint all
     572             :     //   masked content as if this mask is an opaque white one(no mask).
     573           0 :     paintResult.transparentBlackMask =
     574           0 :       !(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
     575             : 
     576           0 :     MOZ_ASSERT(!paintResult.maskSurface);
     577           0 :     return paintResult;
     578             :   }
     579             : 
     580           3 :   paintResult.maskTransform = ToMatrix(maskSurfaceMatrix);
     581           3 :   if (!paintResult.maskTransform.Invert()) {
     582           0 :     return paintResult;
     583             :   }
     584             : 
     585           3 :   paintResult.maskSurface = maskDT->Snapshot();
     586           3 :   return paintResult;
     587             : }
     588             : 
     589             : static bool
     590           5 : ValidateSVGFrame(nsIFrame* aFrame)
     591             : {
     592             : #ifdef DEBUG
     593           5 :   NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
     594             :                (NS_SVGDisplayListPaintingEnabled() &&
     595             :                 !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
     596             :                "Should not use nsSVGIntegrationUtils on this SVG frame");
     597             : #endif
     598             : 
     599           5 :   bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
     600           5 :   if (hasSVGLayout) {
     601             : #ifdef DEBUG
     602           2 :     nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
     603           2 :     MOZ_ASSERT(svgFrame && aFrame->GetContent()->IsSVGElement(),
     604             :                "A non-SVG frame carries NS_FRAME_SVG_LAYOUT flag?");
     605             : #endif
     606             : 
     607           2 :     const nsIContent* content = aFrame->GetContent();
     608           2 :     if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
     609             :       // The SVG spec says not to draw _anything_
     610           0 :       return false;
     611             :     }
     612             :   }
     613             : 
     614           5 :   return true;
     615             : }
     616             : 
     617           5 : struct EffectOffsets {
     618             :   // The offset between the reference frame and the bounding box of the
     619             :   // target frame in app unit.
     620             :   nsPoint  offsetToBoundingBox;
     621             :   // The offset between the reference frame and the bounding box of the
     622             :   // target frame in device unit.
     623             :   gfxPoint offsetToBoundingBoxInDevPx;
     624             :   // The offset between the reference frame and the bounding box of the
     625             :   // target frame in app unit.
     626             :   nsPoint  offsetToUserSpace;
     627             :   // The offset between the reference frame and the bounding box of the
     628             :   // target frame in device unit.
     629             :   gfxPoint offsetToUserSpaceInDevPx;
     630             : };
     631             : 
     632             : EffectOffsets
     633           5 : ComputeEffectOffset(nsIFrame* aFrame, const PaintFramesParams& aParams)
     634             : {
     635           5 :   EffectOffsets result;
     636             : 
     637           5 :   result.offsetToBoundingBox =
     638          10 :     aParams.builder->ToReferenceFrame(aFrame) -
     639          10 :     nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
     640           5 :   if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
     641             :     /* Snap the offset if the reference frame is not a SVG frame,
     642             :      * since other frames will be snapped to pixel when rendering. */
     643           3 :     result.offsetToBoundingBox =
     644           6 :       nsPoint(
     645             :         aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.x),
     646             :         aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.y));
     647             :   }
     648             : 
     649             :   // After applying only "aOffsetToBoundingBox", aParams.ctx would have its
     650             :   // origin at the top left corner of frame's bounding box (over all
     651             :   // continuations).
     652             :   // However, SVG painting needs the origin to be located at the origin of the
     653             :   // SVG frame's "user space", i.e. the space in which, for example, the
     654             :   // frame's BBox lives.
     655             :   // SVG geometry frames and foreignObject frames apply their own offsets, so
     656             :   // their position is relative to their user space. So for these frame types,
     657             :   // if we want aParams.ctx to be in user space, we first need to subtract the
     658             :   // frame's position so that SVG painting can later add it again and the
     659             :   // frame is painted in the right place.
     660           5 :   gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
     661             :   nsPoint toUserSpace =
     662           5 :     nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
     663          10 :             nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
     664             : 
     665           5 :   result.offsetToUserSpace = result.offsetToBoundingBox - toUserSpace;
     666             : 
     667             : #ifdef DEBUG
     668           5 :   bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
     669           5 :   NS_ASSERTION(hasSVGLayout ||
     670             :                result.offsetToBoundingBox == result.offsetToUserSpace,
     671             :                "For non-SVG frames there shouldn't be any additional offset");
     672             : #endif
     673             : 
     674             :   result.offsetToUserSpaceInDevPx =
     675             :     nsLayoutUtils::PointToGfxPoint(result.offsetToUserSpace,
     676           5 :                                    aFrame->PresContext()->AppUnitsPerDevPixel());
     677             :   result.offsetToBoundingBoxInDevPx =
     678             :     nsLayoutUtils::PointToGfxPoint(result.offsetToBoundingBox,
     679           5 :                                    aFrame->PresContext()->AppUnitsPerDevPixel());
     680             : 
     681           5 :   return result;
     682             : }
     683             : 
     684             : /**
     685             :  * Setup transform matrix of a gfx context by a specific frame. Move the
     686             :  * origin of aParams.ctx to the user space of aFrame.
     687             :  */
     688             : static EffectOffsets
     689           5 : MoveContextOriginToUserSpace(nsIFrame* aFrame, const PaintFramesParams& aParams)
     690             : {
     691           5 :   EffectOffsets offset = ComputeEffectOffset(aFrame, aParams);
     692             : 
     693           5 :   aParams.ctx.SetMatrix(
     694          10 :     aParams.ctx.CurrentMatrix().PreTranslate(offset.offsetToUserSpaceInDevPx));
     695             : 
     696           5 :   return offset;
     697             : }
     698             : 
     699             : bool
     700           0 : nsSVGIntegrationUtils::IsMaskResourceReady(nsIFrame* aFrame)
     701             : {
     702             :   nsIFrame* firstFrame =
     703           0 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
     704             :   nsSVGEffects::EffectProperties effectProperties =
     705           0 :     nsSVGEffects::GetEffectProperties(firstFrame);
     706           0 :   nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
     707           0 :   const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
     708             : 
     709           0 :   for (uint32_t i = 0; i < maskFrames.Length(); i++) {
     710             :     // Refers to a valid SVG mask.
     711           0 :     if (maskFrames[i]) {
     712           0 :       continue;
     713             :     }
     714             : 
     715             :     // Refers to an external resource, which is not ready yet.
     716           0 :     if (!svgReset->mMask.mLayers[i].mImage.IsComplete()) {
     717           0 :       return false;
     718             :     }
     719             :   }
     720             : 
     721             :   // Either all mask resources are ready, or no mask resource is needed.
     722           0 :   return true;
     723             : }
     724             : 
     725             : class AutoPopGroup
     726             : {
     727             : public:
     728           0 :   AutoPopGroup() : mContext(nullptr) { }
     729             : 
     730           0 :   ~AutoPopGroup() {
     731           0 :     if (mContext) {
     732           0 :       mContext->PopGroupAndBlend();
     733             :     }
     734           0 :   }
     735             : 
     736           0 :   void SetContext(gfxContext* aContext) {
     737           0 :     mContext = aContext;
     738           0 :   }
     739             : 
     740             : private:
     741             :   gfxContext* mContext;
     742             : };
     743             : 
     744             : void
     745           0 : nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
     746             : {
     747           0 :   nsSVGUtils::MaskUsage maskUsage;
     748           0 :   nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
     749           0 :                                  maskUsage);
     750             : 
     751           0 :   nsIFrame* frame = aParams.frame;
     752           0 :   if (!ValidateSVGFrame(frame)) {
     753           0 :     return;
     754             :   }
     755             : 
     756           0 :   gfxContext& ctx = aParams.ctx;
     757             :   nsIFrame* firstFrame =
     758           0 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
     759             :   nsSVGEffects::EffectProperties effectProperties =
     760           0 :     nsSVGEffects::GetEffectProperties(firstFrame);
     761             : 
     762           0 :   RefPtr<DrawTarget> maskTarget = ctx.GetDrawTarget();
     763             : 
     764           0 :   if (maskUsage.shouldGenerateMaskLayer &&
     765           0 :       maskUsage.shouldGenerateClipMaskLayer) {
     766             :     // We will paint both mask of positioned mask and clip-path into
     767             :     // maskTarget.
     768             :     //
     769             :     // Create one extra draw target for drawing positioned mask, so that we do
     770             :     // not have to copy the content of maskTarget before painting
     771             :     // clip-path into it.
     772           0 :     maskTarget = maskTarget->CreateSimilarDrawTarget(maskTarget->GetSize(),
     773           0 :                                                      SurfaceFormat::A8);
     774             :   }
     775             : 
     776           0 :   nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
     777           0 :   AutoPopGroup autoPop;
     778           0 :   bool shouldPushOpacity = (maskUsage.opacity != 1.0) &&
     779           0 :                            (maskFrames.Length() != 1);
     780           0 :   if (shouldPushOpacity) {
     781           0 :     ctx.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, maskUsage.opacity);
     782           0 :     autoPop.SetContext(&ctx);
     783             :   }
     784             : 
     785           0 :   gfxContextMatrixAutoSaveRestore matSR;
     786             : 
     787             :   // Paint clip-path-basic-shape onto ctx
     788           0 :   gfxContextAutoSaveRestore basicShapeSR;
     789           0 :   if (maskUsage.shouldApplyBasicShape) {
     790           0 :     matSR.SetContext(&ctx);
     791             : 
     792           0 :     MoveContextOriginToUserSpace(firstFrame, aParams);
     793             : 
     794           0 :     basicShapeSR.SetContext(&ctx);
     795           0 :     nsCSSClipPathInstance::ApplyBasicShapeClip(ctx, frame);
     796           0 :     if (!maskUsage.shouldGenerateMaskLayer) {
     797             :       // Only have basic-shape clip-path effect. Fill clipped region by
     798             :       // opaque white.
     799           0 :       ctx.SetColor(Color(1.0, 1.0, 1.0, 1.0));
     800           0 :       ctx.Fill();
     801             : 
     802           0 :       return;
     803             :     }
     804             :   }
     805             : 
     806             :   // Paint mask onto ctx.
     807           0 :   if (maskUsage.shouldGenerateMaskLayer) {
     808           0 :     matSR.Restore();
     809           0 :     matSR.SetContext(&ctx);
     810             : 
     811           0 :     EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
     812           0 :     PaintMaskSurface(aParams, maskTarget,
     813             :                      shouldPushOpacity ?  1.0 : maskUsage.opacity,
     814             :                      firstFrame->StyleContext(), maskFrames,
     815           0 :                      ctx.CurrentMatrix(),
     816           0 :                      offsets.offsetToUserSpace);
     817             :   }
     818             : 
     819             :   // Paint clip-path onto ctx.
     820           0 :   if (maskUsage.shouldGenerateClipMaskLayer || maskUsage.shouldApplyClipPath) {
     821           0 :     matSR.Restore();
     822           0 :     matSR.SetContext(&ctx);
     823             : 
     824           0 :     MoveContextOriginToUserSpace(firstFrame, aParams);
     825           0 :     Matrix clipMaskTransform;
     826           0 :     gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
     827             : 
     828           0 :     nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
     829             :     RefPtr<SourceSurface> maskSurface =
     830           0 :       maskUsage.shouldGenerateMaskLayer ? maskTarget->Snapshot() : nullptr;
     831           0 :     clipPathFrame->PaintClipMask(ctx, frame, cssPxToDevPxMatrix,
     832             :                                    &clipMaskTransform, maskSurface,
     833           0 :                                    ToMatrix(ctx.CurrentMatrix()));
     834             :   }
     835             : }
     836             : 
     837             : void
     838           5 : nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
     839             : {
     840           5 :   MOZ_ASSERT(UsingMaskOrClipPathForFrame(aParams.frame),
     841             :              "Should not use this method when no mask or clipPath effect"
     842             :              "on this frame");
     843             : 
     844             :   /* SVG defines the following rendering model:
     845             :    *
     846             :    *  1. Render geometry
     847             :    *  2. Apply filter
     848             :    *  3. Apply clipping, masking, group opacity
     849             :    *
     850             :    * We handle #3 here and perform a couple of optimizations:
     851             :    *
     852             :    * + Use cairo's clipPath when representable natively (single object
     853             :    *   clip region).
     854             :    *
     855             :    * + Merge opacity and masking if both used together.
     856             :    */
     857           5 :   nsIFrame* frame = aParams.frame;
     858           5 :   if (!ValidateSVGFrame(frame)) {
     859           0 :     return;
     860             :   }
     861             : 
     862           5 :   nsSVGUtils::MaskUsage maskUsage;
     863           5 :   nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
     864           5 :                                  maskUsage);
     865             : 
     866           5 :   if (maskUsage.opacity == 0.0f) {
     867           0 :     return;
     868             :   }
     869             : 
     870           5 :   gfxContext& context = aParams.ctx;
     871          10 :   gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);
     872             : 
     873             :   /* Properties are added lazily and may have been removed by a restyle,
     874             :      so make sure all applicable ones are set again. */
     875             :   nsIFrame* firstFrame =
     876           5 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
     877             :   nsSVGEffects::EffectProperties effectProperties =
     878           5 :     nsSVGEffects::GetEffectProperties(firstFrame);
     879             : 
     880           5 :   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
     881             : 
     882           5 :   gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
     883          10 :   nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
     884             : 
     885          10 :   bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
     886          10 :                              maskUsage.shouldGenerateClipMaskLayer ||
     887          10 :                              maskUsage.shouldGenerateMaskLayer);
     888           5 :   bool shouldPushMask = false;
     889             : 
     890             :   /* Check if we need to do additional operations on this child's
     891             :    * rendering, which necessitates rendering into another surface. */
     892           5 :   if (shouldGenerateMask) {
     893           6 :     gfxContextMatrixAutoSaveRestore matSR;
     894             : 
     895           3 :     Matrix maskTransform;
     896           6 :     RefPtr<SourceSurface> maskSurface;
     897           3 :     bool opacityApplied = false;
     898             : 
     899           3 :     if (maskUsage.shouldGenerateMaskLayer) {
     900           3 :       matSR.SetContext(&context);
     901             : 
     902             :       // For css-mask, we want to generate a mask for each continuation frame,
     903             :       // so we setup context matrix by the position of the current frame,
     904             :       // instead of the first continuation frame.
     905           3 :       EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
     906             :       MaskPaintResult paintResult =
     907             :         CreateAndPaintMaskSurface(aParams, maskUsage.opacity,
     908             :                                   firstFrame->StyleContext(),
     909           6 :                                   maskFrames, offsets.offsetToUserSpace);
     910             : 
     911           3 :       if (paintResult.transparentBlackMask) {
     912           0 :         return;
     913             :       }
     914             : 
     915           3 :       maskSurface = paintResult.maskSurface;
     916           3 :       if (maskSurface) {
     917           3 :         shouldPushMask = true;
     918           3 :         maskTransform = paintResult.maskTransform;
     919           3 :         opacityApplied = paintResult.opacityApplied;
     920             :       }
     921             :     }
     922             : 
     923           3 :     if (maskUsage.shouldGenerateClipMaskLayer) {
     924           0 :       matSR.Restore();
     925           0 :       matSR.SetContext(&context);
     926             : 
     927           0 :       MoveContextOriginToUserSpace(firstFrame, aParams);
     928           0 :       Matrix clipMaskTransform;
     929             :       RefPtr<SourceSurface> clipMaskSurface =
     930           0 :         clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
     931             :                                    &clipMaskTransform, maskSurface,
     932           0 :                                   maskTransform);
     933             : 
     934           0 :       if (clipMaskSurface) {
     935           0 :         maskSurface = clipMaskSurface;
     936           0 :         maskTransform = clipMaskTransform;
     937             :       } else {
     938             :         // Either entire surface is clipped out, or gfx buffer allocation
     939             :         // failure in nsSVGClipPathFrame::GetClipMask.
     940           0 :         return;
     941             :       }
     942             : 
     943           0 :       shouldPushMask = true;
     944             :     }
     945             : 
     946             :     // opacity != 1.0f.
     947           6 :     if (!maskUsage.shouldGenerateClipMaskLayer &&
     948           3 :         !maskUsage.shouldGenerateMaskLayer) {
     949           0 :       MOZ_ASSERT(maskUsage.opacity != 1.0f);
     950             : 
     951           0 :       matSR.SetContext(&context);
     952           0 :       MoveContextOriginToUserSpace(firstFrame, aParams);
     953           0 :       shouldPushMask = true;
     954             :     }
     955             : 
     956           3 :     if (shouldPushMask) {
     957           3 :       if (aParams.layerManager->GetRoot()->GetContentFlags() &
     958             :           Layer::CONTENT_COMPONENT_ALPHA) {
     959           3 :         context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
     960             :                                            opacityApplied
     961             :                                              ? 1.0
     962             :                                              : maskUsage.opacity,
     963           3 :                                            maskSurface, maskTransform);
     964             :       } else {
     965           0 :         context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
     966             :                                       opacityApplied ? 1.0 : maskUsage.opacity,
     967           0 :                                       maskSurface, maskTransform);
     968             :       }
     969             :     }
     970             :   }
     971             : 
     972             :   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
     973             :    * we can just do normal painting and get it clipped appropriately.
     974             :    */
     975           5 :   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     976           4 :     gfxContextMatrixAutoSaveRestore matSR(&context);
     977             : 
     978           2 :     MoveContextOriginToUserSpace(firstFrame, aParams);
     979             : 
     980           2 :     MOZ_ASSERT(!maskUsage.shouldApplyClipPath ||
     981             :                !maskUsage.shouldApplyBasicShape);
     982           2 :     if (maskUsage.shouldApplyClipPath) {
     983           2 :       clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
     984             :     } else {
     985           0 :       nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
     986             :     }
     987             :   }
     988             : 
     989             :   /* Paint the child */
     990           5 :   context.SetMatrix(matrixAutoSaveRestore.Matrix());
     991           5 :   BasicLayerManager* basic = aParams.layerManager->AsBasicLayerManager();
     992          10 :   RefPtr<gfxContext> oldCtx = basic->GetTarget();
     993           5 :   basic->SetTarget(&context);
     994          10 :   aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
     995          10 :                                        aParams.builder);
     996           5 :   basic->SetTarget(oldCtx);
     997             : 
     998           5 :   if (gfxPrefs::DrawMaskLayer()) {
     999           0 :     gfxContextAutoSaveRestore saver(&context);
    1000             : 
    1001           0 :     context.NewPath();
    1002             :     gfxRect drawingRect =
    1003             :       nsLayoutUtils::RectToGfxRect(aParams.borderArea,
    1004           0 :                                    frame->PresContext()->AppUnitsPerDevPixel());
    1005           0 :     context.Rectangle(drawingRect, true);
    1006           0 :     Color overlayColor(0.0f, 0.0f, 0.0f, 0.8f);
    1007           0 :     if (maskUsage.shouldGenerateMaskLayer) {
    1008           0 :       overlayColor.r = 1.0f; // red represents css positioned mask.
    1009             :     }
    1010           0 :     if (maskUsage.shouldApplyClipPath ||
    1011           0 :         maskUsage.shouldGenerateClipMaskLayer) {
    1012           0 :       overlayColor.g = 1.0f; // green represents clip-path:<clip-source>.
    1013             :     }
    1014           0 :     if (maskUsage.shouldApplyBasicShape) {
    1015           0 :       overlayColor.b = 1.0f; // blue represents
    1016             :                              // clip-path:<basic-shape>||<geometry-box>.
    1017             :     }
    1018             : 
    1019           0 :     context.SetColor(overlayColor);
    1020           0 :     context.Fill();
    1021             :   }
    1022             : 
    1023           5 :   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
    1024           2 :     context.PopClip();
    1025             :   }
    1026             : 
    1027           5 :   if (shouldPushMask) {
    1028           3 :     context.PopGroupAndBlend();
    1029             :   }
    1030             : 
    1031             : }
    1032             : 
    1033             : void
    1034           0 : nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
    1035             : {
    1036           0 :   MOZ_ASSERT(!aParams.builder->IsForGenerateGlyphMask(),
    1037             :              "Filter effect is discarded while generating glyph mask.");
    1038           0 :   MOZ_ASSERT(aParams.frame->StyleEffects()->HasFilters(),
    1039             :              "Should not use this method when no filter effect on this frame");
    1040             : 
    1041           0 :   nsIFrame* frame = aParams.frame;
    1042           0 :   if (!ValidateSVGFrame(frame)) {
    1043           0 :     return;
    1044             :   }
    1045             : 
    1046           0 :   float opacity = nsSVGUtils::ComputeOpacity(frame, aParams.handleOpacity);
    1047           0 :   if (opacity == 0.0f) {
    1048           0 :     return;
    1049             :   }
    1050             : 
    1051             :   /* Properties are added lazily and may have been removed by a restyle,
    1052             :      so make sure all applicable ones are set again. */
    1053             :   nsIFrame* firstFrame =
    1054           0 :     nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
    1055             :   nsSVGEffects::EffectProperties effectProperties =
    1056           0 :     nsSVGEffects::GetEffectProperties(firstFrame);
    1057             : 
    1058           0 :   if (effectProperties.HasInvalidFilter()) {
    1059           0 :     return;
    1060             :   }
    1061             : 
    1062           0 :   gfxContext& context = aParams.ctx;
    1063             : 
    1064           0 :   gfxContextAutoSaveRestore autoSR(&context);
    1065           0 :   EffectOffsets offsets = MoveContextOriginToUserSpace(firstFrame, aParams);
    1066             : 
    1067           0 :   if (opacity != 1.0f) {
    1068             :     context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
    1069           0 :                                   nullptr, Matrix());
    1070             :   }
    1071             : 
    1072             :   /* Paint the child and apply filters */
    1073           0 :   RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
    1074           0 :                                      offsets.offsetToUserSpaceInDevPx);
    1075           0 :   nsRegion dirtyRegion = aParams.dirtyRect - offsets.offsetToBoundingBox;
    1076           0 :   gfxSize scaleFactors = context.CurrentMatrix().ScaleFactors(true);
    1077             :   gfxMatrix scaleMatrix(scaleFactors.width, 0.0f,
    1078             :                         0.0f, scaleFactors.height,
    1079           0 :                         0.0f, 0.0f);
    1080           0 :   gfxMatrix reverseScaleMatrix = scaleMatrix;
    1081           0 :   DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
    1082           0 :   MOZ_ASSERT(invertible);
    1083           0 :   context.SetMatrix(reverseScaleMatrix * context.CurrentMatrix());
    1084             : 
    1085             :   gfxMatrix tm =
    1086           0 :     scaleMatrix * nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
    1087           0 :   nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
    1088             :                                        tm, &callback, &dirtyRegion,
    1089           0 :                                        aParams.imgParams);
    1090             : 
    1091           0 :   if (opacity != 1.0f) {
    1092           0 :     context.PopGroupAndBlend();
    1093             :   }
    1094             : }
    1095             : 
    1096           0 : class PaintFrameCallback : public gfxDrawingCallback {
    1097             : public:
    1098           0 :   PaintFrameCallback(nsIFrame* aFrame,
    1099             :                      const nsSize aPaintServerSize,
    1100             :                      const IntSize aRenderSize,
    1101             :                      uint32_t aFlags)
    1102           0 :    : mFrame(aFrame)
    1103             :    , mPaintServerSize(aPaintServerSize)
    1104             :    , mRenderSize(aRenderSize)
    1105           0 :    , mFlags (aFlags)
    1106           0 :   {}
    1107             :   virtual bool operator()(gfxContext* aContext,
    1108             :                           const gfxRect& aFillRect,
    1109             :                           const SamplingFilter aSamplingFilter,
    1110             :                           const gfxMatrix& aTransform) override;
    1111             : private:
    1112             :   nsIFrame* mFrame;
    1113             :   nsSize mPaintServerSize;
    1114             :   IntSize mRenderSize;
    1115             :   uint32_t mFlags;
    1116             : };
    1117             : 
    1118             : bool
    1119           0 : PaintFrameCallback::operator()(gfxContext* aContext,
    1120             :                                const gfxRect& aFillRect,
    1121             :                                const SamplingFilter aSamplingFilter,
    1122             :                                const gfxMatrix& aTransform)
    1123             : {
    1124           0 :   if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)
    1125           0 :     return false;
    1126             : 
    1127           0 :   AutoSetRestorePaintServerState paintServer(mFrame);
    1128             : 
    1129           0 :   aContext->Save();
    1130             : 
    1131             :   // Clip to aFillRect so that we don't paint outside.
    1132           0 :   aContext->NewPath();
    1133           0 :   aContext->Rectangle(aFillRect);
    1134           0 :   aContext->Clip();
    1135             : 
    1136           0 :   gfxMatrix invmatrix = aTransform;
    1137           0 :   if (!invmatrix.Invert()) {
    1138           0 :     return false;
    1139             :   }
    1140           0 :   aContext->Multiply(invmatrix);
    1141             : 
    1142             :   // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
    1143             :   // to have it anchored at the top left corner of the bounding box of all of
    1144             :   // mFrame's continuations. So we add a translation transform.
    1145           0 :   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
    1146           0 :   nsPoint offset = nsSVGIntegrationUtils::GetOffsetToBoundingBox(mFrame);
    1147           0 :   gfxPoint devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel;
    1148           0 :   aContext->Multiply(gfxMatrix::Translation(devPxOffset));
    1149             : 
    1150             :   gfxSize paintServerSize =
    1151           0 :     gfxSize(mPaintServerSize.width, mPaintServerSize.height) /
    1152           0 :       mFrame->PresContext()->AppUnitsPerDevPixel();
    1153             : 
    1154             :   // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
    1155             :   // want it to render with mRenderSize, so we need to set up a scale transform.
    1156           0 :   gfxFloat scaleX = mRenderSize.width / paintServerSize.width;
    1157           0 :   gfxFloat scaleY = mRenderSize.height / paintServerSize.height;
    1158           0 :   aContext->Multiply(gfxMatrix::Scaling(scaleX, scaleY));
    1159             : 
    1160             :   // Draw.
    1161           0 :   nsRect dirty(-offset.x, -offset.y,
    1162           0 :                mPaintServerSize.width, mPaintServerSize.height);
    1163             : 
    1164             :   using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
    1165           0 :   PaintFrameFlags flags = PaintFrameFlags::PAINT_IN_TRANSFORM;
    1166           0 :   if (mFlags & nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) {
    1167           0 :     flags |= PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES;
    1168             :   }
    1169           0 :   nsLayoutUtils::PaintFrame(aContext, mFrame,
    1170             :                             dirty, NS_RGBA(0, 0, 0, 0),
    1171             :                             nsDisplayListBuilderMode::PAINTING,
    1172           0 :                             flags);
    1173             : 
    1174           0 :   nsIFrame* currentFrame = mFrame;
    1175           0 :    while ((currentFrame = currentFrame->GetNextContinuation()) != nullptr) {
    1176           0 :     offset = currentFrame->GetOffsetToCrossDoc(mFrame);
    1177           0 :     devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel;
    1178             : 
    1179           0 :     aContext->Save();
    1180           0 :     aContext->Multiply(gfxMatrix::Scaling(1/scaleX, 1/scaleY));
    1181           0 :     aContext->Multiply(gfxMatrix::Translation(devPxOffset));
    1182           0 :     aContext->Multiply(gfxMatrix::Scaling(scaleX, scaleY));
    1183             : 
    1184           0 :     nsLayoutUtils::PaintFrame(aContext, currentFrame,
    1185           0 :                               dirty - offset, NS_RGBA(0, 0, 0, 0),
    1186             :                               nsDisplayListBuilderMode::PAINTING,
    1187           0 :                               flags);
    1188             : 
    1189           0 :     aContext->Restore();
    1190             :   }
    1191             : 
    1192           0 :   aContext->Restore();
    1193             : 
    1194           0 :   return true;
    1195             : }
    1196             : 
    1197             : /* static */ already_AddRefed<gfxDrawable>
    1198           0 : nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame*         aFrame,
    1199             :                                                nsIFrame*         aTarget,
    1200             :                                                const nsSize&     aPaintServerSize,
    1201             :                                                const IntSize& aRenderSize,
    1202             :                                                const DrawTarget* aDrawTarget,
    1203             :                                                const gfxMatrix&  aContextMatrix,
    1204             :                                                uint32_t          aFlags)
    1205             : {
    1206             :   // aPaintServerSize is the size that would be filled when using
    1207             :   // background-repeat:no-repeat and background-size:auto. For normal background
    1208             :   // images, this would be the intrinsic size of the image; for gradients and
    1209             :   // patterns this would be the whole target frame fill area.
    1210             :   // aRenderSize is what we will be actually filling after accounting for
    1211             :   // background-size.
    1212           0 :   if (aFrame->IsFrameOfType(nsIFrame::eSVGPaintServer)) {
    1213             :     // aFrame is either a pattern or a gradient. These fill the whole target
    1214             :     // frame by default, so aPaintServerSize is the whole target background fill
    1215             :     // area.
    1216             :     nsSVGPaintServerFrame* server =
    1217           0 :       static_cast<nsSVGPaintServerFrame*>(aFrame);
    1218             : 
    1219             :     gfxRect overrideBounds(0, 0,
    1220           0 :                            aPaintServerSize.width, aPaintServerSize.height);
    1221           0 :     overrideBounds.Scale(1.0 / aFrame->PresContext()->AppUnitsPerDevPixel());
    1222           0 :     imgDrawingParams imgParams(aFlags);
    1223             :     RefPtr<gfxPattern> pattern =
    1224           0 :       server->GetPaintServerPattern(aTarget, aDrawTarget,
    1225             :                                     aContextMatrix, &nsStyleSVG::mFill, 1.0,
    1226           0 :                                     imgParams, &overrideBounds);
    1227             : 
    1228           0 :     if (!pattern)
    1229           0 :       return nullptr;
    1230             : 
    1231             :     // pattern is now set up to fill aPaintServerSize. But we want it to
    1232             :     // fill aRenderSize, so we need to add a scaling transform.
    1233             :     // We couldn't just have set overrideBounds to aRenderSize - it would have
    1234             :     // worked for gradients, but for patterns it would result in a different
    1235             :     // pattern size.
    1236           0 :     gfxFloat scaleX = overrideBounds.Width() / aRenderSize.width;
    1237           0 :     gfxFloat scaleY = overrideBounds.Height() / aRenderSize.height;
    1238           0 :     gfxMatrix scaleMatrix = gfxMatrix::Scaling(scaleX, scaleY);
    1239           0 :     pattern->SetMatrix(scaleMatrix * pattern->GetMatrix());
    1240             :     RefPtr<gfxDrawable> drawable =
    1241           0 :       new gfxPatternDrawable(pattern, aRenderSize);
    1242           0 :     return drawable.forget();
    1243             :   }
    1244             : 
    1245           0 :   if (aFrame->IsFrameOfType(nsIFrame::eSVG) &&
    1246           0 :       !static_cast<nsSVGDisplayableFrame*>(do_QueryFrame(aFrame))) {
    1247           0 :     MOZ_ASSERT_UNREACHABLE("We should prevent painting of unpaintable SVG "
    1248             :                            "before we get here");
    1249             :     return nullptr;
    1250             :   }
    1251             : 
    1252             :   // We don't want to paint into a surface as long as we don't need to, so we
    1253             :   // set up a drawing callback.
    1254             :   RefPtr<gfxDrawingCallback> cb =
    1255           0 :     new PaintFrameCallback(aFrame, aPaintServerSize, aRenderSize, aFlags);
    1256           0 :   RefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, aRenderSize);
    1257           0 :   return drawable.forget();
    1258             : }

Generated by: LCOV version 1.13