Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/SVGContentUtils.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 "SVGContentUtils.h"
10
11
// Keep others in (case-insensitive) order:
12
#include "gfx2DGlue.h"
13
#include "gfxMatrix.h"
14
#include "gfxPlatform.h"
15
#include "mozilla/gfx/2D.h"
16
#include "mozilla/dom/SVGSVGElement.h"
17
#include "mozilla/RefPtr.h"
18
#include "mozilla/SVGContextPaint.h"
19
#include "mozilla/TextUtils.h"
20
#include "nsComputedDOMStyle.h"
21
#include "nsFontMetrics.h"
22
#include "nsIFrame.h"
23
#include "nsIScriptError.h"
24
#include "nsLayoutUtils.h"
25
#include "nsMathUtils.h"
26
#include "SVGAnimationElement.h"
27
#include "SVGAnimatedPreserveAspectRatio.h"
28
#include "nsContentUtils.h"
29
#include "mozilla/gfx/2D.h"
30
#include "mozilla/gfx/Types.h"
31
#include "mozilla/FloatingPoint.h"
32
#include "mozilla/ComputedStyle.h"
33
#include "nsSVGPathDataParser.h"
34
#include "SVGPathData.h"
35
#include "SVGPathElement.h"
36
37
using namespace mozilla;
38
using namespace mozilla::dom;
39
using namespace mozilla::dom::SVGPreserveAspectRatio_Binding;
40
using namespace mozilla::gfx;
41
42
SVGSVGElement*
43
SVGContentUtils::GetOuterSVGElement(nsSVGElement *aSVGElement)
44
0
{
45
0
  Element* element = nullptr;
46
0
  Element* ancestor = aSVGElement->GetParentElementCrossingShadowRoot();
47
0
48
0
  while (ancestor && ancestor->IsSVGElement() &&
49
0
                     !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
50
0
    element = ancestor;
51
0
    ancestor = element->GetParentElementCrossingShadowRoot();
52
0
  }
53
0
54
0
  if (element && element->IsSVGElement(nsGkAtoms::svg)) {
55
0
    return static_cast<SVGSVGElement*>(element);
56
0
  }
57
0
  return nullptr;
58
0
}
59
60
void
61
SVGContentUtils::ActivateByHyperlink(nsIContent *aContent)
62
0
{
63
0
  MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eANIMATION),
64
0
             "Expecting an animation element");
65
0
66
0
  static_cast<SVGAnimationElement*>(aContent)->ActivateByHyperlink();
67
0
}
68
69
enum DashState {
70
  eDashedStroke,
71
  eContinuousStroke, //< all dashes, no gaps
72
  eNoStroke          //< all gaps, no dashes
73
};
74
75
static DashState
76
GetStrokeDashData(SVGContentUtils::AutoStrokeOptions* aStrokeOptions,
77
                  nsSVGElement* aElement,
78
                  const nsStyleSVG* aStyleSVG,
79
                  SVGContextPaint* aContextPaint)
