Coverage Report

Created: 2018-09-25 14:53

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