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 /* Some inline functions declared in cbindgen.toml */
8 
9 #ifndef mozilla_ServoStyleConstsInlines_h
10 #define mozilla_ServoStyleConstsInlines_h
11 
12 #include "mozilla/ServoStyleConsts.h"
13 #include "mozilla/AspectRatio.h"
14 #include "mozilla/EndianUtils.h"
15 #include "mozilla/URLExtraData.h"
16 #include "nsGkAtoms.h"
17 #include "MainThreadUtils.h"
18 #include "nsNetUtil.h"
19 #include <type_traits>
20 #include <new>
21 
22 // TODO(emilio): there are quite a few other implementations scattered around
23 // that should move here.
24 
25 namespace mozilla {
26 
27 // We need to explicitly instantiate these so that the clang plugin can see that
28 // they're trivially copiable...
29 //
30 // https://github.com/eqrion/cbindgen/issues/402 tracks doing something like
31 // this automatically from cbindgen.
32 template struct StyleOwned<RawServoAnimationValueMap>;
33 template struct StyleOwned<RawServoAuthorStyles>;
34 template struct StyleOwned<RawServoSourceSizeList>;
35 template struct StyleOwned<StyleUseCounters>;
36 template struct StyleOwnedOrNull<StyleUseCounters>;
37 template struct StyleOwnedOrNull<RawServoSelectorList>;
38 template struct StyleStrong<ComputedStyle>;
39 template struct StyleStrong<ServoCssRules>;
40 template struct StyleStrong<RawServoAnimationValue>;
41 template struct StyleStrong<RawServoDeclarationBlock>;
42 template struct StyleStrong<RawServoStyleSheetContents>;
43 template struct StyleStrong<RawServoKeyframe>;
44 template struct StyleStrong<RawServoMediaList>;
45 template struct StyleStrong<RawServoStyleRule>;
46 template struct StyleStrong<RawServoImportRule>;
47 template struct StyleStrong<RawServoKeyframesRule>;
48 template struct StyleStrong<RawServoMediaRule>;
49 template struct StyleStrong<RawServoMozDocumentRule>;
50 template struct StyleStrong<RawServoNamespaceRule>;
51 template struct StyleStrong<RawServoPageRule>;
52 template struct StyleStrong<RawServoSupportsRule>;
53 template struct StyleStrong<RawServoFontFeatureValuesRule>;
54 template struct StyleStrong<RawServoFontFaceRule>;
55 template struct StyleStrong<RawServoCounterStyleRule>;
56 
57 template <typename T>
58 inline void StyleOwnedSlice<T>::Clear() {
59   if (!len) {
60     return;
61   }
62   for (size_t i : IntegerRange(len)) {
63     ptr[i].~T();
64   }
65   free(ptr);
66   ptr = (T*)alignof(T);
67   len = 0;
68 }
69 
70 template <typename T>
71 inline void StyleOwnedSlice<T>::CopyFrom(const StyleOwnedSlice& aOther) {
72   Clear();
73   len = aOther.len;
74   if (!len) {
75     ptr = (T*)alignof(T);
76   } else {
77     ptr = (T*)malloc(len * sizeof(T));
78     size_t i = 0;
79     for (const T& elem : aOther.AsSpan()) {
80       new (ptr + i++) T(elem);
81     }
82   }
83 }
84 
85 template <typename T>
86 inline void StyleOwnedSlice<T>::SwapElements(StyleOwnedSlice& aOther) {
87   std::swap(ptr, aOther.ptr);
88   std::swap(len, aOther.len);
89 }
90 
91 template <typename T>
92 inline StyleOwnedSlice<T>::StyleOwnedSlice(const StyleOwnedSlice& aOther)
93     : StyleOwnedSlice() {
94   CopyFrom(aOther);
95 }
96 
97 template <typename T>
98 inline StyleOwnedSlice<T>::StyleOwnedSlice(StyleOwnedSlice&& aOther)
99     : StyleOwnedSlice() {
100   SwapElements(aOther);
101 }
102 
103 template <typename T>
104 inline StyleOwnedSlice<T>::StyleOwnedSlice(Vector<T>&& aVector)
105     : StyleOwnedSlice() {
106   if (!aVector.length()) {
107     return;
108   }
109 
110   // We could handle this if Vector provided the relevant APIs, see bug 1610702.
111   MOZ_DIAGNOSTIC_ASSERT(aVector.length() == aVector.capacity(),
112                         "Shouldn't over-allocate");
113   len = aVector.length();
114   ptr = aVector.extractRawBuffer();
115   MOZ_ASSERT(ptr,
116              "How did extractRawBuffer return null if we're not using inline "
117              "capacity?");
118 }
119 
120 template <typename T>
121 inline StyleOwnedSlice<T>& StyleOwnedSlice<T>::operator=(
122     const StyleOwnedSlice& aOther) {
123   CopyFrom(aOther);
124   return *this;
125 }
126 
127 template <typename T>
128 inline StyleOwnedSlice<T>& StyleOwnedSlice<T>::operator=(
129     StyleOwnedSlice&& aOther) {
130   Clear();
131   SwapElements(aOther);
132   return *this;
133 }
134 
135 template <typename T>
136 inline StyleOwnedSlice<T>::~StyleOwnedSlice() {
137   Clear();
138 }
139 
140 // This code is basically a C++ port of the Arc::clone() implementation in
141 // servo/components/servo_arc/lib.rs.
142 static constexpr const size_t kStaticRefcount =
143     std::numeric_limits<size_t>::max();
144 static constexpr const size_t kMaxRefcount =
145     std::numeric_limits<intptr_t>::max();
146 
147 template <typename T>
148 inline void StyleArcInner<T>::IncrementRef() {
149   if (count.load(std::memory_order_relaxed) != kStaticRefcount) {
150     auto old_size = count.fetch_add(1, std::memory_order_relaxed);
151     if (MOZ_UNLIKELY(old_size > kMaxRefcount)) {
152       ::abort();
153     }
154   }
155 }
156 
157 // This is a C++ port-ish of Arc::drop().
158 template <typename T>
159 inline bool StyleArcInner<T>::DecrementRef() {
160   if (count.load(std::memory_order_relaxed) == kStaticRefcount) {
161     return false;
162   }
163   if (count.fetch_sub(1, std::memory_order_release) != 1) {
164     return false;
165   }
166 #ifdef MOZ_TSAN
167   // TSan doesn't understand std::atomic_thread_fence, so in order
168   // to avoid a false positive for every time a refcounted object
169   // is deleted, we replace the fence with an atomic operation.
170   count.load(std::memory_order_acquire);
171 #else
172   std::atomic_thread_fence(std::memory_order_acquire);
173 #endif
174   MOZ_LOG_DTOR(this, "ServoArc", 8);
175   return true;
176 }
177 
178 static constexpr const uint64_t kArcSliceCanary = 0xf3f3f3f3f3f3f3f3;
179 
180 #define ASSERT_CANARY \
181   MOZ_DIAGNOSTIC_ASSERT(_0.ptr->data.header.header == kArcSliceCanary, "Uh?");
182 
183 template <typename T>
184 inline StyleArcSlice<T>::StyleArcSlice() {
185   _0.ptr = reinterpret_cast<decltype(_0.ptr)>(Servo_StyleArcSlice_EmptyPtr());
186   ASSERT_CANARY
187 }
188 
189 template <typename T>
190 inline StyleArcSlice<T>::StyleArcSlice(const StyleArcSlice& aOther) {
191   MOZ_DIAGNOSTIC_ASSERT(aOther._0.ptr);
192   _0.ptr = aOther._0.ptr;
193   _0.ptr->IncrementRef();
194   ASSERT_CANARY
195 }
196 
197 template <typename T>
198 inline StyleArcSlice<T>::StyleArcSlice(
199     const StyleForgottenArcSlicePtr<T>& aPtr) {
200   // See the forget() implementation to see why reinterpret_cast() is ok.
201   _0.ptr = reinterpret_cast<decltype(_0.ptr)>(aPtr._0);
202   ASSERT_CANARY
203 }
204 
205 template <typename T>
206 inline size_t StyleArcSlice<T>::Length() const {
207   ASSERT_CANARY
208   return _0.ptr->data.header.length;
209 }
210 
211 template <typename T>
212 inline bool StyleArcSlice<T>::IsEmpty() const {
213   ASSERT_CANARY
214   return Length() == 0;
215 }
216 
217 template <typename T>
218 inline Span<const T> StyleArcSlice<T>::AsSpan() const {
219   ASSERT_CANARY
220   // Explicitly specify template argument here to avoid instantiating Span<T>
221   // first and then implicitly converting to Span<const T>
222   return Span<const T>{_0.ptr->data.slice, Length()};
223 }
224 
225 template <typename T>
226 inline bool StyleArcSlice<T>::operator==(const StyleArcSlice& aOther) const {
227   ASSERT_CANARY
228   return _0.ptr == aOther._0.ptr || AsSpan() == aOther.AsSpan();
229 }
230 
231 template <typename T>
232 inline bool StyleArcSlice<T>::operator!=(const StyleArcSlice& aOther) const {
233   return !(*this == aOther);
234 }
235 
236 template <typename T>
237 inline void StyleArcSlice<T>::Release() {
238   ASSERT_CANARY
239   if (MOZ_LIKELY(!_0.ptr->DecrementRef())) {
240     return;
241   }
242   for (T& elem : Span(_0.ptr->data.slice, Length())) {
243     elem.~T();
244   }
245   free(_0.ptr);  // Drop the allocation now.
246 }
247 
248 template <typename T>
249 inline StyleArcSlice<T>::~StyleArcSlice() {
250   Release();
251 }
252 
253 template <typename T>
254 inline StyleArcSlice<T>& StyleArcSlice<T>::operator=(StyleArcSlice&& aOther) {
255   ASSERT_CANARY
256   std::swap(_0.ptr, aOther._0.ptr);
257   ASSERT_CANARY
258   return *this;
259 }
260 
261 template <typename T>
262 inline StyleArcSlice<T>& StyleArcSlice<T>::operator=(
263     const StyleArcSlice& aOther) {
264   ASSERT_CANARY
265 
266   if (_0.ptr == aOther._0.ptr) {
267     return *this;
268   }
269 
270   Release();
271 
272   _0.ptr = aOther._0.ptr;
273   _0.ptr->IncrementRef();
274 
275   ASSERT_CANARY
276   return *this;
277 }
278 
279 #undef ASSERT_CANARY
280 
281 template <typename T>
282 inline StyleArc<T>::StyleArc(const StyleArc& aOther) : p(aOther.p) {
283   p->IncrementRef();
284 }
285 
286 template <typename T>
287 inline void StyleArc<T>::Release() {
288   if (MOZ_LIKELY(!p->DecrementRef())) {
289     return;
290   }
291   p->data.~T();
292   free(p);
293 }
294 
295 template <typename T>
296 inline StyleArc<T>& StyleArc<T>::operator=(const StyleArc& aOther) {
297   if (p != aOther.p) {
298     Release();
299     p = aOther.p;
300     p->IncrementRef();
301   }
302   return *this;
303 }
304 
305 template <typename T>
306 inline StyleArc<T>& StyleArc<T>::operator=(StyleArc&& aOther) {
307   std::swap(p, aOther.p);
308   return *this;
309 }
310 
311 template <typename T>
312 inline StyleArc<T>::~StyleArc() {
313   Release();
314 }
315 
316 inline bool StyleAtom::IsStatic() const { return !!(_0 & 1); }
317 
318 inline nsAtom* StyleAtom::AsAtom() const {
319   if (IsStatic()) {
320     auto* atom = reinterpret_cast<const nsStaticAtom*>(
321         reinterpret_cast<const uint8_t*>(&detail::gGkAtoms) + (_0 >> 1));
322     MOZ_ASSERT(atom->IsStatic());
323     return const_cast<nsStaticAtom*>(atom);
324   }
325   return reinterpret_cast<nsAtom*>(_0);
326 }
327 
328 inline void StyleAtom::AddRef() {
329   if (!IsStatic()) {
330     AsAtom()->AddRef();
331   }
332 }
333 
334 inline void StyleAtom::Release() {
335   if (!IsStatic()) {
336     AsAtom()->Release();
337   }
338 }
339 
340 inline StyleAtom::StyleAtom(already_AddRefed<nsAtom> aAtom) {
341   nsAtom* atom = aAtom.take();
342   if (atom->IsStatic()) {
343     ptrdiff_t offset = reinterpret_cast<const uint8_t*>(atom->AsStatic()) -
344                        reinterpret_cast<const uint8_t*>(&detail::gGkAtoms);
345     _0 = (offset << 1) | 1;
346   } else {
347     _0 = reinterpret_cast<uintptr_t>(atom);
348   }
349   MOZ_ASSERT(IsStatic() == atom->IsStatic());
350   MOZ_ASSERT(AsAtom() == atom);
351 }
352 
353 inline StyleAtom::StyleAtom(const StyleAtom& aOther) : _0(aOther._0) {
354   AddRef();
355 }
356 
357 inline StyleAtom& StyleAtom::operator=(const StyleAtom& aOther) {
358   if (MOZ_LIKELY(this != &aOther)) {
359     Release();
360     _0 = aOther._0;
361     AddRef();
362   }
363   return *this;
364 }
365 
366 inline StyleAtom::~StyleAtom() { Release(); }
367 
368 inline nsAtom* StyleCustomIdent::AsAtom() const { return _0.AsAtom(); }
369 
370 inline nsDependentCSubstring StyleOwnedStr::AsString() const {
371   Span<const uint8_t> s = _0.AsSpan();
372   return nsDependentCSubstring(reinterpret_cast<const char*>(s.Elements()),
373                                s.Length());
374 }
375 
376 template <typename T>
377 inline Span<const T> StyleGenericTransform<T>::Operations() const {
378   return _0.AsSpan();
379 }
380 
381 template <typename T>
382 inline bool StyleGenericTransform<T>::IsNone() const {
383   return Operations().IsEmpty();
384 }
385 
386 inline StyleAngle StyleAngle::Zero() { return {0.0f}; }
387 
388 inline float StyleAngle::ToDegrees() const { return _0; }
389 
390 inline double StyleAngle::ToRadians() const {
391   return double(ToDegrees()) * M_PI / 180.0;
392 }
393 
394 inline bool StyleUrlExtraData::IsShared() const { return !!(_0 & 1); }
395 
396 inline StyleUrlExtraData::~StyleUrlExtraData() {
397   if (!IsShared()) {
398     reinterpret_cast<URLExtraData*>(_0)->Release();
399   }
400 }
401 
402 inline const URLExtraData& StyleUrlExtraData::get() const {
403   if (IsShared()) {
404     return *URLExtraData::sShared[_0 >> 1].get();
405   }
406   return *reinterpret_cast<const URLExtraData*>(_0);
407 }
408 
409 inline nsDependentCSubstring StyleCssUrl::SpecifiedSerialization() const {
410   return _0->serialization.AsString();
411 }
412 
413 inline const URLExtraData& StyleCssUrl::ExtraData() const {
414   return _0->extra_data.get();
415 }
416 
417 inline StyleLoadData& StyleCssUrl::LoadData() const {
418   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
419   if (MOZ_LIKELY(_0->load_data.tag == StyleLoadDataSource::Tag::Owned)) {
420     return const_cast<StyleLoadData&>(_0->load_data.owned._0);
421   }
422   return const_cast<StyleLoadData&>(*Servo_LoadData_GetLazy(&_0->load_data));
423 }
424 
425 inline nsIURI* StyleCssUrl::GetURI() const {
426   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
427   auto& loadData = LoadData();
428   if (!(loadData.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_URI)) {
429     loadData.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_URI;
430     nsDependentCSubstring serialization = SpecifiedSerialization();
431     // https://drafts.csswg.org/css-values-4/#url-empty:
432     //
433     //     If the value of the url() is the empty string (like url("") or
434     //     url()), the url must resolve to an invalid resource (similar to what
435     //     the url about:invalid does).
436     //
437     if (!serialization.IsEmpty()) {
438       RefPtr<nsIURI> resolved;
439       NS_NewURI(getter_AddRefs(resolved), serialization, nullptr,
440                 ExtraData().BaseURI());
441       loadData.resolved_uri = resolved.forget().take();
442     }
443   }
444   return loadData.resolved_uri;
445 }
446 
447 inline nsDependentCSubstring StyleComputedUrl::SpecifiedSerialization() const {
448   return _0.SpecifiedSerialization();
449 }
450 inline const URLExtraData& StyleComputedUrl::ExtraData() const {
451   return _0.ExtraData();
452 }
453 inline StyleLoadData& StyleComputedUrl::LoadData() const {
454   return _0.LoadData();
455 }
456 inline StyleCorsMode StyleComputedUrl::CorsMode() const {
457   return _0._0->cors_mode;
458 }
459 inline nsIURI* StyleComputedUrl::GetURI() const { return _0.GetURI(); }
460 
461 inline bool StyleComputedUrl::IsLocalRef() const {
462   return Servo_CssUrl_IsLocalRef(&_0);
463 }
464 
465 inline bool StyleComputedUrl::HasRef() const {
466   if (IsLocalRef()) {
467     return true;
468   }
469   if (nsIURI* uri = GetURI()) {
470     bool hasRef = false;
471     return NS_SUCCEEDED(uri->GetHasRef(&hasRef)) && hasRef;
472   }
473   return false;
474 }
475 
476 inline bool StyleComputedImageUrl::IsImageResolved() const {
477   return bool(LoadData().flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE);
478 }
479 
480 inline imgRequestProxy* StyleComputedImageUrl::GetImage() const {
481   MOZ_ASSERT(IsImageResolved());
482   return LoadData().resolved_image;
483 }
484 
485 template <>
486 inline bool StyleGradient::Repeating() const {
487   if (IsLinear()) {
488     return AsLinear().repeating;
489   }
490   if (IsRadial()) {
491     return AsRadial().repeating;
492   }
493   return AsConic().repeating;
494 }
495 
496 template <>
497 bool StyleGradient::IsOpaque() const;
498 
499 template <typename Integer>
500 inline StyleGenericGridLine<Integer>::StyleGenericGridLine()
501     : ident(do_AddRef(static_cast<nsAtom*>(nsGkAtoms::_empty))),
502       line_num(0),
503       is_span(false) {}
504 
505 template <>
506 inline nsAtom* StyleGridLine::LineName() const {
507   return ident.AsAtom();
508 }
509 
510 template <>
511 inline bool StyleGridLine::IsAuto() const {
512   return LineName()->IsEmpty() && line_num == 0 && !is_span;
513 }
514 
515 using LengthPercentage = StyleLengthPercentage;
516 using LengthPercentageOrAuto = StyleLengthPercentageOrAuto;
517 using NonNegativeLengthPercentage = StyleNonNegativeLengthPercentage;
518 using NonNegativeLengthPercentageOrAuto =
519     StyleNonNegativeLengthPercentageOrAuto;
520 using NonNegativeLengthPercentageOrNormal =
521     StyleNonNegativeLengthPercentageOrNormal;
522 using Length = StyleLength;
523 using LengthOrAuto = StyleLengthOrAuto;
524 using NonNegativeLength = StyleNonNegativeLength;
525 using NonNegativeLengthOrAuto = StyleNonNegativeLengthOrAuto;
526 using BorderRadius = StyleBorderRadius;
527 
528 bool StyleCSSPixelLength::IsZero() const { return _0 == 0.0f; }
529 
530 void StyleCSSPixelLength::ScaleBy(float aScale) { _0 *= aScale; }
531 
532 StyleCSSPixelLength StyleCSSPixelLength::ScaledBy(float aScale) const {
533   return FromPixels(ToCSSPixels() * aScale);
534 }
535 
536 nscoord StyleCSSPixelLength::ToAppUnits() const {
537   // We want to resolve the length part of the calc() expression rounding 0.5
538   // away from zero, instead of the default behavior of
539   // NSToCoordRound{,WithClamp} which do floor(x + 0.5).
540   //
541   // This is what the rust code in the app_units crate does, and not doing this
542   // would regress bug 1323735, for example.
543   //
544   // FIXME(emilio, bug 1528114): Probably we should do something smarter.
545   if (IsZero()) {
546     // Avoid the expensive FP math below.
547     return 0;
548   }
549   float length = _0 * float(mozilla::AppUnitsPerCSSPixel());
550   if (length >= float(nscoord_MAX)) {
551     return nscoord_MAX;
552   }
553   if (length <= float(nscoord_MIN)) {
554     return nscoord_MIN;
555   }
556   return NSToIntRound(length);
557 }
558 
559 bool LengthPercentage::IsLength() const { return Tag() == TAG_LENGTH; }
560 
561 StyleLengthPercentageUnion::StyleLengthPercentageUnion() {
562   length = {TAG_LENGTH, {0.0f}};
563   MOZ_ASSERT(IsLength());
564 }
565 
566 static_assert(sizeof(LengthPercentage) == sizeof(uint64_t), "");
567 
568 Length& LengthPercentage::AsLength() {
569   MOZ_ASSERT(IsLength());
570   return length.length;
571 }
572 
573 const Length& LengthPercentage::AsLength() const {
574   return const_cast<LengthPercentage*>(this)->AsLength();
575 }
576 
577 bool LengthPercentage::IsPercentage() const { return Tag() == TAG_PERCENTAGE; }
578 
579 StylePercentage& LengthPercentage::AsPercentage() {
580   MOZ_ASSERT(IsPercentage());
581   return percentage.percentage;
582 }
583 
584 const StylePercentage& LengthPercentage::AsPercentage() const {
585   return const_cast<LengthPercentage*>(this)->AsPercentage();
586 }
587 
588 bool LengthPercentage::IsCalc() const { return Tag() == TAG_CALC; }
589 
590 StyleCalcLengthPercentage& LengthPercentage::AsCalc() {
591   MOZ_ASSERT(IsCalc());
592   // NOTE: in 32-bits, the pointer is not swapped, and goes along with the tag.
593 #ifdef SERVO_32_BITS
594   return *calc.ptr;
595 #else
596   return *reinterpret_cast<StyleCalcLengthPercentage*>(
597       NativeEndian::swapFromLittleEndian(calc.ptr));
598 #endif
599 }
600 
601 const StyleCalcLengthPercentage& LengthPercentage::AsCalc() const {
602   return const_cast<LengthPercentage*>(this)->AsCalc();
603 }
604 
605 StyleLengthPercentageUnion::StyleLengthPercentageUnion(const Self& aOther) {
606   if (aOther.IsLength()) {
607     length = {TAG_LENGTH, aOther.AsLength()};
608   } else if (aOther.IsPercentage()) {
609     percentage = {TAG_PERCENTAGE, aOther.AsPercentage()};
610   } else {
611     MOZ_ASSERT(aOther.IsCalc());
612     auto* ptr = new StyleCalcLengthPercentage(aOther.AsCalc());
613     // NOTE: in 32-bits, the pointer is not swapped, and goes along with the
614     // tag.
615     calc = {
616 #ifdef SERVO_32_BITS
617         TAG_CALC,
618         ptr,
619 #else
620         NativeEndian::swapToLittleEndian(reinterpret_cast<uintptr_t>(ptr)),
621 #endif
622     };
623   }
624   MOZ_ASSERT(Tag() == aOther.Tag());
625 }
626 
627 StyleLengthPercentageUnion::~StyleLengthPercentageUnion() {
628   if (IsCalc()) {
629     delete &AsCalc();
630   }
631 }
632 
633 LengthPercentage& LengthPercentage::operator=(const LengthPercentage& aOther) {
634   if (this != &aOther) {
635     this->~LengthPercentage();
636     new (this) LengthPercentage(aOther);
637   }
638   return *this;
639 }
640 
641 bool LengthPercentage::operator==(const LengthPercentage& aOther) const {
642   if (Tag() != aOther.Tag()) {
643     return false;
644   }
645   if (IsLength()) {
646     return AsLength() == aOther.AsLength();
647   }
648   if (IsPercentage()) {
649     return AsPercentage() == aOther.AsPercentage();
650   }
651   return AsCalc() == aOther.AsCalc();
652 }
653 
654 bool LengthPercentage::operator!=(const LengthPercentage& aOther) const {
655   return !(*this == aOther);
656 }
657 
658 LengthPercentage LengthPercentage::Zero() { return {}; }
659 
660 LengthPercentage LengthPercentage::FromPixels(CSSCoord aCoord) {
661   LengthPercentage l;
662   MOZ_ASSERT(l.IsLength());
663   l.length.length = {aCoord};
664   return l;
665 }
666 
667 LengthPercentage LengthPercentage::FromAppUnits(nscoord aCoord) {
668   return FromPixels(CSSPixel::FromAppUnits(aCoord));
669 }
670 
671 LengthPercentage LengthPercentage::FromPercentage(float aPercentage) {
672   LengthPercentage l;
673   l.percentage = {TAG_PERCENTAGE, {aPercentage}};
674   return l;
675 }
676 
677 bool LengthPercentage::HasPercent() const { return IsPercentage() || IsCalc(); }
678 
679 bool LengthPercentage::ConvertsToLength() const { return IsLength(); }
680 
681 nscoord LengthPercentage::ToLength() const {
682   MOZ_ASSERT(ConvertsToLength());
683   return AsLength().ToAppUnits();
684 }
685 
686 CSSCoord LengthPercentage::ToLengthInCSSPixels() const {
687   MOZ_ASSERT(ConvertsToLength());
688   return AsLength().ToCSSPixels();
689 }
690 
691 bool LengthPercentage::ConvertsToPercentage() const { return IsPercentage(); }
692 
693 float LengthPercentage::ToPercentage() const {
694   MOZ_ASSERT(ConvertsToPercentage());
695   return AsPercentage()._0;
696 }
697 
698 bool LengthPercentage::HasLengthAndPercentage() const {
699   if (!IsCalc()) {
700     return false;
701   }
702   MOZ_ASSERT(!ConvertsToLength() && !ConvertsToPercentage(),
703              "Should've been simplified earlier");
704   return true;
705 }
706 
707 bool LengthPercentage::IsDefinitelyZero() const {
708   if (IsLength()) {
709     return AsLength().IsZero();
710   }
711   if (IsPercentage()) {
712     return AsPercentage()._0 == 0.0f;
713   }
714   // calc() should've been simplified to a percentage.
715   return false;
716 }
717 
718 template <>
719 CSSCoord StyleCalcNode::ResolveToCSSPixels(CSSCoord aPercentageBasis) const;
720 
721 template <>
722 void StyleCalcNode::ScaleLengthsBy(float);
723 
724 CSSCoord LengthPercentage::ResolveToCSSPixels(CSSCoord aPercentageBasis) const {
725   if (IsLength()) {
726     return AsLength().ToCSSPixels();
727   }
728   if (IsPercentage()) {
729     return AsPercentage()._0 * aPercentageBasis;
730   }
731   return AsCalc().node.ResolveToCSSPixels(aPercentageBasis);
732 }
733 
734 template <typename T>
735 CSSCoord LengthPercentage::ResolveToCSSPixelsWith(T aPercentageGetter) const {
736   static_assert(std::is_same<decltype(aPercentageGetter()), CSSCoord>::value,
737                 "Should return CSS pixels");
738   if (ConvertsToLength()) {
739     return ToLengthInCSSPixels();
740   }
741   return ResolveToCSSPixels(aPercentageGetter());
742 }
743 
744 template <typename T, typename U>
745 nscoord LengthPercentage::Resolve(T aPercentageGetter, U aRounder) const {
746   static_assert(std::is_same<decltype(aPercentageGetter()), nscoord>::value,
747                 "Should return app units");
748   static_assert(std::is_same<decltype(aRounder(1.0f)), nscoord>::value,
749                 "Should return app units");
750   if (ConvertsToLength()) {
751     return ToLength();
752   }
753   if (IsPercentage() && AsPercentage()._0 == 0.0f) {
754     return 0.0f;
755   }
756   nscoord basis = aPercentageGetter();
757   if (IsPercentage()) {
758     return aRounder(basis * AsPercentage()._0);
759   }
760   return AsCalc().node.Resolve(basis, aRounder);
761 }
762 
763 nscoord LengthPercentage::Resolve(nscoord aPercentageBasis) const {
764   return Resolve([=] { return aPercentageBasis; }, NSToCoordFloorClamped);
765 }
766 
767 template <typename T>
768 nscoord LengthPercentage::Resolve(T aPercentageGetter) const {
769   return Resolve(aPercentageGetter, NSToCoordFloorClamped);
770 }
771 
772 template <typename T>
773 nscoord LengthPercentage::Resolve(nscoord aPercentageBasis,
774                                   T aPercentageRounder) const {
775   return Resolve([aPercentageBasis] { return aPercentageBasis; },
776                  aPercentageRounder);
777 }
778 
779 void LengthPercentage::ScaleLengthsBy(float aScale) {
780   if (IsLength()) {
781     AsLength().ScaleBy(aScale);
782   }
783   if (IsCalc()) {
784     AsCalc().node.ScaleLengthsBy(aScale);
785   }
786 }
787 
788 #define IMPL_LENGTHPERCENTAGE_FORWARDS(ty_)                                 \
789   template <>                                                               \
790   inline bool ty_::HasPercent() const {                                     \
791     return IsLengthPercentage() && AsLengthPercentage().HasPercent();       \
792   }                                                                         \
793   template <>                                                               \
794   inline bool ty_::ConvertsToLength() const {                               \
795     return IsLengthPercentage() && AsLengthPercentage().ConvertsToLength(); \
796   }                                                                         \
797   template <>                                                               \
798   inline bool ty_::HasLengthAndPercentage() const {                         \
799     return IsLengthPercentage() &&                                          \
800            AsLengthPercentage().HasLengthAndPercentage();                   \
801   }                                                                         \
802   template <>                                                               \
803   inline nscoord ty_::ToLength() const {                                    \
804     MOZ_ASSERT(ConvertsToLength());                                         \
805     return AsLengthPercentage().ToLength();                                 \
806   }                                                                         \
807   template <>                                                               \
808   inline bool ty_::ConvertsToPercentage() const {                           \
809     return IsLengthPercentage() &&                                          \
810            AsLengthPercentage().ConvertsToPercentage();                     \
811   }                                                                         \
812   template <>                                                               \
813   inline float ty_::ToPercentage() const {                                  \
814     MOZ_ASSERT(ConvertsToPercentage());                                     \
815     return AsLengthPercentage().ToPercentage();                             \
816   }
817 
818 IMPL_LENGTHPERCENTAGE_FORWARDS(LengthPercentageOrAuto)
819 IMPL_LENGTHPERCENTAGE_FORWARDS(StyleSize)
820 IMPL_LENGTHPERCENTAGE_FORWARDS(StyleMaxSize)
821 
822 template <>
823 inline bool LengthOrAuto::IsLength() const {
824   return IsLengthPercentage();
825 }
826 
827 template <>
828 inline const Length& LengthOrAuto::AsLength() const {
829   return AsLengthPercentage();
830 }
831 
832 template <>
833 inline nscoord LengthOrAuto::ToLength() const {
834   return AsLength().ToAppUnits();
835 }
836 
837 template <>
838 inline bool StyleFlexBasis::IsAuto() const {
839   return IsSize() && AsSize().IsAuto();
840 }
841 
842 template <>
843 inline bool StyleSize::BehavesLikeInitialValueOnBlockAxis() const {
844   return IsAuto() || !IsLengthPercentage();
845 }
846 
847 template <>
848 inline bool StyleMaxSize::BehavesLikeInitialValueOnBlockAxis() const {
849   return IsNone() || !IsLengthPercentage();
850 }
851 
852 template <>
853 inline bool StyleBackgroundSize::IsInitialValue() const {
854   return IsExplicitSize() && explicit_size.width.IsAuto() &&
855          explicit_size.height.IsAuto();
856 }
857 
858 template <typename T>
859 const T& StyleRect<T>::Get(mozilla::Side aSide) const {
860   static_assert(sizeof(StyleRect<T>) == sizeof(T) * 4, "");
861   static_assert(alignof(StyleRect<T>) == alignof(T), "");
862   return reinterpret_cast<const T*>(this)[aSide];
863 }
864 
865 template <typename T>
866 T& StyleRect<T>::Get(mozilla::Side aSide) {
867   return const_cast<T&>(static_cast<const StyleRect&>(*this).Get(aSide));
868 }
869 
870 template <typename T>
871 template <typename Predicate>
872 bool StyleRect<T>::All(Predicate aPredicate) const {
873   return aPredicate(_0) && aPredicate(_1) && aPredicate(_2) && aPredicate(_3);
874 }
875 
876 template <typename T>
877 template <typename Predicate>
878 bool StyleRect<T>::Any(Predicate aPredicate) const {
879   return aPredicate(_0) || aPredicate(_1) || aPredicate(_2) || aPredicate(_3);
880 }
881 
882 template <>
883 inline const LengthPercentage& BorderRadius::Get(HalfCorner aCorner) const {
884   static_assert(sizeof(BorderRadius) == sizeof(LengthPercentage) * 8, "");
885   static_assert(alignof(BorderRadius) == alignof(LengthPercentage), "");
886   auto* self = reinterpret_cast<const LengthPercentage*>(this);
887   return self[aCorner];
888 }
889 
890 template <>
891 inline bool StyleTrackBreadth::HasPercent() const {
892   return IsBreadth() && AsBreadth().HasPercent();
893 }
894 
895 // Implemented in nsStyleStructs.cpp
896 template <>
897 bool StyleTransform::HasPercent() const;
898 
899 template <>
900 inline bool StyleTransformOrigin::HasPercent() const {
901   // NOTE(emilio): `depth` is just a `<length>` so doesn't have a percentage at
902   // all.
903   return horizontal.HasPercent() || vertical.HasPercent();
904 }
905 
906 template <>
907 inline Maybe<size_t> StyleGridTemplateComponent::RepeatAutoIndex() const {
908   if (!IsTrackList()) {
909     return Nothing();
910   }
911   auto& list = *AsTrackList();
912   return list.auto_repeat_index < list.values.Length()
913              ? Some(list.auto_repeat_index)
914              : Nothing();
915 }
916 
917 template <>
918 inline bool StyleGridTemplateComponent::HasRepeatAuto() const {
919   return RepeatAutoIndex().isSome();
920 }
921 
922 template <>
923 inline Span<const StyleGenericTrackListValue<LengthPercentage, StyleInteger>>
924 StyleGridTemplateComponent::TrackListValues() const {
925   if (IsTrackList()) {
926     return AsTrackList()->values.AsSpan();
927   }
928   return {};
929 }
930 
931 template <>
932 inline const StyleGenericTrackRepeat<LengthPercentage, StyleInteger>*
933 StyleGridTemplateComponent::GetRepeatAutoValue() const {
934   auto index = RepeatAutoIndex();
935   if (!index) {
936     return nullptr;
937   }
938   return &TrackListValues()[*index].AsTrackRepeat();
939 }
940 
941 constexpr const auto kPaintOrderShift = StylePAINT_ORDER_SHIFT;
942 constexpr const auto kPaintOrderMask = StylePAINT_ORDER_MASK;
943 
944 template <>
945 inline nsRect StyleGenericClipRect<LengthOrAuto>::ToLayoutRect(
946     nscoord aAutoSize) const {
947   nscoord x = left.IsLength() ? left.ToLength() : 0;
948   nscoord y = top.IsLength() ? top.ToLength() : 0;
949   nscoord width = right.IsLength() ? right.ToLength() - x : aAutoSize;
950   nscoord height = bottom.IsLength() ? bottom.ToLength() - y : aAutoSize;
951   return nsRect(x, y, width, height);
952 }
953 
954 using RestyleHint = StyleRestyleHint;
955 
956 inline RestyleHint RestyleHint::RestyleSubtree() {
957   return RESTYLE_SELF | RESTYLE_DESCENDANTS;
958 }
959 
960 inline RestyleHint RestyleHint::RecascadeSubtree() {
961   return RECASCADE_SELF | RECASCADE_DESCENDANTS;
962 }
963 
964 inline RestyleHint RestyleHint::ForAnimations() {
965   return RESTYLE_CSS_TRANSITIONS | RESTYLE_CSS_ANIMATIONS | RESTYLE_SMIL;
966 }
967 
968 inline bool RestyleHint::DefinitelyRecascadesAllSubtree() const {
969   if (!(*this & (RECASCADE_DESCENDANTS | RESTYLE_DESCENDANTS))) {
970     return false;
971   }
972   return bool(*this & (RESTYLE_SELF | RECASCADE_SELF));
973 }
974 
975 template <>
976 ImageResolution StyleImage::GetResolution() const;
977 
978 template <>
979 inline const StyleImage& StyleImage::FinalImage() const {
980   if (!IsImageSet()) {
981     return *this;
982   }
983   auto& set = AsImageSet();
984   auto& selectedItem = set->items.AsSpan()[set->selected_index];
985   return selectedItem.image.FinalImage();
986 }
987 
988 template <>
989 Maybe<CSSIntSize> StyleImage::GetIntrinsicSize() const;
990 
991 template <>
992 inline bool StyleImage::IsImageRequestType() const {
993   auto& finalImage = FinalImage();
994   return finalImage.IsUrl() || finalImage.IsRect();
995 }
996 
997 template <>
998 inline const StyleComputedImageUrl* StyleImage::GetImageRequestURLValue()
999     const {
1000   auto& finalImage = FinalImage();
1001   if (finalImage.IsUrl()) {
1002     return &finalImage.AsUrl();
1003   }
1004   if (finalImage.IsRect()) {
1005     return &finalImage.AsRect()->url;
1006   }
1007   return nullptr;
1008 }
1009 
1010 template <>
1011 inline imgRequestProxy* StyleImage::GetImageRequest() const {
1012   auto* url = GetImageRequestURLValue();
1013   return url ? url->GetImage() : nullptr;
1014 }
1015 
1016 template <>
1017 inline bool StyleImage::IsResolved() const {
1018   auto* url = GetImageRequestURLValue();
1019   return !url || url->IsImageResolved();
1020 }
1021 
1022 template <>
1023 bool StyleImage::IsOpaque() const;
1024 template <>
1025 bool StyleImage::IsSizeAvailable() const;
1026 template <>
1027 bool StyleImage::IsComplete() const;
1028 template <>
1029 Maybe<StyleImage::ActualCropRect> StyleImage::ComputeActualCropRect() const;
1030 template <>
1031 void StyleImage::ResolveImage(dom::Document&, const StyleImage*);
1032 
1033 template <>
1034 inline AspectRatio StyleRatio<StyleNonNegativeNumber>::ToLayoutRatio(
1035     UseBoxSizing aUseBoxSizing) const {
1036   // 0/1, 1/0, and 0/0 are all degenerate ratios (which behave as auto), and we
1037   // always return 0.0f.
1038   // https://drafts.csswg.org/css-values-4/#degenerate-ratio
1039   return AspectRatio::FromSize(_0, _1, aUseBoxSizing);
1040 }
1041 
1042 template <>
1043 inline AspectRatio StyleAspectRatio::ToLayoutRatio() const {
1044   return HasRatio() ? ratio.AsRatio().ToLayoutRatio(auto_ ? UseBoxSizing::No
1045                                                           : UseBoxSizing::Yes)
1046                     : AspectRatio();
1047 }
1048 
1049 inline bool StyleFontFamilyList::ContainsFallback() const {
1050   if (fallback == StyleGenericFontFamily::None) {
1051     return false;
1052   }
1053   for (const auto& family : list.AsSpan()) {
1054     if (family.IsGeneric() && family.AsGeneric() == fallback) {
1055       return true;
1056     }
1057   }
1058   return false;
1059 }
1060 
1061 }  // namespace mozilla
1062 
1063 #endif
1064