Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/SVGViewportElement.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
#include <stdint.h>
8
#include "mozilla/ArrayUtils.h"
9
#include "mozilla/ContentEvents.h"
10
#include "mozilla/EventDispatcher.h"
11
#include "mozilla/Likely.h"
12
#include "mozilla/dom/SVGLengthBinding.h"
13
#include "mozilla/dom/SVGMatrix.h"
14
#include "mozilla/dom/SVGViewportElement.h"
15
#include "mozilla/dom/SVGViewElement.h"
16
17
#include "DOMSVGLength.h"
18
#include "DOMSVGPoint.h"
19
#include "nsCOMPtr.h"
20
#include "nsContentUtils.h"
21
#include "nsFrameSelection.h"
22
#include "nsError.h"
23
#include "nsGkAtoms.h"
24
#include "nsIDocument.h"
25
#include "nsIFrame.h"
26
#include "nsIPresShell.h"
27
#include "nsISVGSVGFrame.h" //XXX
28
#include "nsLayoutUtils.h"
29
#include "nsStyleUtil.h"
30
#include "nsSMILTypes.h"
31
#include "SVGContentUtils.h"
32
33
#include <algorithm>
34
#include "prtime.h"
35
36
using namespace mozilla::gfx;
37
38
namespace mozilla {
39
namespace dom {
40
41
nsSVGElement::LengthInfo SVGViewportElement::sLengthInfo[4] =
42
{
43
  { &nsGkAtoms::x, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
44
  { &nsGkAtoms::y, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
45
  { &nsGkAtoms::width, 100, SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::X },
46
  { &nsGkAtoms::height, 100, SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE, SVGContentUtils::Y },
47
};
48
49
//----------------------------------------------------------------------
50
// Implementation
51
52
SVGViewportElement::SVGViewportElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
53
  : SVGGraphicsElement(std::move(aNodeInfo)),
54
    mViewportWidth(0),
55
    mViewportHeight(0),
56
    mHasChildrenOnlyTransform(false)
57
0
{
58
0
}
59
60
SVGViewportElement::~SVGViewportElement()
61
0
{
62
0
}
63
64
//----------------------------------------------------------------------
65
66
already_AddRefed<SVGAnimatedRect>
67
SVGViewportElement::ViewBox()
68
0
{
69
0
  return mViewBox.ToSVGAnimatedRect(this);
70
0
}
71
72
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
73
SVGViewportElement::PreserveAspectRatio()
74
0
{
75
0
  return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
76
0
}
77
78
//----------------------------------------------------------------------
79
// nsIContent methods
80
81
NS_IMETHODIMP_(bool)
82
SVGViewportElement::IsAttributeMapped(const nsAtom* name) const
83
0
{
84
0
  // We want to map the 'width' and 'height' attributes into style for
85
0
  // outer-<svg>, except when the attributes aren't set (since their default
86
0
  // values of '100%' can cause unexpected and undesirable behaviour for SVG
87
0
  // inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to
88
0
  // prevent mapping of the default values into style (it only maps attributes
89
0
  // that are set). We also rely on a check in nsSVGElement::
90
0
  // UpdateContentStyleRule() to prevent us mapping the attributes when they're
91
0
  // given a <length> value that is not currently recognized by the SVG
92
0
  // specification.
93
0
94
0
  if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) {
95
0
    return true;
96
0
  }
97
0
98
0
  static const MappedAttributeEntry* const map[] = {
99
0
    sColorMap,
100
0
    sFEFloodMap,
101
0
    sFillStrokeMap,
102
0
    sFiltersMap,
103
0
    sFontSpecificationMap,
104
0
    sGradientStopMap,
105
0
    sGraphicsMap,
106
0
    sLightingEffectsMap,
107
0
    sMarkersMap,
108
0
    sTextContentElementsMap,
109
0
    sViewportsMap
110
0
  };
111
0
112
0
  return FindAttributeDependence(name, map) ||
113
0
    SVGGraphicsElement::IsAttributeMapped(name);
114
0
}
115
116
//----------------------------------------------------------------------
117
// nsSVGElement overrides
118
119
// Helper for GetViewBoxTransform on root <svg> node
120
// * aLength: internal value for our <svg> width or height attribute.
121
// * aViewportLength: length of the corresponding dimension of the viewport.
122
// * aSelf: the outermost <svg> node itself.
123
// NOTE: aSelf is not an ancestor viewport element, so it can't be used to
124
// resolve percentage lengths. (It can only be used to resolve
125
// 'em'/'ex'-valued units).
126
inline float
127
ComputeSynthesizedViewBoxDimension(const nsSVGLength2& aLength,
128
                                   float aViewportLength,
129
                                   const SVGViewportElement* aSelf)
130
0
{
131
0
  if (aLength.IsPercentage()) {
132
0
    return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f;
133
0
  }
134
0
135
0
  return aLength.GetAnimValue(const_cast<SVGViewportElement*>(aSelf));
136
0
}
137
138
//----------------------------------------------------------------------
139
// public helpers:
140
141
void
142
SVGViewportElement::UpdateHasChildrenOnlyTransform()
143
0
{
144
0
  bool hasChildrenOnlyTransform =
145
0
    HasViewBoxOrSyntheticViewBox() ||
146
0
    (IsRoot() && (GetCurrentTranslate() != SVGPoint(0.0f, 0.0f) ||
147
0
                  GetCurrentScale() != 1.0f));
148
0
  mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
149
0
}
150
151
void
152
SVGViewportElement::ChildrenOnlyTransformChanged(uint32_t aFlags)
153
0
{
154
0
  // Avoid wasteful calls:
155
0
  MOZ_ASSERT(!(GetPrimaryFrame()->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
156
0
             "Non-display SVG frames don't maintain overflow rects");
157
0
158
0
  nsChangeHint changeHint;
159
0
160
0
  bool hadChildrenOnlyTransform = mHasChildrenOnlyTransform;
161
0
162
0
  UpdateHasChildrenOnlyTransform();
163
0
164
0
  if (hadChildrenOnlyTransform != mHasChildrenOnlyTransform) {
165
0
    // Reconstruct the frame tree to handle stacking context changes:
166
0
    // XXXjwatt don't do this for root-<svg> or even outer-<svg>?
167
0
    changeHint = nsChangeHint_ReconstructFrame;
168
0
  } else {
169
0
    // We just assume the old and new transforms are different.
170
0
    changeHint = nsChangeHint(nsChangeHint_UpdateOverflow |
171
0
                              nsChangeHint_ChildrenOnlyTransform);
172
0
  }
173
0
174
0
  // If we're not reconstructing the frame tree, then we only call
175
0
  // PostRestyleEvent if we're not being called under reflow to avoid recursing
176
0
  // to death. See bug 767056 comments 10 and 12. Since our nsSVGOuterSVGFrame
177
0
  // is being reflowed we're going to invalidate and repaint its entire area
178
0
  // anyway (which will include our children).
179
0
  if ((changeHint & nsChangeHint_ReconstructFrame) ||
180
0
      !(aFlags & eDuringReflow)) {
181
0
    nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
182
0
  }
183
0
}
184
185
gfx::Matrix
186
SVGViewportElement::GetViewBoxTransform() const
187
0
{
188
0
  float viewportWidth, viewportHeight;
189
0
  if (IsInner()) {
190
0
    SVGViewportElement *ctx = GetCtx();
191
0
    viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
192
0
    viewportHeight = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
193
0
  } else {
194
0
    viewportWidth = mViewportWidth;
195
0
    viewportHeight = mViewportHeight;
196
0
  }
197
0
198
0
  if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
199
0
    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
200
0
  }
201
0
202
0
  nsSVGViewBoxRect viewBox =
203
0
    GetViewBoxWithSynthesis(viewportWidth, viewportHeight);
204
0
205
0
  if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
206
0
    return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
207
0
  }
208
0
209
0
  return SVGContentUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
210
0
                                              viewBox.x, viewBox.y,
211
0
                                              viewBox.width, viewBox.height,
212
0
                                              GetPreserveAspectRatioWithOverride());
213
0
}
214
//----------------------------------------------------------------------
215
// SVGViewportElement
216
217
float
218
SVGViewportElement::GetLength(uint8_t aCtxType)
219
0
{
220
0
  const nsSVGViewBoxRect* viewbox =
221
0
    GetViewBoxInternal().HasRect() ? &GetViewBoxInternal().GetAnimValue()
222
0
                                   : nullptr;
223
0
224
0
  float h, w;
225
0
  if (viewbox) {
226
0
    w = viewbox->width;
227
0
    h = viewbox->height;
228
0
  } else if (IsInner()) {
229
0
    SVGViewportElement *ctx = GetCtx();
230
0
    w = mLengthAttributes[ATTR_WIDTH].GetAnimValue(ctx);
231
0
    h = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(ctx);
232
0
  } else if (ShouldSynthesizeViewBox()) {
233
0
    w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
234
0
                                           mViewportWidth, this);
235
0
    h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
236
0
                                           mViewportHeight, this);
237
0
  } else {
238
0
    w = mViewportWidth;
239
0
    h = mViewportHeight;
240
0
  }