80
0
{
81
0
  size_t dashArrayLength;
82
0
  Float totalLengthOfDashes = 0.0, totalLengthOfGaps = 0.0;
83
0
  Float pathScale = 1.0;
84
0
85
0
  if (aContextPaint && aStyleSVG->StrokeDasharrayFromObject()) {
86
0
    const FallibleTArray<Float>& dashSrc = aContextPaint->GetStrokeDashArray();
87
0
    dashArrayLength = dashSrc.Length();
88
0
    if (dashArrayLength <= 0) {
89
0
      return eContinuousStroke;
90
0
    }
91
0
    Float* dashPattern = aStrokeOptions->InitDashPattern(dashArrayLength);
92
0
    if (!dashPattern) {
93
0
      return eContinuousStroke;
94
0
    }
95
0
    for (size_t i = 0; i < dashArrayLength; i++) {
96
0
      if (dashSrc[i] < 0.0) {
97
0
        return eContinuousStroke; // invalid
98
0
      }
99
0
      dashPattern[i] = Float(dashSrc[i]);
100
0
      (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashSrc[i];
101
0
    }
102
0
  } else {
103
0
    const nsTArray<nsStyleCoord>& dasharray = aStyleSVG->mStrokeDasharray;
104
0
    dashArrayLength = aStyleSVG->mStrokeDasharray.Length();
105
0
    if (dashArrayLength <= 0) {
106
0
      return eContinuousStroke;
107
0
    }
108
0
    if (aElement->IsNodeOfType(nsINode::eSHAPE)) {
109
0
      pathScale = static_cast<SVGGeometryElement*>(aElement)->
110
0
        GetPathLengthScale(SVGGeometryElement::eForStroking);
111
0
      if (pathScale <= 0) {
112
0
        return eContinuousStroke;
113
0
      }
114
0
    }
115
0
    Float* dashPattern = aStrokeOptions->InitDashPattern(dashArrayLength);
116
0
    if (!dashPattern) {
117
0
      return eContinuousStroke;
118
0
    }
119
0
    for (uint32_t i = 0; i < dashArrayLength; i++) {
120
0
      Float dashLength =
121
0
        SVGContentUtils::CoordToFloat(aElement, dasharray[i]) * pathScale;
122
0
      if (dashLength < 0.0) {
123
0
        return eContinuousStroke; // invalid
124
0
      }
125
0
      dashPattern[i] = dashLength;
126
0
      (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashLength;
127
0
    }
128
0
  }
129
0
130
0
  // Now that aStrokeOptions.mDashPattern is fully initialized (we didn't
131
0
  // return early above) we can safely set mDashLength:
132
0
  aStrokeOptions->mDashLength = dashArrayLength;
133
0
134
0
  if ((dashArrayLength % 2) == 1) {
135
0
    // If we have a dash pattern with an odd number of lengths the pattern
136
0
    // repeats a second time, per the SVG spec., and as implemented by Moz2D.
137
0
    // When deciding whether to return eNoStroke or eContinuousStroke below we
138
0
    // need to take into account that in the repeat pattern the dashes become
139
0
    // gaps, and the gaps become dashes.
140
0
    Float origTotalLengthOfDashes = totalLengthOfDashes;
141
0
    totalLengthOfDashes += totalLengthOfGaps;
142
0
    totalLengthOfGaps += origTotalLengthOfDashes;
143
0
  }
144
0
145
0
  // Stroking using dashes is much slower than stroking a continuous line
146
0
  // (see bug 609361 comment 40), and much, much slower than not stroking the
147
0
  // line at all. Here we check for cases when the dash pattern causes the
148
0
  // stroke to essentially be continuous or to be nonexistent in which case
149
0
  // we can avoid expensive stroking operations (the underlying platform
150
0
  // graphics libraries don't seem to optimize for this).
151
0
  if (totalLengthOfGaps <= 0) {
152
0
    return eContinuousStroke;
153
0
  }
154
0
  // We can only return eNoStroke if the value of stroke-linecap isn't
155
0
  // adding caps to zero length dashes.
156
0
  if (totalLengthOfDashes <= 0 &&
157
0
      aStyleSVG->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_BUTT) {
158
0
    return eNoStroke;
159
0
  }
160
0
161
0
  if (aContextPaint && aStyleSVG->StrokeDashoffsetFromObject()) {
162
0
    aStrokeOptions->mDashOffset = Float(aContextPaint->GetStrokeDashOffset());
163
0
  } else {
164
0
    aStrokeOptions->mDashOffset =
165
0
      SVGContentUtils::CoordToFloat(aElement, aStyleSVG->mStrokeDashoffset) *
166
0
      pathScale;
167
0
  }
168
0
169
0
  return eDashedStroke;
170
0
}
171
172
void
173
SVGContentUtils::GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
174
                                  nsSVGElement* aElement,
175
                                  ComputedStyle* aComputedStyle,
176
                                  SVGContextPaint* aContextPaint,
177
                                  StrokeOptionFlags aFlags)
178
0
{
179
0
  RefPtr<ComputedStyle> computedStyle;
180
0
  if (aComputedStyle) {
181
0
    computedStyle = aComputedStyle;
182
0
  } else {
183
0
    computedStyle =
184
0
      nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
185
0
  }
186
0
187
0
  if (!computedStyle) {
188
0
    return;
189
0
  }
190
0
191
0
  const nsStyleSVG* styleSVG = computedStyle->StyleSVG();
192
0
193
0
  bool checkedDashAndStrokeIsDashed = false;
194
0
  if (aFlags != eIgnoreStrokeDashing) {
195
0
    DashState dashState =
196
0
      GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint);
197
0
198
0
    if (dashState == eNoStroke) {
199
0
      // Hopefully this will shortcircuit any stroke operations:
200
0
      aStrokeOptions->mLineWidth = 0;
201
0
      return;
202
0
    }
203
0
    if (dashState == eContinuousStroke && aStrokeOptions->mDashPattern) {
204
0
      // Prevent our caller from wasting time looking at a pattern without gaps:
205
0
      aStrokeOptions->DiscardDashPattern();
206
0
    }
207
0
    checkedDashAndStrokeIsDashed = (dashState == eDashedStroke);
208
0
  }
209
0
210
0
  aStrokeOptions->mLineWidth =
211
0
    GetStrokeWidth(aElement, computedStyle, aContextPaint);
212
0
213
0
  aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit);
