Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/svg/nsSVGPatternFrame.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 "nsSVGPatternFrame.h"
9
10
// Keep others in (case-insensitive) order:
11
#include "AutoReferenceChainGuard.h"
12
#include "gfx2DGlue.h"
13
#include "gfxContext.h"
14
#include "gfxMatrix.h"
15
#include "gfxPattern.h"
16
#include "gfxPlatform.h"
17
#include "mozilla/gfx/2D.h"
18
#include "nsGkAtoms.h"
19
#include "nsSVGDisplayableFrame.h"
20
#include "mozilla/ComputedStyle.h"
21
#include "SVGObserverUtils.h"
22
#include "SVGGeometryFrame.h"
23
#include "mozilla/dom/SVGPatternElement.h"
24
#include "mozilla/dom/SVGUnitTypesBinding.h"
25
#include "nsSVGUtils.h"
26
#include "nsSVGAnimatedTransformList.h"
27
#include "SVGContentUtils.h"
28
29
using namespace mozilla;
30
using namespace mozilla::dom;
31
using namespace mozilla::dom::SVGUnitTypes_Binding;
32
using namespace mozilla::gfx;
33
using namespace mozilla::image;
34
35
//----------------------------------------------------------------------
36
// Implementation
37
38
nsSVGPatternFrame::nsSVGPatternFrame(ComputedStyle* aStyle)
39
  : nsSVGPaintServerFrame(aStyle, kClassID)
40
  , mSource(nullptr)
41
  , mLoopFlag(false)
42
  , mNoHRefURI(false)
43
0
{
44
0
}
45
46
NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame)
47
48
//----------------------------------------------------------------------
49
// nsIFrame methods:
50
51
nsresult
52
nsSVGPatternFrame::AttributeChanged(int32_t         aNameSpaceID,
53
                                    nsAtom*        aAttribute,
54
                                    int32_t         aModType)
55
0
{
56
0
  if (aNameSpaceID == kNameSpaceID_None &&
57
0
      (aAttribute == nsGkAtoms::patternUnits ||
58
0
       aAttribute == nsGkAtoms::patternContentUnits ||
59
0
       aAttribute == nsGkAtoms::patternTransform ||
60
0
       aAttribute == nsGkAtoms::x ||
61
0
       aAttribute == nsGkAtoms::y ||
62
0
       aAttribute == nsGkAtoms::width ||
63
0
       aAttribute == nsGkAtoms::height ||
64
0
       aAttribute == nsGkAtoms::preserveAspectRatio ||
65
0
       aAttribute == nsGkAtoms::viewBox)) {
66
0
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
67
0
  }
68
0
69
0
  if ((aNameSpaceID == kNameSpaceID_XLink ||
70
0
       aNameSpaceID == kNameSpaceID_None) &&
71
0
      aAttribute == nsGkAtoms::href) {
72
0
    // Blow away our reference, if any
73
0
    DeleteProperty(SVGObserverUtils::HrefToTemplateProperty());
74
0
    mNoHRefURI = false;
75
0
    // And update whoever references us
76
0
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
77
0
  }
78
0
79
0
  return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID,
80
0
                                                 aAttribute, aModType);
81
0
}
82
83
#ifdef DEBUG
84
void
85
nsSVGPatternFrame::Init(nsIContent*       aContent,
86
                        nsContainerFrame* aParent,
87
                        nsIFrame*         aPrevInFlow)
88
{
89
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::pattern), "Content is not an SVG pattern");
90
91
  nsSVGPaintServerFrame::Init(aContent, aParent, aPrevInFlow);
