Backed out 4 changesets (bug 1825722) for causing reftest failures CLOSED TREE
[gecko.git] / dom / svg / SVGRectElement.cpp
blob9f40acff264d0d133050917c7e6cbf4befe227ca
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/SVGRectElement.h"
8 #include "mozilla/dom/SVGLengthBinding.h"
9 #include "mozilla/dom/SVGRectElementBinding.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/gfx/Matrix.h"
12 #include "mozilla/gfx/Rect.h"
13 #include "mozilla/gfx/PathHelpers.h"
14 #include "nsGkAtoms.h"
15 #include "SVGGeometryProperty.h"
16 #include <algorithm>
18 NS_IMPL_NS_NEW_SVG_ELEMENT(Rect)
20 using namespace mozilla::gfx;
22 namespace mozilla::dom {
24 class DOMSVGAnimatedLength;
26 JSObject* SVGRectElement::WrapNode(JSContext* aCx,
27 JS::Handle<JSObject*> aGivenProto) {
28 return SVGRectElement_Binding::Wrap(aCx, this, aGivenProto);
31 SVGElement::LengthInfo SVGRectElement::sLengthInfo[6] = {
32 {nsGkAtoms::x, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
33 SVGContentUtils::X},
34 {nsGkAtoms::y, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
35 SVGContentUtils::Y},
36 {nsGkAtoms::width, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
37 SVGContentUtils::X},
38 {nsGkAtoms::height, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
39 SVGContentUtils::Y},
40 {nsGkAtoms::rx, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
41 SVGContentUtils::X},
42 {nsGkAtoms::ry, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
43 SVGContentUtils::Y}};
45 //----------------------------------------------------------------------
46 // Implementation
48 SVGRectElement::SVGRectElement(
49 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
50 : SVGRectElementBase(std::move(aNodeInfo)) {}
52 bool SVGRectElement::IsAttributeMapped(const nsAtom* aAttribute) const {
53 return IsInLengthInfo(aAttribute, sLengthInfo) ||
54 SVGRectElementBase::IsAttributeMapped(aAttribute);
57 namespace SVGT = SVGGeometryProperty::Tags;
59 //----------------------------------------------------------------------
60 // nsINode methods
62 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGRectElement)
64 //----------------------------------------------------------------------
66 already_AddRefed<DOMSVGAnimatedLength> SVGRectElement::X() {
67 return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
70 already_AddRefed<DOMSVGAnimatedLength> SVGRectElement::Y() {
71 return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
74 already_AddRefed<DOMSVGAnimatedLength> SVGRectElement::Width() {
75 return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
78 already_AddRefed<DOMSVGAnimatedLength> SVGRectElement::Height() {
79 return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
82 already_AddRefed<DOMSVGAnimatedLength> SVGRectElement::Rx() {
83 return mLengthAttributes[ATTR_RX].ToDOMAnimatedLength(this);
86 already_AddRefed<DOMSVGAnimatedLength> SVGRectElement::Ry() {
87 return mLengthAttributes[ATTR_RY].ToDOMAnimatedLength(this);
90 //----------------------------------------------------------------------
91 // SVGElement methods
93 /* virtual */
94 bool SVGRectElement::HasValidDimensions() const {
95 float width, height;
97 if (SVGGeometryProperty::ResolveAll<SVGT::Width, SVGT::Height>(this, &width,
98 &height)) {
99 return width > 0 && height > 0;
101 // This function might be called for an element in display:none subtree
102 // (e.g. SMIL animateMotion), we fall back to use SVG attributes.
103 return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() &&
104 mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 &&
105 mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() &&
106 mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0;
109 SVGElement::LengthAttributesInfo SVGRectElement::GetLengthInfo() {
110 return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
111 ArrayLength(sLengthInfo));
114 //----------------------------------------------------------------------
115 // SVGGeometryElement methods
117 bool SVGRectElement::GetGeometryBounds(Rect* aBounds,
118 const StrokeOptions& aStrokeOptions,
119 const Matrix& aToBoundsSpace,
120 const Matrix* aToNonScalingStrokeSpace) {
121 Rect rect;
122 Float rx, ry;
124 DebugOnly<bool> ok =
125 SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width,
126 SVGT::Height, SVGT::Rx, SVGT::Ry>(
127 this, &rect.x, &rect.y, &rect.width, &rect.height, &rx, &ry);
128 MOZ_ASSERT(ok, "SVGGeometryProperty::ResolveAll failed");
130 if (rect.IsEmpty()) {
131 // Rendering of the element disabled
132 rect.SetEmpty(); // Make sure width/height are zero and not negative
133 // We still want the x/y position from 'rect'
134 *aBounds = aToBoundsSpace.TransformBounds(rect);
135 return true;
138 if (!aToBoundsSpace.IsRectilinear()) {
139 // We can't ignore the radii in this case if we want tight bounds
140 rx = std::max(rx, 0.0f);
141 ry = std::max(ry, 0.0f);
143 if (rx != 0 || ry != 0) {
144 return false;
148 if (aStrokeOptions.mLineWidth > 0.f) {
149 if (aToNonScalingStrokeSpace) {
150 if (aToNonScalingStrokeSpace->IsRectilinear()) {
151 MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular());
152 rect = aToNonScalingStrokeSpace->TransformBounds(rect);
153 // Note that, in principle, an author could cause the corners of the
154 // rect to be beveled by specifying stroke-linejoin or setting
155 // stroke-miterlimit to be less than sqrt(2). In that very unlikely
156 // event the bounds that we calculate here may be too big if
157 // aToBoundsSpace is non-rectilinear. This is likely to be so rare it's
158 // not worth handling though.
159 rect.Inflate(aStrokeOptions.mLineWidth / 2.f);
160 Matrix nonScalingToBounds =
161 aToNonScalingStrokeSpace->Inverse() * aToBoundsSpace;
162 *aBounds = nonScalingToBounds.TransformBounds(rect);
163 return true;
165 return false;
167 // The "beveled" comment above applies here too
168 rect.Inflate(aStrokeOptions.mLineWidth / 2.f);
171 *aBounds = aToBoundsSpace.TransformBounds(rect);
172 return true;
175 void SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath) {
176 float x, y, width, height, rx, ry;
178 DebugOnly<bool> ok =
179 SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width,
180 SVGT::Height, SVGT::Rx, SVGT::Ry>(
181 this, &x, &y, &width, &height, &rx, &ry);
182 MOZ_ASSERT(ok, "SVGGeometryProperty::ResolveAll failed");
184 if (width <= 0 || height <= 0) {
185 aSimplePath->Reset();
186 return;
189 rx = std::max(rx, 0.0f);
190 ry = std::max(ry, 0.0f);
192 if (rx != 0 || ry != 0) {
193 aSimplePath->Reset();
194 return;
197 aSimplePath->SetRect(x, y, width, height);
200 already_AddRefed<Path> SVGRectElement::BuildPath(PathBuilder* aBuilder) {
201 float x, y, width, height, rx, ry;
203 if (!SVGGeometryProperty::ResolveAll<SVGT::X, SVGT::Y, SVGT::Width,
204 SVGT::Height, SVGT::Rx, SVGT::Ry>(
205 this, &x, &y, &width, &height, &rx, &ry)) {
206 // This function might be called for element in display:none subtree
207 // (e.g. getTotalLength), we fall back to use SVG attributes.
208 GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
209 // If either the 'rx' or the 'ry' attribute isn't set, then we have to
210 // set it to the value of the other:
211 bool hasRx = mLengthAttributes[ATTR_RX].IsExplicitlySet();
212 bool hasRy = mLengthAttributes[ATTR_RY].IsExplicitlySet();
213 if (hasRx && !hasRy) {
214 ry = rx;
215 } else if (hasRy && !hasRx) {
216 rx = ry;
220 if (width <= 0 || height <= 0) {
221 return nullptr;
224 rx = std::max(rx, 0.0f);
225 ry = std::max(ry, 0.0f);
227 if (rx == 0 && ry == 0) {
228 // Optimization for the no rounded corners case.
229 Rect r(x, y, width, height);
230 aBuilder->MoveTo(r.TopLeft());
231 aBuilder->LineTo(r.TopRight());
232 aBuilder->LineTo(r.BottomRight());
233 aBuilder->LineTo(r.BottomLeft());
234 aBuilder->Close();
235 } else {
236 // Clamp rx and ry to half the rect's width and height respectively:
237 rx = std::min(rx, width / 2);
238 ry = std::min(ry, height / 2);
240 RectCornerRadii radii(rx, ry);
241 AppendRoundedRectToPath(aBuilder, Rect(x, y, width, height), radii);
244 return aBuilder->Finish();
247 bool SVGRectElement::IsLengthChangedViaCSS(const ComputedStyle& aNewStyle,
248 const ComputedStyle& aOldStyle) {
249 const auto& newSVGReset = *aNewStyle.StyleSVGReset();
250 const auto& oldSVGReset = *aOldStyle.StyleSVGReset();
251 const auto& newPosition = *aNewStyle.StylePosition();
252 const auto& oldPosition = *aOldStyle.StylePosition();
253 return newSVGReset.mX != oldSVGReset.mX || newSVGReset.mY != oldSVGReset.mY ||
254 newPosition.mWidth != oldPosition.mWidth ||
255 newPosition.mHeight != oldPosition.mHeight ||
256 newSVGReset.mRx != oldSVGReset.mRx ||
257 newSVGReset.mRy != oldSVGReset.mRy;
260 nsCSSPropertyID SVGRectElement::GetCSSPropertyIdForAttrEnum(uint8_t aAttrEnum) {
261 switch (aAttrEnum) {
262 case ATTR_X:
263 return eCSSProperty_x;
264 case ATTR_Y:
265 return eCSSProperty_y;
266 case ATTR_WIDTH:
267 return eCSSProperty_width;
268 case ATTR_HEIGHT:
269 return eCSSProperty_height;
270 case ATTR_RX:
271 return eCSSProperty_rx;
272 case ATTR_RY:
273 return eCSSProperty_ry;
274 default:
275 MOZ_ASSERT_UNREACHABLE("Unknown attr enum");
276 return eCSSProperty_UNKNOWN;
280 } // namespace mozilla::dom