241
0
242
0
  w = std::max(w, 0.0f);
243
0
  h = std::max(h, 0.0f);
244
0
245
0
  switch (aCtxType) {
246
0
  case SVGContentUtils::X:
247
0
    return w;
248
0
  case SVGContentUtils::Y:
249
0
    return h;
250
0
  case SVGContentUtils::XY:
251
0
    return float(SVGContentUtils::ComputeNormalizedHypotenuse(w, h));
252
0
  }
253
0
  return 0;
254
0
}
255
256
//----------------------------------------------------------------------
257
// nsSVGElement methods
258
259
/* virtual */ gfxMatrix
260
SVGViewportElement::PrependLocalTransformsTo(const gfxMatrix& aMatrix,
261
                                        SVGTransformTypes aWhich) const
262
0
{
263
0
  // 'transform' attribute (or an override from a fragment identifier):
264
0
  gfxMatrix userToParent;
265
0
266
0
  if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
267
0
    userToParent = GetUserToParentTransform(mAnimateMotionTransform,
268
0
                                            GetTransformInternal());
269
0
    if (aWhich == eUserSpaceToParent) {
270
0
      return userToParent * aMatrix;
271
0
    }
272
0
  }
273
0
274
0
  gfxMatrix childToUser;
275
0
276
0
  if (IsInner()) {
277
0
    float x, y;
278
0
    const_cast<SVGViewportElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
279
0
    childToUser = ThebesMatrix(GetViewBoxTransform().PostTranslate(x, y));
280
0
  } else if (IsRoot()) {
281
0
    SVGPoint translate = GetCurrentTranslate();
282
0
    float scale = GetCurrentScale();
283
0
    childToUser = ThebesMatrix(GetViewBoxTransform()
284
0
                                 .PostScale(scale, scale)
285
0
                                 .PostTranslate(translate.GetX(),
286
0
                                                translate.GetY()));
287
0
  } else {
288
0
    // outer-<svg>, but inline in some other content:
289
0
    childToUser = ThebesMatrix(GetViewBoxTransform());
290
0
  }