92
}
93
#endif /* DEBUG */
94
95
//----------------------------------------------------------------------
96
// nsSVGContainerFrame methods:
97
98
// If our GetCanvasTM is getting called, we
99
// need to return *our current* transformation
100
// matrix, which depends on our units parameters
101
// and X, Y, Width, and Height
102
gfxMatrix
103
nsSVGPatternFrame::GetCanvasTM()
104
0
{
105
0
  if (mCTM) {
106
0
    return *mCTM;
107
0
  }
108
0
109
0
  // Do we know our rendering parent?
110
0
  if (mSource) {
111
0
    // Yes, use it!
112
0
    return mSource->GetCanvasTM();
113
0
  }
114
0
115
0
  // We get here when geometry in the <pattern> container is updated
116
0
  return gfxMatrix();
117
0
}
118
119
// -------------------------------------------------------------------------
120
// Helper functions
121
// -------------------------------------------------------------------------
122
123
/** Calculate the maximum expansion of a matrix */
124
static float
125
MaxExpansion(const Matrix &aMatrix)
126
0
{
127
0
  // maximum expansion derivation from
128
0
  // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
129
0
  // and also implemented in cairo_matrix_transformed_circle_major_axis
130
0
  double a = aMatrix._11;
131
0
  double b = aMatrix._12;
132
0
  double c = aMatrix._21;
133
0
  double d = aMatrix._22;
134
0
  double f = (a * a + b * b + c * c + d * d) / 2;
135
0
  double g = (a * a + b * b - c * c - d * d) / 2;
136
0
  double h = a * c + b * d;
137
0
  return sqrt(f + sqrt(g * g + h * h));
138
0
}
139
140
// The SVG specification says that the 'patternContentUnits' attribute "has no effect if
141
// attribute ‘viewBox’ is specified". We still need to include a bbox scale
142
// if the viewBox is specified and _patternUnits_ is set to or defaults to
143
// objectBoundingBox though, since in that case the viewBox is relative to the bbox
144
static bool
145
IncludeBBoxScale(const nsSVGViewBox& aViewBox,
146
                 uint32_t aPatternContentUnits, uint32_t aPatternUnits)
147
0
{
148
0
  return (!aViewBox.IsExplicitlySet() &&
149
0
          aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) ||
150
0
         (aViewBox.IsExplicitlySet() &&
151
0
          aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
152
0
}
153
154
// Given the matrix for the pattern element's own transform, this returns a
155
// combined matrix including the transforms applicable to its target.
156
static Matrix
157
GetPatternMatrix(uint16_t aPatternUnits,
158
                 const Matrix &patternTransform,
159
                 const gfxRect &bbox,
160
                 const gfxRect &callerBBox,
161
                 const Matrix &callerCTM)
162
0
{
163
0
  // We really want the pattern matrix to handle translations
164
0
  gfxFloat minx = bbox.X();
165
0
  gfxFloat miny = bbox.Y();
166
0
167
0
  if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
168
0
    minx += callerBBox.X();
169
0
    miny += callerBBox.Y();
170
0
  }
171
0
172
0
  float scale = 1.0f / MaxExpansion(callerCTM);
173
0
  Matrix patternMatrix = patternTransform;
174
0
  patternMatrix.PreScale(scale, scale);
175
0
  patternMatrix.PreTranslate(minx, miny);
176
0
177
0
  return patternMatrix;
178
0
}
179
180
static nsresult
181
GetTargetGeometry(gfxRect *aBBox,
182
                  const nsSVGViewBox &aViewBox,
183
                  uint16_t aPatternContentUnits,
184
                  uint16_t aPatternUnits,
185
                  nsIFrame *aTarget,
186
                  const Matrix &aContextMatrix,
187
                  const gfxRect *aOverrideBounds)
188
0
{
189
0
  *aBBox =
190
0
    aOverrideBounds
191
0
      ? *aOverrideBounds
192
0
      : nsSVGUtils::GetBBox(aTarget, nsSVGUtils::eUseFrameBoundsForOuterSVG |
193
0
                                     nsSVGUtils::eBBoxIncludeFillGeometry);
194
0
195
0
  // Sanity check
196
0
  if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
197
0
      (aBBox->Width() <= 0 || aBBox->Height() <= 0)) {
198
0
    return NS_ERROR_FAILURE;
199
0
  }
200
0
201
0
  // OK, now fix up the bounding box to reflect user coordinates
