Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/svg/nsSVGGradientFrame.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 "nsSVGGradientFrame.h"
9
#include <algorithm>
10
11
// Keep others in (case-insensitive) order:
12
#include "AutoReferenceChainGuard.h"
13
#include "gfxPattern.h"
14
#include "mozilla/dom/SVGGradientElement.h"
15
#include "mozilla/dom/SVGGradientElementBinding.h"
16
#include "mozilla/dom/SVGStopElement.h"
17
#include "mozilla/dom/SVGUnitTypesBinding.h"
18
#include "nsContentUtils.h"
19
#include "SVGObserverUtils.h"
20
#include "nsSVGAnimatedTransformList.h"
21
22
// XXX Tight coupling with content classes ahead!
23
24
using namespace mozilla;
25
using namespace mozilla::dom;
26
using namespace mozilla::dom::SVGGradientElement_Binding;
27
using namespace mozilla::dom::SVGUnitTypes_Binding;
28
using namespace mozilla::gfx;
29
30
//----------------------------------------------------------------------
31
// Implementation
32
33
nsSVGGradientFrame::nsSVGGradientFrame(ComputedStyle* aStyle,
34
                                       ClassID aID)
35
  : nsSVGPaintServerFrame(aStyle, aID)
36
  , mSource(nullptr)
37
  , mLoopFlag(false)
38
  , mNoHRefURI(false)
39
0
{
40
0
}
41
42
//----------------------------------------------------------------------
43
// nsIFrame methods:
44
45
nsresult
46
nsSVGGradientFrame::AttributeChanged(int32_t         aNameSpaceID,
47
                                     nsAtom*        aAttribute,
48
                                     int32_t         aModType)
49
0
{
50
0
  if (aNameSpaceID == kNameSpaceID_None &&
51
0
      (aAttribute == nsGkAtoms::gradientUnits ||
52
0
       aAttribute == nsGkAtoms::gradientTransform ||
53
0
       aAttribute == nsGkAtoms::spreadMethod)) {
54
0
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
55
0
  } else if ((aNameSpaceID == kNameSpaceID_XLink ||
56
0
              aNameSpaceID == kNameSpaceID_None) &&
57
0
             aAttribute == nsGkAtoms::href) {
58
0
    // Blow away our reference, if any
59
0
    DeleteProperty(SVGObserverUtils::HrefToTemplateProperty());
60
0
    mNoHRefURI = false;
61
0
    // And update whoever references us
62
0
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
63
0
  }
64
0
65
0
  return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID,
66
0
                                                 aAttribute, aModType);
67
0
}
68
69
//----------------------------------------------------------------------
70
71
uint16_t
72
nsSVGGradientFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault)
73
0
{
74
0
  const nsSVGEnum& thisEnum =
75
0
    static_cast<dom::SVGGradientElement*>(GetContent())->mEnumAttributes[aIndex];
76
0
77
0
  if (thisEnum.IsExplicitlySet())
78
0
    return thisEnum.GetAnimValue();
79
0
80
0
  // Before we recurse, make sure we'll break reference loops and over long
81
0
  // reference chains:
82
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
83
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
84
0
                                        &sRefChainLengthCounter);
85
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
86
0
    // Break reference chain
87
0
    return static_cast<dom::SVGGradientElement*>(aDefault)->
88
0
             mEnumAttributes[aIndex].GetAnimValue();
89
0
  }
90
0
91
0
  nsSVGGradientFrame *next = GetReferencedGradient();
92
0
93
0
  return next ? next->GetEnumValue(aIndex, aDefault)
94
0
              : static_cast<dom::SVGGradientElement*>(aDefault)->
95
0
                  mEnumAttributes[aIndex].GetAnimValue();