214
0
215
0
  switch (styleSVG->mStrokeLinejoin) {
216
0
  case NS_STYLE_STROKE_LINEJOIN_MITER:
217
0
    aStrokeOptions->mLineJoin = JoinStyle::MITER_OR_BEVEL;
218
0
    break;
219
0
  case NS_STYLE_STROKE_LINEJOIN_ROUND:
220
0
    aStrokeOptions->mLineJoin = JoinStyle::ROUND;
221
0
    break;
222
0
  case NS_STYLE_STROKE_LINEJOIN_BEVEL:
223
0
    aStrokeOptions->mLineJoin = JoinStyle::BEVEL;
224
0
    break;
225
0
  }
226
0
227
0
  if (ShapeTypeHasNoCorners(aElement) && !checkedDashAndStrokeIsDashed) {
228
0
    // Note: if aFlags == eIgnoreStrokeDashing then we may be returning the
229
0
    // wrong linecap value here, since the actual linecap used on render in this
230
0
    // case depends on whether the stroke is dashed or not.
231
0
    aStrokeOptions->mLineCap = CapStyle::BUTT;
232
0
  } else {
233
0
    switch (styleSVG->mStrokeLinecap) {
234
0
      case NS_STYLE_STROKE_LINECAP_BUTT:
235
0
        aStrokeOptions->mLineCap = CapStyle::BUTT;
236
0
        break;
237
0
      case NS_STYLE_STROKE_LINECAP_ROUND:
238
0
        aStrokeOptions->mLineCap = CapStyle::ROUND;
239
0
        break;
240
0
      case NS_STYLE_STROKE_LINECAP_SQUARE:
241
0
        aStrokeOptions->mLineCap = CapStyle::SQUARE;
242
0
        break;
243
0
    }
244
0
  }
245
0
}
246
247
Float
248
SVGContentUtils::GetStrokeWidth(nsSVGElement* aElement,
249
                                ComputedStyle* aComputedStyle,
250
                                SVGContextPaint* aContextPaint)
251
0
{
252
0
  RefPtr<ComputedStyle> computedStyle;
253
0
  if (aComputedStyle) {
254
0
    computedStyle = aComputedStyle;
255
0
  } else {
256
0
    computedStyle =
257
0
      nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
258
0
  }
259
0
260
0
  if (!computedStyle) {
261
0
    return 0.0f;
262
0
  }
263
0
264
0
  const nsStyleSVG* styleSVG = computedStyle->StyleSVG();
265
0
266
0
  if (aContextPaint && styleSVG->StrokeWidthFromObject()) {
267
0
    return aContextPaint->GetStrokeWidth();
268
0
  }
269
0
270
0
  return SVGContentUtils::CoordToFloat(aElement, styleSVG->mStrokeWidth);
271
0
}
272
273
float
274
SVGContentUtils::GetFontSize(Element* aElement)
275
0
{
276
0
  if (!aElement) {
277
0
    return 1.0f;
278
0
  }
279
0
280
0
  nsPresContext* pc = nsContentUtils::GetContextForContent(aElement);
281
0
  if (!pc) {
282
0
    return 1.0f;
283
0
  }
284
0
285
0
  RefPtr<ComputedStyle> computedStyle =
286
0
    nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
287
0
  if (!computedStyle) {
288
0
    // ReportToConsole
289
0
    NS_WARNING("Couldn't get ComputedStyle for content in GetFontStyle");
290
0
    return 1.0f;
291
0
  }
292
0
293
0
  return GetFontSize(computedStyle, pc);
294
0
}
295
296
float
297
SVGContentUtils::GetFontSize(nsIFrame* aFrame)
298
0
{
299
0
  MOZ_ASSERT(aFrame, "NULL frame in GetFontSize");
300
0
  return GetFontSize(aFrame->Style(), aFrame->PresContext());
301
0
}
302
303
float
304
SVGContentUtils::GetFontSize(ComputedStyle* aComputedStyle,
305
                             nsPresContext* aPresContext)
306
0
{
307
0
  MOZ_ASSERT(aComputedStyle);
308
0
  MOZ_ASSERT(aPresContext);
309
0
310
0
  nscoord fontSize = aComputedStyle->StyleFont()->mSize;
311
0
  return nsPresContext::AppUnitsToFloatCSSPixels(fontSize) /
312
0
         aPresContext->EffectiveTextZoom();
313
0
}
314
315
float
316
SVGContentUtils::GetFontXHeight(Element* aElement)
317
0
{
318
0
  if (!aElement) {
319
0
    return 1.0f;
320
0
  }
321
0
322
0
  nsPresContext* pc = nsContentUtils::GetContextForContent(aElement);
323
0
  if (!pc) {
324
0
    return 1.0f;
325
0
  }
326
0
327
0
  RefPtr<ComputedStyle> style =
328
0
    nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
329
0
  if (!style) {
330
0
    // ReportToConsole
331
0
    NS_WARNING("Couldn't get ComputedStyle for content in GetFontStyle");
332
0
    return 1.0f;
333
0
  }
334
0
335
0
  return GetFontXHeight(style, pc);
336
0
}
337
338
float
339
SVGContentUtils::GetFontXHeight(nsIFrame *aFrame)
340
0
{
341
0
  MOZ_ASSERT(aFrame, "NULL frame in GetFontXHeight");
342
0
  return GetFontXHeight(aFrame->Style(), aFrame->PresContext());
343
0
}
344
345
float
346
SVGContentUtils::GetFontXHeight(ComputedStyle* aComputedStyle,
347
                                nsPresContext* aPresContext)