202
0
  // We handle device unit scaling in pattern matrix
203
0
  float scale = MaxExpansion(aContextMatrix);
204
0
  if (scale <= 0) {
205
0
    return NS_ERROR_FAILURE;
206
0
  }
207
0
  aBBox->Scale(scale);
208
0
  return NS_OK;
209
0
}
210
211
already_AddRefed<SourceSurface>
212
nsSVGPatternFrame::PaintPattern(const DrawTarget* aDrawTarget,
213
                                Matrix* patternMatrix,
214
                                const Matrix &aContextMatrix,
215
                                nsIFrame *aSource,
216
                                nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
217
                                float aGraphicOpacity,
218
                                const gfxRect *aOverrideBounds,
219
                                imgDrawingParams& aImgParams)
220
0
{
221
0
  /*
222
0
   * General approach:
223
0
   *    Set the content geometry stuff
224
0
   *    Calculate our bbox (using x,y,width,height & patternUnits &
225
0
   *                        patternTransform)
226
0
   *    Create the surface
227
0
   *    Calculate the content transformation matrix
228
0
   *    Get our children (we may need to get them from another Pattern)
229
0
   *    Call SVGPaint on all of our children
230
0
   *    Return
231
0
   */
232
0
233
0
  nsSVGPatternFrame* patternWithChildren = GetPatternWithChildren();
234
0
  if (!patternWithChildren) {
235
0
    // Either no kids or a bad reference
236
0
    return nullptr;
237
0
  }
238
0
  nsIFrame* firstKid = patternWithChildren->mFrames.FirstChild();
239
0
240
0
  const nsSVGViewBox& viewBox = GetViewBox();
241
0
242
0
  uint16_t patternContentUnits =
243
0
    GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
244
0
  uint16_t patternUnits =
245
0
    GetEnumValue(SVGPatternElement::PATTERNUNITS);
246
0
247
0
  /*
248
0
   * Get the content geometry information.  This is a little tricky --
249
0
   * our parent is probably a <defs>, but we are rendering in the context
250
0
   * of some geometry source.  Our content geometry information needs to
251
0
   * come from our rendering parent as opposed to our content parent.  We
252
0
   * get that information from aSource, which is passed to us from the
253
0
   * backend renderer.
254
0
   *
255
0
   * There are three "geometries" that we need:
256
0
   *   1) The bounding box for the pattern.  We use this to get the
257
0
   *      width and height for the surface, and as the return to
258
0
   *      GetBBox.
259
0
   *   2) The transformation matrix for the pattern.  This is not *quite*
260
0
   *      the same as the canvas transformation matrix that we will
261
0
   *      provide to our rendering children since we "fudge" it a little
262
0
   *      to get the renderer to handle the translations correctly for us.
263
0
   *   3) The CTM that we return to our children who make up the pattern.
264
0
   */
265
0
266
0
  // Get all of the information we need from our "caller" -- i.e.
267
0
  // the geometry that is being rendered with a pattern
268
0
  gfxRect callerBBox;
269
0
  if (NS_FAILED(GetTargetGeometry(&callerBBox,
270
0
                                  viewBox,
271
0
                                  patternContentUnits, patternUnits,
272
0
                                  aSource,
273
0
                                  aContextMatrix,
274
0
                                  aOverrideBounds))) {
275
0
    return nullptr;
276
0
  }
277
0
278
0
  // Construct the CTM that we will provide to our children when we
279
0
  // render them into the tile.
280
0
  gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
281
0
                               callerBBox, aContextMatrix, aSource);
282
0
  if (ctm.IsSingular()) {
283
0
    return nullptr;
284
0
  }
285
0
286
0
  if (patternWithChildren->mCTM) {
287
0
    *patternWithChildren->mCTM = ctm;
288
0
  } else {
289
0
    patternWithChildren->mCTM = new gfxMatrix(ctm);
290
0
  }
291
0
292
0
  // Get the bounding box of the pattern.  This will be used to determine