96
0
}
97
98
uint16_t
99
nsSVGGradientFrame::GetGradientUnits()
100
0
{
101
0
  // This getter is called every time the others are called - maybe cache it?
102
0
  return GetEnumValue(dom::SVGGradientElement::GRADIENTUNITS);
103
0
}
104
105
uint16_t
106
nsSVGGradientFrame::GetSpreadMethod()
107
0
{
108
0
  return GetEnumValue(dom::SVGGradientElement::SPREADMETHOD);
109
0
}
110
111
const nsSVGAnimatedTransformList*
112
nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault)
113
0
{
114
0
  nsSVGAnimatedTransformList *thisTransformList =
115
0
    static_cast<dom::SVGGradientElement*>(GetContent())->GetAnimatedTransformList();
116
0
117
0
  if (thisTransformList && thisTransformList->IsExplicitlySet())
118
0
    return thisTransformList;
119
0
120
0
  // Before we recurse, make sure we'll break reference loops and over long
121
0
  // reference chains:
122
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
123
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
124
0
                                        &sRefChainLengthCounter);
125
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
126
0
    // Break reference chain
127
0
    return static_cast<const dom::SVGGradientElement*>(aDefault)->
128
0
             mGradientTransform.get();
129
0
  }
130
0
131
0
  nsSVGGradientFrame *next = GetReferencedGradient();
132
0
133
0
  return next ? next->GetGradientTransformList(aDefault)
134
0
              : static_cast<const dom::SVGGradientElement*>(aDefault)->
135
0
                  mGradientTransform.get();
136
0
}
137
138
gfxMatrix
139
nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource,
140
                                         const gfxRect *aOverrideBounds)
141
0
{
142
0
  gfxMatrix bboxMatrix;
143
0
144
0
  uint16_t gradientUnits = GetGradientUnits();
145
0
  if (gradientUnits != SVG_UNIT_TYPE_USERSPACEONUSE) {
146
0
    NS_ASSERTION(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
147
0
                 "Unknown gradientUnits type");
148
0
    // objectBoundingBox is the default anyway
149
0
150
0
    gfxRect bbox =
151
0
      aOverrideBounds
152
0
        ? *aOverrideBounds
153
0
        : nsSVGUtils::GetBBox(aSource, nsSVGUtils::eUseFrameBoundsForOuterSVG |
154
0
                                       nsSVGUtils::eBBoxIncludeFillGeometry);
155
0
    bboxMatrix =
156
0
      gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y());
157
0
  }
158
0
159
0
  const nsSVGAnimatedTransformList* animTransformList =
160
0
    GetGradientTransformList(GetContent());
161
0
  if (!animTransformList)
162
0
    return bboxMatrix;
163
0
164
0
  gfxMatrix gradientTransform =
165
0
    animTransformList->GetAnimValue().GetConsolidationMatrix();
166
0
  return bboxMatrix.PreMultiply(gradientTransform);
167
0
}
168
169
dom::SVGLinearGradientElement*
170
nsSVGGradientFrame::GetLinearGradientWithLength(uint32_t aIndex,
171
  dom::SVGLinearGradientElement* aDefault)
172
0
{
173
0
  // If this was a linear gradient with the required length, we would have
174
0
  // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength.
175
0
  // Since we didn't find the length, continue looking down the chain.
176
0
177
0
  // Before we recurse, make sure we'll break reference loops and over long
178
0
  // reference chains:
179
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
180
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
181
0
                                        &sRefChainLengthCounter);
182
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
183
0
    // Break reference chain
184
0
    return aDefault;
185
0
  }
186
0
187
0
  nsSVGGradientFrame *next = GetReferencedGradient();
188
0
  return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault;
189
0
}
190
191
dom::SVGRadialGradientElement*
192
nsSVGGradientFrame::GetRadialGradientWithLength(uint32_t aIndex,
193
  dom::SVGRadialGradientElement* aDefault)
194
0
{
195
0
  // If this was a radial gradient with the required length, we would have
196
0
  // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength.
197
0
  // Since we didn't find the length, continue looking down the chain.
198
0
199
0
  // Before we recurse, make sure we'll break reference loops and over long
200
0
  // reference chains:
201
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
202
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
203
0
                                        &sRefChainLengthCounter);
204
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
205
0
    // Break reference chain
206
0
    return aDefault;
207
0
  }
208
0
209
0
  nsSVGGradientFrame *next = GetReferencedGradient();
210
0
  return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault;
