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