293
0
  // the size of the surface, and will also be used to define the bounding
294
0
  // box for the pattern tile.
295
0
  gfxRect bbox = GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource);
296
0
  if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
297
0
    return nullptr;
298
0
  }
299
0
300
0
  // Get the pattern transform
301
0
  Matrix patternTransform = ToMatrix(GetPatternTransform());
302
0
303
0
  // revert the vector effect transform so that the pattern appears unchanged
304
0
  if (aFillOrStroke == &nsStyleSVG::mStroke) {
305
0
    gfxMatrix userToOuterSVG;
306
0
    if (nsSVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
307
0
      patternTransform *= ToMatrix(userToOuterSVG);
308
0
      if (patternTransform.IsSingular()) {
309
0
        NS_WARNING("Singular matrix painting non-scaling-stroke");
310
0
        return nullptr;
311
0
      }
312
0
    }
313
0
  }
314
0
315
0
  // Get the transformation matrix that we will hand to the renderer's pattern
316
0
  // routine.
317
0
  *patternMatrix = GetPatternMatrix(patternUnits, patternTransform,
318
0
                                    bbox, callerBBox, aContextMatrix);
319
0
  if (patternMatrix->IsSingular()) {
320
0
    return nullptr;
321
0
  }
322
0
323
0
  // Now that we have all of the necessary geometries, we can
324
0
  // create our surface.
325
0
  gfxRect transformedBBox = ThebesRect(patternTransform.TransformBounds(ToRect(bbox)));
326
0
327
0
  bool resultOverflows;
328
0
  IntSize surfaceSize =
329
0
    nsSVGUtils::ConvertToSurfaceSize(
330
0
      transformedBBox.Size(), &resultOverflows);
331
0
332
0
  // 0 disables rendering, < 0 is an error
333
0
  if (surfaceSize.width <= 0 || surfaceSize.height <= 0) {
334
0
    return nullptr;
335
0
  }
336
0
337
0
  gfxFloat patternWidth = bbox.Width();
338
0
  gfxFloat patternHeight = bbox.Height();
339
0
340
0
  if (resultOverflows ||
341
0
      patternWidth != surfaceSize.width ||
342
0
      patternHeight != surfaceSize.height) {
343
0
    // scale drawing to pattern surface size
344
0
    gfxMatrix tempTM =
345
0
      gfxMatrix(surfaceSize.width / patternWidth, 0.0,
346
0
                0.0, surfaceSize.height / patternHeight,
347
0
                0.0, 0.0);
348
0
    patternWithChildren->mCTM->PreMultiply(tempTM);
349
0
350
0
    // and rescale pattern to compensate
351
0
    patternMatrix->PreScale(patternWidth / surfaceSize.width,
352
0
                            patternHeight / surfaceSize.height);
353
0
  }
354
0
355
0
  RefPtr<DrawTarget> dt =
356
0
    aDrawTarget->CreateSimilarDrawTarget(surfaceSize, SurfaceFormat::B8G8R8A8);
357
0
  if (!dt || !dt->IsValid()) {
358
0
    return nullptr;
359
0
  }
360
0
  dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height));
361
0
362
0
  RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
363
0
  MOZ_ASSERT(ctx); // already checked the draw target above
364
0
365
0
  if (aGraphicOpacity != 1.0f) {
366
0
    ctx->Save();
367
0
    ctx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aGraphicOpacity);
368
0
  }
369
0
370
0
  // OK, now render -- note that we use "firstKid", which
371
0
  // we got at the beginning because it takes care of the
372
0
  // referenced pattern situation for us
373
0
374
0
  if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) {
375
0
    // Set the geometrical parent of the pattern we are rendering
376
0
    patternWithChildren->mSource = static_cast<SVGGeometryFrame*>(aSource);
377
0
  }
378
0
379
0
  // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
380
0
  // give back a clear surface if there's a loop