211
0
}
212
213
//----------------------------------------------------------------------
214
// nsSVGPaintServerFrame methods:
215
216
//helper
217
static void GetStopInformation(nsIFrame* aStopFrame,
218
                               float *aOffset,
219
                               nscolor *aStopColor,
220
                               float *aStopOpacity)
221
0
{
222
0
  nsIContent* stopContent = aStopFrame->GetContent();
223
0
  MOZ_ASSERT(stopContent && stopContent->IsSVGElement(nsGkAtoms::stop));
224
0
225
0
  static_cast<SVGStopElement*>(stopContent)->
226
0
    GetAnimatedNumberValues(aOffset, nullptr);
227
0
228
0
  const nsStyleSVGReset* styleSVGReset = aStopFrame->StyleSVGReset();
229
0
  *aOffset = mozilla::clamped(*aOffset, 0.0f, 1.0f);
230
0
  *aStopColor = styleSVGReset->mStopColor.CalcColor(aStopFrame);
231
0
  *aStopOpacity = styleSVGReset->mStopOpacity;
232
0
}
233
234
already_AddRefed<gfxPattern>
235
nsSVGGradientFrame::GetPaintServerPattern(nsIFrame* aSource,
236
                                          const DrawTarget* aDrawTarget,
237
                                          const gfxMatrix& aContextMatrix,
238
                                          nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
239
                                          float aGraphicOpacity,
240
                                          imgDrawingParams& aImgParams,
241
                                          const gfxRect* aOverrideBounds)
242
0
{
243
0
  uint16_t gradientUnits = GetGradientUnits();
244
0
  MOZ_ASSERT(gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX ||
245
0
             gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE);
246
0
  if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
247
0
    // Set mSource for this consumer.
248
0
    // If this gradient is applied to text, our caller will be the glyph, which
249
0
    // is not an element, so we need to get the parent
250
0
    mSource = aSource->GetContent()->IsText() ?
251
0
                aSource->GetParent() : aSource;
252
0
  }
253
0
254
0
  AutoTArray<nsIFrame*,8> stopFrames;
255
0
  GetStopFrames(&stopFrames);
256
0
257
0
  uint32_t nStops = stopFrames.Length();
258
0
259
0
  // SVG specification says that no stops should be treated like
260
0
  // the corresponding fill or stroke had "none" specified.
261
0
  if (nStops == 0) {
262
0
    RefPtr<gfxPattern> pattern = new gfxPattern(Color());
263
0
    return do_AddRef(new gfxPattern(Color()));
264
0
  }
265
0
266
0
  if (nStops == 1 || GradientVectorLengthIsZero()) {
267
0
    auto lastStopFrame = stopFrames[nStops-1];
268
0
    auto svgReset = lastStopFrame->StyleSVGReset();
269
0
    // The gradient paints a single colour, using the stop-color of the last
270
0
    // gradient step if there are more than one.
271
0
    float stopOpacity = svgReset->mStopOpacity;
272
0
    nscolor stopColor = svgReset->mStopColor.CalcColor(lastStopFrame);
273
0
274
0
    Color stopColor2 = Color::FromABGR(stopColor);
275
0
    stopColor2.a *= stopOpacity * aGraphicOpacity;
276
0
    return do_AddRef(new gfxPattern(stopColor2));
277
0
  }
278
0
279
0
  // Get the transform list (if there is one). We do this after the returns
280
0
  // above since this call can be expensive when "gradientUnits" is set to
281
0
  // "objectBoundingBox" (since that requiring a GetBBox() call).
282
0
  gfxMatrix patternMatrix = GetGradientTransform(aSource, aOverrideBounds);
283
0
284
0
  if (patternMatrix.IsSingular()) {
285
0
    return nullptr;
286
0
  }
287
0
288
0
  // revert any vector effect transform so that the gradient appears unchanged
289
0
  if (aFillOrStroke == &nsStyleSVG::mStroke) {
290
0
    gfxMatrix userToOuterSVG;
291
0
    if (nsSVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
292
0
      patternMatrix *= userToOuterSVG;
293
0
    }
294
0
  }