348
0
{
349
0
  MOZ_ASSERT(aComputedStyle && aPresContext);
350
0
351
0
  RefPtr<nsFontMetrics> fontMetrics =
352
0
    nsLayoutUtils::GetFontMetricsForComputedStyle(aComputedStyle, aPresContext);
353
0
354
0
  if (!fontMetrics) {
355
0
    // ReportToConsole
356
0
    NS_WARNING("no FontMetrics in GetFontXHeight()");
357
0
    return 1.0f;
358
0
  }
359
0
360
0
  nscoord xHeight = fontMetrics->XHeight();
361
0
  return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
362
0
         aPresContext->EffectiveTextZoom();
363
0
}
364
nsresult
365
SVGContentUtils::ReportToConsole(nsIDocument* doc,
366
                                 const char* aWarning,
367
                                 const char16_t **aParams,
368
                                 uint32_t aParamsLength)
369
0
{
370
0
  return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
371
0
                                         NS_LITERAL_CSTRING("SVG"), doc,
372
0
                                         nsContentUtils::eSVG_PROPERTIES,
373
0
                                         aWarning,
374
0
                                         aParams, aParamsLength);
375
0
}
376
377
bool
378
SVGContentUtils::EstablishesViewport(nsIContent *aContent)
379
0
{
380
0
  // Although SVG 1.1 states that <image> is an element that establishes a
381
0
  // viewport, this is really only for the document it references, not
382
0
  // for any child content, which is what this function is used for.
383
0
  return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::svg,
384
0
                                                  nsGkAtoms::foreignObject,
385
0
                                                  nsGkAtoms::symbol);
386
0
}
387
388
SVGViewportElement*
389
SVGContentUtils::GetNearestViewportElement(const nsIContent *aContent)
390
0
{
391
0
  nsIContent *element = aContent->GetFlattenedTreeParent();
392
0
393
0
  while (element && element->IsSVGElement()) {
394
0
    if (EstablishesViewport(element)) {
395
0
      if (element->IsSVGElement(nsGkAtoms::foreignObject)) {
396
0
        return nullptr;
397
0
      }
398
0
      MOZ_ASSERT(element->IsAnyOfSVGElements(nsGkAtoms::svg,
399
0
                                             nsGkAtoms::symbol),
400
0
                 "upcoming static_cast is only valid for "
401
0
                 "SVGViewportElement subclasses");
402
0
      return static_cast<SVGViewportElement*>(element);
403
0
    }
404
0
    element = element->GetFlattenedTreeParent();
405
0
  }
406
0
  return nullptr;
407
0
}
408
409
static gfx::Matrix
410
GetCTMInternal(nsSVGElement *aElement, bool aScreenCTM, bool aHaveRecursed)
411
0
{
412
0
  gfxMatrix matrix = aElement->PrependLocalTransformsTo(gfxMatrix(),
413
0
    aHaveRecursed ? eAllTransforms : eUserSpaceToParent);
414
0
  nsSVGElement *element = aElement;
415
0
  nsIContent *ancestor = aElement->GetFlattenedTreeParent();
416
0
417
0
  while (ancestor && ancestor->IsSVGElement() &&
418
0
                     !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
419
0
    element = static_cast<nsSVGElement*>(ancestor);
420
0
    matrix *= element->PrependLocalTransformsTo(gfxMatrix()); // i.e. *A*ppend
421
0
    if (!aScreenCTM && SVGContentUtils::EstablishesViewport(element)) {
422
0
      if (!element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG) &&
423
0
          !element->NodeInfo()->Equals(nsGkAtoms::symbol, kNameSpaceID_SVG)) {
424
0
        NS_ERROR("New (SVG > 1.1) SVG viewport establishing element?");
425
0
        return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
426
0
      }
427
0
      // XXX spec seems to say x,y translation should be undone for IsInnerSVG
428
0
      return gfx::ToMatrix(matrix);
429
0
    }
430
0
    ancestor = ancestor->GetFlattenedTreeParent();
431
0
  }
432
0
  if (!aScreenCTM) {
433
0
    // didn't find a nearestViewportElement
434
0
    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
435
0
  }
436
0
  if (!element->IsSVGElement(nsGkAtoms::svg)) {
437
0
    // Not a valid SVG fragment
438
0
    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
439
0
  }