381
0
  if (!(patternWithChildren->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) {
382
0
    AutoSetRestorePaintServerState paintServer(patternWithChildren);
383
0
    for (nsIFrame* kid = firstKid; kid;
384
0
         kid = kid->GetNextSibling()) {
385
0
      // The CTM of each frame referencing us can be different
386
0
      nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
387
0
      if (SVGFrame) {
388
0
        SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
389
0
      }
390
0
      gfxMatrix tm = *(patternWithChildren->mCTM);
391
0
      if (kid->GetContent()->IsSVGElement()) {
392
0
        tm = static_cast<nsSVGElement*>(kid->GetContent())->
393
0
               PrependLocalTransformsTo(tm, eUserSpaceToParent);
394
0
      }
395
0
396
0
      nsSVGUtils::PaintFrameWithEffects(kid, *ctx, tm, aImgParams);
397
0
    }
398
0
  }
399
0
400
0
  patternWithChildren->mSource = nullptr;
401
0
402
0
  if (aGraphicOpacity != 1.0f) {
403
0
    ctx->PopGroupAndBlend();
404
0
    ctx->Restore();
405
0
  }
406
0
407
0
  // caller now owns the surface
408
0
  return dt->Snapshot();
409
0
}
410
411
/* Will probably need something like this... */
412
// How do we handle the insertion of a new frame?
413
// We really don't want to rerender this every time,
414
// do we?
415
nsSVGPatternFrame*
416
nsSVGPatternFrame::GetPatternWithChildren()
417
0
{
418
0
  // Do we have any children ourselves?
419
0
  if (!mFrames.IsEmpty())
420
0
    return this;
421
0
422
0
  // No, see if we chain to someone who does
423
0
424
0
  // Before we recurse, make sure we'll break reference loops and over long
425
0
  // reference chains:
426
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
427
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
428
0
                                        &sRefChainLengthCounter);
429
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
430
0
    // Break reference chain
431
0
    return nullptr;
432
0
  }
433
0
434
0
  nsSVGPatternFrame* next = GetReferencedPattern();
435
0
  if (!next)
436
0
    return nullptr;
437
0
438
0
  return next->GetPatternWithChildren();
439
0
}
440
441
uint16_t
442
nsSVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
443
0
{
444
0
  nsSVGEnum& thisEnum =
445
0
    static_cast<SVGPatternElement *>(GetContent())->mEnumAttributes[aIndex];
446
0
447
0
  if (thisEnum.IsExplicitlySet())
448
0
    return thisEnum.GetAnimValue();
449
0
450
0
  // Before we recurse, make sure we'll break reference loops and over long
451
0
  // reference chains:
452
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
453
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
454
0
                                        &sRefChainLengthCounter);
455
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
456
0
    // Break reference chain
457
0
    return static_cast<SVGPatternElement *>(aDefault)->
458
0
             mEnumAttributes[aIndex].GetAnimValue();
459
0
  }
460
0
461
0
  nsSVGPatternFrame *next = GetReferencedPattern();
462
0
  return next ? next->GetEnumValue(aIndex, aDefault)
463
0
              : static_cast<SVGPatternElement*>(aDefault)->
464
0
                  mEnumAttributes[aIndex].GetAnimValue();
465
0
}
466
467
nsSVGAnimatedTransformList*
468
nsSVGPatternFrame::GetPatternTransformList(nsIContent* aDefault)
469
0
{
470
0
  nsSVGAnimatedTransformList *thisTransformList =
471
0
    static_cast<SVGPatternElement *>(GetContent())->GetAnimatedTransformList();
472
0
473
0
  if (thisTransformList && thisTransformList->IsExplicitlySet())
474
0
    return thisTransformList;
475
0
476
0
  // Before we recurse, make sure we'll break reference loops and over long
477
0
  // reference chains:
478
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
479
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
480
0
                                        &sRefChainLengthCounter);
481
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
482
0
    // Break reference chain
483
0
    return static_cast<SVGPatternElement*>(aDefault)->mPatternTransform.get();
484
0
  }
485
0
486
0
  nsSVGPatternFrame *next = GetReferencedPattern();