295
0
296
0
  if (!patternMatrix.Invert()) {
297
0
    return nullptr;
298
0
  }
299
0
300
0
  RefPtr<gfxPattern> gradient = CreateGradient();
301
0
  if (!gradient) {
302
0
    return nullptr;
303
0
  }
304
0
305
0
  uint16_t aSpread = GetSpreadMethod();
306
0
  if (aSpread == SVG_SPREADMETHOD_PAD)
307
0
    gradient->SetExtend(ExtendMode::CLAMP);
308
0
  else if (aSpread == SVG_SPREADMETHOD_REFLECT)
309
0
    gradient->SetExtend(ExtendMode::REFLECT);
310
0
  else if (aSpread == SVG_SPREADMETHOD_REPEAT)
311
0
    gradient->SetExtend(ExtendMode::REPEAT);
312
0
313
0
  gradient->SetMatrix(patternMatrix);
314
0
315
0
  // setup stops
316
0
  float lastOffset = 0.0f;
317
0
318
0
  for (uint32_t i = 0; i < nStops; i++) {
319
0
    float offset, stopOpacity;
320
0
    nscolor stopColor;
321
0
322
0
    GetStopInformation(stopFrames[i], &offset, &stopColor, &stopOpacity);
323
0
324
0
    if (offset < lastOffset)
325
0
      offset = lastOffset;
326
0
    else
327
0
      lastOffset = offset;
328
0
329
0
    Color stopColor2 = Color::FromABGR(stopColor);
330
0
    stopColor2.a *= stopOpacity * aGraphicOpacity;
331
0
    gradient->AddColorStop(offset, stopColor2);
332
0
  }
333
0
334
0
  return gradient.forget();
335
0
}
336
337
// Private (helper) methods
338
339
nsSVGGradientFrame *
340
nsSVGGradientFrame::GetReferencedGradient()
341
0
{
342
0
  if (mNoHRefURI)
343
0
    return nullptr;
344
0
345
0
  SVGTemplateElementObserver* observer =
346
0
    GetProperty(SVGObserverUtils::HrefToTemplateProperty());
347
0
348
0
  if (!observer) {
349
0
    // Fetch our gradient element's href or xlink:href attribute
350
0
    dom::SVGGradientElement* grad =
351
0
      static_cast<dom::SVGGradientElement*>(GetContent());
352
0
    nsAutoString href;
353
0
    if (grad->mStringAttributes[dom::SVGGradientElement::HREF]
354
0
          .IsExplicitlySet()) {
355
0
      grad->mStringAttributes[dom::SVGGradientElement::HREF]
356
0
        .GetAnimValue(href, grad);
357
0
    } else {
358
0
      grad->mStringAttributes[dom::SVGGradientElement::XLINK_HREF]
359
0
        .GetAnimValue(href, grad);
360
0
    }
361
0
362
0
    if (href.IsEmpty()) {
363
0
      mNoHRefURI = true;
364
0
      return nullptr; // no URL
365
0
    }
366
0
367
0
    // Convert href to an nsIURI
368
0
    nsCOMPtr<nsIURI> targetURI;
369
0
    nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
370
0
    nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
371
0
                                              mContent->GetUncomposedDoc(), base);
372
0
373
0
    // There's no clear refererer policy spec about non-CSS SVG resource references
374
0
    // Bug 1415044 to investigate which referrer we should use
375
0
    RefPtr<URLAndReferrerInfo> target =
376
0
      new URLAndReferrerInfo(targetURI,
377
0
                             mContent->OwnerDoc()->GetDocumentURI(),
378
0
                             mContent->OwnerDoc()->GetReferrerPolicy());
379
0
380
0
    observer = SVGObserverUtils::GetTemplateElementObserver(target, this,
381
0
                 SVGObserverUtils::HrefToTemplateProperty());
382
0
    if (!observer) {
383
0
      return nullptr;
384
0
    }
385
0
  }
386
0
387
0
  nsIFrame* result = observer->GetReferencedFrame();
388
0
  if (!result)
389
0
    return nullptr;
390
0
391
0
  LayoutFrameType frameType = result->Type();
