Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / SVGGeometryProperty.h
blob2564883a6f6a48aba1c970f5e212ca031d3c6c62
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"
16 #include "nsIFrame.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
26 namespace Tags {
28 #define SVGGEOMETRYPROPERTY_GENERATETAG(tagName, resolver, direction, \
29 styleStruct) \
30 struct tagName { \
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
44 struct Height;
45 struct Width {
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;
56 struct 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) {
62 return aAspectRatio;
64 constexpr static uint32_t DefaultObjectSize =
65 kFallbackIntrinsicHeightInPixels;
66 using CounterPart = Width;
69 struct Ry;
70 struct Rx {
71 using ResolverType = ResolverTypes::LengthPercentRXY;
72 constexpr static auto CtxDirection = SVGContentUtils::X;
73 constexpr static auto Getter = &nsStyleSVGReset::mRx;
74 using CounterPart = Ry;
76 struct Ry {
77 using ResolverType = ResolverTypes::LengthPercentRXY;
78 constexpr static auto CtxDirection = SVGContentUtils::Y;
79 constexpr static auto Getter = &nsStyleSVGReset::mRy;
80 using CounterPart = Rx;
83 } // namespace Tags
85 namespace details {
86 template <class T>
87 using AlwaysFloat = float;
88 using dummy = int[];
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)}; });
99 template <class Tag>
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);
106 template <class Tag>
107 float ResolveImpl(ComputedStyle const& aStyle, const SVGElement* aElement,
108 ResolverTypes::LengthPercentWidthHeight) {
109 static_assert(
110 std::is_same<Tag, Tags::Width>{} || std::is_same<Tag, Tags::Height>{},
111 "Wrong tag");
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
124 // specified in:
125 // https://svgwg.org/svg2-draft/embedded.html#ImageElement
127 SVGImageFrame* imgf = do_QueryFrame(aElement->GetPrimaryFrame());
128 if (!imgf) {
129 return 0.f;
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.
139 return 0.f;
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());
147 if (aspectRatio) {
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);
174 if (aspectRatio) {
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.
193 return 0.f;
196 template <class Tag>
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>{},
200 "Wrong tag");
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|
214 return 0.f;
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
224 template <class Tag>
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) {
232 if (!aElement) {
233 return false;
235 if (const nsIFrame* f = aElement->GetPrimaryFrame()) {
236 aFunc(f->Style());
237 return true;
240 if (RefPtr<const ComputedStyle> computedStyle =
241 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement)) {
242 aFunc(computedStyle.get());
243 return true;
246 return false;
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));
262 if (res) {
263 return true;
266 SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes = 0);
267 return false;
270 #undef SVGGEOMETRYPROPERTY_EVAL_ALL
272 nsCSSUnit SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit);
273 nsCSSPropertyID AttrEnumToCSSPropId(const SVGElement* aElement,
274 uint8_t aAttrEnum);
276 bool IsNonNegativeGeometryProperty(nsCSSPropertyID aProp);
277 bool ElementMapsLengthsToStyle(SVGElement const* aElement);
279 } // namespace mozilla::dom::SVGGeometryProperty
281 #endif // DOM_SVG_SVGGEOMETRYPROPERTY_H_