440
0
  if (element == aElement && !aHaveRecursed) {
441
0
    // We get here when getScreenCTM() is called on an outer-<svg>.
442
0
    // Consistency with other elements would have us include only the
443
0
    // eFromUserSpace transforms, but we include the eAllTransforms
444
0
    // transforms in this case since that's what we've been doing for
445
0
    // a while, and it keeps us consistent with WebKit and Opera (if not
446
0
    // really with the ambiguous spec).
447
0
    matrix = aElement->PrependLocalTransformsTo(gfxMatrix());
448
0
  }
449
0
  if (!ancestor || !ancestor->IsElement()) {
450
0
    return gfx::ToMatrix(matrix);
451
0
  }
452
0
  if (ancestor->IsSVGElement()) {
453
0
    return
454
0
      gfx::ToMatrix(matrix) * GetCTMInternal(static_cast<nsSVGElement*>(ancestor), true, true);
455
0
  }
456
0
457
0
  // XXX this does not take into account CSS transform, or that the non-SVG
458
0
  // content that we've hit may itself be inside an SVG foreignObject higher up
459
0
  nsIDocument* currentDoc = aElement->GetComposedDoc();
460
0
  float x = 0.0f, y = 0.0f;
461
0
  if (currentDoc && element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
462
0
    nsIPresShell *presShell = currentDoc->GetShell();
463
0
    if (presShell) {
464
0
      nsIFrame* frame = element->GetPrimaryFrame();
465
0
      nsIFrame* ancestorFrame = presShell->GetRootFrame();
466
0
      if (frame && ancestorFrame) {
467
0
        nsPoint point = frame->GetOffsetTo(ancestorFrame);
468
0
        x = nsPresContext::AppUnitsToFloatCSSPixels(point.x);
469
0
        y = nsPresContext::AppUnitsToFloatCSSPixels(point.y);
470
0
      }
471
0
    }
472
0
  }
473
0
  return ToMatrix(matrix).PostTranslate(x, y);
474
0
}
475
476
gfx::Matrix
477
SVGContentUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM)
478
0
{
479
0
  return GetCTMInternal(aElement, aScreenCTM, false);
480
0
}
481
482
void
483
SVGContentUtils::RectilinearGetStrokeBounds(const Rect& aRect,
484
                                            const Matrix& aToBoundsSpace,
485
                                            const Matrix& aToNonScalingStrokeSpace,
486
                                            float aStrokeWidth,
487
                                            Rect* aBounds)
488
0
{
489
0
  MOZ_ASSERT(aToBoundsSpace.IsRectilinear(),
490
0
             "aToBoundsSpace must be rectilinear");
491
0
  MOZ_ASSERT(aToNonScalingStrokeSpace.IsRectilinear(),
492
0
             "aToNonScalingStrokeSpace must be rectilinear");
493
0
494
0
  Matrix nonScalingToSource = aToNonScalingStrokeSpace.Inverse();
495
0
  Matrix nonScalingToBounds = nonScalingToSource * aToBoundsSpace;
496
0
497
0
  *aBounds = aToBoundsSpace.TransformBounds(aRect);
498
0
499
0
  // Compute the amounts dx and dy that nonScalingToBounds scales a half-width
500
0
  // stroke in the x and y directions, and then inflate aBounds by those amounts
501
0
  // so that when aBounds is transformed back to non-scaling-stroke space
502
0
  // it will map onto the correct stroked bounds.
503
0
504
0
  Float dx = 0.0f;
505
0
  Float dy = 0.0f;
506
0
  // nonScalingToBounds is rectilinear, so either _12 and _21 are zero or _11
507
0
  // and _22 are zero, and in each case the non-zero entries (from among _11,
508
0
  // _12, _21, _22) simply scale the stroke width in the x and y directions.
509
0
  if (FuzzyEqual(nonScalingToBounds._12, 0) &&
510
0
      FuzzyEqual(nonScalingToBounds._21, 0)) {
511
0
    dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._11);
512
0
    dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._22);
513
0
  } else {
514
0
    dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._21);
515
0
    dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._12);
516
0
  }
517
0
518
0
  aBounds->Inflate(dx, dy);
519
0
}
520
521
double
522
SVGContentUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight)
523
0
{
524
0
  return NS_hypot(aWidth, aHeight) / M_SQRT2;
525
0
}
526
527
float
528
SVGContentUtils::AngleBisect(float a1, float a2)
529
0
{
530
0
  float delta = fmod(a2 - a1, static_cast<float>(2*M_PI));
531
0
  if (delta < 0) {
532
0
    delta += static_cast<float>(2*M_PI);
533
0
  }
534
0
  /* delta is now the angle from a1 around to a2, in the range [0, 2*M_PI) */
535
0
  float r = a1 + delta/2;
536
0
  if (delta >= M_PI) {
537
0
    /* the arc from a2 to a1 is smaller, so use the ray on that side */
538
0
    r += static_cast<float>(M_PI);
539
0
  }
540
0
  return r;
541
0
}
542
543
gfx::Matrix
544
SVGContentUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
545
                                     float aViewboxX, float aViewboxY,