487
0
  return next ? next->GetPatternTransformList(aDefault)
488
0
              : static_cast<SVGPatternElement*>(aDefault)->mPatternTransform.get();
489
0
}
490
491
gfxMatrix
492
nsSVGPatternFrame::GetPatternTransform()
493
0
{
494
0
  nsSVGAnimatedTransformList* animTransformList =
495
0
    GetPatternTransformList(GetContent());
496
0
  if (!animTransformList)
497
0
    return gfxMatrix();
498
0
499
0
  return animTransformList->GetAnimValue().GetConsolidationMatrix();
500
0
}
501
502
const nsSVGViewBox &
503
nsSVGPatternFrame::GetViewBox(nsIContent* aDefault)
504
0
{
505
0
  const nsSVGViewBox &thisViewBox =
506
0
    static_cast<SVGPatternElement *>(GetContent())->mViewBox;
507
0
508
0
  if (thisViewBox.IsExplicitlySet())
509
0
    return thisViewBox;
510
0
511
0
  // Before we recurse, make sure we'll break reference loops and over long
512
0
  // reference chains:
513
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
514
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
515
0
                                        &sRefChainLengthCounter);
516
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
517
0
    // Break reference chain
518
0
    return static_cast<SVGPatternElement *>(aDefault)->mViewBox;
519
0
  }
520
0
521
0
  nsSVGPatternFrame *next = GetReferencedPattern();
522
0
  return next ? next->GetViewBox(aDefault)
523
0
              : static_cast<SVGPatternElement *>(aDefault)->mViewBox;
524
0
}
525
526
const SVGAnimatedPreserveAspectRatio &
527
nsSVGPatternFrame::GetPreserveAspectRatio(nsIContent *aDefault)
528
0
{
529
0
  const SVGAnimatedPreserveAspectRatio &thisPar =
530
0
    static_cast<SVGPatternElement *>(GetContent())->mPreserveAspectRatio;
531
0
532
0
  if (thisPar.IsExplicitlySet())
533
0
    return thisPar;
534
0
535
0
  // Before we recurse, make sure we'll break reference loops and over long
536
0
  // reference chains:
537
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
538
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
539
0
                                        &sRefChainLengthCounter);
540
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
541
0
    // Break reference chain
542
0
    return static_cast<SVGPatternElement *>(aDefault)->mPreserveAspectRatio;
543
0
  }
544
0
545
0
  nsSVGPatternFrame *next = GetReferencedPattern();
546
0
  return next ? next->GetPreserveAspectRatio(aDefault)
547
0
              : static_cast<SVGPatternElement *>(aDefault)->mPreserveAspectRatio;
548
0
}
549
550
const nsSVGLength2 *
551
nsSVGPatternFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault)
552
0
{
553
0
  const nsSVGLength2 *thisLength =
554
0
    &static_cast<SVGPatternElement *>(GetContent())->mLengthAttributes[aIndex];
555
0
556
0
  if (thisLength->IsExplicitlySet())
557
0
    return thisLength;
558
0
559
0
  // Before we recurse, make sure we'll break reference loops and over long
560
0
  // reference chains:
561
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
562
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
563
0
                                        &sRefChainLengthCounter);
564
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
565
0
    // Break reference chain
566
0
    return &static_cast<SVGPatternElement *>(aDefault)->mLengthAttributes[aIndex];
567
0
  }
568
0
569
0
  nsSVGPatternFrame *next = GetReferencedPattern();
570
0
  return next ? next->GetLengthValue(aIndex, aDefault)
571
0
              : &static_cast<SVGPatternElement *>(aDefault)->mLengthAttributes[aIndex];