392
0
  if (frameType != LayoutFrameType::SVGLinearGradient &&
393
0
      frameType != LayoutFrameType::SVGRadialGradient)
394
0
    return nullptr;
395
0
396
0
  return static_cast<nsSVGGradientFrame*>(result);
397
0
}
398
399
void
400
nsSVGGradientFrame::GetStopFrames(nsTArray<nsIFrame*>* aStopFrames)
401
0
{
402
0
  nsIFrame *stopFrame = nullptr;
403
0
  for (stopFrame = mFrames.FirstChild(); stopFrame;
404
0
       stopFrame = stopFrame->GetNextSibling()) {
405
0
    if (stopFrame->IsSVGStopFrame()) {
406
0
      aStopFrames->AppendElement(stopFrame);
407
0
    }
408
0
  }
409
0
  if (aStopFrames->Length() > 0) {
410
0
    return;
411
0
  }
412
0
413
0
  // Our gradient element doesn't have stops - try to "inherit" them
414
0
415
0
  // Before we recurse, make sure we'll break reference loops and over long
416
0
  // reference chains:
417
0
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
418
0
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
419
0
                                        &sRefChainLengthCounter);
420
0
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
421
0
    // Break reference chain
422
0
    return;
423
0
  }
424
0
425
0
  nsSVGGradientFrame* next = GetReferencedGradient();
426
0
  if (next) {
427
0
    next->GetStopFrames(aStopFrames);
428
0
  }
429
0
}
430
431
// -------------------------------------------------------------------------
432
// Linear Gradients
433
// -------------------------------------------------------------------------
434
435
#ifdef DEBUG
436
void
437
nsSVGLinearGradientFrame::Init(nsIContent*       aContent,
438
                               nsContainerFrame* aParent,
439
                               nsIFrame*         aPrevInFlow)
440
{
441
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::linearGradient),
442
               "Content is not an SVG linearGradient");
443
444
  nsSVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
445
}
446
#endif /* DEBUG */
447
448
nsresult
449
nsSVGLinearGradientFrame::AttributeChanged(int32_t         aNameSpaceID,
450
                                           nsAtom*        aAttribute,
451
                                           int32_t         aModType)
452
0
{
453
0
  if (aNameSpaceID == kNameSpaceID_None &&
454
0
      (aAttribute == nsGkAtoms::x1 ||
455
0
       aAttribute == nsGkAtoms::y1 ||
456
0
       aAttribute == nsGkAtoms::x2 ||
457
0
       aAttribute == nsGkAtoms::y2)) {
458
0
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
459
0
  }
460
0
461
0
  return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
462
0
                                              aAttribute, aModType);
463
0
}
464
465
//----------------------------------------------------------------------
466
467
float
468
nsSVGLinearGradientFrame::GetLengthValue(uint32_t aIndex)
469
0
{
470
0
  dom::SVGLinearGradientElement* lengthElement =
471
0
    GetLinearGradientWithLength(aIndex,
472
0
      static_cast<dom::SVGLinearGradientElement*>(GetContent()));
473
0
  // We passed in mContent as a fallback, so, assuming mContent is non-null, the
474
0
  // return value should also be non-null.
475
0
  MOZ_ASSERT(lengthElement,
476
0
    "Got unexpected null element from GetLinearGradientWithLength");
477
0
  const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex];
478
0
479
0
  // Object bounding box units are handled by setting the appropriate
480
0
  // transform in GetGradientTransform, but we need to handle user
481
0
  // space units as part of the individual Get* routines.  Fixes 323669.
482
0
483
0
  uint16_t gradientUnits = GetGradientUnits();
484
0
  if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
485
0
    return nsSVGUtils::UserSpace(mSource, &length);
486
0
  }
487
0
488
0
  NS_ASSERTION(
489
0
    gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
490
0
    "Unknown gradientUnits type");
491
0
492
0
  return length.GetAnimValue(static_cast<SVGViewportElement*>(nullptr));
493
0
}
494
495
dom::SVGLinearGradientElement*
496
nsSVGLinearGradientFrame::GetLinearGradientWithLength(uint32_t aIndex,
497
  dom::SVGLinearGradientElement* aDefault)
