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