572
0
}
573
574
// Private (helper) methods
575
nsSVGPatternFrame *
576
nsSVGPatternFrame::GetReferencedPattern()
577
0
{
578
0
  if (mNoHRefURI)
579
0
    return nullptr;
580
0
581
0
  SVGTemplateElementObserver* observer =
582
0
    GetProperty(SVGObserverUtils::HrefToTemplateProperty());
583
0
584
0
  if (!observer) {
585
0
    // Fetch our pattern element's href or xlink:href attribute
586
0
    SVGPatternElement *pattern = static_cast<SVGPatternElement *>(GetContent());
587
0
    nsAutoString href;
588
0
    if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
589
0
      pattern->mStringAttributes[SVGPatternElement::HREF]
590
0
        .GetAnimValue(href, pattern);
591
0
    } else {
592
0
      pattern->mStringAttributes[SVGPatternElement::XLINK_HREF]
593
0
        .GetAnimValue(href, pattern);
594
0
    }
595
0
596
0
    if (href.IsEmpty()) {
597
0
      mNoHRefURI = true;
598
0
      return nullptr; // no URL
599
0
    }
600
0
601
0
    // Convert href to an nsIURI
602
0
    nsCOMPtr<nsIURI> targetURI;
603
0
    nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
604
0
    nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
605
0
                                              mContent->GetUncomposedDoc(), base);
606
0
607
0
    // There's no clear refererer policy spec about non-CSS SVG resource references
608
0
    // Bug 1415044 to investigate which referrer we should use
609
0
    RefPtr<URLAndReferrerInfo> target =
610
0
      new URLAndReferrerInfo(targetURI,
611
0
                             mContent->OwnerDoc()->GetDocumentURI(),
612
0
                             mContent->OwnerDoc()->GetReferrerPolicy());
613
0
614
0
    observer = SVGObserverUtils::GetTemplateElementObserver(target, this,
615
0
                 SVGObserverUtils::HrefToTemplateProperty());
616
0
    if (!observer) {
617
0
      return nullptr;
618
0
    }
619
0
  }
620
0
621
0
  nsIFrame* result = observer->GetReferencedFrame();
622
0
  if (!result)
623
0
    return nullptr;
624
0
625
0
  LayoutFrameType frameType = result->Type();
626
0
  if (frameType != LayoutFrameType::SVGPattern)
627
0
    return nullptr;
628
0
629
0
  return static_cast<nsSVGPatternFrame*>(result);
630
0
}
631
632
gfxRect
633
nsSVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
634
                                  const gfxRect &aTargetBBox,
635
                                  const Matrix &aTargetCTM,
636
                                  nsIFrame *aTarget)
637
0
{
638
0
  // We need to initialize our box
639
0
  float x,y,width,height;
640
0
641
0
  // Get the pattern x,y,width, and height
642
0
  const nsSVGLength2 *tmpX, *tmpY, *tmpHeight, *tmpWidth;
643
0
  tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
644
0
  tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
645
0
  tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
646
0
  tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
647
0
648
0
  if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
649
0
    x = nsSVGUtils::ObjectSpace(aTargetBBox, tmpX);
650
0
    y = nsSVGUtils::ObjectSpace(aTargetBBox, tmpY);
651
0
    width = nsSVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
652
0
    height = nsSVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
653
0
  } else {
654
0
    float scale = MaxExpansion(aTargetCTM);
655
0
    x = nsSVGUtils::UserSpace(aTarget, tmpX) * scale;
656
0
    y = nsSVGUtils::UserSpace(aTarget, tmpY) * scale;
657
0
    width = nsSVGUtils::UserSpace(aTarget, tmpWidth) * scale;
658
0
    height = nsSVGUtils::UserSpace(aTarget, tmpHeight) * scale;
659
0
  }
660
0
661
0
  return gfxRect(x, y, width, height);
662
0
}
663
664
gfxMatrix
665
nsSVGPatternFrame::ConstructCTM(const nsSVGViewBox& aViewBox,
666
                                uint16_t aPatternContentUnits,
667
                                uint16_t aPatternUnits,
668
                                const gfxRect &callerBBox,
669
                                const Matrix &callerCTM,
670
                                nsIFrame *aTarget)
