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