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 #ifndef DOM_SVG_SVGGEOMETRYPROPERTY_H_
8 #define DOM_SVG_SVGGEOMETRYPROPERTY_H_
10 #include "mozilla/SVGImageFrame.h"
11 #include "mozilla/dom/SVGElement.h"
12 #include "ComputedStyle.h"
13 #include "SVGAnimatedLength.h"
14 #include "nsComputedDOMStyle.h"
15 #include "nsGkAtoms.h"
17 #include <type_traits>
19 namespace mozilla::dom::SVGGeometryProperty
{
20 namespace ResolverTypes
{
21 struct LengthPercentNoAuto
{};
22 struct LengthPercentRXY
{};
23 struct LengthPercentWidthHeight
{};
24 } // namespace ResolverTypes
28 #define SVGGEOMETRYPROPERTY_GENERATETAG(tagName, resolver, direction, \
31 using ResolverType = ResolverTypes::resolver; \
32 constexpr static auto CtxDirection = SVGContentUtils::direction; \
33 constexpr static auto Getter = &styleStruct::m##tagName; \
36 SVGGEOMETRYPROPERTY_GENERATETAG(X
, LengthPercentNoAuto
, X
, nsStyleSVGReset
);
37 SVGGEOMETRYPROPERTY_GENERATETAG(Y
, LengthPercentNoAuto
, Y
, nsStyleSVGReset
);
38 SVGGEOMETRYPROPERTY_GENERATETAG(Cx
, LengthPercentNoAuto
, X
, nsStyleSVGReset
);
39 SVGGEOMETRYPROPERTY_GENERATETAG(Cy
, LengthPercentNoAuto
, Y
, nsStyleSVGReset
);
40 SVGGEOMETRYPROPERTY_GENERATETAG(R
, LengthPercentNoAuto
, XY
, nsStyleSVGReset
);
42 #undef SVGGEOMETRYPROPERTY_GENERATETAG
46 using ResolverType
= ResolverTypes::LengthPercentWidthHeight
;
47 constexpr static auto CtxDirection
= SVGContentUtils::X
;
48 constexpr static auto Getter
= &nsStylePosition::GetWidth
;
49 constexpr static auto SizeGetter
= &gfx::Size::width
;
50 static AspectRatio
AspectRatioRelative(AspectRatio aAspectRatio
) {
51 return aAspectRatio
.Inverted();
53 constexpr static uint32_t DefaultObjectSize
= kFallbackIntrinsicWidthInPixels
;
54 using CounterPart
= Height
;
57 using ResolverType
= ResolverTypes::LengthPercentWidthHeight
;
58 constexpr static auto CtxDirection
= SVGContentUtils::Y
;
59 constexpr static auto Getter
= &nsStylePosition::GetHeight
;
60 constexpr static auto SizeGetter
= &gfx::Size::height
;
61 static AspectRatio
AspectRatioRelative(AspectRatio aAspectRatio
) {
64 constexpr static uint32_t DefaultObjectSize
=
65 kFallbackIntrinsicHeightInPixels
;
66 using CounterPart
= Width
;
71 using ResolverType
= ResolverTypes::LengthPercentRXY
;
72 constexpr static auto CtxDirection
= SVGContentUtils::X
;
73 constexpr static auto Getter
= &nsStyleSVGReset::mRx
;
74 using CounterPart
= Ry
;
77 using ResolverType
= ResolverTypes::LengthPercentRXY
;
78 constexpr static auto CtxDirection
= SVGContentUtils::Y
;
79 constexpr static auto Getter
= &nsStyleSVGReset::mRy
;
80 using CounterPart
= Rx
;
87 using AlwaysFloat
= float;
90 using CtxDirectionType
= decltype(SVGContentUtils::X
);
92 template <CtxDirectionType CTD
>
93 float ResolvePureLengthPercentage(const SVGElement
* aElement
,
94 const LengthPercentage
& aLP
) {
95 return aLP
.ResolveToCSSPixelsWith(
96 [&] { return CSSCoord
{SVGElementMetrics(aElement
).GetAxisLength(CTD
)}; });
100 float ResolveImpl(ComputedStyle
const& aStyle
, const SVGElement
* aElement
,
101 ResolverTypes::LengthPercentNoAuto
) {
102 auto const& value
= aStyle
.StyleSVGReset()->*Tag::Getter
;
103 return ResolvePureLengthPercentage
<Tag::CtxDirection
>(aElement
, value
);
107 float ResolveImpl(ComputedStyle
const& aStyle
, const SVGElement
* aElement
,
108 ResolverTypes::LengthPercentWidthHeight
) {
110 std::is_same
<Tag
, Tags::Width
>{} || std::is_same
<Tag
, Tags::Height
>{},
113 auto const& value
= std::invoke(Tag::Getter
, aStyle
.StylePosition());
114 if (value
.IsLengthPercentage()) {
115 return ResolvePureLengthPercentage
<Tag::CtxDirection
>(
116 aElement
, value
.AsLengthPercentage());
119 if (aElement
->IsSVGElement(nsGkAtoms::image
)) {
120 // It's not clear per SVG2 spec what should be done for values other
121 // than |auto| (e.g. |max-content|). We treat them as nonsense, thus
122 // using the initial value behavior, i.e. |auto|.
123 // The following procedure follows the Default Sizing Algorithm as
125 // https://svgwg.org/svg2-draft/embedded.html#ImageElement
127 SVGImageFrame
* imgf
= do_QueryFrame(aElement
->GetPrimaryFrame());
132 using Other
= typename
Tag::CounterPart
;
133 auto const& valueOther
= std::invoke(Other::Getter
, aStyle
.StylePosition());
135 gfx::Size intrinsicImageSize
;
136 AspectRatio aspectRatio
;
137 if (!imgf
->GetIntrinsicImageDimensions(intrinsicImageSize
, aspectRatio
)) {
138 // No image container, just return 0.
142 if (valueOther
.IsLengthPercentage()) {
143 // We are |auto|, but the other side has specifed length.
144 float lengthOther
= ResolvePureLengthPercentage
<Other::CtxDirection
>(
145 aElement
, valueOther
.AsLengthPercentage());
148 // Preserve aspect ratio if it's present.
149 return Other::AspectRatioRelative(aspectRatio
)
150 .ApplyToFloat(lengthOther
);
153 float intrinsicLength
= intrinsicImageSize
.*Tag::SizeGetter
;
154 if (intrinsicLength
>= 0) {
155 // Use the intrinsic length if it's present.
156 return intrinsicLength
;
159 // No specified size, no aspect ratio, no intrinsic length,
160 // then use default size.
161 return Tag::DefaultObjectSize
;
164 // |width| and |height| are both |auto|
165 if (intrinsicImageSize
.*Tag::SizeGetter
>= 0) {
166 return intrinsicImageSize
.*Tag::SizeGetter
;
169 if (intrinsicImageSize
.*Other::SizeGetter
>= 0 && aspectRatio
) {
170 return Other::AspectRatioRelative(aspectRatio
)
171 .ApplyTo(intrinsicImageSize
.*Other::SizeGetter
);
175 // Resolve as a contain constraint against the default object size.
176 auto defaultAspectRatioRelative
=
177 AspectRatio
{float(Other::DefaultObjectSize
) / Tag::DefaultObjectSize
};
178 auto aspectRatioRelative
= Tag::AspectRatioRelative(aspectRatio
);
180 if (defaultAspectRatioRelative
< aspectRatioRelative
) {
181 // Using default length in our side and the intrinsic aspect ratio,
182 // the other side cannot be contained.
183 return aspectRatioRelative
.Inverted().ApplyTo(Other::DefaultObjectSize
);
186 return Tag::DefaultObjectSize
;
189 return Tag::DefaultObjectSize
;
192 // For other elements, |auto| and |max-content| etc. are treated as 0.
197 float ResolveImpl(ComputedStyle
const& aStyle
, const SVGElement
* aElement
,
198 ResolverTypes::LengthPercentRXY
) {
199 static_assert(std::is_same
<Tag
, Tags::Rx
>{} || std::is_same
<Tag
, Tags::Ry
>{},
202 auto const& value
= aStyle
.StyleSVGReset()->*Tag::Getter
;
203 if (value
.IsLengthPercentage()) {
204 return ResolvePureLengthPercentage
<Tag::CtxDirection
>(
205 aElement
, value
.AsLengthPercentage());
208 MOZ_ASSERT(value
.IsAuto());
209 using Rother
= typename
Tag::CounterPart
;
210 auto const& valueOther
= aStyle
.StyleSVGReset()->*Rother::Getter
;
212 if (valueOther
.IsAuto()) {
213 // Per SVG2, |Rx|, |Ry| resolve to 0 if both are |auto|
217 // If |Rx| is auto while |Ry| not, |Rx| gets the value of |Ry|.
218 return ResolvePureLengthPercentage
<Rother::CtxDirection
>(
219 aElement
, valueOther
.AsLengthPercentage());
222 } // namespace details
225 float ResolveWith(const ComputedStyle
& aStyle
, const SVGElement
* aElement
) {
226 return details::ResolveImpl
<Tag
>(aStyle
, aElement
,
227 typename
Tag::ResolverType
{});
230 template <class Func
>
231 bool DoForComputedStyle(const Element
* aElement
, Func aFunc
) {
235 if (const nsIFrame
* f
= aElement
->GetPrimaryFrame()) {
240 if (RefPtr
<const ComputedStyle
> computedStyle
=
241 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement
)) {
242 aFunc(computedStyle
.get());
249 #define SVGGEOMETRYPROPERTY_EVAL_ALL(expr) \
250 (void)details::dummy { 0, (static_cast<void>(expr), 0)... }
252 // To add support for new properties, or to handle special cases for
253 // existing properties, you can add a new tag in |Tags| and |ResolverTypes|
254 // namespace, then implement the behavior in |details::ResolveImpl|.
255 template <class... Tags
>
256 bool ResolveAll(const SVGElement
* aElement
,
257 details::AlwaysFloat
<Tags
>*... aRes
) {
258 bool res
= DoForComputedStyle(aElement
, [&](auto const* style
) {
259 SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes
= ResolveWith
<Tags
>(*style
, aElement
));
266 SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes
= 0);
270 #undef SVGGEOMETRYPROPERTY_EVAL_ALL
272 nsCSSUnit
SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit
);
273 nsCSSPropertyID
AttrEnumToCSSPropId(const SVGElement
* aElement
,
276 bool IsNonNegativeGeometryProperty(nsCSSPropertyID aProp
);
277 bool ElementMapsLengthsToStyle(SVGElement
const* aElement
);
279 } // namespace mozilla::dom::SVGGeometryProperty
281 #endif // DOM_SVG_SVGGEOMETRYPROPERTY_H_