546
                                     float aViewboxWidth, float aViewboxHeight,
547
                                     const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio)
548
0
{
549
0
  return GetViewBoxTransform(aViewportWidth, aViewportHeight,
550
0
                             aViewboxX, aViewboxY,
551
0
                             aViewboxWidth, aViewboxHeight,
552
0
                             aPreserveAspectRatio.GetAnimValue());
553
0
}
554
555
gfx::Matrix
556
SVGContentUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
557
                                     float aViewboxX, float aViewboxY,
558
                                     float aViewboxWidth, float aViewboxHeight,
559
                                     const SVGPreserveAspectRatio &aPreserveAspectRatio)
560
0
{
561
0
  NS_ASSERTION(aViewportWidth  >= 0, "viewport width must be nonnegative!");
562
0
  NS_ASSERTION(aViewportHeight >= 0, "viewport height must be nonnegative!");
563
0
  NS_ASSERTION(aViewboxWidth  > 0, "viewBox width must be greater than zero!");
564
0
  NS_ASSERTION(aViewboxHeight > 0, "viewBox height must be greater than zero!");
565
0
566
0
  uint16_t align = aPreserveAspectRatio.GetAlign();
567
0
  uint16_t meetOrSlice = aPreserveAspectRatio.GetMeetOrSlice();
568
0
569
0
  // default to the defaults
570
0
  if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN)
571
0
    align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
572
0
  if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN)
573
0
    meetOrSlice = SVG_MEETORSLICE_MEET;
574
0
575
0
  float a, d, e, f;
576
0
  a = aViewportWidth / aViewboxWidth;
577
0
  d = aViewportHeight / aViewboxHeight;
578
0
  e = 0.0f;
579
0
  f = 0.0f;
580
0
581
0
  if (align != SVG_PRESERVEASPECTRATIO_NONE &&
582
0
      a != d) {
583
0
    if ((meetOrSlice == SVG_MEETORSLICE_MEET && a < d) ||
584
0
        (meetOrSlice == SVG_MEETORSLICE_SLICE && d < a)) {
585
0
      d = a;
586
0
      switch (align) {
587
0
      case SVG_PRESERVEASPECTRATIO_XMINYMIN:
588
0
      case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
589
0
      case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
590
0
        break;
591
0
      case SVG_PRESERVEASPECTRATIO_XMINYMID:
592
0
      case SVG_PRESERVEASPECTRATIO_XMIDYMID:
593
0
      case SVG_PRESERVEASPECTRATIO_XMAXYMID:
594
0
        f = (aViewportHeight - a * aViewboxHeight) / 2.0f;
595
0
        break;
596
0
      case SVG_PRESERVEASPECTRATIO_XMINYMAX:
597
0
      case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
598
0
      case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
599
0
        f = aViewportHeight - a * aViewboxHeight;
600
0
        break;
601
0
      default:
602
0
        MOZ_ASSERT_UNREACHABLE("Unknown value for align");
603
0
      }
604
0
    }
605
0
    else if (
606
0
      (meetOrSlice == SVG_MEETORSLICE_MEET &&
607
0
      d < a) ||
608
0
      (meetOrSlice == SVG_MEETORSLICE_SLICE &&
609
0
      a < d)) {
610
0
      a = d;
611
0
      switch (align) {
612
0
      case SVG_PRESERVEASPECTRATIO_XMINYMIN:
613
0
      case SVG_PRESERVEASPECTRATIO_XMINYMID:
614
0
      case SVG_PRESERVEASPECTRATIO_XMINYMAX:
615
0
        break;
616
0
      case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
617
0
      case SVG_PRESERVEASPECTRATIO_XMIDYMID:
618
0
      case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
619
0
        e = (aViewportWidth - a * aViewboxWidth) / 2.0f;
620
0
        break;
621
0
      case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
622
0
      case SVG_PRESERVEASPECTRATIO_XMAXYMID:
623
0
      case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
624
0
        e = aViewportWidth - a * aViewboxWidth;
625
0
        break;
626
0
      default:
627
0
        MOZ_ASSERT_UNREACHABLE("Unknown value for align");
628
0
      }
629
0
    }
630
0
    else MOZ_ASSERT_UNREACHABLE("Unknown value for meetOrSlice");
631
0
  }
632
0
633
0
  if (aViewboxX) e += -a * aViewboxX;
634
0
  if (aViewboxY) f += -d * aViewboxY;
635
0
636
0
  return gfx::Matrix(a, 0.0f, 0.0f, d, e, f);