498
0
{
499
0
  dom::SVGLinearGradientElement* thisElement =
500
0
    static_cast<dom::SVGLinearGradientElement*>(GetContent());
501
0
  const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
502
0
503
0
  if (length.IsExplicitlySet()) {
504
0
    return thisElement;
505
0
  }
506
0
507
0
  return nsSVGGradientFrame::GetLinearGradientWithLength(aIndex, aDefault);
508
0
}
509
510
bool
511
nsSVGLinearGradientFrame::GradientVectorLengthIsZero()
512
0
{
513
0
  return GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1) ==
514
0
         GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2) &&
515
0
         GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1) ==
516
0
         GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
517
0
}
518
519
already_AddRefed<gfxPattern>
520
nsSVGLinearGradientFrame::CreateGradient()
521
0
{
522
0
  float x1, y1, x2, y2;
523
0
524
0
  x1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X1);
525
0
  y1 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y1);
526
0
  x2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_X2);
527
0
  y2 = GetLengthValue(dom::SVGLinearGradientElement::ATTR_Y2);
528
0
529
0
  RefPtr<gfxPattern> pattern = new gfxPattern(x1, y1, x2, y2);
530
0
  return pattern.forget();
531
0
}
532
533
// -------------------------------------------------------------------------
534
// Radial Gradients
535
// -------------------------------------------------------------------------
536
537
#ifdef DEBUG
538
void
539
nsSVGRadialGradientFrame::Init(nsIContent*       aContent,
540
                               nsContainerFrame* aParent,
541
                               nsIFrame*         aPrevInFlow)
542
{
543
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::radialGradient),
544
               "Content is not an SVG radialGradient");
545
546
  nsSVGGradientFrame::Init(aContent, aParent, aPrevInFlow);
547
}
548
#endif /* DEBUG */
549
550
nsresult
551
nsSVGRadialGradientFrame::AttributeChanged(int32_t         aNameSpaceID,
552
                                           nsAtom*        aAttribute,
553
                                           int32_t         aModType)
554
0
{
555
0
  if (aNameSpaceID == kNameSpaceID_None &&
556
0
      (aAttribute == nsGkAtoms::r ||
557
0
       aAttribute == nsGkAtoms::cx ||
558
0
       aAttribute == nsGkAtoms::cy ||
559
0
       aAttribute == nsGkAtoms::fx ||
560
0
       aAttribute == nsGkAtoms::fy)) {
561
0
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
562
0
  }
563
0
564
0
  return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
565
0
                                              aAttribute, aModType);
566
0
}
567
568
//----------------------------------------------------------------------
569
570
float
571
nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex)
572
0
{
573
0
  dom::SVGRadialGradientElement* lengthElement =
574
0
    GetRadialGradientWithLength(aIndex,
575
0
      static_cast<dom::SVGRadialGradientElement*>(GetContent()));
576
0
  // We passed in mContent as a fallback, so, assuming mContent is non-null,
577
0
  // the return value should also be non-null.
578
0
  MOZ_ASSERT(lengthElement,
579
0
    "Got unexpected null element from GetRadialGradientWithLength");
580
0
  return GetLengthValueFromElement(aIndex, *lengthElement);
581
0
}
582
583
float
584
nsSVGRadialGradientFrame::GetLengthValue(uint32_t aIndex, float aDefaultValue)
585
0
{
586
0
  dom::SVGRadialGradientElement* lengthElement =
587
0
    GetRadialGradientWithLength(aIndex, nullptr);
588
0
589
0
  return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement)
590
0
                       : aDefaultValue;
591
0
}
592
593
float
594
nsSVGRadialGradientFrame::GetLengthValueFromElement(uint32_t aIndex,
595
  dom::SVGRadialGradientElement& aElement)
596
0
{
597
0
  const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex];
598
0
599
0
  // Object bounding box units are handled by setting the appropriate
600
0
  // transform in GetGradientTransform, but we need to handle user
601
0
  // space units as part of the individual Get* routines.  Fixes 323669.
