Bug 1691109 [wpt PR 27513] - Increase timeout duration for wpt/fetch/api/basic/keepal...
[gecko.git] / dom / svg / SVGViewportElement.cpp
blobc2d0044fd1c723681101d34c79931b6457e78201
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/. */
7 #include "mozilla/dom/SVGViewportElement.h"
9 #include <stdint.h>
10 #include "mozilla/AlreadyAddRefed.h"
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/ContentEvents.h"
13 #include "mozilla/EventDispatcher.h"
14 #include "mozilla/Likely.h"
15 #include "mozilla/SMILTypes.h"
16 #include "mozilla/SVGContentUtils.h"
17 #include "mozilla/dom/Document.h"
18 #include "mozilla/dom/SVGLengthBinding.h"
19 #include "mozilla/dom/SVGViewElement.h"
21 #include "DOMSVGLength.h"
22 #include "DOMSVGPoint.h"
23 #include "nsContentUtils.h"
24 #include "nsFrameSelection.h"
25 #include "nsError.h"
26 #include "nsGkAtoms.h"
27 #include "nsIFrame.h"
28 #include "nsLayoutUtils.h"
29 #include "nsStyleUtil.h"
31 #include <algorithm>
32 #include "prtime.h"
34 using namespace mozilla::gfx;
36 namespace mozilla {
37 namespace dom {
39 SVGElement::LengthInfo SVGViewportElement::sLengthInfo[4] = {
40 {nsGkAtoms::x, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
41 SVGContentUtils::X},
42 {nsGkAtoms::y, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
43 SVGContentUtils::Y},
44 {nsGkAtoms::width, 100, SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE,
45 SVGContentUtils::X},
46 {nsGkAtoms::height, 100, SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE,
47 SVGContentUtils::Y},
50 //----------------------------------------------------------------------
51 // Implementation
53 SVGViewportElement::SVGViewportElement(
54 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
55 : SVGGraphicsElement(std::move(aNodeInfo)),
56 mViewportWidth(0),
57 mViewportHeight(0),
58 mHasChildrenOnlyTransform(false) {}
60 //----------------------------------------------------------------------
62 already_AddRefed<SVGAnimatedRect> SVGViewportElement::ViewBox() {
63 return mViewBox.ToSVGAnimatedRect(this);
66 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
67 SVGViewportElement::PreserveAspectRatio() {
68 return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
71 bool SVGViewportElement::IsNodeOfType(uint32_t aFlags) const {
72 return !(aFlags & ~eUSE_TARGET);
75 //----------------------------------------------------------------------
76 // nsIContent methods
78 NS_IMETHODIMP_(bool)
79 SVGViewportElement::IsAttributeMapped(const nsAtom* name) const {
80 // We want to map the 'width' and 'height' attributes into style for
81 // outer-<svg>, except when the attributes aren't set (since their default
82 // values of '100%' can cause unexpected and undesirable behaviour for SVG
83 // inline in HTML). We rely on SVGElement::UpdateContentStyleRule() to
84 // prevent mapping of the default values into style (it only maps attributes
85 // that are set). We also rely on a check in SVGElement::
86 // UpdateContentStyleRule() to prevent us mapping the attributes when they're
87 // given a <length> value that is not currently recognized by the SVG
88 // specification.
90 if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) {
91 return true;
94 static const MappedAttributeEntry* const map[] = {sColorMap,
95 sFEFloodMap,
96 sFillStrokeMap,
97 sFiltersMap,
98 sFontSpecificationMap,
99 sGradientStopMap,
100 sGraphicsMap,
101 sLightingEffectsMap,
102 sMarkersMap,
103 sTextContentElementsMap,
104 sViewportsMap};
106 return FindAttributeDependence(name, map) ||
107 SVGGraphicsElement::IsAttributeMapped(name);
110 //----------------------------------------------------------------------
111 // SVGElement overrides
113 // Helper for GetViewBoxTransform on root <svg> node
114 // * aLength: internal value for our <svg> width or height attribute.
115 // * aViewportLength: length of the corresponding dimension of the viewport.
116 // * aSelf: the outermost <svg> node itself.
117 // NOTE: aSelf is not an ancestor viewport element, so it can't be used to
118 // resolve percentage lengths. (It can only be used to resolve
119 // 'em'/'ex'-valued units).
120 inline float ComputeSynthesizedViewBoxDimension(
121 const SVGAnimatedLength& aLength, float aViewportLength,
122 const SVGViewportElement* aSelf) {
123 if (aLength.IsPercentage()) {
124 return aViewportLength * aLength.GetAnimValInSpecifiedUnits() / 100.0f;
127 return aLength.GetAnimValue(const_cast<SVGViewportElement*>(aSelf));
130 //----------------------------------------------------------------------
131 // public helpers:
133 void SVGViewportElement::UpdateHasChildrenOnlyTransform() {
134 bool hasChildrenOnlyTransform =
135 HasViewBoxOrSyntheticViewBox() ||
136 (IsRootSVGSVGElement() &&
137 static_cast<SVGSVGElement*>(this)->IsScaledOrTranslated());
138 mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
141 void SVGViewportElement::ChildrenOnlyTransformChanged(uint32_t aFlags) {
142 // Avoid wasteful calls:
143 MOZ_ASSERT(!(GetPrimaryFrame()->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
144 "Non-display SVG frames don't maintain overflow rects");
146 nsChangeHint changeHint;
148 bool hadChildrenOnlyTransform = mHasChildrenOnlyTransform;
150 UpdateHasChildrenOnlyTransform();
152 if (hadChildrenOnlyTransform != mHasChildrenOnlyTransform) {
153 // Reconstruct the frame tree to handle stacking context changes:
154 // XXXjwatt don't do this for root-<svg> or even outer-<svg>?
155 changeHint = nsChangeHint_ReconstructFrame;
156 } else {
157 // We just assume the old and new transforms are different.
158 changeHint = nsChangeHint(nsChangeHint_UpdateOverflow |
159 nsChangeHint_ChildrenOnlyTransform);
162 // If we're not reconstructing the frame tree, then we only call
163 // PostRestyleEvent if we're not being called under reflow to avoid recursing
164 // to death. See bug 767056 comments 10 and 12. Since our SVGOuterSVGFrame
165 // is being reflowed we're going to invalidate and repaint its entire area
166 // anyway (which will include our children).
167 if ((changeHint & nsChangeHint_ReconstructFrame) ||
168 !(aFlags & eDuringReflow)) {
169 nsLayoutUtils::PostRestyleEvent(this, RestyleHint{0}, changeHint);
173 gfx::Matrix SVGViewportElement::GetViewBoxTransform() const {
174 float viewportWidth, viewportHeight;
175 if (IsInner()) {
176 SVGElement* self = const_cast<SVGViewportElement*>(this);
177 viewportWidth = mLengthAttributes[ATTR_WIDTH].GetAnimValue(self);
178 viewportHeight = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(self);
179 } else {
180 viewportWidth = mViewportWidth;
181 viewportHeight = mViewportHeight;
184 if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
185 return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
188 SVGViewBox viewBox = GetViewBoxWithSynthesis(viewportWidth, viewportHeight);
190 if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
191 return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
194 return SVGContentUtils::GetViewBoxTransform(
195 viewportWidth, viewportHeight, viewBox.x, viewBox.y, viewBox.width,
196 viewBox.height, GetPreserveAspectRatioWithOverride());
198 //----------------------------------------------------------------------
199 // SVGViewportElement
201 float SVGViewportElement::GetLength(uint8_t aCtxType) {
202 const SVGViewBox* viewbox = GetViewBoxInternal().HasRect()
203 ? &GetViewBoxInternal().GetAnimValue()
204 : nullptr;
206 float h = 0.0f, w = 0.0f;
207 bool shouldComputeWidth =
208 (aCtxType == SVGContentUtils::X || aCtxType == SVGContentUtils::XY),
209 shouldComputeHeight =
210 (aCtxType == SVGContentUtils::Y || aCtxType == SVGContentUtils::XY);
212 if (viewbox) {
213 w = viewbox->width;
214 h = viewbox->height;
215 } else if (IsInner()) {
216 // Resolving length for inner <svg> is exactly the same as other
217 // ordinary element. We shouldn't use the SVGViewportElement overload
218 // of GetAnimValue().
219 SVGElement* self = this;
220 if (shouldComputeWidth) {
221 w = mLengthAttributes[ATTR_WIDTH].GetAnimValue(self);
223 if (shouldComputeHeight) {
224 h = mLengthAttributes[ATTR_HEIGHT].GetAnimValue(self);
226 } else if (ShouldSynthesizeViewBox()) {
227 if (shouldComputeWidth) {
228 w = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
229 mViewportWidth, this);
231 if (shouldComputeHeight) {
232 h = ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
233 mViewportHeight, this);
235 } else {
236 w = mViewportWidth;
237 h = mViewportHeight;
240 w = std::max(w, 0.0f);
241 h = std::max(h, 0.0f);
243 switch (aCtxType) {
244 case SVGContentUtils::X:
245 return w;
246 case SVGContentUtils::Y:
247 return h;
248 case SVGContentUtils::XY:
249 return float(SVGContentUtils::ComputeNormalizedHypotenuse(w, h));
251 return 0;
254 //----------------------------------------------------------------------
255 // SVGElement methods
257 /* virtual */
258 gfxMatrix SVGViewportElement::PrependLocalTransformsTo(
259 const gfxMatrix& aMatrix, SVGTransformTypes aWhich) const {
260 // 'transform' attribute (or an override from a fragment identifier):
261 gfxMatrix userToParent;
263 if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
264 userToParent = GetUserToParentTransform(mAnimateMotionTransform.get(),
265 GetTransformInternal());
266 if (aWhich == eUserSpaceToParent) {
267 return userToParent * aMatrix;
271 gfxMatrix childToUser;
273 if (IsInner()) {
274 float x, y;
275 const_cast<SVGViewportElement*>(this)->GetAnimatedLengthValues(&x, &y,
276 nullptr);
277 childToUser = ThebesMatrix(GetViewBoxTransform().PostTranslate(x, y));
278 } else if (IsRootSVGSVGElement()) {
279 const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(this);
280 const SVGPoint& translate = svg->GetCurrentTranslate();
281 float scale = svg->CurrentScale();
282 childToUser =
283 ThebesMatrix(GetViewBoxTransform()
284 .PostScale(scale, scale)
285 .PostTranslate(translate.GetX(), translate.GetY()));
286 } else {
287 // outer-<svg>, but inline in some other content:
288 childToUser = ThebesMatrix(GetViewBoxTransform());
291 if (aWhich == eAllTransforms) {
292 return childToUser * userToParent * aMatrix;
295 MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
297 // The following may look broken because pre-multiplying our eChildToUserSpace
298 // transform with another matrix without including our eUserSpaceToParent
299 // transform between the two wouldn't make sense. We don't expect that to
300 // ever happen though. We get here either when the identity matrix has been
301 // passed because our caller just wants our eChildToUserSpace transform, or
302 // when our eUserSpaceToParent transform has already been multiplied into the
303 // matrix that our caller passes (such as when we're called from PaintSVG).
304 return childToUser * aMatrix;
307 /* virtual */
308 bool SVGViewportElement::HasValidDimensions() const {
309 return !IsInner() ||
310 ((!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
311 mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
312 (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
313 mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0));
316 SVGAnimatedViewBox* SVGViewportElement::GetAnimatedViewBox() {
317 return &mViewBox;
320 SVGAnimatedPreserveAspectRatio*
321 SVGViewportElement::GetAnimatedPreserveAspectRatio() {
322 return &mPreserveAspectRatio;
325 bool SVGViewportElement::ShouldSynthesizeViewBox() const {
326 MOZ_ASSERT(!HasViewBox(), "Should only be called if we lack a viewBox");
328 return IsRootSVGSVGElement() && OwnerDoc()->IsBeingUsedAsImage();
331 //----------------------------------------------------------------------
332 // implementation helpers
334 SVGViewBox SVGViewportElement::GetViewBoxWithSynthesis(
335 float aViewportWidth, float aViewportHeight) const {
336 if (GetViewBoxInternal().HasRect()) {
337 return GetViewBoxInternal().GetAnimValue();
340 if (ShouldSynthesizeViewBox()) {
341 // Special case -- fake a viewBox, using height & width attrs.
342 // (Use |this| as context, since if we get here, we're outermost <svg>.)
343 return SVGViewBox(
344 0, 0,
345 ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_WIDTH],
346 mViewportWidth, this),
347 ComputeSynthesizedViewBoxDimension(mLengthAttributes[ATTR_HEIGHT],
348 mViewportHeight, this));
351 // No viewBox attribute, so we shouldn't auto-scale. This is equivalent
352 // to having a viewBox that exactly matches our viewport size.
353 return SVGViewBox(0, 0, aViewportWidth, aViewportHeight);
356 SVGElement::LengthAttributesInfo SVGViewportElement::GetLengthInfo() {
357 return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
358 ArrayLength(sLengthInfo));
361 } // namespace dom
362 } // namespace mozilla