1use crate::parser::{Parse, ParserContext};
11use crate::selector_map::PrecomputedHashMap;
12use crate::str::HTML_SPACE_CHARACTERS;
13use crate::values::computed::LengthPercentage as ComputedLengthPercentage;
14use crate::values::computed::{Context, Percentage, ToComputedValue};
15use crate::values::generics::length::GenericAnchorSizeFunction;
16use crate::values::generics::position::Position as GenericPosition;
17use crate::values::generics::position::PositionComponent as GenericPositionComponent;
18use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
19use crate::values::generics::position::ZIndex as GenericZIndex;
20use crate::values::generics::position::{AspectRatio as GenericAspectRatio, GenericAnchorSide};
21use crate::values::generics::position::{GenericAnchorFunction, GenericInset};
22use crate::values::specified;
23use crate::values::specified::{AllowQuirks, Integer, LengthPercentage, NonNegativeNumber};
24use crate::values::DashedIdent;
25use crate::{Atom, Zero};
26use cssparser::Parser;
27use selectors::parser::SelectorParseErrorKind;
28use servo_arc::Arc;
29use smallvec::{smallvec, SmallVec};
30use std::collections::hash_map::Entry;
31use std::fmt::{self, Write};
32use style_traits::arc_slice::ArcSlice;
33use style_traits::values::specified::AllowedNumericType;
34use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
35
36pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
38
39pub type PositionOrAuto = GenericPositionOrAuto<Position>;
41
42pub type HorizontalPosition = PositionComponent<HorizontalPositionKeyword>;
44
45pub type VerticalPosition = PositionComponent<VerticalPositionKeyword>;
47
48#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
50pub enum PositionComponent<S> {
51 Center,
53 Length(LengthPercentage),
55 Side(S, Option<LengthPercentage>),
57}
58
59#[derive(
61 Clone,
62 Copy,
63 Debug,
64 Eq,
65 Hash,
66 MallocSizeOf,
67 Parse,
68 PartialEq,
69 SpecifiedValueInfo,
70 ToComputedValue,
71 ToCss,
72 ToResolvedValue,
73 ToShmem,
74)]
75#[allow(missing_docs)]
76#[repr(u8)]
77pub enum HorizontalPositionKeyword {
78 Left,
79 Right,
80}
81
82#[derive(
84 Clone,
85 Copy,
86 Debug,
87 Eq,
88 Hash,
89 MallocSizeOf,
90 Parse,
91 PartialEq,
92 SpecifiedValueInfo,
93 ToComputedValue,
94 ToCss,
95 ToResolvedValue,
96 ToShmem,
97)]
98#[allow(missing_docs)]
99#[repr(u8)]
100pub enum VerticalPositionKeyword {
101 Top,
102 Bottom,
103}
104
105impl Parse for Position {
106 fn parse<'i, 't>(
107 context: &ParserContext,
108 input: &mut Parser<'i, 't>,
109 ) -> Result<Self, ParseError<'i>> {
110 let position = Self::parse_three_value_quirky(context, input, AllowQuirks::No)?;
111 if position.is_three_value_syntax() {
112 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
113 }
114 Ok(position)
115 }
116}
117
118impl Position {
119 pub fn parse_three_value_quirky<'i, 't>(
121 context: &ParserContext,
122 input: &mut Parser<'i, 't>,
123 allow_quirks: AllowQuirks,
124 ) -> Result<Self, ParseError<'i>> {
125 match input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) {
126 Ok(x_pos @ PositionComponent::Center) => {
127 if let Ok(y_pos) =
128 input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))
129 {
130 return Ok(Self::new(x_pos, y_pos));
131 }
132 let x_pos = input
133 .try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))
134 .unwrap_or(x_pos);
135 let y_pos = PositionComponent::Center;
136 return Ok(Self::new(x_pos, y_pos));
137 },
138 Ok(PositionComponent::Side(x_keyword, lp)) => {
139 if input
140 .try_parse(|i| i.expect_ident_matching("center"))
141 .is_ok()
142 {
143 let x_pos = PositionComponent::Side(x_keyword, lp);
144 let y_pos = PositionComponent::Center;
145 return Ok(Self::new(x_pos, y_pos));
146 }
147 if let Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) {
148 let y_lp = input
149 .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
150 .ok();
151 let x_pos = PositionComponent::Side(x_keyword, lp);
152 let y_pos = PositionComponent::Side(y_keyword, y_lp);
153 return Ok(Self::new(x_pos, y_pos));
154 }
155 let x_pos = PositionComponent::Side(x_keyword, None);
156 let y_pos = lp.map_or(PositionComponent::Center, PositionComponent::Length);
157 return Ok(Self::new(x_pos, y_pos));
158 },
159 Ok(x_pos @ PositionComponent::Length(_)) => {
160 if let Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) {
161 let y_pos = PositionComponent::Side(y_keyword, None);
162 return Ok(Self::new(x_pos, y_pos));
163 }
164 if let Ok(y_lp) =
165 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
166 {
167 let y_pos = PositionComponent::Length(y_lp);
168 return Ok(Self::new(x_pos, y_pos));
169 }
170 let y_pos = PositionComponent::Center;
171 let _ = input.try_parse(|i| i.expect_ident_matching("center"));
172 return Ok(Self::new(x_pos, y_pos));
173 },
174 Err(_) => {},
175 }
176 let y_keyword = VerticalPositionKeyword::parse(input)?;
177 let lp_and_x_pos: Result<_, ParseError> = input.try_parse(|i| {
178 let y_lp = i
179 .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
180 .ok();
181 if let Ok(x_keyword) = i.try_parse(HorizontalPositionKeyword::parse) {
182 let x_lp = i
183 .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
184 .ok();
185 let x_pos = PositionComponent::Side(x_keyword, x_lp);
186 return Ok((y_lp, x_pos));
187 };
188 i.expect_ident_matching("center")?;
189 let x_pos = PositionComponent::Center;
190 Ok((y_lp, x_pos))
191 });
192 if let Ok((y_lp, x_pos)) = lp_and_x_pos {
193 let y_pos = PositionComponent::Side(y_keyword, y_lp);
194 return Ok(Self::new(x_pos, y_pos));
195 }
196 let x_pos = PositionComponent::Center;
197 let y_pos = PositionComponent::Side(y_keyword, None);
198 Ok(Self::new(x_pos, y_pos))
199 }
200
201 #[inline]
203 pub fn center() -> Self {
204 Self::new(PositionComponent::Center, PositionComponent::Center)
205 }
206
207 #[inline]
209 fn is_three_value_syntax(&self) -> bool {
210 self.horizontal.component_count() != self.vertical.component_count()
211 }
212}
213
214impl ToCss for Position {
215 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
216 where
217 W: Write,
218 {
219 match (&self.horizontal, &self.vertical) {
220 (
221 x_pos @ &PositionComponent::Side(_, Some(_)),
222 &PositionComponent::Length(ref y_lp),
223 ) => {
224 x_pos.to_css(dest)?;
225 dest.write_str(" top ")?;
226 y_lp.to_css(dest)
227 },
228 (
229 &PositionComponent::Length(ref x_lp),
230 y_pos @ &PositionComponent::Side(_, Some(_)),
231 ) => {
232 dest.write_str("left ")?;
233 x_lp.to_css(dest)?;
234 dest.write_char(' ')?;
235 y_pos.to_css(dest)
236 },
237 (x_pos, y_pos) => {
238 x_pos.to_css(dest)?;
239 dest.write_char(' ')?;
240 y_pos.to_css(dest)
241 },
242 }
243 }
244}
245
246impl<S: Parse> Parse for PositionComponent<S> {
247 fn parse<'i, 't>(
248 context: &ParserContext,
249 input: &mut Parser<'i, 't>,
250 ) -> Result<Self, ParseError<'i>> {
251 Self::parse_quirky(context, input, AllowQuirks::No)
252 }
253}
254
255impl<S: Parse> PositionComponent<S> {
256 pub fn parse_quirky<'i, 't>(
258 context: &ParserContext,
259 input: &mut Parser<'i, 't>,
260 allow_quirks: AllowQuirks,
261 ) -> Result<Self, ParseError<'i>> {
262 if input
263 .try_parse(|i| i.expect_ident_matching("center"))
264 .is_ok()
265 {
266 return Ok(PositionComponent::Center);
267 }
268 if let Ok(lp) =
269 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
270 {
271 return Ok(PositionComponent::Length(lp));
272 }
273 let keyword = S::parse(context, input)?;
274 let lp = input
275 .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
276 .ok();
277 Ok(PositionComponent::Side(keyword, lp))
278 }
279}
280
281impl<S> GenericPositionComponent for PositionComponent<S> {
282 fn is_center(&self) -> bool {
283 match *self {
284 PositionComponent::Center => true,
285 PositionComponent::Length(LengthPercentage::Percentage(ref per)) => per.0 == 0.5,
286 PositionComponent::Side(_, Some(LengthPercentage::Percentage(ref per))) => per.0 == 0.5,
288 _ => false,
289 }
290 }
291}
292
293impl<S> PositionComponent<S> {
294 pub fn zero() -> Self {
296 PositionComponent::Length(LengthPercentage::Percentage(Percentage::zero()))
297 }
298
299 fn component_count(&self) -> usize {
301 match *self {
302 PositionComponent::Length(..) | PositionComponent::Center => 1,
303 PositionComponent::Side(_, ref lp) => {
304 if lp.is_some() {
305 2
306 } else {
307 1
308 }
309 },
310 }
311 }
312}
313
314impl<S: Side> ToComputedValue for PositionComponent<S> {
315 type ComputedValue = ComputedLengthPercentage;
316
317 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
318 match *self {
319 PositionComponent::Center => ComputedLengthPercentage::new_percent(Percentage(0.5)),
320 PositionComponent::Side(ref keyword, None) => {
321 let p = Percentage(if keyword.is_start() { 0. } else { 1. });
322 ComputedLengthPercentage::new_percent(p)
323 },
324 PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
325 let length = length.to_computed_value(context);
326 ComputedLengthPercentage::hundred_percent_minus(length, AllowedNumericType::All)
328 },
329 PositionComponent::Side(_, Some(ref length))
330 | PositionComponent::Length(ref length) => length.to_computed_value(context),
331 }
332 }
333
334 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
335 PositionComponent::Length(ToComputedValue::from_computed_value(computed))
336 }
337}
338
339impl<S: Side> PositionComponent<S> {
340 pub fn initial_specified_value() -> Self {
342 PositionComponent::Side(S::start(), None)
343 }
344}
345
346#[repr(transparent)]
348#[derive(
349 Clone,
350 Debug,
351 MallocSizeOf,
352 PartialEq,
353 SpecifiedValueInfo,
354 ToComputedValue,
355 ToCss,
356 ToResolvedValue,
357 ToShmem,
358)]
359#[css(comma)]
360pub struct AnchorName(
361 #[css(iterable, if_empty = "none")]
362 #[ignore_malloc_size_of = "Arc"]
363 pub crate::ArcSlice<DashedIdent>,
364);
365
366impl AnchorName {
367 pub fn none() -> Self {
369 Self(Default::default())
370 }
371
372 pub fn is_none(&self) -> bool {
374 self.0.is_empty()
375 }
376}
377
378impl Parse for AnchorName {
379 fn parse<'i, 't>(
380 context: &ParserContext,
381 input: &mut Parser<'i, 't>,
382 ) -> Result<Self, ParseError<'i>> {
383 let location = input.current_source_location();
384 let first = input.expect_ident()?;
385 if first.eq_ignore_ascii_case("none") {
386 return Ok(Self::none());
387 }
388 let mut idents: SmallVec<[DashedIdent; 4]> =
391 smallvec![DashedIdent::from_ident(location, first,)?];
392 while input.try_parse(|input| input.expect_comma()).is_ok() {
393 idents.push(DashedIdent::parse(context, input)?);
394 }
395 Ok(AnchorName(ArcSlice::from_iter(idents.drain(..))))
396 }
397}
398
399#[derive(
401 Clone,
402 Debug,
403 MallocSizeOf,
404 PartialEq,
405 SpecifiedValueInfo,
406 ToComputedValue,
407 ToCss,
408 ToResolvedValue,
409 ToShmem,
410)]
411#[repr(u8)]
412pub enum AnchorScope {
413 None,
415 All,
417 #[css(comma)]
419 Idents(
420 #[css(iterable)]
421 #[ignore_malloc_size_of = "Arc"]
422 crate::ArcSlice<DashedIdent>,
423 ),
424}
425
426impl AnchorScope {
427 pub fn none() -> Self {
429 Self::None
430 }
431
432 pub fn is_none(&self) -> bool {
434 *self == Self::None
435 }
436}
437
438impl Parse for AnchorScope {
439 fn parse<'i, 't>(
440 context: &ParserContext,
441 input: &mut Parser<'i, 't>,
442 ) -> Result<Self, ParseError<'i>> {
443 let location = input.current_source_location();
444 let first = input.expect_ident()?;
445 if first.eq_ignore_ascii_case("none") {
446 return Ok(Self::None);
447 }
448 if first.eq_ignore_ascii_case("all") {
449 return Ok(Self::All);
450 }
451 let mut idents: SmallVec<[DashedIdent; 8]> =
454 smallvec![DashedIdent::from_ident(location, first,)?];
455 while input.try_parse(|input| input.expect_comma()).is_ok() {
456 idents.push(DashedIdent::parse(context, input)?);
457 }
458 Ok(AnchorScope::Idents(ArcSlice::from_iter(idents.drain(..))))
459 }
460}
461
462#[derive(
464 Clone,
465 Debug,
466 MallocSizeOf,
467 Parse,
468 PartialEq,
469 SpecifiedValueInfo,
470 ToComputedValue,
471 ToCss,
472 ToResolvedValue,
473 ToShmem,
474)]
475#[repr(u8)]
476pub enum PositionAnchor {
477 Auto,
479 Ident(DashedIdent),
481}
482
483impl PositionAnchor {
484 pub fn auto() -> Self {
486 Self::Auto
487 }
488
489 pub fn is_auto(&self) -> bool {
491 *self == Self::Auto
492 }
493}
494
495#[derive(
496 Clone,
497 Copy,
498 Debug,
499 Default,
500 Eq,
501 MallocSizeOf,
502 Parse,
503 PartialEq,
504 Serialize,
505 SpecifiedValueInfo,
506 ToComputedValue,
507 ToCss,
508 ToResolvedValue,
509 ToShmem,
510)]
511#[repr(u8)]
512pub enum PositionTryFallbacksTryTacticKeyword {
514 #[css(skip)]
516 #[default]
517 None,
518 FlipBlock,
520 FlipInline,
522 FlipStart,
524}
525
526impl PositionTryFallbacksTryTacticKeyword {
527 fn is_none(&self) -> bool {
528 *self == Self::None
529 }
530}
531
532#[derive(
533 Clone,
534 Copy,
535 Debug,
536 Default,
537 Eq,
538 MallocSizeOf,
539 PartialEq,
540 Serialize,
541 SpecifiedValueInfo,
542 ToComputedValue,
543 ToCss,
544 ToResolvedValue,
545 ToShmem,
546)]
547#[repr(C)]
548pub struct PositionTryFallbacksTryTactic(
553 pub PositionTryFallbacksTryTacticKeyword,
554 pub PositionTryFallbacksTryTacticKeyword,
555 pub PositionTryFallbacksTryTacticKeyword,
556);
557
558impl Parse for PositionTryFallbacksTryTactic {
559 fn parse<'i, 't>(
560 _context: &ParserContext,
561 input: &mut Parser<'i, 't>,
562 ) -> Result<Self, ParseError<'i>> {
563 let first = input.try_parse(PositionTryFallbacksTryTacticKeyword::parse)?;
564 let second = input
565 .try_parse(PositionTryFallbacksTryTacticKeyword::parse)
566 .unwrap_or_default();
567 let third = input
568 .try_parse(PositionTryFallbacksTryTacticKeyword::parse)
569 .unwrap_or_default();
570 if first == second || first == third || (!second.is_none() && second == third) {
571 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
572 }
573 Ok(Self(first, second, third))
574 }
575}
576
577impl PositionTryFallbacksTryTactic {
578 fn is_empty(&self) -> bool {
579 self.0.is_none()
580 }
581}
582
583#[derive(
584 Clone,
585 Debug,
586 MallocSizeOf,
587 PartialEq,
588 SpecifiedValueInfo,
589 ToComputedValue,
590 ToCss,
591 ToResolvedValue,
592 ToShmem,
593)]
594#[repr(C)]
595pub struct DashedIdentAndOrTryTactic {
598 pub ident: DashedIdent,
600 pub try_tactic: PositionTryFallbacksTryTactic,
602}
603
604impl Parse for DashedIdentAndOrTryTactic {
605 fn parse<'i, 't>(
606 context: &ParserContext,
607 input: &mut Parser<'i, 't>,
608 ) -> Result<Self, ParseError<'i>> {
609 let mut result = Self {
610 ident: DashedIdent::empty(),
611 try_tactic: PositionTryFallbacksTryTactic::default(),
612 };
613
614 loop {
615 if result.ident.is_empty() {
616 if let Ok(ident) = input.try_parse(|i| DashedIdent::parse(context, i)) {
617 result.ident = ident;
618 continue;
619 }
620 }
621 if result.try_tactic.is_empty() {
622 if let Ok(try_tactic) =
623 input.try_parse(|i| PositionTryFallbacksTryTactic::parse(context, i))
624 {
625 result.try_tactic = try_tactic;
626 continue;
627 }
628 }
629 break;
630 }
631
632 if result.ident.is_empty() && result.try_tactic.is_empty() {
633 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
634 }
635 return Ok(result);
636 }
637}
638
639#[derive(
640 Clone,
641 Debug,
642 MallocSizeOf,
643 Parse,
644 PartialEq,
645 SpecifiedValueInfo,
646 ToComputedValue,
647 ToCss,
648 ToResolvedValue,
649 ToShmem,
650)]
651#[repr(u8)]
652pub enum PositionTryFallbacksItem {
655 IdentAndOrTactic(DashedIdentAndOrTryTactic),
657 #[parse(parse_fn = "PositionArea::parse_except_none")]
658 PositionArea(PositionArea),
660}
661
662#[derive(
663 Clone,
664 Debug,
665 Default,
666 MallocSizeOf,
667 PartialEq,
668 SpecifiedValueInfo,
669 ToComputedValue,
670 ToCss,
671 ToResolvedValue,
672 ToShmem,
673)]
674#[css(comma)]
675#[repr(C)]
676pub struct PositionTryFallbacks(
678 #[css(iterable, if_empty = "none")]
679 #[ignore_malloc_size_of = "Arc"]
680 pub crate::ArcSlice<PositionTryFallbacksItem>,
681);
682
683impl PositionTryFallbacks {
684 #[inline]
685 pub fn none() -> Self {
687 Self(Default::default())
688 }
689
690 pub fn is_none(&self) -> bool {
692 self.0.is_empty()
693 }
694}
695
696impl Parse for PositionTryFallbacks {
697 fn parse<'i, 't>(
698 context: &ParserContext,
699 input: &mut Parser<'i, 't>,
700 ) -> Result<Self, ParseError<'i>> {
701 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
702 return Ok(Self::none());
703 }
704 let mut items: SmallVec<[PositionTryFallbacksItem; 4]> =
707 smallvec![PositionTryFallbacksItem::parse(context, input)?];
708 while input.try_parse(|input| input.expect_comma()).is_ok() {
709 items.push(PositionTryFallbacksItem::parse(context, input)?);
710 }
711 Ok(Self(ArcSlice::from_iter(items.drain(..))))
712 }
713}
714
715#[derive(
717 Clone,
718 Copy,
719 Debug,
720 Default,
721 Eq,
722 MallocSizeOf,
723 Parse,
724 PartialEq,
725 SpecifiedValueInfo,
726 ToComputedValue,
727 ToCss,
728 ToResolvedValue,
729 ToShmem,
730)]
731#[repr(u8)]
732pub enum PositionTryOrder {
733 #[default]
734 Normal,
736 MostWidth,
738 MostHeight,
740 MostBlockSize,
742 MostInlineSize,
744}
745
746impl PositionTryOrder {
747 #[inline]
748 pub fn normal() -> Self {
750 Self::Normal
751 }
752
753 pub fn is_normal(&self) -> bool {
755 *self == Self::Normal
756 }
757}
758
759#[derive(
760 Clone,
761 Copy,
762 Debug,
763 Eq,
764 MallocSizeOf,
765 Parse,
766 PartialEq,
767 Serialize,
768 SpecifiedValueInfo,
769 ToComputedValue,
770 ToCss,
771 ToResolvedValue,
772 ToShmem,
773)]
774#[css(bitflags(single = "always", mixed = "anchors-valid,anchors-visible,no-overflow"))]
775#[repr(C)]
776pub struct PositionVisibility(u8);
778bitflags! {
779 impl PositionVisibility: u8 {
780 const ALWAYS = 0;
782 const ANCHORS_VALID = 1 << 0;
784 const ANCHORS_VISIBLE = 1 << 1;
786 const NO_OVERFLOW = 1 << 2;
788 }
789}
790
791impl Default for PositionVisibility {
792 fn default() -> Self {
793 Self::ALWAYS
794 }
795}
796
797impl PositionVisibility {
798 #[inline]
799 pub fn always() -> Self {
801 Self::ALWAYS
802 }
803}
804
805#[derive(
806 Clone,
807 Copy,
808 Debug,
809 Default,
810 Eq,
811 MallocSizeOf,
812 Parse,
813 PartialEq,
814 SpecifiedValueInfo,
815 ToComputedValue,
816 ToCss,
817 ToResolvedValue,
818 ToShmem,
819)]
820#[allow(missing_docs)]
821#[repr(u8)]
822pub enum PositionAreaKeyword {
825 #[default]
826 None,
827
828 Center,
830 SpanAll,
831
832 Left,
834 Right,
835 SpanLeft,
836 SpanRight,
837 XStart,
838 XEnd,
839 SpanXStart,
840 SpanXEnd,
841 XSelfStart,
842 XSelfEnd,
843 SpanXSelfStart,
844 SpanXSelfEnd,
845 Top,
847 Bottom,
848 SpanTop,
849 SpanBottom,
850 YStart,
851 YEnd,
852 SpanYStart,
853 SpanYEnd,
854 YSelfStart,
855 YSelfEnd,
856 SpanYSelfStart,
857 SpanYSelfEnd,
858
859 BlockStart,
861 BlockEnd,
862 SpanBlockStart,
863 SpanBlockEnd,
864 InlineStart,
866 InlineEnd,
867 SpanInlineStart,
868 SpanInlineEnd,
869
870 SelfBlockStart,
872 SelfBlockEnd,
873 SpanSelfBlockStart,
874 SpanSelfBlockEnd,
875 SelfInlineStart,
877 SelfInlineEnd,
878 SpanSelfInlineStart,
879 SpanSelfInlineEnd,
880
881 Start,
883 End,
884 SpanStart,
885 SpanEnd,
886
887 SelfStart,
889 SelfEnd,
890 SpanSelfStart,
891 SpanSelfEnd,
892}
893
894#[allow(missing_docs)]
895impl PositionAreaKeyword {
896 #[inline]
897 pub fn none() -> Self {
898 Self::None
899 }
900
901 pub fn is_none(&self) -> bool {
902 *self == Self::None
903 }
904
905 pub fn is_common(&self) -> bool {
907 *self == Self::Center || *self == Self::SpanAll
908 }
909
910 pub fn is_horizontal(&self) -> bool {
911 matches!(
912 self,
913 Self::Left
914 | Self::Right
915 | Self::SpanLeft
916 | Self::SpanRight
917 | Self::XStart
918 | Self::XEnd
919 | Self::SpanXStart
920 | Self::SpanXEnd
921 | Self::XSelfStart
922 | Self::XSelfEnd
923 | Self::SpanXSelfStart
924 | Self::SpanXSelfEnd
925 )
926 }
927 pub fn is_vertical(&self) -> bool {
928 matches!(
929 self,
930 Self::Top
931 | Self::Bottom
932 | Self::SpanTop
933 | Self::SpanBottom
934 | Self::YStart
935 | Self::YEnd
936 | Self::SpanYStart
937 | Self::SpanYEnd
938 | Self::YSelfStart
939 | Self::YSelfEnd
940 | Self::SpanYSelfStart
941 | Self::SpanYSelfEnd
942 )
943 }
944
945 pub fn is_block(&self) -> bool {
946 matches!(
947 self,
948 Self::BlockStart | Self::BlockEnd | Self::SpanBlockStart | Self::SpanBlockEnd
949 )
950 }
951 pub fn is_inline(&self) -> bool {
952 matches!(
953 self,
954 Self::InlineStart | Self::InlineEnd | Self::SpanInlineStart | Self::SpanInlineEnd
955 )
956 }
957
958 pub fn is_self_block(&self) -> bool {
959 matches!(
960 self,
961 Self::SelfBlockStart
962 | Self::SelfBlockEnd
963 | Self::SpanSelfBlockStart
964 | Self::SpanSelfBlockEnd
965 )
966 }
967 pub fn is_self_inline(&self) -> bool {
968 matches!(
969 self,
970 Self::SelfInlineStart
971 | Self::SelfInlineEnd
972 | Self::SpanSelfInlineStart
973 | Self::SpanSelfInlineEnd
974 )
975 }
976
977 pub fn is_inferred_logical(&self) -> bool {
978 matches!(
979 self,
980 Self::Start | Self::End | Self::SpanStart | Self::SpanEnd
981 )
982 }
983
984 pub fn is_self_inferred_logical(&self) -> bool {
985 matches!(
986 self,
987 Self::SelfStart | Self::SelfEnd | Self::SpanSelfStart | Self::SpanSelfEnd
988 )
989 }
990}
991
992#[inline]
993fn is_compatible_pairing(first: PositionAreaKeyword, second: PositionAreaKeyword) -> bool {
994 if first.is_none() || second.is_none() {
995 return false;
998 }
999 if first.is_common() || second.is_common() {
1000 return true;
1001 }
1002 if first.is_horizontal() {
1003 return second.is_vertical();
1004 }
1005 if first.is_vertical() {
1006 return second.is_horizontal();
1007 }
1008 if first.is_block() {
1009 return second.is_inline();
1010 }
1011 if first.is_inline() {
1012 return second.is_block();
1013 }
1014 if first.is_self_block() {
1015 return second.is_self_inline();
1016 }
1017 if first.is_self_inline() {
1018 return second.is_self_block();
1019 }
1020 if first.is_inferred_logical() {
1021 return second.is_inferred_logical();
1022 }
1023 if first.is_self_inferred_logical() {
1024 return second.is_self_inferred_logical();
1025 }
1026
1027 debug_assert!(false, "Not reached");
1028
1029 false
1032}
1033
1034#[derive(
1035 Clone,
1036 Copy,
1037 Debug,
1038 Eq,
1039 MallocSizeOf,
1040 PartialEq,
1041 SpecifiedValueInfo,
1042 ToComputedValue,
1043 ToCss,
1044 ToResolvedValue,
1045 ToShmem,
1046)]
1047#[repr(C)]
1048pub struct PositionArea {
1050 pub first: PositionAreaKeyword,
1052 #[css(skip_if = "PositionAreaKeyword::is_none")]
1054 pub second: PositionAreaKeyword,
1055}
1056
1057#[allow(missing_docs)]
1058impl PositionArea {
1059 #[inline]
1060 pub fn none() -> Self {
1061 Self {
1062 first: PositionAreaKeyword::None,
1063 second: PositionAreaKeyword::None,
1064 }
1065 }
1066
1067 #[inline]
1068 pub fn is_none(&self) -> bool {
1069 self.first.is_none()
1070 }
1071
1072 pub fn parse_except_none<'i, 't>(
1073 context: &ParserContext,
1074 input: &mut Parser<'i, 't>,
1075 ) -> Result<Self, ParseError<'i>> {
1076 Self::parse_internal(context, input, false)
1077 }
1078
1079 fn parse_internal<'i, 't>(
1080 _context: &ParserContext,
1081 input: &mut Parser<'i, 't>,
1082 allow_none: bool,
1083 ) -> Result<Self, ParseError<'i>> {
1084 let mut location = input.current_source_location();
1085 let mut first = PositionAreaKeyword::parse(input)?;
1086 if first.is_none() {
1087 if allow_none {
1088 return Ok(Self::none());
1089 }
1090 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1091 }
1092
1093 location = input.current_source_location();
1094 let second = input.try_parse(PositionAreaKeyword::parse);
1095 if let Ok(PositionAreaKeyword::None) = second {
1096 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1098 }
1099 let mut second = second.unwrap_or(PositionAreaKeyword::None);
1100 if second.is_none() {
1101 return Ok(Self { first, second });
1107 }
1108
1109 if !is_compatible_pairing(first, second) {
1110 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1111 }
1112
1113 if first.is_inferred_logical()
1116 || second.is_inferred_logical()
1117 || first.is_self_inferred_logical()
1118 || second.is_self_inferred_logical()
1119 || (first.is_common() && second.is_common())
1120 {
1121 if first == second {
1125 second = PositionAreaKeyword::None;
1126 }
1127 } else if second == PositionAreaKeyword::SpanAll {
1128 second = PositionAreaKeyword::None;
1131 } else if first == PositionAreaKeyword::SpanAll {
1132 first = second;
1134 second = PositionAreaKeyword::None;
1135 } else if first.is_vertical()
1136 || second.is_horizontal()
1137 || first.is_inline()
1138 || second.is_block()
1139 || first.is_self_inline()
1140 || second.is_self_block()
1141 {
1142 std::mem::swap(&mut first, &mut second);
1144 }
1145
1146 Ok(Self { first, second })
1147 }
1148}
1149
1150impl Parse for PositionArea {
1151 fn parse<'i, 't>(
1152 context: &ParserContext,
1153 input: &mut Parser<'i, 't>,
1154 ) -> Result<Self, ParseError<'i>> {
1155 Self::parse_internal(context, input, true)
1156 }
1157}
1158
1159pub trait Side {
1161 fn start() -> Self;
1163
1164 fn is_start(&self) -> bool;
1166}
1167
1168impl Side for HorizontalPositionKeyword {
1169 #[inline]
1170 fn start() -> Self {
1171 HorizontalPositionKeyword::Left
1172 }
1173
1174 #[inline]
1175 fn is_start(&self) -> bool {
1176 *self == Self::start()
1177 }
1178}
1179
1180impl Side for VerticalPositionKeyword {
1181 #[inline]
1182 fn start() -> Self {
1183 VerticalPositionKeyword::Top
1184 }
1185
1186 #[inline]
1187 fn is_start(&self) -> bool {
1188 *self == Self::start()
1189 }
1190}
1191
1192#[derive(
1196 Clone,
1197 Copy,
1198 Debug,
1199 Eq,
1200 MallocSizeOf,
1201 Parse,
1202 PartialEq,
1203 SpecifiedValueInfo,
1204 ToComputedValue,
1205 ToResolvedValue,
1206 ToShmem,
1207)]
1208#[css(bitflags(
1209 mixed = "row,column,dense",
1210 validate_mixed = "Self::validate_and_simplify"
1211))]
1212#[repr(C)]
1213pub struct GridAutoFlow(u8);
1214bitflags! {
1215 impl GridAutoFlow: u8 {
1216 const ROW = 1 << 0;
1218 const COLUMN = 1 << 1;
1220 const DENSE = 1 << 2;
1222 }
1223}
1224
1225impl GridAutoFlow {
1226 fn validate_and_simplify(&mut self) -> bool {
1228 if self.contains(Self::ROW | Self::COLUMN) {
1229 return false;
1231 }
1232 if *self == Self::DENSE {
1233 self.insert(Self::ROW);
1235 }
1236 true
1237 }
1238}
1239
1240impl ToCss for GridAutoFlow {
1241 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1242 where
1243 W: Write,
1244 {
1245 let dense = self.intersects(Self::DENSE);
1246 if self.intersects(Self::ROW) {
1247 return if dense {
1248 dest.write_str("dense")
1249 } else {
1250 dest.write_str("row")
1251 };
1252 }
1253 debug_assert!(self.intersects(Self::COLUMN));
1254 if dense {
1255 dest.write_str("column dense")
1256 } else {
1257 dest.write_str("column")
1258 }
1259 }
1260}
1261
1262#[repr(u8)]
1263#[derive(
1264 Clone,
1265 Copy,
1266 Debug,
1267 Eq,
1268 MallocSizeOf,
1269 PartialEq,
1270 SpecifiedValueInfo,
1271 ToComputedValue,
1272 ToCss,
1273 ToResolvedValue,
1274 ToShmem,
1275)]
1276pub enum MasonryPlacement {
1278 Pack,
1280 Next,
1282}
1283
1284#[repr(u8)]
1285#[derive(
1286 Clone,
1287 Copy,
1288 Debug,
1289 Eq,
1290 MallocSizeOf,
1291 PartialEq,
1292 SpecifiedValueInfo,
1293 ToComputedValue,
1294 ToCss,
1295 ToResolvedValue,
1296 ToShmem,
1297)]
1298pub enum MasonryItemOrder {
1300 DefiniteFirst,
1302 Ordered,
1304}
1305
1306#[derive(
1307 Clone,
1308 Copy,
1309 Debug,
1310 Eq,
1311 MallocSizeOf,
1312 PartialEq,
1313 SpecifiedValueInfo,
1314 ToComputedValue,
1315 ToCss,
1316 ToResolvedValue,
1317 ToShmem,
1318)]
1319#[repr(C)]
1320pub struct MasonryAutoFlow {
1323 #[css(contextual_skip_if = "is_pack_with_non_default_order")]
1325 pub placement: MasonryPlacement,
1326 #[css(skip_if = "is_item_order_definite_first")]
1328 pub order: MasonryItemOrder,
1329}
1330
1331#[inline]
1332fn is_pack_with_non_default_order(placement: &MasonryPlacement, order: &MasonryItemOrder) -> bool {
1333 *placement == MasonryPlacement::Pack && *order != MasonryItemOrder::DefiniteFirst
1334}
1335
1336#[inline]
1337fn is_item_order_definite_first(order: &MasonryItemOrder) -> bool {
1338 *order == MasonryItemOrder::DefiniteFirst
1339}
1340
1341impl MasonryAutoFlow {
1342 #[inline]
1343 pub fn initial() -> MasonryAutoFlow {
1345 MasonryAutoFlow {
1346 placement: MasonryPlacement::Pack,
1347 order: MasonryItemOrder::DefiniteFirst,
1348 }
1349 }
1350}
1351
1352impl Parse for MasonryAutoFlow {
1353 fn parse<'i, 't>(
1355 _context: &ParserContext,
1356 input: &mut Parser<'i, 't>,
1357 ) -> Result<MasonryAutoFlow, ParseError<'i>> {
1358 let mut value = MasonryAutoFlow::initial();
1359 let mut got_placement = false;
1360 let mut got_order = false;
1361 while !input.is_exhausted() {
1362 let location = input.current_source_location();
1363 let ident = input.expect_ident()?;
1364 let success = match_ignore_ascii_case! { &ident,
1365 "pack" if !got_placement => {
1366 got_placement = true;
1367 true
1368 },
1369 "next" if !got_placement => {
1370 value.placement = MasonryPlacement::Next;
1371 got_placement = true;
1372 true
1373 },
1374 "definite-first" if !got_order => {
1375 got_order = true;
1376 true
1377 },
1378 "ordered" if !got_order => {
1379 value.order = MasonryItemOrder::Ordered;
1380 got_order = true;
1381 true
1382 },
1383 _ => false
1384 };
1385 if !success {
1386 return Err(location
1387 .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
1388 }
1389 }
1390
1391 if got_placement || got_order {
1392 Ok(value)
1393 } else {
1394 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1395 }
1396 }
1397}
1398
1399#[derive(
1400 Clone,
1401 Debug,
1402 MallocSizeOf,
1403 PartialEq,
1404 SpecifiedValueInfo,
1405 ToComputedValue,
1406 ToCss,
1407 ToResolvedValue,
1408 ToShmem,
1409)]
1410#[repr(C)]
1411pub struct TemplateAreas {
1413 #[css(skip)]
1415 pub areas: crate::OwnedSlice<NamedArea>,
1416 #[css(iterable)]
1421 pub strings: crate::OwnedSlice<crate::OwnedStr>,
1422 #[css(skip)]
1424 pub width: u32,
1425}
1426
1427#[derive(Default)]
1429pub struct TemplateAreasParser {
1430 areas: Vec<NamedArea>,
1431 area_indices: PrecomputedHashMap<Atom, usize>,
1432 strings: Vec<crate::OwnedStr>,
1433 width: u32,
1434 row: u32,
1435}
1436
1437impl TemplateAreasParser {
1438 pub fn try_parse_string<'i>(
1440 &mut self,
1441 input: &mut Parser<'i, '_>,
1442 ) -> Result<(), ParseError<'i>> {
1443 input.try_parse(|input| {
1444 self.parse_string(input.expect_string()?)
1445 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1446 })
1447 }
1448
1449 fn parse_string(&mut self, string: &str) -> Result<(), ()> {
1451 self.row += 1;
1452 let mut simplified_string = String::new();
1453 let mut current_area_index: Option<usize> = None;
1454 let mut column = 0u32;
1455 for token in TemplateAreasTokenizer(string) {
1456 column += 1;
1457 if column > 1 {
1458 simplified_string.push(' ');
1459 }
1460 let name = if let Some(token) = token? {
1461 simplified_string.push_str(token);
1462 Atom::from(token)
1463 } else {
1464 if let Some(index) = current_area_index.take() {
1465 if self.areas[index].columns.end != column {
1466 return Err(());
1467 }
1468 }
1469 simplified_string.push('.');
1470 continue;
1471 };
1472 if let Some(index) = current_area_index {
1473 if self.areas[index].name == name {
1474 if self.areas[index].rows.start == self.row {
1475 self.areas[index].columns.end += 1;
1476 }
1477 continue;
1478 }
1479 if self.areas[index].columns.end != column {
1480 return Err(());
1481 }
1482 }
1483 match self.area_indices.entry(name) {
1484 Entry::Occupied(ref e) => {
1485 let index = *e.get();
1486 if self.areas[index].columns.start != column
1487 || self.areas[index].rows.end != self.row
1488 {
1489 return Err(());
1490 }
1491 self.areas[index].rows.end += 1;
1492 current_area_index = Some(index);
1493 },
1494 Entry::Vacant(v) => {
1495 let index = self.areas.len();
1496 let name = v.key().clone();
1497 v.insert(index);
1498 self.areas.push(NamedArea {
1499 name,
1500 columns: UnsignedRange {
1501 start: column,
1502 end: column + 1,
1503 },
1504 rows: UnsignedRange {
1505 start: self.row,
1506 end: self.row + 1,
1507 },
1508 });
1509 current_area_index = Some(index);
1510 },
1511 }
1512 }
1513 if column == 0 {
1514 return Err(());
1517 }
1518 if let Some(index) = current_area_index {
1519 if self.areas[index].columns.end != column + 1 {
1520 debug_assert_ne!(self.areas[index].rows.start, self.row);
1521 return Err(());
1522 }
1523 }
1524 if self.row == 1 {
1525 self.width = column;
1526 } else if self.width != column {
1527 return Err(());
1528 }
1529
1530 self.strings.push(simplified_string.into());
1531 Ok(())
1532 }
1533
1534 pub fn finish(self) -> Result<TemplateAreas, ()> {
1536 if self.strings.is_empty() {
1537 return Err(());
1538 }
1539 Ok(TemplateAreas {
1540 areas: self.areas.into(),
1541 strings: self.strings.into(),
1542 width: self.width,
1543 })
1544 }
1545}
1546
1547impl TemplateAreas {
1548 fn parse_internal(input: &mut Parser) -> Result<Self, ()> {
1549 let mut parser = TemplateAreasParser::default();
1550 while parser.try_parse_string(input).is_ok() {}
1551 parser.finish()
1552 }
1553}
1554
1555impl Parse for TemplateAreas {
1556 fn parse<'i, 't>(
1557 _: &ParserContext,
1558 input: &mut Parser<'i, 't>,
1559 ) -> Result<Self, ParseError<'i>> {
1560 Self::parse_internal(input)
1561 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1562 }
1563}
1564
1565#[derive(
1567 Clone,
1568 Debug,
1569 MallocSizeOf,
1570 PartialEq,
1571 SpecifiedValueInfo,
1572 ToComputedValue,
1573 ToCss,
1574 ToResolvedValue,
1575 ToShmem,
1576)]
1577#[repr(transparent)]
1578pub struct TemplateAreasArc(#[ignore_malloc_size_of = "Arc"] pub Arc<TemplateAreas>);
1579
1580impl Parse for TemplateAreasArc {
1581 fn parse<'i, 't>(
1582 context: &ParserContext,
1583 input: &mut Parser<'i, 't>,
1584 ) -> Result<Self, ParseError<'i>> {
1585 let parsed = TemplateAreas::parse(context, input)?;
1586 Ok(TemplateAreasArc(Arc::new(parsed)))
1587 }
1588}
1589
1590#[repr(C)]
1593#[derive(
1594 Clone,
1595 Debug,
1596 MallocSizeOf,
1597 PartialEq,
1598 SpecifiedValueInfo,
1599 ToComputedValue,
1600 ToResolvedValue,
1601 ToShmem,
1602)]
1603pub struct UnsignedRange {
1604 pub start: u32,
1606 pub end: u32,
1608}
1609
1610#[derive(
1611 Clone,
1612 Debug,
1613 MallocSizeOf,
1614 PartialEq,
1615 SpecifiedValueInfo,
1616 ToComputedValue,
1617 ToResolvedValue,
1618 ToShmem,
1619)]
1620#[repr(C)]
1621pub struct NamedArea {
1624 pub name: Atom,
1626 pub rows: UnsignedRange,
1628 pub columns: UnsignedRange,
1630}
1631
1632struct TemplateAreasTokenizer<'a>(&'a str);
1635
1636impl<'a> Iterator for TemplateAreasTokenizer<'a> {
1637 type Item = Result<Option<&'a str>, ()>;
1638
1639 fn next(&mut self) -> Option<Self::Item> {
1640 let rest = self.0.trim_start_matches(HTML_SPACE_CHARACTERS);
1641 if rest.is_empty() {
1642 return None;
1643 }
1644 if rest.starts_with('.') {
1645 self.0 = &rest[rest.find(|c| c != '.').unwrap_or(rest.len())..];
1646 return Some(Ok(None));
1647 }
1648 if !rest.starts_with(is_name_code_point) {
1649 return Some(Err(()));
1650 }
1651 let token_len = rest.find(|c| !is_name_code_point(c)).unwrap_or(rest.len());
1652 let token = &rest[..token_len];
1653 self.0 = &rest[token_len..];
1654 Some(Ok(Some(token)))
1655 }
1656}
1657
1658fn is_name_code_point(c: char) -> bool {
1659 c >= 'A' && c <= 'Z'
1660 || c >= 'a' && c <= 'z'
1661 || c >= '\u{80}'
1662 || c == '_'
1663 || c >= '0' && c <= '9'
1664 || c == '-'
1665}
1666
1667#[repr(C, u8)]
1673#[derive(
1674 Clone,
1675 Debug,
1676 MallocSizeOf,
1677 Parse,
1678 PartialEq,
1679 SpecifiedValueInfo,
1680 ToComputedValue,
1681 ToCss,
1682 ToResolvedValue,
1683 ToShmem,
1684)]
1685pub enum GridTemplateAreas {
1686 None,
1688 Areas(TemplateAreasArc),
1690}
1691
1692impl GridTemplateAreas {
1693 #[inline]
1694 pub fn none() -> GridTemplateAreas {
1696 GridTemplateAreas::None
1697 }
1698}
1699
1700pub type ZIndex = GenericZIndex<Integer>;
1702
1703pub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;
1705
1706impl Parse for AspectRatio {
1707 fn parse<'i, 't>(
1708 context: &ParserContext,
1709 input: &mut Parser<'i, 't>,
1710 ) -> Result<Self, ParseError<'i>> {
1711 use crate::values::generics::position::PreferredRatio;
1712 use crate::values::specified::Ratio;
1713
1714 let location = input.current_source_location();
1715 let mut auto = input.try_parse(|i| i.expect_ident_matching("auto"));
1716 let ratio = input.try_parse(|i| Ratio::parse(context, i));
1717 if auto.is_err() {
1718 auto = input.try_parse(|i| i.expect_ident_matching("auto"));
1719 }
1720
1721 if auto.is_err() && ratio.is_err() {
1722 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1723 }
1724
1725 Ok(AspectRatio {
1726 auto: auto.is_ok(),
1727 ratio: match ratio {
1728 Ok(ratio) => PreferredRatio::Ratio(ratio),
1729 Err(..) => PreferredRatio::None,
1730 },
1731 })
1732 }
1733}
1734
1735impl AspectRatio {
1736 pub fn from_mapped_ratio(w: f32, h: f32) -> Self {
1738 use crate::values::generics::position::PreferredRatio;
1739 use crate::values::generics::ratio::Ratio;
1740 AspectRatio {
1741 auto: true,
1742 ratio: PreferredRatio::Ratio(Ratio(
1743 NonNegativeNumber::new(w),
1744 NonNegativeNumber::new(h),
1745 )),
1746 }
1747 }
1748}
1749
1750pub type Inset = GenericInset<specified::Percentage, LengthPercentage>;
1752
1753impl Inset {
1754 #[inline]
1757 pub fn parse_quirky<'i, 't>(
1758 context: &ParserContext,
1759 input: &mut Parser<'i, 't>,
1760 allow_quirks: AllowQuirks,
1761 ) -> Result<Self, ParseError<'i>> {
1762 if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
1763 {
1764 return Ok(Self::LengthPercentage(l));
1765 }
1766 match input.try_parse(|i| i.expect_ident_matching("auto")) {
1767 Ok(_) => return Ok(Self::Auto),
1768 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
1769 return Err(e.into())
1770 },
1771 Err(_) => (),
1772 };
1773 Self::parse_anchor_functions_quirky(context, input, allow_quirks)
1774 }
1775
1776 fn parse_as_anchor_function_fallback<'i, 't>(
1777 context: &ParserContext,
1778 input: &mut Parser<'i, 't>,
1779 ) -> Result<Self, ParseError<'i>> {
1780 if let Ok(l) =
1781 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::No))
1782 {
1783 return Ok(Self::LengthPercentage(l));
1784 }
1785 Self::parse_anchor_functions_quirky(context, input, AllowQuirks::No)
1786 }
1787
1788 fn parse_anchor_functions_quirky<'i, 't>(
1789 context: &ParserContext,
1790 input: &mut Parser<'i, 't>,
1791 allow_quirks: AllowQuirks,
1792 ) -> Result<Self, ParseError<'i>> {
1793 debug_assert!(
1794 static_prefs::pref!("layout.css.anchor-positioning.enabled"),
1795 "How are we parsing with pref off?"
1796 );
1797 if let Ok(inner) = input.try_parse(|i| AnchorFunction::parse(context, i)) {
1798 return Ok(Self::AnchorFunction(Box::new(inner)));
1799 }
1800 if let Ok(inner) =
1801 input.try_parse(|i| GenericAnchorSizeFunction::<Inset>::parse(context, i))
1802 {
1803 return Ok(Self::AnchorSizeFunction(Box::new(inner)));
1804 }
1805 Ok(Self::AnchorContainingCalcFunction(input.try_parse(
1806 |i| LengthPercentage::parse_quirky_with_anchor_functions(context, i, allow_quirks),
1807 )?))
1808 }
1809}
1810
1811impl Parse for Inset {
1812 fn parse<'i, 't>(
1813 context: &ParserContext,
1814 input: &mut Parser<'i, 't>,
1815 ) -> Result<Self, ParseError<'i>> {
1816 Self::parse_quirky(context, input, AllowQuirks::No)
1817 }
1818}
1819
1820pub type AnchorFunction = GenericAnchorFunction<specified::Percentage, Inset>;
1822
1823impl Parse for AnchorFunction {
1824 fn parse<'i, 't>(
1825 context: &ParserContext,
1826 input: &mut Parser<'i, 't>,
1827 ) -> Result<Self, ParseError<'i>> {
1828 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
1829 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1830 }
1831 input.expect_function_matching("anchor")?;
1832 input.parse_nested_block(|i| {
1833 let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
1834 let side = GenericAnchorSide::parse(context, i)?;
1835 let target_element = if target_element.is_none() {
1836 i.try_parse(|i| DashedIdent::parse(context, i)).ok()
1837 } else {
1838 target_element
1839 };
1840 let fallback = i
1841 .try_parse(|i| {
1842 i.expect_comma()?;
1843 Inset::parse_as_anchor_function_fallback(context, i)
1844 })
1845 .ok();
1846 Ok(Self {
1847 target_element: target_element.unwrap_or_else(DashedIdent::empty),
1848 side,
1849 fallback: fallback.into(),
1850 })
1851 })
1852 }
1853}