671
0
{
672
0
  SVGViewportElement *ctx = nullptr;
673
0
  nsIContent* targetContent = aTarget->GetContent();
674
0
  gfxFloat scaleX, scaleY;
675
0
676
0
  // The objectBoundingBox conversion must be handled in the CTM:
677
0
  if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
678
0
    scaleX = callerBBox.Width();
679
0
    scaleY = callerBBox.Height();
680
0
  } else {
681
0
    if (targetContent->IsSVGElement()) {
682
0
      ctx = static_cast<nsSVGElement*>(targetContent)->GetCtx();
683
0
    }
684
0
    scaleX = scaleY = MaxExpansion(callerCTM);
685
0
  }
686
0
687
0
  if (!aViewBox.IsExplicitlySet()) {
688
0
    return gfxMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0);
689
0
  }
690
0
  const nsSVGViewBoxRect viewBoxRect = aViewBox.GetAnimValue();
691
0
692
0
  if (viewBoxRect.height <= 0.0f || viewBoxRect.width <= 0.0f) {
693
0
    return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
694
0
  }
695
0
696
0
  float viewportWidth, viewportHeight;
697
0
  if (targetContent->IsSVGElement()) {
698
0
    // If we're dealing with an SVG target only retrieve the context once.
699
0
    // Calling the nsIFrame* variant of GetAnimValue would look it up on
700
0
    // every call.
701
0
    viewportWidth =
702
0
      GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx);
703
0
    viewportHeight =
704
0
      GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx);
705
0
  } else {
706
0
    // No SVG target, call the nsIFrame* variant of GetAnimValue.
707
0
    viewportWidth =
708
0
      GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget);
709
0
    viewportHeight =
710
0
      GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget);
711
0
  }
712
0
713
0
  if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
714
0
    return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
715
0
  }
716
0
717
0
  Matrix tm = SVGContentUtils::GetViewBoxTransform(
718
0
    viewportWidth * scaleX, viewportHeight * scaleY,
719
0
    viewBoxRect.x, viewBoxRect.y,
720
0
    viewBoxRect.width, viewBoxRect.height,
721
0
    GetPreserveAspectRatio());
722
0
723
0
  return ThebesMatrix(tm);
724
0
}
725
726
//----------------------------------------------------------------------
727
// nsSVGPaintServerFrame methods:
728
already_AddRefed<gfxPattern>
729
nsSVGPatternFrame::GetPaintServerPattern(nsIFrame *aSource,
730
                                         const DrawTarget* aDrawTarget,
731
                                         const gfxMatrix& aContextMatrix,
732
                                         nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
733
                                         float aGraphicOpacity,
734
                                         imgDrawingParams& aImgParams,
735
                                         const gfxRect *aOverrideBounds)
736
0
{
737
0
  if (aGraphicOpacity == 0.0f) {
738
0
    return do_AddRef(new gfxPattern(Color()));
739
0
  }
740
0
741
0
  // Paint it!
742
0
  Matrix pMatrix;
743
0
  RefPtr<SourceSurface> surface =
744
0
    PaintPattern(aDrawTarget, &pMatrix, ToMatrix(aContextMatrix), aSource,
745
0
                 aFillOrStroke, aGraphicOpacity, aOverrideBounds, aImgParams);
746
0
747
0
  if (!surface) {
748
0
    return nullptr;
749
0
  }
750
0
751
0
  RefPtr<gfxPattern> pattern = new gfxPattern(surface, pMatrix);
752
0
753
0
  if (!pattern) {
754
0
    return nullptr;
755
0
  }
756
0
757
0
  pattern->SetExtend(ExtendMode::REPEAT);
758
0
  return pattern.forget();
759
0
}
760
761
// -------------------------------------------------------------------------
762
// Public functions
763
// -------------------------------------------------------------------------
764
765
nsIFrame* NS_NewSVGPatternFrame(nsIPresShell*   aPresShell,
766
                                ComputedStyle* aStyle)
767
0
{
768
0
  return new (aPresShell) nsSVGPatternFrame(aStyle);
769
0
}
770