/* -*- 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/. */ /* Some inline functions declared in cbindgen.toml */ #ifndef mozilla_ServoStyleConstsInlines_h #define mozilla_ServoStyleConstsInlines_h #include "mozilla/ServoStyleConsts.h" #include "mozilla/AspectRatio.h" #include "mozilla/EndianUtils.h" #include "mozilla/URLExtraData.h" #include "mozilla/dom/WorkerCommon.h" #include "nsGkAtoms.h" #include "MainThreadUtils.h" #include "nsNetUtil.h" #include #include // TODO(emilio): there are quite a few other implementations scattered around // that should move here. namespace mozilla { // We need to explicitly instantiate these so that the clang plugin can see that // they're trivially copiable... // // https://github.com/eqrion/cbindgen/issues/402 tracks doing something like // this automatically from cbindgen. template struct StyleOwned; template struct StyleOwned; template struct StyleOwned; template struct StyleOwned; template struct StyleOwnedOrNull; template struct StyleOwnedOrNull; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template struct StyleStrong; template inline void StyleOwnedSlice::Clear() { if (!len) { return; } for (size_t i : IntegerRange(len)) { ptr[i].~T(); } free(ptr); ptr = (T*)alignof(T); len = 0; } template inline void StyleOwnedSlice::CopyFrom(const StyleOwnedSlice& aOther) { Clear(); len = aOther.len; if (!len) { ptr = (T*)alignof(T); } else { ptr = (T*)malloc(len * sizeof(T)); size_t i = 0; for (const T& elem : aOther.AsSpan()) { new (ptr + i++) T(elem); } } } template inline void StyleOwnedSlice::SwapElements(StyleOwnedSlice& aOther) { std::swap(ptr, aOther.ptr); std::swap(len, aOther.len); } template inline StyleOwnedSlice::StyleOwnedSlice(const StyleOwnedSlice& aOther) : StyleOwnedSlice() { CopyFrom(aOther); } template inline StyleOwnedSlice::StyleOwnedSlice(StyleOwnedSlice&& aOther) : StyleOwnedSlice() { SwapElements(aOther); } template inline StyleOwnedSlice::StyleOwnedSlice(Vector&& aVector) : StyleOwnedSlice() { if (!aVector.length()) { return; } // We could handle this if Vector provided the relevant APIs, see bug 1610702. MOZ_DIAGNOSTIC_ASSERT(aVector.length() == aVector.capacity(), "Shouldn't over-allocate"); len = aVector.length(); ptr = aVector.extractRawBuffer(); MOZ_ASSERT(ptr, "How did extractRawBuffer return null if we're not using inline " "capacity?"); } template inline StyleOwnedSlice& StyleOwnedSlice::operator=( const StyleOwnedSlice& aOther) { CopyFrom(aOther); return *this; } template inline StyleOwnedSlice& StyleOwnedSlice::operator=( StyleOwnedSlice&& aOther) { Clear(); SwapElements(aOther); return *this; } template inline StyleOwnedSlice::~StyleOwnedSlice() { Clear(); } // This code is basically a C++ port of the Arc::clone() implementation in // servo/components/servo_arc/lib.rs. static constexpr const size_t kStaticRefcount = std::numeric_limits::max(); static constexpr const size_t kMaxRefcount = std::numeric_limits::max(); template inline void StyleArcInner::IncrementRef() { if (count.load(std::memory_order_relaxed) != kStaticRefcount) { auto old_size = count.fetch_add(1, std::memory_order_relaxed); if (MOZ_UNLIKELY(old_size > kMaxRefcount)) { ::abort(); } } } // This is a C++ port-ish of Arc::drop(). template inline bool StyleArcInner::DecrementRef() { if (count.load(std::memory_order_relaxed) == kStaticRefcount) { return false; } if (count.fetch_sub(1, std::memory_order_release) != 1) { return false; } #ifdef MOZ_TSAN // TSan doesn't understand std::atomic_thread_fence, so in order // to avoid a false positive for every time a refcounted object // is deleted, we replace the fence with an atomic operation. count.load(std::memory_order_acquire); #else std::atomic_thread_fence(std::memory_order_acquire); #endif MOZ_LOG_DTOR(this, "ServoArc", 8); return true; } static constexpr const uint64_t kArcSliceCanary = 0xf3f3f3f3f3f3f3f3; #define ASSERT_CANARY \ MOZ_DIAGNOSTIC_ASSERT(_0.ptr->data.header.header == kArcSliceCanary, "Uh?"); template inline StyleArcSlice::StyleArcSlice() { _0.ptr = reinterpret_cast(Servo_StyleArcSlice_EmptyPtr()); ASSERT_CANARY } template inline StyleArcSlice::StyleArcSlice(const StyleArcSlice& aOther) { MOZ_DIAGNOSTIC_ASSERT(aOther._0.ptr); _0.ptr = aOther._0.ptr; _0.ptr->IncrementRef(); ASSERT_CANARY } template inline StyleArcSlice::StyleArcSlice( const StyleForgottenArcSlicePtr& aPtr) { // See the forget() implementation to see why reinterpret_cast() is ok. _0.ptr = reinterpret_cast(aPtr._0); ASSERT_CANARY } template inline size_t StyleArcSlice::Length() const { ASSERT_CANARY return _0.ptr->data.header.length; } template inline bool StyleArcSlice::IsEmpty() const { ASSERT_CANARY return Length() == 0; } template inline Span StyleArcSlice::AsSpan() const { ASSERT_CANARY // Explicitly specify template argument here to avoid instantiating Span // first and then implicitly converting to Span return Span{_0.ptr->data.slice, Length()}; } template inline bool StyleArcSlice::operator==(const StyleArcSlice& aOther) const { ASSERT_CANARY return _0.ptr == aOther._0.ptr || AsSpan() == aOther.AsSpan(); } template inline bool StyleArcSlice::operator!=(const StyleArcSlice& aOther) const { return !(*this == aOther); } template inline void StyleArcSlice::Release() { ASSERT_CANARY if (MOZ_LIKELY(!_0.ptr->DecrementRef())) { return; } for (T& elem : Span(_0.ptr->data.slice, Length())) { elem.~T(); } free(_0.ptr); // Drop the allocation now. } template inline StyleArcSlice::~StyleArcSlice() { Release(); } template inline StyleArcSlice& StyleArcSlice::operator=(StyleArcSlice&& aOther) { ASSERT_CANARY std::swap(_0.ptr, aOther._0.ptr); ASSERT_CANARY return *this; } template inline StyleArcSlice& StyleArcSlice::operator=( const StyleArcSlice& aOther) { ASSERT_CANARY if (_0.ptr == aOther._0.ptr) { return *this; } Release(); _0.ptr = aOther._0.ptr; _0.ptr->IncrementRef(); ASSERT_CANARY return *this; } #undef ASSERT_CANARY template inline StyleArc::StyleArc(const StyleArc& aOther) : p(aOther.p) { p->IncrementRef(); } template inline void StyleArc::Release() { if (MOZ_LIKELY(!p->DecrementRef())) { return; } p->data.~T(); free(p); } template inline StyleArc& StyleArc::operator=(const StyleArc& aOther) { if (p != aOther.p) { Release(); p = aOther.p; p->IncrementRef(); } return *this; } template inline StyleArc& StyleArc::operator=(StyleArc&& aOther) { std::swap(p, aOther.p); return *this; } template inline StyleArc::~StyleArc() { Release(); } inline bool StyleAtom::IsStatic() const { return !!(_0 & 1); } inline nsAtom* StyleAtom::AsAtom() const { if (IsStatic()) { auto* atom = reinterpret_cast( reinterpret_cast(&detail::gGkAtoms) + (_0 >> 1)); MOZ_ASSERT(atom->IsStatic()); return const_cast(atom); } return reinterpret_cast(_0); } inline void StyleAtom::AddRef() { if (!IsStatic()) { AsAtom()->AddRef(); } } inline void StyleAtom::Release() { if (!IsStatic()) { AsAtom()->Release(); } } inline StyleAtom::StyleAtom(already_AddRefed aAtom) { nsAtom* atom = aAtom.take(); if (atom->IsStatic()) { ptrdiff_t offset = reinterpret_cast(atom->AsStatic()) - reinterpret_cast(&detail::gGkAtoms); _0 = (offset << 1) | 1; } else { _0 = reinterpret_cast(atom); } MOZ_ASSERT(IsStatic() == atom->IsStatic()); MOZ_ASSERT(AsAtom() == atom); } inline StyleAtom::StyleAtom(const StyleAtom& aOther) : _0(aOther._0) { AddRef(); } inline StyleAtom& StyleAtom::operator=(const StyleAtom& aOther) { if (MOZ_LIKELY(this != &aOther)) { Release(); _0 = aOther._0; AddRef(); } return *this; } inline StyleAtom::~StyleAtom() { Release(); } inline nsAtom* StyleCustomIdent::AsAtom() const { return _0.AsAtom(); } inline nsDependentCSubstring StyleOwnedStr::AsString() const { Span s = _0.AsSpan(); return nsDependentCSubstring(reinterpret_cast(s.Elements()), s.Length()); } template inline Span StyleGenericTransform::Operations() const { return _0.AsSpan(); } template inline bool StyleGenericTransform::IsNone() const { return Operations().IsEmpty(); } inline StyleAngle StyleAngle::Zero() { return {0.0f}; } inline float StyleAngle::ToDegrees() const { return _0; } inline double StyleAngle::ToRadians() const { return double(ToDegrees()) * M_PI / 180.0; } inline bool StyleUrlExtraData::IsShared() const { return !!(_0 & 1); } inline StyleUrlExtraData::~StyleUrlExtraData() { if (!IsShared()) { reinterpret_cast(_0)->Release(); } } inline const URLExtraData& StyleUrlExtraData::get() const { if (IsShared()) { return *URLExtraData::sShared[_0 >> 1].get(); } return *reinterpret_cast(_0); } inline nsDependentCSubstring StyleCssUrl::SpecifiedSerialization() const { return _0->serialization.AsString(); } inline const URLExtraData& StyleCssUrl::ExtraData() const { return _0->extra_data.get(); } inline StyleLoadData& StyleCssUrl::LoadData() const { if (MOZ_LIKELY(_0->load_data.tag == StyleLoadDataSource::Tag::Owned)) { MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread() || dom::IsCurrentThreadRunningWorker()); return const_cast(_0->load_data.owned._0); } MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(), "Lazy load datas should come from user-agent sheets, " "which don't make sense on workers"); return const_cast(*Servo_LoadData_GetLazy(&_0->load_data)); } inline nsIURI* StyleCssUrl::GetURI() const { auto& loadData = LoadData(); if (!(loadData.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_URI)) { loadData.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_URI; nsDependentCSubstring serialization = SpecifiedSerialization(); // https://drafts.csswg.org/css-values-4/#url-empty: // // If the value of the url() is the empty string (like url("") or // url()), the url must resolve to an invalid resource (similar to what // the url about:invalid does). // if (!serialization.IsEmpty()) { RefPtr resolved; NS_NewURI(getter_AddRefs(resolved), serialization, nullptr, ExtraData().BaseURI()); loadData.resolved_uri = resolved.forget().take(); } } return loadData.resolved_uri; } inline nsDependentCSubstring StyleComputedUrl::SpecifiedSerialization() const { return _0.SpecifiedSerialization(); } inline const URLExtraData& StyleComputedUrl::ExtraData() const { return _0.ExtraData(); } inline StyleLoadData& StyleComputedUrl::LoadData() const { return _0.LoadData(); } inline StyleCorsMode StyleComputedUrl::CorsMode() const { return _0._0->cors_mode; } inline nsIURI* StyleComputedUrl::GetURI() const { return _0.GetURI(); } inline bool StyleComputedUrl::IsLocalRef() const { return Servo_CssUrl_IsLocalRef(&_0); } inline bool StyleComputedUrl::HasRef() const { if (IsLocalRef()) { return true; } if (nsIURI* uri = GetURI()) { bool hasRef = false; return NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef; } return false; } inline bool StyleComputedImageUrl::IsImageResolved() const { return bool(LoadData().flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE); } inline imgRequestProxy* StyleComputedImageUrl::GetImage() const { MOZ_ASSERT(IsImageResolved()); return LoadData().resolved_image; } template <> inline bool StyleGradient::Repeating() const { if (IsLinear()) { return AsLinear().repeating; } if (IsRadial()) { return AsRadial().repeating; } return AsConic().repeating; } template <> bool StyleGradient::IsOpaque() const; template inline StyleGenericGridLine::StyleGenericGridLine() : ident(do_AddRef(static_cast(nsGkAtoms::_empty))), line_num(0), is_span(false) {} template <> inline nsAtom* StyleGridLine::LineName() const { return ident.AsAtom(); } template <> inline bool StyleGridLine::IsAuto() const { return LineName()->IsEmpty() && line_num == 0 && !is_span; } using LengthPercentage = StyleLengthPercentage; using LengthPercentageOrAuto = StyleLengthPercentageOrAuto; using NonNegativeLengthPercentage = StyleNonNegativeLengthPercentage; using NonNegativeLengthPercentageOrAuto = StyleNonNegativeLengthPercentageOrAuto; using NonNegativeLengthPercentageOrNormal = StyleNonNegativeLengthPercentageOrNormal; using Length = StyleLength; using LengthOrAuto = StyleLengthOrAuto; using NonNegativeLength = StyleNonNegativeLength; using NonNegativeLengthOrAuto = StyleNonNegativeLengthOrAuto; using BorderRadius = StyleBorderRadius; bool StyleCSSPixelLength::IsZero() const { return _0 == 0.0f; } void StyleCSSPixelLength::ScaleBy(float aScale) { _0 *= aScale; } StyleCSSPixelLength StyleCSSPixelLength::ScaledBy(float aScale) const { return FromPixels(ToCSSPixels() * aScale); } nscoord StyleCSSPixelLength::ToAppUnits() const { // We want to resolve the length part of the calc() expression rounding 0.5 // away from zero, instead of the default behavior of // NSToCoordRound{,WithClamp} which do floor(x + 0.5). // // This is what the rust code in the app_units crate does, and not doing this // would regress bug 1323735, for example. // // FIXME(emilio, bug 1528114): Probably we should do something smarter. if (IsZero()) { // Avoid the expensive FP math below. return 0; } float length = _0 * float(mozilla::AppUnitsPerCSSPixel()); if (length >= float(nscoord_MAX)) { return nscoord_MAX; } if (length <= float(nscoord_MIN)) { return nscoord_MIN; } return NSToIntRound(length); } bool LengthPercentage::IsLength() const { return Tag() == TAG_LENGTH; } StyleLengthPercentageUnion::StyleLengthPercentageUnion() { length = {TAG_LENGTH, {0.0f}}; MOZ_ASSERT(IsLength()); } static_assert(sizeof(LengthPercentage) == sizeof(uint64_t), ""); Length& LengthPercentage::AsLength() { MOZ_ASSERT(IsLength()); return length.length; } const Length& LengthPercentage::AsLength() const { return const_cast(this)->AsLength(); } bool LengthPercentage::IsPercentage() const { return Tag() == TAG_PERCENTAGE; } StylePercentage& LengthPercentage::AsPercentage() { MOZ_ASSERT(IsPercentage()); return percentage.percentage; } const StylePercentage& LengthPercentage::AsPercentage() const { return const_cast(this)->AsPercentage(); } bool LengthPercentage::IsCalc() const { return Tag() == TAG_CALC; } StyleCalcLengthPercentage& LengthPercentage::AsCalc() { MOZ_ASSERT(IsCalc()); // NOTE: in 32-bits, the pointer is not swapped, and goes along with the tag. #ifdef SERVO_32_BITS return *calc.ptr; #else return *reinterpret_cast( NativeEndian::swapFromLittleEndian(calc.ptr)); #endif } const StyleCalcLengthPercentage& LengthPercentage::AsCalc() const { return const_cast(this)->AsCalc(); } StyleLengthPercentageUnion::StyleLengthPercentageUnion(const Self& aOther) { if (aOther.IsLength()) { length = {TAG_LENGTH, aOther.AsLength()}; } else if (aOther.IsPercentage()) { percentage = {TAG_PERCENTAGE, aOther.AsPercentage()}; } else { MOZ_ASSERT(aOther.IsCalc()); auto* ptr = new StyleCalcLengthPercentage(aOther.AsCalc()); // NOTE: in 32-bits, the pointer is not swapped, and goes along with the // tag. calc = { #ifdef SERVO_32_BITS TAG_CALC, ptr, #else NativeEndian::swapToLittleEndian(reinterpret_cast(ptr)), #endif }; } MOZ_ASSERT(Tag() == aOther.Tag()); } StyleLengthPercentageUnion::~StyleLengthPercentageUnion() { if (IsCalc()) { delete &AsCalc(); } } LengthPercentage& LengthPercentage::operator=(const LengthPercentage& aOther) { if (this != &aOther) { this->~LengthPercentage(); new (this) LengthPercentage(aOther); } return *this; } bool LengthPercentage::operator==(const LengthPercentage& aOther) const { if (Tag() != aOther.Tag()) { return false; } if (IsLength()) { return AsLength() == aOther.AsLength(); } if (IsPercentage()) { return AsPercentage() == aOther.AsPercentage(); } return AsCalc() == aOther.AsCalc(); } bool LengthPercentage::operator!=(const LengthPercentage& aOther) const { return !(*this == aOther); } LengthPercentage LengthPercentage::Zero() { return {}; } LengthPercentage LengthPercentage::FromPixels(CSSCoord aCoord) { LengthPercentage l; MOZ_ASSERT(l.IsLength()); l.length.length = {aCoord}; return l; } LengthPercentage LengthPercentage::FromAppUnits(nscoord aCoord) { return FromPixels(CSSPixel::FromAppUnits(aCoord)); } LengthPercentage LengthPercentage::FromPercentage(float aPercentage) { LengthPercentage l; l.percentage = {TAG_PERCENTAGE, {aPercentage}}; return l; } bool LengthPercentage::HasPercent() const { return IsPercentage() || IsCalc(); } bool LengthPercentage::ConvertsToLength() const { return IsLength(); } nscoord LengthPercentage::ToLength() const { MOZ_ASSERT(ConvertsToLength()); return AsLength().ToAppUnits(); } CSSCoord LengthPercentage::ToLengthInCSSPixels() const { MOZ_ASSERT(ConvertsToLength()); return AsLength().ToCSSPixels(); } bool LengthPercentage::ConvertsToPercentage() const { return IsPercentage(); } float LengthPercentage::ToPercentage() const { MOZ_ASSERT(ConvertsToPercentage()); return AsPercentage()._0; } bool LengthPercentage::HasLengthAndPercentage() const { if (!IsCalc()) { return false; } MOZ_ASSERT(!ConvertsToLength() && !ConvertsToPercentage(), "Should've been simplified earlier"); return true; } bool LengthPercentage::IsDefinitelyZero() const { if (IsLength()) { return AsLength().IsZero(); } if (IsPercentage()) { return AsPercentage()._0 == 0.0f; } // calc() should've been simplified to a percentage. return false; } template <> CSSCoord StyleCalcNode::ResolveToCSSPixels(CSSCoord aPercentageBasis) const; template <> void StyleCalcNode::ScaleLengthsBy(float); CSSCoord LengthPercentage::ResolveToCSSPixels(CSSCoord aPercentageBasis) const { if (IsLength()) { return AsLength().ToCSSPixels(); } if (IsPercentage()) { return AsPercentage()._0 * aPercentageBasis; } return AsCalc().node.ResolveToCSSPixels(aPercentageBasis); } template CSSCoord LengthPercentage::ResolveToCSSPixelsWith(T aPercentageGetter) const { static_assert(std::is_same::value, "Should return CSS pixels"); if (ConvertsToLength()) { return ToLengthInCSSPixels(); } return ResolveToCSSPixels(aPercentageGetter()); } template nscoord LengthPercentage::Resolve(T aPercentageGetter, U aRounder) const { static_assert(std::is_same::value, "Should return app units"); static_assert(std::is_same::value, "Should return app units"); if (ConvertsToLength()) { return ToLength(); } if (IsPercentage() && AsPercentage()._0 == 0.0f) { return 0.0f; } nscoord basis = aPercentageGetter(); if (IsPercentage()) { return aRounder(basis * AsPercentage()._0); } return AsCalc().node.Resolve(basis, aRounder); } // Note: the static_cast<> wrappers below are needed to disambiguate between // the versions of NSToCoordTruncClamped that take float vs. double as the arg. nscoord LengthPercentage::Resolve(nscoord aPercentageBasis) const { return Resolve([=] { return aPercentageBasis; }, static_cast(NSToCoordTruncClamped)); } template nscoord LengthPercentage::Resolve(T aPercentageGetter) const { return Resolve(aPercentageGetter, static_cast(NSToCoordTruncClamped)); } template nscoord LengthPercentage::Resolve(nscoord aPercentageBasis, T aPercentageRounder) const { return Resolve([aPercentageBasis] { return aPercentageBasis; }, aPercentageRounder); } void LengthPercentage::ScaleLengthsBy(float aScale) { if (IsLength()) { AsLength().ScaleBy(aScale); } if (IsCalc()) { AsCalc().node.ScaleLengthsBy(aScale); } } #define IMPL_LENGTHPERCENTAGE_FORWARDS(ty_) \ template <> \ inline bool ty_::HasPercent() const { \ return IsLengthPercentage() && AsLengthPercentage().HasPercent(); \ } \ template <> \ inline bool ty_::ConvertsToLength() const { \ return IsLengthPercentage() && AsLengthPercentage().ConvertsToLength(); \ } \ template <> \ inline bool ty_::HasLengthAndPercentage() const { \ return IsLengthPercentage() && \ AsLengthPercentage().HasLengthAndPercentage(); \ } \ template <> \ inline nscoord ty_::ToLength() const { \ MOZ_ASSERT(ConvertsToLength()); \ return AsLengthPercentage().ToLength(); \ } \ template <> \ inline bool ty_::ConvertsToPercentage() const { \ return IsLengthPercentage() && \ AsLengthPercentage().ConvertsToPercentage(); \ } \ template <> \ inline float ty_::ToPercentage() const { \ MOZ_ASSERT(ConvertsToPercentage()); \ return AsLengthPercentage().ToPercentage(); \ } IMPL_LENGTHPERCENTAGE_FORWARDS(LengthPercentageOrAuto) IMPL_LENGTHPERCENTAGE_FORWARDS(StyleSize) IMPL_LENGTHPERCENTAGE_FORWARDS(StyleMaxSize) template <> inline bool LengthOrAuto::IsLength() const { return IsLengthPercentage(); } template <> inline const Length& LengthOrAuto::AsLength() const { return AsLengthPercentage(); } template <> inline nscoord LengthOrAuto::ToLength() const { return AsLength().ToAppUnits(); } template <> inline bool StyleFlexBasis::IsAuto() const { return IsSize() && AsSize().IsAuto(); } template <> inline bool StyleSize::BehavesLikeInitialValueOnBlockAxis() const { return IsAuto() || !IsLengthPercentage(); } template <> inline bool StyleMaxSize::BehavesLikeInitialValueOnBlockAxis() const { return IsNone() || !IsLengthPercentage(); } template <> inline bool StyleBackgroundSize::IsInitialValue() const { return IsExplicitSize() && explicit_size.width.IsAuto() && explicit_size.height.IsAuto(); } template const T& StyleRect::Get(mozilla::Side aSide) const { static_assert(sizeof(StyleRect) == sizeof(T) * 4, ""); static_assert(alignof(StyleRect) == alignof(T), ""); return reinterpret_cast(this)[aSide]; } template T& StyleRect::Get(mozilla::Side aSide) { return const_cast(static_cast(*this).Get(aSide)); } template template bool StyleRect::All(Predicate aPredicate) const { return aPredicate(_0) && aPredicate(_1) && aPredicate(_2) && aPredicate(_3); } template template bool StyleRect::Any(Predicate aPredicate) const { return aPredicate(_0) || aPredicate(_1) || aPredicate(_2) || aPredicate(_3); } template <> inline const LengthPercentage& BorderRadius::Get(HalfCorner aCorner) const { static_assert(sizeof(BorderRadius) == sizeof(LengthPercentage) * 8, ""); static_assert(alignof(BorderRadius) == alignof(LengthPercentage), ""); auto* self = reinterpret_cast(this); return self[aCorner]; } template <> inline bool StyleTrackBreadth::HasPercent() const { return IsBreadth() && AsBreadth().HasPercent(); } // Implemented in nsStyleStructs.cpp template <> bool StyleTransform::HasPercent() const; template <> inline bool StyleTransformOrigin::HasPercent() const { // NOTE(emilio): `depth` is just a `` so doesn't have a percentage at // all. return horizontal.HasPercent() || vertical.HasPercent(); } template <> inline Maybe StyleGridTemplateComponent::RepeatAutoIndex() const { if (!IsTrackList()) { return Nothing(); } auto& list = *AsTrackList(); return list.auto_repeat_index < list.values.Length() ? Some(list.auto_repeat_index) : Nothing(); } template <> inline bool StyleGridTemplateComponent::HasRepeatAuto() const { return RepeatAutoIndex().isSome(); } template <> inline Span> StyleGridTemplateComponent::TrackListValues() const { if (IsTrackList()) { return AsTrackList()->values.AsSpan(); } return {}; } template <> inline const StyleGenericTrackRepeat* StyleGridTemplateComponent::GetRepeatAutoValue() const { auto index = RepeatAutoIndex(); if (!index) { return nullptr; } return &TrackListValues()[*index].AsTrackRepeat(); } constexpr const auto kPaintOrderShift = StylePAINT_ORDER_SHIFT; constexpr const auto kPaintOrderMask = StylePAINT_ORDER_MASK; template <> inline nsRect StyleGenericClipRect::ToLayoutRect( nscoord aAutoSize) const { nscoord x = left.IsLength() ? left.ToLength() : 0; nscoord y = top.IsLength() ? top.ToLength() : 0; nscoord width = right.IsLength() ? right.ToLength() - x : aAutoSize; nscoord height = bottom.IsLength() ? bottom.ToLength() - y : aAutoSize; return nsRect(x, y, width, height); } using RestyleHint = StyleRestyleHint; inline RestyleHint RestyleHint::RestyleSubtree() { return RESTYLE_SELF | RESTYLE_DESCENDANTS; } inline RestyleHint RestyleHint::RecascadeSubtree() { return RECASCADE_SELF | RECASCADE_DESCENDANTS; } inline RestyleHint RestyleHint::ForAnimations() { return RESTYLE_CSS_TRANSITIONS | RESTYLE_CSS_ANIMATIONS | RESTYLE_SMIL; } inline bool RestyleHint::DefinitelyRecascadesAllSubtree() const { if (!(*this & (RECASCADE_DESCENDANTS | RESTYLE_DESCENDANTS))) { return false; } return bool(*this & (RESTYLE_SELF | RECASCADE_SELF)); } template <> ImageResolution StyleImage::GetResolution() const; template <> inline const StyleImage& StyleImage::FinalImage() const { if (!IsImageSet()) { return *this; } auto& set = AsImageSet(); auto& selectedItem = set->items.AsSpan()[set->selected_index]; return selectedItem.image.FinalImage(); } template <> Maybe StyleImage::GetIntrinsicSize() const; template <> inline bool StyleImage::IsImageRequestType() const { auto& finalImage = FinalImage(); return finalImage.IsUrl() || finalImage.IsRect(); } template <> inline const StyleComputedImageUrl* StyleImage::GetImageRequestURLValue() const { auto& finalImage = FinalImage(); if (finalImage.IsUrl()) { return &finalImage.AsUrl(); } if (finalImage.IsRect()) { return &finalImage.AsRect()->url; } return nullptr; } template <> inline imgRequestProxy* StyleImage::GetImageRequest() const { auto* url = GetImageRequestURLValue(); return url ? url->GetImage() : nullptr; } template <> inline bool StyleImage::IsResolved() const { auto* url = GetImageRequestURLValue(); return !url || url->IsImageResolved(); } template <> bool StyleImage::IsOpaque() const; template <> bool StyleImage::IsSizeAvailable() const; template <> bool StyleImage::IsComplete() const; template <> Maybe StyleImage::ComputeActualCropRect() const; template <> void StyleImage::ResolveImage(dom::Document&, const StyleImage*); template <> inline AspectRatio StyleRatio::ToLayoutRatio( UseBoxSizing aUseBoxSizing) const { // 0/1, 1/0, and 0/0 are all degenerate ratios (which behave as auto), and we // always return 0.0f. // https://drafts.csswg.org/css-values-4/#degenerate-ratio return AspectRatio::FromSize(_0, _1, aUseBoxSizing); } template <> inline AspectRatio StyleAspectRatio::ToLayoutRatio() const { return HasRatio() ? ratio.AsRatio().ToLayoutRatio(auto_ ? UseBoxSizing::No : UseBoxSizing::Yes) : AspectRatio(); } inline void StyleFontWeight::ToString(nsACString& aString) const { Servo_FontWeight_ToCss(this, &aString); } inline void StyleFontStretch::ToString(nsACString& aString) const { Servo_FontStretch_ToCss(this, &aString); } inline void StyleFontStyle::ToString(nsACString& aString) const { Servo_FontStyle_ToCss(this, &aString); } inline bool StyleFontWeight::IsBold() const { return *this >= BOLD_THRESHOLD; } inline bool StyleFontStyle::IsItalic() const { return *this == ITALIC; } inline bool StyleFontStyle::IsOblique() const { return !IsItalic() && !IsNormal(); } inline float StyleFontStyle::ObliqueAngle() const { return ToFloat(); } using FontStretch = StyleFontStretch; using FontSlantStyle = StyleFontStyle; using FontWeight = StyleFontWeight; template <> inline double StyleComputedTimingFunction::At(double aPortion, bool aBeforeFlag) const { return Servo_EasingFunctionAt( this, aPortion, aBeforeFlag ? StyleEasingBeforeFlag::Set : StyleEasingBeforeFlag::Unset); } template <> inline void StyleComputedTimingFunction::AppendToString( nsACString& aOut) const { return Servo_SerializeEasing(this, &aOut); } template <> inline double StyleComputedTimingFunction::GetPortion( const Maybe& aFn, double aPortion, bool aBeforeFlag) { return aFn ? aFn->At(aPortion, aBeforeFlag) : aPortion; } } // namespace mozilla #endif