SVGGeometryProperty.h (10067B)
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 #ifndef DOM_SVG_SVGGEOMETRYPROPERTY_H_ 8 #define DOM_SVG_SVGGEOMETRYPROPERTY_H_ 9 10 #include <type_traits> 11 12 #include "ComputedStyle.h" 13 #include "SVGAnimatedLength.h" 14 #include "mozilla/SVGImageFrame.h" 15 #include "mozilla/dom/SVGElement.h" 16 #include "nsComputedDOMStyle.h" 17 #include "nsGkAtoms.h" 18 #include "nsIFrame.h" 19 20 namespace mozilla::dom::SVGGeometryProperty { 21 namespace ResolverTypes { 22 struct LengthPercentNoAuto {}; 23 struct LengthPercentRXY {}; 24 struct LengthPercentWidthHeight {}; 25 } // namespace ResolverTypes 26 27 namespace Tags { 28 29 #define SVGGEOMETRYPROPERTY_GENERATETAG(tagName, resolver, direction, \ 30 styleStruct) \ 31 struct tagName { \ 32 using ResolverType = ResolverTypes::resolver; \ 33 constexpr static auto CtxDirection = SVGContentUtils::direction; \ 34 constexpr static auto Getter = &styleStruct::m##tagName; \ 35 } 36 37 SVGGEOMETRYPROPERTY_GENERATETAG(X, LengthPercentNoAuto, X, nsStyleSVGReset); 38 SVGGEOMETRYPROPERTY_GENERATETAG(Y, LengthPercentNoAuto, Y, nsStyleSVGReset); 39 SVGGEOMETRYPROPERTY_GENERATETAG(Cx, LengthPercentNoAuto, X, nsStyleSVGReset); 40 SVGGEOMETRYPROPERTY_GENERATETAG(Cy, LengthPercentNoAuto, Y, nsStyleSVGReset); 41 SVGGEOMETRYPROPERTY_GENERATETAG(R, LengthPercentNoAuto, XY, nsStyleSVGReset); 42 43 #undef SVGGEOMETRYPROPERTY_GENERATETAG 44 45 using StyleSizeGetter = AnchorResolvedSize (nsStylePosition::*)( 46 const AnchorPosResolutionParams& aParams) const; 47 48 struct Height; 49 struct Width { 50 using ResolverType = ResolverTypes::LengthPercentWidthHeight; 51 constexpr static auto CtxDirection = SVGContentUtils::X; 52 constexpr static StyleSizeGetter Getter = &nsStylePosition::GetWidth; 53 constexpr static auto SizeGetter = &gfx::Size::width; 54 static AspectRatio AspectRatioRelative(AspectRatio aAspectRatio) { 55 return aAspectRatio.Inverted(); 56 } 57 constexpr static uint32_t DefaultObjectSize = kFallbackIntrinsicWidthInPixels; 58 using CounterPart = Height; 59 }; 60 struct Height { 61 using ResolverType = ResolverTypes::LengthPercentWidthHeight; 62 constexpr static auto CtxDirection = SVGContentUtils::Y; 63 constexpr static StyleSizeGetter Getter = &nsStylePosition::GetHeight; 64 constexpr static auto SizeGetter = &gfx::Size::height; 65 static AspectRatio AspectRatioRelative(AspectRatio aAspectRatio) { 66 return aAspectRatio; 67 } 68 constexpr static uint32_t DefaultObjectSize = 69 kFallbackIntrinsicHeightInPixels; 70 using CounterPart = Width; 71 }; 72 73 struct Ry; 74 struct Rx { 75 using ResolverType = ResolverTypes::LengthPercentRXY; 76 constexpr static auto CtxDirection = SVGContentUtils::X; 77 constexpr static auto Getter = &nsStyleSVGReset::mRx; 78 using CounterPart = Ry; 79 }; 80 struct Ry { 81 using ResolverType = ResolverTypes::LengthPercentRXY; 82 constexpr static auto CtxDirection = SVGContentUtils::Y; 83 constexpr static auto Getter = &nsStyleSVGReset::mRy; 84 using CounterPart = Rx; 85 }; 86 87 } // namespace Tags 88 89 namespace details { 90 template <class T> 91 using AlwaysFloat = float; 92 using dummy = int[]; 93 94 using CtxDirectionType = decltype(SVGContentUtils::X); 95 96 template <CtxDirectionType CTD> 97 float ResolvePureLengthPercentage(const SVGElement* aElement, 98 const LengthPercentage& aLP) { 99 return aLP.ResolveToCSSPixelsWith( 100 [&] { return CSSCoord{SVGElementMetrics(aElement).GetAxisLength(CTD)}; }); 101 } 102 103 template <class Tag> 104 float ResolveImpl(ComputedStyle const& aStyle, const SVGElement* aElement, 105 ResolverTypes::LengthPercentNoAuto) { 106 auto const& value = aStyle.StyleSVGReset()->*Tag::Getter; 107 return ResolvePureLengthPercentage<Tag::CtxDirection>(aElement, value); 108 } 109 110 template <class Tag> 111 float ResolveImpl(ComputedStyle const& aStyle, const SVGElement* aElement, 112 ResolverTypes::LengthPercentWidthHeight) { 113 static_assert( 114 std::is_same<Tag, Tags::Width>{} || std::is_same<Tag, Tags::Height>{}, 115 "Wrong tag"); 116 117 auto const value = std::invoke( 118 Tag::Getter, aStyle.StylePosition(), 119 AnchorPosResolutionParams{nullptr, aStyle.StyleDisplay()->mPosition}); 120 if (value->IsLengthPercentage()) { 121 return ResolvePureLengthPercentage<Tag::CtxDirection>( 122 aElement, value->AsLengthPercentage()); 123 } 124 125 if (aElement->IsSVGElement(nsGkAtoms::image)) { 126 // It's not clear per SVG2 spec what should be done for values other 127 // than |auto| (e.g. |max-content|). We treat them as nonsense, thus 128 // using the initial value behavior, i.e. |auto|. 129 // The following procedure follows the Default Sizing Algorithm as 130 // specified in: 131 // https://svgwg.org/svg2-draft/embedded.html#ImageElement 132 133 SVGImageFrame* imgf = do_QueryFrame(aElement->GetPrimaryFrame()); 134 if (!imgf) { 135 return 0.f; 136 } 137 138 using Other = typename Tag::CounterPart; 139 auto const valueOther = std::invoke( 140 Other::Getter, aStyle.StylePosition(), 141 AnchorPosResolutionParams{nullptr, aStyle.StyleDisplay()->mPosition}); 142 143 gfx::Size intrinsicImageSize; 144 AspectRatio aspectRatio; 145 if (!imgf->GetIntrinsicImageDimensions(intrinsicImageSize, aspectRatio)) { 146 // No image container, just return 0. 147 return 0.f; 148 } 149 150 if (valueOther->IsLengthPercentage()) { 151 // We are |auto|, but the other side has specifed length. 152 float lengthOther = ResolvePureLengthPercentage<Other::CtxDirection>( 153 aElement, valueOther->AsLengthPercentage()); 154 155 if (aspectRatio) { 156 // Preserve aspect ratio if it's present. 157 return Other::AspectRatioRelative(aspectRatio) 158 .ApplyToFloat(lengthOther); 159 } 160 161 float intrinsicLength = intrinsicImageSize.*Tag::SizeGetter; 162 if (intrinsicLength >= 0) { 163 // Use the intrinsic length if it's present. 164 return intrinsicLength; 165 } 166 167 // No specified size, no aspect ratio, no intrinsic length, 168 // then use default size. 169 return Tag::DefaultObjectSize; 170 } 171 172 // |width| and |height| are both |auto| 173 if (intrinsicImageSize.*Tag::SizeGetter >= 0) { 174 return intrinsicImageSize.*Tag::SizeGetter; 175 } 176 177 if (intrinsicImageSize.*Other::SizeGetter >= 0 && aspectRatio) { 178 return Other::AspectRatioRelative(aspectRatio) 179 .ApplyTo(intrinsicImageSize.*Other::SizeGetter); 180 } 181 182 if (aspectRatio) { 183 // Resolve as a contain constraint against the default object size. 184 auto defaultAspectRatioRelative = 185 AspectRatio{float(Other::DefaultObjectSize) / Tag::DefaultObjectSize}; 186 auto aspectRatioRelative = Tag::AspectRatioRelative(aspectRatio); 187 188 if (defaultAspectRatioRelative < aspectRatioRelative) { 189 // Using default length in our side and the intrinsic aspect ratio, 190 // the other side cannot be contained. 191 return aspectRatioRelative.Inverted().ApplyTo(Other::DefaultObjectSize); 192 } 193 194 return Tag::DefaultObjectSize; 195 } 196 197 return Tag::DefaultObjectSize; 198 } 199 200 // For other elements, |auto| and |max-content| etc. are treated as 0. 201 return 0.f; 202 } 203 204 template <class Tag> 205 float ResolveImpl(ComputedStyle const& aStyle, const SVGElement* aElement, 206 ResolverTypes::LengthPercentRXY) { 207 static_assert(std::is_same<Tag, Tags::Rx>{} || std::is_same<Tag, Tags::Ry>{}, 208 "Wrong tag"); 209 210 auto const& value = aStyle.StyleSVGReset()->*Tag::Getter; 211 if (value.IsLengthPercentage()) { 212 return ResolvePureLengthPercentage<Tag::CtxDirection>( 213 aElement, value.AsLengthPercentage()); 214 } 215 216 MOZ_ASSERT(value.IsAuto()); 217 using Rother = typename Tag::CounterPart; 218 auto const& valueOther = aStyle.StyleSVGReset()->*Rother::Getter; 219 220 if (valueOther.IsAuto()) { 221 // Per SVG2, |Rx|, |Ry| resolve to 0 if both are |auto| 222 return 0.f; 223 } 224 225 // If |Rx| is auto while |Ry| not, |Rx| gets the value of |Ry|. 226 return ResolvePureLengthPercentage<Rother::CtxDirection>( 227 aElement, valueOther.AsLengthPercentage()); 228 } 229 230 } // namespace details 231 232 template <class Tag> 233 float ResolveWith(const ComputedStyle& aStyle, const SVGElement* aElement) { 234 return details::ResolveImpl<Tag>(aStyle, aElement, 235 typename Tag::ResolverType{}); 236 } 237 238 template <class Func> 239 bool DoForComputedStyle(const Element* aElement, Func aFunc) { 240 if (!aElement) { 241 return false; 242 } 243 if (const nsIFrame* f = aElement->GetPrimaryFrame()) { 244 aFunc(f->Style()); 245 return true; 246 } 247 248 if (RefPtr<const ComputedStyle> computedStyle = 249 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement)) { 250 aFunc(computedStyle.get()); 251 return true; 252 } 253 254 return false; 255 } 256 257 #define SVGGEOMETRYPROPERTY_EVAL_ALL(expr) \ 258 (void)details::dummy { 0, (static_cast<void>(expr), 0)... } 259 260 // To add support for new properties, or to handle special cases for 261 // existing properties, you can add a new tag in |Tags| and |ResolverTypes| 262 // namespace, then implement the behavior in |details::ResolveImpl|. 263 template <class... Tags> 264 bool ResolveAll(const SVGElement* aElement, 265 details::AlwaysFloat<Tags>*... aRes) { 266 bool res = DoForComputedStyle(aElement, [&](auto const* style) { 267 SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes = ResolveWith<Tags>(*style, aElement)); 268 }); 269 270 if (res) { 271 return true; 272 } 273 274 SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes = 0); 275 return false; 276 } 277 278 #undef SVGGEOMETRYPROPERTY_EVAL_ALL 279 280 nsCSSUnit SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit); 281 NonCustomCSSPropertyId AttrEnumToCSSPropId(const SVGElement* aElement, 282 uint8_t aAttrEnum); 283 284 bool IsNonNegativeGeometryProperty(NonCustomCSSPropertyId aProp); 285 bool ElementMapsLengthsToStyle(SVGElement const* aElement); 286 287 } // namespace mozilla::dom::SVGGeometryProperty 288 289 #endif // DOM_SVG_SVGGEOMETRYPROPERTY_H_