637
0
}
638
639
static bool
640
ParseNumber(RangedPtr<const char16_t>& aIter,
641
            const RangedPtr<const char16_t>& aEnd,
642
            double& aValue)
643
0
{
644
0
  int32_t sign;
645
0
  if (!SVGContentUtils::ParseOptionalSign(aIter, aEnd, sign)) {
646
0
    return false;
647
0
  }
648
0
649
0
  // Absolute value of the integer part of the mantissa.
650
0
  double intPart = 0.0;
651
0
652
0
  bool gotDot = *aIter == '.';
653
0
654
0
  if (!gotDot) {
655
0
    if (!mozilla::IsAsciiDigit(*aIter)) {
656
0
      return false;
657
0
    }
658
0
    do {
659
0
      intPart = 10.0 * intPart + mozilla::AsciiAlphanumericToNumber(*aIter);
660
0
      ++aIter;
661
0
    } while (aIter != aEnd && mozilla::IsAsciiDigit(*aIter));
662
0
663
0
    if (aIter != aEnd) {
664
0
      gotDot = *aIter == '.';
665
0
    }
666
0
  }
667
0
668
0
  // Fractional part of the mantissa.
669
0
  double fracPart = 0.0;
670
0
671
0
  if (gotDot) {
672
0
    ++aIter;
673
0
    if (aIter == aEnd || !mozilla::IsAsciiDigit(*aIter)) {
674
0
      return false;
675
0
    }
676
0
677
0
    // Power of ten by which we need to divide the fraction
678
0
    double divisor = 1.0;
679
0
680
0
    do {
681
0
      fracPart = 10.0 * fracPart + mozilla::AsciiAlphanumericToNumber(*aIter);
682
0
      divisor *= 10.0;
683
0
      ++aIter;
684
0
    } while (aIter != aEnd && mozilla::IsAsciiDigit(*aIter));
685
0
686
0
    fracPart /= divisor;
687
0
  }
688
0
689
0
  bool gotE = false;
690
0
  int32_t exponent = 0;
691
0
  int32_t expSign;
692
0
693
0
  if (aIter != aEnd && (*aIter == 'e' || *aIter == 'E')) {
694
0
695
0
    RangedPtr<const char16_t> expIter(aIter);
696
0
697
0
    ++expIter;
698
0
    if (expIter != aEnd) {
699
0
      expSign = *expIter == '-' ? -1 : 1;
700
0
      if (*expIter == '-' || *expIter == '+') {
701
0
        ++expIter;
702
0
      }
703
0
      if (expIter != aEnd && mozilla::IsAsciiDigit(*expIter)) {
704
0
        // At this point we're sure this is an exponent
705
0
        // and not the start of a unit such as em or ex.
706
0
        gotE = true;
707
0
      }
708
0
    }
709
0
710
0
    if (gotE) {
711
0
      aIter = expIter;
712
0
      do {
713
0
        exponent = 10.0 * exponent + mozilla::AsciiAlphanumericToNumber(*aIter);
714
0
        ++aIter;
715
0
      } while (aIter != aEnd && mozilla::IsAsciiDigit(*aIter));
716
0
    }
717
0
  }
718
0
719
0
  // Assemble the number
720
0
  aValue = sign * (intPart + fracPart);
721
0
  if (gotE) {
722
0
    aValue *= pow(10.0, expSign * exponent);
723
0
  }
724
0
  return true;
725
0
}
726
727
template<class floatType>
728
bool
729
SVGContentUtils::ParseNumber(RangedPtr<const char16_t>& aIter,
730
                             const RangedPtr<const char16_t>& aEnd,
731
                             floatType& aValue)
732
0
{
733
0
  RangedPtr<const char16_t> iter(aIter);
734
0
735
0
  double value;
736
0
  if (!::ParseNumber(iter, aEnd, value)) {
737
0
    return false;
738
0
  }
739
0
  floatType floatValue = floatType(value);
740
0
  if (!IsFinite(floatValue)) {
741
0
    return false;
742
0
  }
743
0
  aValue = floatValue;
744
0
  aIter = iter;
745
0
  return true;
746
0
}
Unexecuted instantiation: bool SVGContentUtils::ParseNumber<float>(mozilla::RangedPtr<char16_t const>&, mozilla::RangedPtr<char16_t const> const&, float&)
Unexecuted instantiation: bool SVGContentUtils::ParseNumber<double>(mozilla::RangedPtr<char16_t const>&, mozilla::RangedPtr<char16_t const> const&, double&)
747
748
template bool
749
SVGContentUtils::ParseNumber<float>(RangedPtr<const char16_t>& aIter,
750
                                    const RangedPtr<const char16_t>& aEnd,
751
                                    float& aValue);
752
753
template bool
754
SVGContentUtils::ParseNumber<double>(RangedPtr<const char16_t>& aIter,
755
                                     const RangedPtr<const char16_t>& aEnd,
756
                                     double& aValue);
