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"
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
,
34 {nsGkAtoms::y
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
36 {nsGkAtoms::width
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
38 {nsGkAtoms::height
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
40 {nsGkAtoms::rx
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
42 {nsGkAtoms::ry
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
45 //----------------------------------------------------------------------
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 //----------------------------------------------------------------------
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 //----------------------------------------------------------------------
94 bool SVGRectElement::HasValidDimensions() const {
97 if (SVGGeometryProperty::ResolveAll
<SVGT::Width
, SVGT::Height
>(this, &width
,
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 std::size(sLengthInfo
));
114 //----------------------------------------------------------------------
115 // SVGGeometryElement methods
117 bool SVGRectElement::GetGeometryBounds(Rect
* aBounds
,
118 const StrokeOptions
& aStrokeOptions
,
119 const Matrix
& aToBoundsSpace
,
120 const Matrix
* aToNonScalingStrokeSpace
) {
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
);
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) {
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
);
167 // The "beveled" comment above applies here too
168 rect
.Inflate(aStrokeOptions
.mLineWidth
/ 2.f
);
171 *aBounds
= aToBoundsSpace
.TransformBounds(rect
);
175 void SVGRectElement::GetAsSimplePath(SimplePath
* aSimplePath
) {
176 float x
, y
, width
, height
, rx
, ry
;
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();
189 rx
= std::max(rx
, 0.0f
);
190 ry
= std::max(ry
, 0.0f
);
192 if (rx
!= 0 || ry
!= 0) {
193 aSimplePath
->Reset();
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
) {
215 } else if (hasRy
&& !hasRx
) {
220 if (width
<= 0 || height
<= 0) {
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());
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
.GetWidth() != oldPosition
.GetWidth() ||
255 newPosition
.GetHeight() != oldPosition
.GetHeight() ||
256 newSVGReset
.mRx
!= oldSVGReset
.mRx
||
257 newSVGReset
.mRy
!= oldSVGReset
.mRy
;
260 nsCSSPropertyID
SVGRectElement::GetCSSPropertyIdForAttrEnum(uint8_t aAttrEnum
) {
263 return eCSSProperty_x
;
265 return eCSSProperty_y
;
267 return eCSSProperty_width
;
269 return eCSSProperty_height
;
271 return eCSSProperty_rx
;
273 return eCSSProperty_ry
;
275 MOZ_ASSERT_UNREACHABLE("Unknown attr enum");
276 return eCSSProperty_UNKNOWN
;
280 } // namespace mozilla::dom