602
0
603
0
  uint16_t gradientUnits = GetGradientUnits();
604
0
  if (gradientUnits == SVG_UNIT_TYPE_USERSPACEONUSE) {
605
0
    return nsSVGUtils::UserSpace(mSource, &length);
606
0
  }
607
0
608
0
  NS_ASSERTION(
609
0
    gradientUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX,
610
0
    "Unknown gradientUnits type");
611
0
612
0
  return length.GetAnimValue(static_cast<SVGViewportElement*>(nullptr));
613
0
}
614
615
dom::SVGRadialGradientElement*
616
nsSVGRadialGradientFrame::GetRadialGradientWithLength(uint32_t aIndex,
617
  dom::SVGRadialGradientElement* aDefault)
618
0
{
619
0
  dom::SVGRadialGradientElement* thisElement =
620
0
    static_cast<dom::SVGRadialGradientElement*>(GetContent());
621
0
  const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex];
622
0
623
0
  if (length.IsExplicitlySet()) {
624
0
    return thisElement;
625
0
  }
626
0
627
0
  return nsSVGGradientFrame::GetRadialGradientWithLength(aIndex, aDefault);
628
0
}
629
630
bool
631
nsSVGRadialGradientFrame::GradientVectorLengthIsZero()
632
0
{
633
0
  return GetLengthValue(dom::SVGRadialGradientElement::ATTR_R) == 0;
634
0
}
635
636
already_AddRefed<gfxPattern>
637
nsSVGRadialGradientFrame::CreateGradient()
638
0
{
639
0
  float cx, cy, r, fx, fy, fr;
640
0
641
0
  cx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CX);
642
0
  cy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_CY);
643
0
  r  = GetLengthValue(dom::SVGRadialGradientElement::ATTR_R);
644
0
  // If fx or fy are not set, use cx/cy instead
645
0
  fx = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FX, cx);
646
0
  fy = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FY, cy);
647
0
  fr = GetLengthValue(dom::SVGRadialGradientElement::ATTR_FR);
648
0
649
0
  if (fx != cx || fy != cy) {
650
0
    // The focal point (fFx and fFy) must be clamped to be *inside* - not on -
651
0
    // the circumference of the gradient or we'll get rendering anomalies. We
652
0
    // calculate the distance from the focal point to the gradient center and
653
0
    // make sure it is *less* than the gradient radius.
654
0
    // 1/128 is the limit of the fractional part of cairo's 24.8 fixed point
655
0
    // representation divided by 2 to ensure that we get different cairo
656
0
    // fractions
657
0
    double dMax = std::max(0.0, r - 1.0/128);
658
0
    float dx = fx - cx;
659
0
    float dy = fy - cy;
660
0
    double d = sqrt((dx * dx) + (dy * dy));
661
0
    if (d > dMax) {
662
0
      double angle = atan2(dy, dx);
663
0
      fx = (float)(dMax * cos(angle)) + cx;
664
0
      fy = (float)(dMax * sin(angle)) + cy;
665
0
    }
666
0
  }
667
0
668
0
  RefPtr<gfxPattern> pattern = new gfxPattern(fx, fy, fr, cx, cy, r);
669
0
  return pattern.forget();
670
0
}
671
672
// -------------------------------------------------------------------------
673
// Public functions
674
// -------------------------------------------------------------------------
675
676
nsIFrame*
677
NS_NewSVGLinearGradientFrame(nsIPresShell*   aPresShell,
678
                             ComputedStyle* aStyle)
679
0
{
680
0
  return new (aPresShell) nsSVGLinearGradientFrame(aStyle);
681
0
}
682
683
NS_IMPL_FRAMEARENA_HELPERS(nsSVGLinearGradientFrame)
684
685
nsIFrame*
686
NS_NewSVGRadialGradientFrame(nsIPresShell*   aPresShell,
687
                             ComputedStyle* aStyle)
688
0
{
689
0
  return new (aPresShell) nsSVGRadialGradientFrame(aStyle);
690
0
}
691
692
NS_IMPL_FRAMEARENA_HELPERS(nsSVGRadialGradientFrame)