291
0
292
0
  if (aWhich == eAllTransforms) {
293
0
    return childToUser * userToParent * aMatrix;
294
0
  }
295
0
296
0
  MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
297
0
298
0
  // The following may look broken because pre-multiplying our eChildToUserSpace
299
0
  // transform with another matrix without including our eUserSpaceToParent
300
0
  // transform between the two wouldn't make sense.  We don't expect that to
301
0
  // ever happen though.  We get here either when the identity matrix has been
302
0
  // passed because our caller just wants our eChildToUserSpace transform, or
303
0
  // when our eUserSpaceToParent transform has already been multiplied into the
304
0
  // matrix that our caller passes (such as when we're called from PaintSVG).
305
0
  return childToUser * aMatrix;
306
0
}
307
308
/* virtual */ bool
309
SVGViewportElement::HasValidDimensions() const
310
0
{
311
0
  return !IsInner() ||
312
0
    ((!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
313
0
       mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
314
0
     (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
315
0
       mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0));
316
0
}
317
318
319
320
nsSVGViewBox*
321
SVGViewportElement::GetViewBox()
322
0
{
323
0
  return &mViewBox;
324
0
}
325
326
SVGAnimatedPreserveAspectRatio *
327
SVGViewportElement::GetPreserveAspectRatio()
328
0
{
329
0
  return &mPreserveAspectRatio;
330
0
}
331
332
bool
333
SVGViewportElement::ShouldSynthesizeViewBox() const
334
0
{
335
0
  MOZ_ASSERT(!HasViewBoxRect(), "Should only be called if we lack a viewBox");
336
0
337
0
  return IsRoot() && OwnerDoc()->IsBeingUsedAsImage();
338
0
}
339
340
//----------------------------------------------------------------------
341
// implementation helpers
342
343
nsSVGViewBoxRect
344
SVGViewportElement::GetViewBoxWithSynthesis(
345
  float aViewportWidth, float aViewportHeight) const
346
0
{
347
0
  if (GetViewBoxInternal().HasRect()) {
348
0
    return GetViewBoxInternal().GetAnimValue();
349
0
  }
350
0
351
0
  if (ShouldSynthesizeViewBox()) {
352
0
    // Special case -- fake a viewBox, using height & width attrs.
353
0
    // (Use |this| as context, since if we get here, we're outermost <svg>.)
354
0
    return nsSVGViewBoxRect(0, 0,
355
0
              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
356
0
                                                 mViewportWidth, this),
357
0
              ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
358
0
                                                 mViewportHeight, this));
359
0
360
0
  }
361
0
362
0
  // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
363
0
  // to having a viewBox that exactly matches our viewport size.
364
0
  return nsSVGViewBoxRect(0, 0, aViewportWidth, aViewportHeight);
365
0
}
366
367
nsSVGElement::LengthAttributesInfo
368
SVGViewportElement::GetLengthInfo()
369
0
{
370
0
  return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
371
0
                              ArrayLength(sLengthInfo));
372
0
}
373
374
} // namespace dom
375
} // namespace mozilla