Coverage Report

Created: 2018-09-25 14:53

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