зеркало из https://github.com/mozilla/gecko-dev.git
282 строки
9.5 KiB
C++
282 строки
9.5 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef DOM_SVG_SVGGEOMETRYPROPERTY_H_
|
|
#define DOM_SVG_SVGGEOMETRYPROPERTY_H_
|
|
|
|
#include "mozilla/SVGImageFrame.h"
|
|
#include "mozilla/dom/SVGElement.h"
|
|
#include "ComputedStyle.h"
|
|
#include "SVGAnimatedLength.h"
|
|
#include "nsComputedDOMStyle.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsIFrame.h"
|
|
#include <type_traits>
|
|
|
|
namespace mozilla::dom::SVGGeometryProperty {
|
|
namespace ResolverTypes {
|
|
struct LengthPercentNoAuto {};
|
|
struct LengthPercentRXY {};
|
|
struct LengthPercentWidthHeight {};
|
|
} // namespace ResolverTypes
|
|
|
|
namespace Tags {
|
|
|
|
#define SVGGEOMETRYPROPERTY_GENERATETAG(tagName, resolver, direction, \
|
|
styleStruct) \
|
|
struct tagName { \
|
|
using ResolverType = ResolverTypes::resolver; \
|
|
constexpr static auto CtxDirection = SVGContentUtils::direction; \
|
|
constexpr static auto Getter = &styleStruct::m##tagName; \
|
|
}
|
|
|
|
SVGGEOMETRYPROPERTY_GENERATETAG(X, LengthPercentNoAuto, X, nsStyleSVGReset);
|
|
SVGGEOMETRYPROPERTY_GENERATETAG(Y, LengthPercentNoAuto, Y, nsStyleSVGReset);
|
|
SVGGEOMETRYPROPERTY_GENERATETAG(Cx, LengthPercentNoAuto, X, nsStyleSVGReset);
|
|
SVGGEOMETRYPROPERTY_GENERATETAG(Cy, LengthPercentNoAuto, Y, nsStyleSVGReset);
|
|
SVGGEOMETRYPROPERTY_GENERATETAG(R, LengthPercentNoAuto, XY, nsStyleSVGReset);
|
|
|
|
#undef SVGGEOMETRYPROPERTY_GENERATETAG
|
|
|
|
struct Height;
|
|
struct Width {
|
|
using ResolverType = ResolverTypes::LengthPercentWidthHeight;
|
|
constexpr static auto CtxDirection = SVGContentUtils::X;
|
|
constexpr static auto Getter = &nsStylePosition::GetWidth;
|
|
constexpr static auto SizeGetter = &gfx::Size::width;
|
|
static AspectRatio AspectRatioRelative(AspectRatio aAspectRatio) {
|
|
return aAspectRatio.Inverted();
|
|
}
|
|
constexpr static uint32_t DefaultObjectSize = kFallbackIntrinsicWidthInPixels;
|
|
using CounterPart = Height;
|
|
};
|
|
struct Height {
|
|
using ResolverType = ResolverTypes::LengthPercentWidthHeight;
|
|
constexpr static auto CtxDirection = SVGContentUtils::Y;
|
|
constexpr static auto Getter = &nsStylePosition::GetHeight;
|
|
constexpr static auto SizeGetter = &gfx::Size::height;
|
|
static AspectRatio AspectRatioRelative(AspectRatio aAspectRatio) {
|
|
return aAspectRatio;
|
|
}
|
|
constexpr static uint32_t DefaultObjectSize =
|
|
kFallbackIntrinsicHeightInPixels;
|
|
using CounterPart = Width;
|
|
};
|
|
|
|
struct Ry;
|
|
struct Rx {
|
|
using ResolverType = ResolverTypes::LengthPercentRXY;
|
|
constexpr static auto CtxDirection = SVGContentUtils::X;
|
|
constexpr static auto Getter = &nsStyleSVGReset::mRx;
|
|
using CounterPart = Ry;
|
|
};
|
|
struct Ry {
|
|
using ResolverType = ResolverTypes::LengthPercentRXY;
|
|
constexpr static auto CtxDirection = SVGContentUtils::Y;
|
|
constexpr static auto Getter = &nsStyleSVGReset::mRy;
|
|
using CounterPart = Rx;
|
|
};
|
|
|
|
} // namespace Tags
|
|
|
|
namespace details {
|
|
template <class T>
|
|
using AlwaysFloat = float;
|
|
using dummy = int[];
|
|
|
|
using CtxDirectionType = decltype(SVGContentUtils::X);
|
|
|
|
template <CtxDirectionType CTD>
|
|
float ResolvePureLengthPercentage(const SVGElement* aElement,
|
|
const LengthPercentage& aLP) {
|
|
return aLP.ResolveToCSSPixelsWith(
|
|
[&] { return CSSCoord{SVGElementMetrics(aElement).GetAxisLength(CTD)}; });
|
|
}
|
|
|
|
template <class Tag>
|
|
float ResolveImpl(ComputedStyle const& aStyle, const SVGElement* aElement,
|
|
ResolverTypes::LengthPercentNoAuto) {
|
|
auto const& value = aStyle.StyleSVGReset()->*Tag::Getter;
|
|
return ResolvePureLengthPercentage<Tag::CtxDirection>(aElement, value);
|
|
}
|
|
|
|
template <class Tag>
|
|
float ResolveImpl(ComputedStyle const& aStyle, const SVGElement* aElement,
|
|
ResolverTypes::LengthPercentWidthHeight) {
|
|
static_assert(
|
|
std::is_same<Tag, Tags::Width>{} || std::is_same<Tag, Tags::Height>{},
|
|
"Wrong tag");
|
|
|
|
auto const& value = std::invoke(Tag::Getter, aStyle.StylePosition());
|
|
if (value.IsLengthPercentage()) {
|
|
return ResolvePureLengthPercentage<Tag::CtxDirection>(
|
|
aElement, value.AsLengthPercentage());
|
|
}
|
|
|
|
if (aElement->IsSVGElement(nsGkAtoms::image)) {
|
|
// It's not clear per SVG2 spec what should be done for values other
|
|
// than |auto| (e.g. |max-content|). We treat them as nonsense, thus
|
|
// using the initial value behavior, i.e. |auto|.
|
|
// The following procedure follows the Default Sizing Algorithm as
|
|
// specified in:
|
|
// https://svgwg.org/svg2-draft/embedded.html#ImageElement
|
|
|
|
SVGImageFrame* imgf = do_QueryFrame(aElement->GetPrimaryFrame());
|
|
if (!imgf) {
|
|
return 0.f;
|
|
}
|
|
|
|
using Other = typename Tag::CounterPart;
|
|
auto const& valueOther = std::invoke(Other::Getter, aStyle.StylePosition());
|
|
|
|
gfx::Size intrinsicImageSize;
|
|
AspectRatio aspectRatio;
|
|
if (!imgf->GetIntrinsicImageDimensions(intrinsicImageSize, aspectRatio)) {
|
|
// No image container, just return 0.
|
|
return 0.f;
|
|
}
|
|
|
|
if (valueOther.IsLengthPercentage()) {
|
|
// We are |auto|, but the other side has specifed length.
|
|
float lengthOther = ResolvePureLengthPercentage<Other::CtxDirection>(
|
|
aElement, valueOther.AsLengthPercentage());
|
|
|
|
if (aspectRatio) {
|
|
// Preserve aspect ratio if it's present.
|
|
return Other::AspectRatioRelative(aspectRatio)
|
|
.ApplyToFloat(lengthOther);
|
|
}
|
|
|
|
float intrinsicLength = intrinsicImageSize.*Tag::SizeGetter;
|
|
if (intrinsicLength >= 0) {
|
|
// Use the intrinsic length if it's present.
|
|
return intrinsicLength;
|
|
}
|
|
|
|
// No specified size, no aspect ratio, no intrinsic length,
|
|
// then use default size.
|
|
return Tag::DefaultObjectSize;
|
|
}
|
|
|
|
// |width| and |height| are both |auto|
|
|
if (intrinsicImageSize.*Tag::SizeGetter >= 0) {
|
|
return intrinsicImageSize.*Tag::SizeGetter;
|
|
}
|
|
|
|
if (intrinsicImageSize.*Other::SizeGetter >= 0 && aspectRatio) {
|
|
return Other::AspectRatioRelative(aspectRatio)
|
|
.ApplyTo(intrinsicImageSize.*Other::SizeGetter);
|
|
}
|
|
|
|
if (aspectRatio) {
|
|
// Resolve as a contain constraint against the default object size.
|
|
auto defaultAspectRatioRelative =
|
|
AspectRatio{float(Other::DefaultObjectSize) / Tag::DefaultObjectSize};
|
|
auto aspectRatioRelative = Tag::AspectRatioRelative(aspectRatio);
|
|
|
|
if (defaultAspectRatioRelative < aspectRatioRelative) {
|
|
// Using default length in our side and the intrinsic aspect ratio,
|
|
// the other side cannot be contained.
|
|
return aspectRatioRelative.Inverted().ApplyTo(Other::DefaultObjectSize);
|
|
}
|
|
|
|
return Tag::DefaultObjectSize;
|
|
}
|
|
|
|
return Tag::DefaultObjectSize;
|
|
}
|
|
|
|
// For other elements, |auto| and |max-content| etc. are treated as 0.
|
|
return 0.f;
|
|
}
|
|
|
|
template <class Tag>
|
|
float ResolveImpl(ComputedStyle const& aStyle, const SVGElement* aElement,
|
|
ResolverTypes::LengthPercentRXY) {
|
|
static_assert(std::is_same<Tag, Tags::Rx>{} || std::is_same<Tag, Tags::Ry>{},
|
|
"Wrong tag");
|
|
|
|
auto const& value = aStyle.StyleSVGReset()->*Tag::Getter;
|
|
if (value.IsLengthPercentage()) {
|
|
return ResolvePureLengthPercentage<Tag::CtxDirection>(
|
|
aElement, value.AsLengthPercentage());
|
|
}
|
|
|
|
MOZ_ASSERT(value.IsAuto());
|
|
using Rother = typename Tag::CounterPart;
|
|
auto const& valueOther = aStyle.StyleSVGReset()->*Rother::Getter;
|
|
|
|
if (valueOther.IsAuto()) {
|
|
// Per SVG2, |Rx|, |Ry| resolve to 0 if both are |auto|
|
|
return 0.f;
|
|
}
|
|
|
|
// If |Rx| is auto while |Ry| not, |Rx| gets the value of |Ry|.
|
|
return ResolvePureLengthPercentage<Rother::CtxDirection>(
|
|
aElement, valueOther.AsLengthPercentage());
|
|
}
|
|
|
|
} // namespace details
|
|
|
|
template <class Tag>
|
|
float ResolveWith(const ComputedStyle& aStyle, const SVGElement* aElement) {
|
|
return details::ResolveImpl<Tag>(aStyle, aElement,
|
|
typename Tag::ResolverType{});
|
|
}
|
|
|
|
template <class Func>
|
|
bool DoForComputedStyle(const Element* aElement, Func aFunc) {
|
|
if (!aElement) {
|
|
return false;
|
|
}
|
|
if (const nsIFrame* f = aElement->GetPrimaryFrame()) {
|
|
aFunc(f->Style());
|
|
return true;
|
|
}
|
|
|
|
if (RefPtr<const ComputedStyle> computedStyle =
|
|
nsComputedDOMStyle::GetComputedStyleNoFlush(aElement)) {
|
|
aFunc(computedStyle.get());
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#define SVGGEOMETRYPROPERTY_EVAL_ALL(expr) \
|
|
(void)details::dummy { 0, (static_cast<void>(expr), 0)... }
|
|
|
|
// To add support for new properties, or to handle special cases for
|
|
// existing properties, you can add a new tag in |Tags| and |ResolverTypes|
|
|
// namespace, then implement the behavior in |details::ResolveImpl|.
|
|
template <class... Tags>
|
|
bool ResolveAll(const SVGElement* aElement,
|
|
details::AlwaysFloat<Tags>*... aRes) {
|
|
bool res = DoForComputedStyle(aElement, [&](auto const* style) {
|
|
SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes = ResolveWith<Tags>(*style, aElement));
|
|
});
|
|
|
|
if (res) {
|
|
return true;
|
|
}
|
|
|
|
SVGGEOMETRYPROPERTY_EVAL_ALL(*aRes = 0);
|
|
return false;
|
|
}
|
|
|
|
#undef SVGGEOMETRYPROPERTY_EVAL_ALL
|
|
|
|
nsCSSUnit SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit);
|
|
nsCSSPropertyID AttrEnumToCSSPropId(const SVGElement* aElement,
|
|
uint8_t aAttrEnum);
|
|
|
|
bool IsNonNegativeGeometryProperty(nsCSSPropertyID aProp);
|
|
bool ElementMapsLengthsToStyle(SVGElement const* aElement);
|
|
|
|
} // namespace mozilla::dom::SVGGeometryProperty
|
|
|
|
#endif // DOM_SVG_SVGGEOMETRYPROPERTY_H_
|