757
758
RangedPtr<const char16_t>
759
SVGContentUtils::GetStartRangedPtr(const nsAString& aString)
760
0
{
761
0
  return RangedPtr<const char16_t>(aString.Data(), aString.Length());
762
0
}
763
764
RangedPtr<const char16_t>
765
SVGContentUtils::GetEndRangedPtr(const nsAString& aString)
766
0
{
767
0
  return RangedPtr<const char16_t>(aString.Data() + aString.Length(),
768
0
                                    aString.Data(), aString.Length());
769
0
}
770
771
template<class floatType>
772
bool
773
SVGContentUtils::ParseNumber(const nsAString& aString,
774
                             floatType& aValue)
775
0
{
776
0
  RangedPtr<const char16_t> iter = GetStartRangedPtr(aString);
777
0
  const RangedPtr<const char16_t> end = GetEndRangedPtr(aString);
778
0
779
0
  return ParseNumber(iter, end, aValue) && iter == end;
780
0
}
Unexecuted instantiation: bool SVGContentUtils::ParseNumber<float>(nsTSubstring<char16_t> const&, float&)
Unexecuted instantiation: bool SVGContentUtils::ParseNumber<double>(nsTSubstring<char16_t> const&, double&)
781
782
template bool
783
SVGContentUtils::ParseNumber<float>(const nsAString& aString,
784
                                    float& aValue);
785
template bool
786
SVGContentUtils::ParseNumber<double>(const nsAString& aString,
787
                                     double& aValue);
788
789
/* static */
790
bool
791
SVGContentUtils::ParseInteger(RangedPtr<const char16_t>& aIter,
792
                              const RangedPtr<const char16_t>& aEnd,
793
                              int32_t& aValue)
794
0
{
795
0
  RangedPtr<const char16_t> iter(aIter);
796
0
797
0
  int32_t sign;
798
0
  if (!ParseOptionalSign(iter, aEnd, sign)) {
799
0
    return false;
800
0
  }
801
0
802
0
  if (!mozilla::IsAsciiDigit(*iter)) {
803
0
    return false;
804
0
  }
805
0
806
0
  int64_t value = 0;
807
0
808
0
  do {
809
0
    if (value <= std::numeric_limits<int32_t>::max()) {
810
0
      value = 10 * value + mozilla::AsciiAlphanumericToNumber(*iter);
811
0
    }
812
0
    ++iter;
813
0
  } while (iter != aEnd && mozilla::IsAsciiDigit(*iter));
814
0
815
0
  aIter = iter;
816
0
  aValue = int32_t(clamped(sign * value,
817
0
                           int64_t(std::numeric_limits<int32_t>::min()),
818
0
                           int64_t(std::numeric_limits<int32_t>::max())));
819
0
  return true;
820
0
}
821
822
/* static */
823
bool
824
SVGContentUtils::ParseInteger(const nsAString& aString,
825
                              int32_t& aValue)
826
0
{
827
0
  RangedPtr<const char16_t> iter = GetStartRangedPtr(aString);
828
0
  const RangedPtr<const char16_t> end = GetEndRangedPtr(aString);
829
0
830
0
  return ParseInteger(iter, end, aValue) && iter == end;
831
0
}
832
833
float
834
SVGContentUtils::CoordToFloat(nsSVGElement *aContent,
835
                              const nsStyleCoord &aCoord)
836
0
{
837
0
  switch (aCoord.GetUnit()) {
838
0
  case eStyleUnit_Factor:
839
0
    // user units
840
0
    return aCoord.GetFactorValue();
841
0
842
0
  case eStyleUnit_Coord:
843
0
    return nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue());
844
0
845
0
  case eStyleUnit_Percent: {
846
0
    SVGViewportElement* ctx = aContent->GetCtx();
847
0
    return ctx ? aCoord.GetPercentValue() * ctx->GetLength(SVGContentUtils::XY) : 0.0f;
848
0
  }
849
0
  default:
850
0
    return 0.0f;
851
0
  }
852
0
}
853
854
already_AddRefed<gfx::Path>
855
SVGContentUtils::GetPath(const nsAString& aPathString)
856
0
{
857
0
  SVGPathData pathData;
858
0
  nsSVGPathDataParser parser(aPathString, &pathData);
859
0
  if (!parser.Parse()) {
860
0
    return NULL;
861
0
  }
862
0
863
0
  RefPtr<DrawTarget> drawTarget =
864
0
    gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
865
0
  RefPtr<PathBuilder> builder =
866
0
    drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
867
0
868
0
  return pathData.BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 1);
869
0
}
870
871
bool
872
0
SVGContentUtils::ShapeTypeHasNoCorners(const nsIContent* aContent) {
873
0
  return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::circle,
874
0
                                                  nsGkAtoms::ellipse);
875
0
}