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