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 ToTyped,
359)]
360#[css(comma)]
361pub struct AnchorName(
362 #[css(iterable, if_empty = "none")]
363 #[ignore_malloc_size_of = "Arc"]
364 pub crate::ArcSlice<DashedIdent>,
365);
366
367impl AnchorName {
368 pub fn none() -> Self {
370 Self(Default::default())
371 }
372
373 pub fn is_none(&self) -> bool {
375 self.0.is_empty()
376 }
377}
378
379impl Parse for AnchorName {
380 fn parse<'i, 't>(
381 context: &ParserContext,
382 input: &mut Parser<'i, 't>,
383 ) -> Result<Self, ParseError<'i>> {
384 let location = input.current_source_location();
385 let first = input.expect_ident()?;
386 if first.eq_ignore_ascii_case("none") {
387 return Ok(Self::none());
388 }
389 let mut idents: SmallVec<[DashedIdent; 4]> =
392 smallvec![DashedIdent::from_ident(location, first,)?];
393 while input.try_parse(|input| input.expect_comma()).is_ok() {
394 idents.push(DashedIdent::parse(context, input)?);
395 }
396 Ok(AnchorName(ArcSlice::from_iter(idents.drain(..))))
397 }
398}
399
400#[derive(
402 Clone,
403 Debug,
404 MallocSizeOf,
405 PartialEq,
406 SpecifiedValueInfo,
407 ToComputedValue,
408 ToCss,
409 ToResolvedValue,
410 ToShmem,
411 ToTyped,
412)]
413#[repr(u8)]
414pub enum AnchorScope {
415 None,
417 All,
419 #[css(comma)]
421 Idents(
422 #[css(iterable)]
423 #[ignore_malloc_size_of = "Arc"]
424 crate::ArcSlice<DashedIdent>,
425 ),
426}
427
428impl AnchorScope {
429 pub fn none() -> Self {
431 Self::None
432 }
433
434 pub fn is_none(&self) -> bool {
436 *self == Self::None
437 }
438}
439
440impl Parse for AnchorScope {
441 fn parse<'i, 't>(
442 context: &ParserContext,
443 input: &mut Parser<'i, 't>,
444 ) -> Result<Self, ParseError<'i>> {
445 let location = input.current_source_location();
446 let first = input.expect_ident()?;
447 if first.eq_ignore_ascii_case("none") {
448 return Ok(Self::None);
449 }
450 if first.eq_ignore_ascii_case("all") {
451 return Ok(Self::All);
452 }
453 let mut idents: SmallVec<[DashedIdent; 8]> =
456 smallvec![DashedIdent::from_ident(location, first,)?];
457 while input.try_parse(|input| input.expect_comma()).is_ok() {
458 idents.push(DashedIdent::parse(context, input)?);
459 }
460 Ok(AnchorScope::Idents(ArcSlice::from_iter(idents.drain(..))))
461 }
462}
463
464#[derive(
466 Clone,
467 Debug,
468 MallocSizeOf,
469 Parse,
470 PartialEq,
471 SpecifiedValueInfo,
472 ToComputedValue,
473 ToCss,
474 ToResolvedValue,
475 ToShmem,
476 ToTyped,
477)]
478#[repr(u8)]
479pub enum PositionAnchor {
480 Auto,
482 Ident(DashedIdent),
484}
485
486impl PositionAnchor {
487 pub fn auto() -> Self {
489 Self::Auto
490 }
491
492 pub fn is_auto(&self) -> bool {
494 *self == Self::Auto
495 }
496}
497
498#[derive(
499 Clone,
500 Copy,
501 Debug,
502 Default,
503 Eq,
504 MallocSizeOf,
505 Parse,
506 PartialEq,
507 Serialize,
508 SpecifiedValueInfo,
509 ToComputedValue,
510 ToCss,
511 ToResolvedValue,
512 ToShmem,
513)]
514#[repr(u8)]
515pub enum PositionTryFallbacksTryTacticKeyword {
517 #[css(skip)]
519 #[default]
520 None,
521 FlipBlock,
523 FlipInline,
525 FlipStart,
527}
528
529impl PositionTryFallbacksTryTacticKeyword {
530 fn is_none(&self) -> bool {
531 *self == Self::None
532 }
533}
534
535#[derive(
536 Clone,
537 Copy,
538 Debug,
539 Default,
540 Eq,
541 MallocSizeOf,
542 PartialEq,
543 Serialize,
544 SpecifiedValueInfo,
545 ToComputedValue,
546 ToCss,
547 ToResolvedValue,
548 ToShmem,
549)]
550#[repr(C)]
551pub struct PositionTryFallbacksTryTactic(
556 pub PositionTryFallbacksTryTacticKeyword,
557 pub PositionTryFallbacksTryTacticKeyword,
558 pub PositionTryFallbacksTryTacticKeyword,
559);
560
561impl Parse for PositionTryFallbacksTryTactic {
562 fn parse<'i, 't>(
563 _context: &ParserContext,
564 input: &mut Parser<'i, 't>,
565 ) -> Result<Self, ParseError<'i>> {
566 let first = PositionTryFallbacksTryTacticKeyword::parse(input)?;
567 let second = input
568 .try_parse(PositionTryFallbacksTryTacticKeyword::parse)
569 .unwrap_or_default();
570 let third = input
571 .try_parse(PositionTryFallbacksTryTacticKeyword::parse)
572 .unwrap_or_default();
573 if first == second || first == third || (!second.is_none() && second == third) {
574 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
575 }
576 Ok(Self(first, second, third))
577 }
578}
579
580impl PositionTryFallbacksTryTactic {
581 fn is_empty(&self) -> bool {
582 self.0.is_none()
583 }
584}
585
586#[derive(
587 Clone,
588 Debug,
589 MallocSizeOf,
590 PartialEq,
591 SpecifiedValueInfo,
592 ToComputedValue,
593 ToCss,
594 ToResolvedValue,
595 ToShmem,
596)]
597#[repr(C)]
598pub struct DashedIdentAndOrTryTactic {
601 pub ident: DashedIdent,
603 pub try_tactic: PositionTryFallbacksTryTactic,
605}
606
607impl Parse for DashedIdentAndOrTryTactic {
608 fn parse<'i, 't>(
609 context: &ParserContext,
610 input: &mut Parser<'i, 't>,
611 ) -> Result<Self, ParseError<'i>> {
612 let mut result = Self {
613 ident: DashedIdent::empty(),
614 try_tactic: PositionTryFallbacksTryTactic::default(),
615 };
616
617 loop {
618 if result.ident.is_empty() {
619 if let Ok(ident) = input.try_parse(|i| DashedIdent::parse(context, i)) {
620 result.ident = ident;
621 continue;
622 }
623 }
624 if result.try_tactic.is_empty() {
625 if let Ok(try_tactic) =
626 input.try_parse(|i| PositionTryFallbacksTryTactic::parse(context, i))
627 {
628 result.try_tactic = try_tactic;
629 continue;
630 }
631 }
632 break;
633 }
634
635 if result.ident.is_empty() && result.try_tactic.is_empty() {
636 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
637 }
638 return Ok(result);
639 }
640}
641
642#[derive(
643 Clone,
644 Debug,
645 MallocSizeOf,
646 Parse,
647 PartialEq,
648 SpecifiedValueInfo,
649 ToComputedValue,
650 ToCss,
651 ToResolvedValue,
652 ToShmem,
653)]
654#[repr(u8)]
655pub enum PositionTryFallbacksItem {
658 IdentAndOrTactic(DashedIdentAndOrTryTactic),
660 #[parse(parse_fn = "PositionArea::parse_except_none")]
661 PositionArea(PositionArea),
663}
664
665#[derive(
666 Clone,
667 Debug,
668 Default,
669 MallocSizeOf,
670 PartialEq,
671 SpecifiedValueInfo,
672 ToComputedValue,
673 ToCss,
674 ToResolvedValue,
675 ToShmem,
676 ToTyped,
677)]
678#[css(comma)]
679#[repr(C)]
680pub struct PositionTryFallbacks(
682 #[css(iterable, if_empty = "none")]
683 #[ignore_malloc_size_of = "Arc"]
684 pub crate::ArcSlice<PositionTryFallbacksItem>,
685);
686
687impl PositionTryFallbacks {
688 #[inline]
689 pub fn none() -> Self {
691 Self(Default::default())
692 }
693
694 pub fn is_none(&self) -> bool {
696 self.0.is_empty()
697 }
698}
699
700impl Parse for PositionTryFallbacks {
701 fn parse<'i, 't>(
702 context: &ParserContext,
703 input: &mut Parser<'i, 't>,
704 ) -> Result<Self, ParseError<'i>> {
705 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
706 return Ok(Self::none());
707 }
708 let mut items: SmallVec<[PositionTryFallbacksItem; 4]> =
711 smallvec![PositionTryFallbacksItem::parse(context, input)?];
712 while input.try_parse(|input| input.expect_comma()).is_ok() {
713 items.push(PositionTryFallbacksItem::parse(context, input)?);
714 }
715 Ok(Self(ArcSlice::from_iter(items.drain(..))))
716 }
717}
718
719#[derive(
721 Clone,
722 Copy,
723 Debug,
724 Default,
725 Eq,
726 MallocSizeOf,
727 Parse,
728 PartialEq,
729 SpecifiedValueInfo,
730 ToComputedValue,
731 ToCss,
732 ToResolvedValue,
733 ToShmem,
734 ToTyped,
735)]
736#[repr(u8)]
737pub enum PositionTryOrder {
738 #[default]
739 Normal,
741 MostWidth,
743 MostHeight,
745 MostBlockSize,
747 MostInlineSize,
749}
750
751impl PositionTryOrder {
752 #[inline]
753 pub fn normal() -> Self {
755 Self::Normal
756 }
757
758 pub fn is_normal(&self) -> bool {
760 *self == Self::Normal
761 }
762}
763
764#[derive(
765 Clone,
766 Copy,
767 Debug,
768 Eq,
769 MallocSizeOf,
770 Parse,
771 PartialEq,
772 Serialize,
773 SpecifiedValueInfo,
774 ToComputedValue,
775 ToCss,
776 ToResolvedValue,
777 ToShmem,
778 ToTyped,
779)]
780#[css(bitflags(single = "always", mixed = "anchors-valid,anchors-visible,no-overflow"))]
781#[repr(C)]
782pub struct PositionVisibility(u8);
784bitflags! {
785 impl PositionVisibility: u8 {
786 const ALWAYS = 0;
788 const ANCHORS_VALID = 1 << 0;
790 const ANCHORS_VISIBLE = 1 << 1;
792 const NO_OVERFLOW = 1 << 2;
794 }
795}
796
797impl Default for PositionVisibility {
798 fn default() -> Self {
799 Self::ALWAYS
800 }
801}
802
803impl PositionVisibility {
804 #[inline]
805 pub fn always() -> Self {
807 Self::ALWAYS
808 }
809}
810
811#[derive(PartialEq)]
812pub enum PositionAreaType {
815 Physical,
817 Logical,
819 SelfLogical,
821 Inferred,
823 SelfInferred,
825 Common,
827 None,
829}
830
831#[derive(
832 Clone,
833 Copy,
834 Debug,
835 Default,
836 Eq,
837 MallocSizeOf,
838 Parse,
839 PartialEq,
840 SpecifiedValueInfo,
841 ToComputedValue,
842 ToCss,
843 ToResolvedValue,
844 ToShmem,
845)]
846#[allow(missing_docs)]
847#[repr(u8)]
848pub enum PositionAreaKeyword {
851 #[default]
852 None = 0,
853
854 Center = 1,
856 SpanAll = 2,
857
858 Left = 3,
860 Right = 4,
861 Top = 5,
862 Bottom = 6,
863
864 XStart = 7,
866 XEnd = 8,
867 YStart = 9,
868 YEnd = 10,
869
870 BlockStart = 11,
872 BlockEnd = 12,
873 InlineStart = 13,
874 InlineEnd = 14,
875
876 Start = 15,
878 End = 16,
879
880 #[css(skip)]
886 Span = 1u8 << 5,
887 #[css(skip)]
888 SelfWM = 1u8 << 6, SpanLeft = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::Left as u8,
892 SpanRight = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::Right as u8,
893 SpanTop = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::Top as u8,
894 SpanBottom = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::Bottom as u8,
895
896 SpanXStart = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::XStart as u8,
897 SpanXEnd = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::XEnd as u8,
898 SpanYStart = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::YStart as u8,
899 SpanYEnd = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::YEnd as u8,
900
901 SpanBlockStart = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::BlockStart as u8,
902 SpanBlockEnd = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::BlockEnd as u8,
903 SpanInlineStart = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::InlineStart as u8,
904 SpanInlineEnd = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::InlineEnd as u8,
905
906 SpanStart = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::Start as u8,
907 SpanEnd = PositionAreaKeyword::Span as u8 | PositionAreaKeyword::End as u8,
908
909 XSelfStart = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::XStart as u8,
911 XSelfEnd = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::XEnd as u8,
912 YSelfStart = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::YStart as u8,
913 YSelfEnd = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::YEnd as u8,
914
915 SelfBlockStart = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::BlockStart as u8,
916 SelfBlockEnd = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::BlockEnd as u8,
917 SelfInlineStart = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::InlineStart as u8,
918 SelfInlineEnd = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::InlineEnd as u8,
919
920 SelfStart = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::Start as u8,
921 SelfEnd = PositionAreaKeyword::SelfWM as u8 | PositionAreaKeyword::End as u8,
922
923 SpanXSelfStart = PositionAreaKeyword::Span as u8
925 | PositionAreaKeyword::SelfWM as u8
926 | PositionAreaKeyword::XStart as u8,
927 SpanXSelfEnd = PositionAreaKeyword::Span as u8
928 | PositionAreaKeyword::SelfWM as u8
929 | PositionAreaKeyword::XEnd as u8,
930 SpanYSelfStart = PositionAreaKeyword::Span as u8
931 | PositionAreaKeyword::SelfWM as u8
932 | PositionAreaKeyword::YStart as u8,
933 SpanYSelfEnd = PositionAreaKeyword::Span as u8
934 | PositionAreaKeyword::SelfWM as u8
935 | PositionAreaKeyword::YEnd as u8,
936
937 SpanSelfBlockStart = PositionAreaKeyword::Span as u8
938 | PositionAreaKeyword::SelfWM as u8
939 | PositionAreaKeyword::BlockStart as u8,
940 SpanSelfBlockEnd = PositionAreaKeyword::Span as u8
941 | PositionAreaKeyword::SelfWM as u8
942 | PositionAreaKeyword::BlockEnd as u8,
943 SpanSelfInlineStart = PositionAreaKeyword::Span as u8
944 | PositionAreaKeyword::SelfWM as u8
945 | PositionAreaKeyword::InlineStart as u8,
946 SpanSelfInlineEnd = PositionAreaKeyword::Span as u8
947 | PositionAreaKeyword::SelfWM as u8
948 | PositionAreaKeyword::InlineEnd as u8,
949
950 SpanSelfStart = PositionAreaKeyword::Span as u8
951 | PositionAreaKeyword::SelfWM as u8
952 | PositionAreaKeyword::Start as u8,
953 SpanSelfEnd = PositionAreaKeyword::Span as u8
954 | PositionAreaKeyword::SelfWM as u8
955 | PositionAreaKeyword::End as u8,
956}
957
958#[allow(missing_docs)]
959impl PositionAreaKeyword {
960 #[inline]
961 pub fn none() -> Self {
962 Self::None
963 }
964
965 pub fn is_none(&self) -> bool {
966 *self == Self::None
967 }
968
969 fn get_type(&self) -> PositionAreaType {
971 use PositionAreaKeyword::*;
972 match self {
973 Left | Right | SpanLeft | SpanRight | XStart | XEnd | SpanXStart | SpanXEnd
975 | XSelfStart | XSelfEnd | SpanXSelfStart | SpanXSelfEnd => PositionAreaType::Physical,
976
977 Top | Bottom | SpanTop | SpanBottom | YStart | YEnd | SpanYStart | SpanYEnd
979 | YSelfStart | YSelfEnd | SpanYSelfStart | SpanYSelfEnd => PositionAreaType::Physical,
980
981 BlockStart | BlockEnd | SpanBlockStart | SpanBlockEnd => PositionAreaType::Logical,
983
984 InlineStart | InlineEnd | SpanInlineStart | SpanInlineEnd => PositionAreaType::Logical,
986
987 SelfBlockStart | SelfBlockEnd | SpanSelfBlockStart | SpanSelfBlockEnd => {
989 PositionAreaType::SelfLogical
990 },
991
992 SelfInlineStart | SelfInlineEnd | SpanSelfInlineStart | SpanSelfInlineEnd => {
994 PositionAreaType::SelfLogical
995 },
996
997 Start | End | SpanStart | SpanEnd => PositionAreaType::Inferred,
999
1000 SelfStart | SelfEnd | SpanSelfStart | SpanSelfEnd => PositionAreaType::SelfInferred,
1002
1003 Center | SpanAll => PositionAreaType::Common,
1005
1006 None => PositionAreaType::None,
1007
1008 SelfWM | Span => panic!("invalid PositionAreaKeyword value"),
1010 }
1011 }
1012
1013 #[inline]
1014 pub fn canonical_order_is_first(&self) -> bool {
1015 use PositionAreaKeyword::*;
1016 matches!(
1017 self,
1018 Left | Right
1019 | SpanLeft
1020 | SpanRight
1021 | XStart
1022 | XEnd
1023 | SpanXStart
1024 | SpanXEnd
1025 | XSelfStart
1026 | XSelfEnd
1027 | SpanXSelfStart
1028 | SpanXSelfEnd
1029 | BlockStart
1030 | BlockEnd
1031 | SpanBlockStart
1032 | SpanBlockEnd
1033 | SelfBlockStart
1034 | SelfBlockEnd
1035 | SpanSelfBlockStart
1036 | SpanSelfBlockEnd
1037 )
1038 }
1039
1040 #[inline]
1041 pub fn canonical_order_is_second(&self) -> bool {
1042 use PositionAreaKeyword::*;
1043 matches!(
1044 self,
1045 Top | Bottom
1046 | SpanTop
1047 | SpanBottom
1048 | YStart
1049 | YEnd
1050 | SpanYStart
1051 | SpanYEnd
1052 | YSelfStart
1053 | YSelfEnd
1054 | SpanYSelfStart
1055 | SpanYSelfEnd
1056 | InlineStart
1057 | InlineEnd
1058 | SpanInlineStart
1059 | SpanInlineEnd
1060 | SelfInlineStart
1061 | SelfInlineEnd
1062 | SpanSelfInlineStart
1063 | SpanSelfInlineEnd
1064 )
1065 }
1066
1067 #[inline]
1068 pub fn has_same_canonical_order(&self, other: PositionAreaKeyword) -> bool {
1069 self.canonical_order_is_first() == other.canonical_order_is_first()
1070 || self.canonical_order_is_second() == other.canonical_order_is_second()
1071 }
1072}
1073
1074#[derive(
1075 Clone,
1076 Copy,
1077 Debug,
1078 Eq,
1079 MallocSizeOf,
1080 PartialEq,
1081 SpecifiedValueInfo,
1082 ToCss,
1083 ToResolvedValue,
1084 ToShmem,
1085 ToTyped,
1086)]
1087#[repr(C)]
1088pub struct PositionArea {
1090 pub first: PositionAreaKeyword,
1092 #[css(skip_if = "PositionAreaKeyword::is_none")]
1094 pub second: PositionAreaKeyword,
1095}
1096
1097#[allow(missing_docs)]
1098impl PositionArea {
1099 #[inline]
1100 pub fn none() -> Self {
1101 Self {
1102 first: PositionAreaKeyword::None,
1103 second: PositionAreaKeyword::None,
1104 }
1105 }
1106
1107 #[inline]
1108 pub fn is_none(&self) -> bool {
1109 self.first.is_none()
1110 }
1111
1112 pub fn parse_except_none<'i, 't>(
1113 context: &ParserContext,
1114 input: &mut Parser<'i, 't>,
1115 ) -> Result<Self, ParseError<'i>> {
1116 Self::parse_internal(context, input, false)
1117 }
1118
1119 #[inline]
1120 pub fn get_type(&self) -> PositionAreaType {
1121 match (self.first.get_type(), self.second.get_type()) {
1122 (PositionAreaType::Physical, PositionAreaType::Physical)
1123 if !self.first.has_same_canonical_order(self.second) =>
1124 {
1125 PositionAreaType::Physical
1126 },
1127 (PositionAreaType::Logical, PositionAreaType::Logical)
1128 if !self.first.has_same_canonical_order(self.second) =>
1129 {
1130 PositionAreaType::Logical
1131 },
1132 (PositionAreaType::SelfLogical, PositionAreaType::SelfLogical)
1133 if !self.first.has_same_canonical_order(self.second) =>
1134 {
1135 PositionAreaType::SelfLogical
1136 },
1137 (PositionAreaType::Inferred, PositionAreaType::Inferred) => PositionAreaType::Inferred,
1138 (PositionAreaType::SelfInferred, PositionAreaType::SelfInferred) => {
1139 PositionAreaType::SelfInferred
1140 },
1141 (PositionAreaType::Common, PositionAreaType::Common) => PositionAreaType::Common,
1142
1143 (PositionAreaType::Common, other) | (other, PositionAreaType::Common)
1145 if other != PositionAreaType::None =>
1146 {
1147 other
1148 },
1149
1150 _ => PositionAreaType::None,
1151 }
1152 }
1153
1154 fn parse_internal<'i, 't>(
1155 _context: &ParserContext,
1156 input: &mut Parser<'i, 't>,
1157 allow_none: bool,
1158 ) -> Result<Self, ParseError<'i>> {
1159 let mut location = input.current_source_location();
1160 let mut first = PositionAreaKeyword::parse(input)?;
1161 if first.is_none() {
1162 if allow_none {
1163 return Ok(Self::none());
1164 }
1165 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1166 }
1167
1168 location = input.current_source_location();
1169 let second = input.try_parse(PositionAreaKeyword::parse);
1170 if let Ok(PositionAreaKeyword::None) = second {
1171 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1173 }
1174 let mut second = second.unwrap_or(PositionAreaKeyword::None);
1175 if second.is_none() {
1176 return Ok(Self { first, second });
1182 }
1183
1184 let pair_type = Self { first, second }.get_type();
1185
1186 if pair_type == PositionAreaType::None {
1187 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1190 }
1191
1192 if matches!(
1196 pair_type,
1197 PositionAreaType::Physical | PositionAreaType::Logical | PositionAreaType::SelfLogical
1198 ) {
1199 if second == PositionAreaKeyword::SpanAll {
1200 second = PositionAreaKeyword::None;
1203 } else if first == PositionAreaKeyword::SpanAll {
1204 first = second;
1205 second = PositionAreaKeyword::None;
1206 } else if first.canonical_order_is_second() || second.canonical_order_is_first() {
1207 std::mem::swap(&mut first, &mut second);
1208 }
1209 }
1210 if first == second {
1211 second = PositionAreaKeyword::None;
1212 }
1213
1214 Ok(Self { first, second })
1215 }
1216}
1217
1218impl Parse for PositionArea {
1219 fn parse<'i, 't>(
1220 context: &ParserContext,
1221 input: &mut Parser<'i, 't>,
1222 ) -> Result<Self, ParseError<'i>> {
1223 Self::parse_internal(context, input, true)
1224 }
1225}
1226
1227pub trait Side {
1229 fn start() -> Self;
1231
1232 fn is_start(&self) -> bool;
1234}
1235
1236impl Side for HorizontalPositionKeyword {
1237 #[inline]
1238 fn start() -> Self {
1239 HorizontalPositionKeyword::Left
1240 }
1241
1242 #[inline]
1243 fn is_start(&self) -> bool {
1244 *self == Self::start()
1245 }
1246}
1247
1248impl Side for VerticalPositionKeyword {
1249 #[inline]
1250 fn start() -> Self {
1251 VerticalPositionKeyword::Top
1252 }
1253
1254 #[inline]
1255 fn is_start(&self) -> bool {
1256 *self == Self::start()
1257 }
1258}
1259
1260#[derive(
1264 Clone,
1265 Copy,
1266 Debug,
1267 Eq,
1268 MallocSizeOf,
1269 Parse,
1270 PartialEq,
1271 SpecifiedValueInfo,
1272 ToComputedValue,
1273 ToResolvedValue,
1274 ToShmem,
1275 ToTyped,
1276)]
1277#[css(bitflags(
1278 mixed = "row,column,dense",
1279 validate_mixed = "Self::validate_and_simplify"
1280))]
1281#[repr(C)]
1282pub struct GridAutoFlow(u8);
1283bitflags! {
1284 impl GridAutoFlow: u8 {
1285 const ROW = 1 << 0;
1287 const COLUMN = 1 << 1;
1289 const DENSE = 1 << 2;
1291 }
1292}
1293
1294impl GridAutoFlow {
1295 fn validate_and_simplify(&mut self) -> bool {
1297 if self.contains(Self::ROW | Self::COLUMN) {
1298 return false;
1300 }
1301 if *self == Self::DENSE {
1302 self.insert(Self::ROW);
1304 }
1305 true
1306 }
1307}
1308
1309impl ToCss for GridAutoFlow {
1310 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1311 where
1312 W: Write,
1313 {
1314 let dense = self.intersects(Self::DENSE);
1315 if self.intersects(Self::ROW) {
1316 return if dense {
1317 dest.write_str("dense")
1318 } else {
1319 dest.write_str("row")
1320 };
1321 }
1322 debug_assert!(self.intersects(Self::COLUMN));
1323 if dense {
1324 dest.write_str("column dense")
1325 } else {
1326 dest.write_str("column")
1327 }
1328 }
1329}
1330
1331#[repr(u8)]
1332#[derive(
1333 Clone,
1334 Copy,
1335 Debug,
1336 Eq,
1337 MallocSizeOf,
1338 PartialEq,
1339 SpecifiedValueInfo,
1340 ToComputedValue,
1341 ToCss,
1342 ToResolvedValue,
1343 ToShmem,
1344)]
1345pub enum MasonryPlacement {
1347 Pack,
1349 Next,
1351}
1352
1353#[repr(u8)]
1354#[derive(
1355 Clone,
1356 Copy,
1357 Debug,
1358 Eq,
1359 MallocSizeOf,
1360 PartialEq,
1361 SpecifiedValueInfo,
1362 ToComputedValue,
1363 ToCss,
1364 ToResolvedValue,
1365 ToShmem,
1366)]
1367pub enum MasonryItemOrder {
1369 DefiniteFirst,
1371 Ordered,
1373}
1374
1375#[derive(
1376 Clone,
1377 Copy,
1378 Debug,
1379 Eq,
1380 MallocSizeOf,
1381 PartialEq,
1382 SpecifiedValueInfo,
1383 ToComputedValue,
1384 ToCss,
1385 ToResolvedValue,
1386 ToShmem,
1387 ToTyped,
1388)]
1389#[repr(C)]
1390pub struct MasonryAutoFlow {
1393 #[css(contextual_skip_if = "is_pack_with_non_default_order")]
1395 pub placement: MasonryPlacement,
1396 #[css(skip_if = "is_item_order_definite_first")]
1398 pub order: MasonryItemOrder,
1399}
1400
1401#[inline]
1402fn is_pack_with_non_default_order(placement: &MasonryPlacement, order: &MasonryItemOrder) -> bool {
1403 *placement == MasonryPlacement::Pack && *order != MasonryItemOrder::DefiniteFirst
1404}
1405
1406#[inline]
1407fn is_item_order_definite_first(order: &MasonryItemOrder) -> bool {
1408 *order == MasonryItemOrder::DefiniteFirst
1409}
1410
1411impl MasonryAutoFlow {
1412 #[inline]
1413 pub fn initial() -> MasonryAutoFlow {
1415 MasonryAutoFlow {
1416 placement: MasonryPlacement::Pack,
1417 order: MasonryItemOrder::DefiniteFirst,
1418 }
1419 }
1420}
1421
1422impl Parse for MasonryAutoFlow {
1423 fn parse<'i, 't>(
1425 _context: &ParserContext,
1426 input: &mut Parser<'i, 't>,
1427 ) -> Result<MasonryAutoFlow, ParseError<'i>> {
1428 let mut value = MasonryAutoFlow::initial();
1429 let mut got_placement = false;
1430 let mut got_order = false;
1431 while !input.is_exhausted() {
1432 let location = input.current_source_location();
1433 let ident = input.expect_ident()?;
1434 let success = match_ignore_ascii_case! { &ident,
1435 "pack" if !got_placement => {
1436 got_placement = true;
1437 true
1438 },
1439 "next" if !got_placement => {
1440 value.placement = MasonryPlacement::Next;
1441 got_placement = true;
1442 true
1443 },
1444 "definite-first" if !got_order => {
1445 got_order = true;
1446 true
1447 },
1448 "ordered" if !got_order => {
1449 value.order = MasonryItemOrder::Ordered;
1450 got_order = true;
1451 true
1452 },
1453 _ => false
1454 };
1455 if !success {
1456 return Err(location
1457 .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
1458 }
1459 }
1460
1461 if got_placement || got_order {
1462 Ok(value)
1463 } else {
1464 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1465 }
1466 }
1467}
1468
1469#[derive(
1470 Clone,
1471 Debug,
1472 MallocSizeOf,
1473 PartialEq,
1474 SpecifiedValueInfo,
1475 ToComputedValue,
1476 ToCss,
1477 ToResolvedValue,
1478 ToShmem,
1479)]
1480#[repr(C)]
1481pub struct TemplateAreas {
1483 #[css(skip)]
1485 pub areas: crate::OwnedSlice<NamedArea>,
1486 #[css(iterable)]
1491 pub strings: crate::OwnedSlice<crate::OwnedStr>,
1492 #[css(skip)]
1494 pub width: u32,
1495}
1496
1497#[derive(Default)]
1499pub struct TemplateAreasParser {
1500 areas: Vec<NamedArea>,
1501 area_indices: PrecomputedHashMap<Atom, usize>,
1502 strings: Vec<crate::OwnedStr>,
1503 width: u32,
1504 row: u32,
1505}
1506
1507impl TemplateAreasParser {
1508 pub fn try_parse_string<'i>(
1510 &mut self,
1511 input: &mut Parser<'i, '_>,
1512 ) -> Result<(), ParseError<'i>> {
1513 input.try_parse(|input| {
1514 self.parse_string(input.expect_string()?)
1515 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1516 })
1517 }
1518
1519 fn parse_string(&mut self, string: &str) -> Result<(), ()> {
1521 self.row += 1;
1522 let mut simplified_string = String::new();
1523 let mut current_area_index: Option<usize> = None;
1524 let mut column = 0u32;
1525 for token in TemplateAreasTokenizer(string) {
1526 column += 1;
1527 if column > 1 {
1528 simplified_string.push(' ');
1529 }
1530 let name = if let Some(token) = token? {
1531 simplified_string.push_str(token);
1532 Atom::from(token)
1533 } else {
1534 if let Some(index) = current_area_index.take() {
1535 if self.areas[index].columns.end != column {
1536 return Err(());
1537 }
1538 }
1539 simplified_string.push('.');
1540 continue;
1541 };
1542 if let Some(index) = current_area_index {
1543 if self.areas[index].name == name {
1544 if self.areas[index].rows.start == self.row {
1545 self.areas[index].columns.end += 1;
1546 }
1547 continue;
1548 }
1549 if self.areas[index].columns.end != column {
1550 return Err(());
1551 }
1552 }
1553 match self.area_indices.entry(name) {
1554 Entry::Occupied(ref e) => {
1555 let index = *e.get();
1556 if self.areas[index].columns.start != column
1557 || self.areas[index].rows.end != self.row
1558 {
1559 return Err(());
1560 }
1561 self.areas[index].rows.end += 1;
1562 current_area_index = Some(index);
1563 },
1564 Entry::Vacant(v) => {
1565 let index = self.areas.len();
1566 let name = v.key().clone();
1567 v.insert(index);
1568 self.areas.push(NamedArea {
1569 name,
1570 columns: UnsignedRange {
1571 start: column,
1572 end: column + 1,
1573 },
1574 rows: UnsignedRange {
1575 start: self.row,
1576 end: self.row + 1,
1577 },
1578 });
1579 current_area_index = Some(index);
1580 },
1581 }
1582 }
1583 if column == 0 {
1584 return Err(());
1587 }
1588 if let Some(index) = current_area_index {
1589 if self.areas[index].columns.end != column + 1 {
1590 debug_assert_ne!(self.areas[index].rows.start, self.row);
1591 return Err(());
1592 }
1593 }
1594 if self.row == 1 {
1595 self.width = column;
1596 } else if self.width != column {
1597 return Err(());
1598 }
1599
1600 self.strings.push(simplified_string.into());
1601 Ok(())
1602 }
1603
1604 pub fn finish(self) -> Result<TemplateAreas, ()> {
1606 if self.strings.is_empty() {
1607 return Err(());
1608 }
1609 Ok(TemplateAreas {
1610 areas: self.areas.into(),
1611 strings: self.strings.into(),
1612 width: self.width,
1613 })
1614 }
1615}
1616
1617impl TemplateAreas {
1618 fn parse_internal(input: &mut Parser) -> Result<Self, ()> {
1619 let mut parser = TemplateAreasParser::default();
1620 while parser.try_parse_string(input).is_ok() {}
1621 parser.finish()
1622 }
1623}
1624
1625impl Parse for TemplateAreas {
1626 fn parse<'i, 't>(
1627 _: &ParserContext,
1628 input: &mut Parser<'i, 't>,
1629 ) -> Result<Self, ParseError<'i>> {
1630 Self::parse_internal(input)
1631 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1632 }
1633}
1634
1635#[derive(
1637 Clone,
1638 Debug,
1639 MallocSizeOf,
1640 PartialEq,
1641 SpecifiedValueInfo,
1642 ToComputedValue,
1643 ToCss,
1644 ToResolvedValue,
1645 ToShmem,
1646)]
1647#[repr(transparent)]
1648pub struct TemplateAreasArc(#[ignore_malloc_size_of = "Arc"] pub Arc<TemplateAreas>);
1649
1650impl Parse for TemplateAreasArc {
1651 fn parse<'i, 't>(
1652 context: &ParserContext,
1653 input: &mut Parser<'i, 't>,
1654 ) -> Result<Self, ParseError<'i>> {
1655 let parsed = TemplateAreas::parse(context, input)?;
1656 Ok(TemplateAreasArc(Arc::new(parsed)))
1657 }
1658}
1659
1660#[repr(C)]
1663#[derive(
1664 Clone,
1665 Debug,
1666 MallocSizeOf,
1667 PartialEq,
1668 SpecifiedValueInfo,
1669 ToComputedValue,
1670 ToResolvedValue,
1671 ToShmem,
1672)]
1673pub struct UnsignedRange {
1674 pub start: u32,
1676 pub end: u32,
1678}
1679
1680#[derive(
1681 Clone,
1682 Debug,
1683 MallocSizeOf,
1684 PartialEq,
1685 SpecifiedValueInfo,
1686 ToComputedValue,
1687 ToResolvedValue,
1688 ToShmem,
1689)]
1690#[repr(C)]
1691pub struct NamedArea {
1694 pub name: Atom,
1696 pub rows: UnsignedRange,
1698 pub columns: UnsignedRange,
1700}
1701
1702struct TemplateAreasTokenizer<'a>(&'a str);
1705
1706impl<'a> Iterator for TemplateAreasTokenizer<'a> {
1707 type Item = Result<Option<&'a str>, ()>;
1708
1709 fn next(&mut self) -> Option<Self::Item> {
1710 let rest = self.0.trim_start_matches(HTML_SPACE_CHARACTERS);
1711 if rest.is_empty() {
1712 return None;
1713 }
1714 if rest.starts_with('.') {
1715 self.0 = &rest[rest.find(|c| c != '.').unwrap_or(rest.len())..];
1716 return Some(Ok(None));
1717 }
1718 if !rest.starts_with(is_name_code_point) {
1719 return Some(Err(()));
1720 }
1721 let token_len = rest.find(|c| !is_name_code_point(c)).unwrap_or(rest.len());
1722 let token = &rest[..token_len];
1723 self.0 = &rest[token_len..];
1724 Some(Ok(Some(token)))
1725 }
1726}
1727
1728fn is_name_code_point(c: char) -> bool {
1729 c >= 'A' && c <= 'Z'
1730 || c >= 'a' && c <= 'z'
1731 || c >= '\u{80}'
1732 || c == '_'
1733 || c >= '0' && c <= '9'
1734 || c == '-'
1735}
1736
1737#[repr(C, u8)]
1743#[derive(
1744 Clone,
1745 Debug,
1746 MallocSizeOf,
1747 Parse,
1748 PartialEq,
1749 SpecifiedValueInfo,
1750 ToComputedValue,
1751 ToCss,
1752 ToResolvedValue,
1753 ToShmem,
1754 ToTyped,
1755)]
1756pub enum GridTemplateAreas {
1757 None,
1759 Areas(TemplateAreasArc),
1761}
1762
1763impl GridTemplateAreas {
1764 #[inline]
1765 pub fn none() -> GridTemplateAreas {
1767 GridTemplateAreas::None
1768 }
1769}
1770
1771pub type ZIndex = GenericZIndex<Integer>;
1773
1774pub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;
1776
1777impl Parse for AspectRatio {
1778 fn parse<'i, 't>(
1779 context: &ParserContext,
1780 input: &mut Parser<'i, 't>,
1781 ) -> Result<Self, ParseError<'i>> {
1782 use crate::values::generics::position::PreferredRatio;
1783 use crate::values::specified::Ratio;
1784
1785 let location = input.current_source_location();
1786 let mut auto = input.try_parse(|i| i.expect_ident_matching("auto"));
1787 let ratio = input.try_parse(|i| Ratio::parse(context, i));
1788 if auto.is_err() {
1789 auto = input.try_parse(|i| i.expect_ident_matching("auto"));
1790 }
1791
1792 if auto.is_err() && ratio.is_err() {
1793 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1794 }
1795
1796 Ok(AspectRatio {
1797 auto: auto.is_ok(),
1798 ratio: match ratio {
1799 Ok(ratio) => PreferredRatio::Ratio(ratio),
1800 Err(..) => PreferredRatio::None,
1801 },
1802 })
1803 }
1804}
1805
1806impl AspectRatio {
1807 pub fn from_mapped_ratio(w: f32, h: f32) -> Self {
1809 use crate::values::generics::position::PreferredRatio;
1810 use crate::values::generics::ratio::Ratio;
1811 AspectRatio {
1812 auto: true,
1813 ratio: PreferredRatio::Ratio(Ratio(
1814 NonNegativeNumber::new(w),
1815 NonNegativeNumber::new(h),
1816 )),
1817 }
1818 }
1819}
1820
1821pub type Inset = GenericInset<specified::Percentage, LengthPercentage>;
1823
1824impl Inset {
1825 #[inline]
1828 pub fn parse_quirky<'i, 't>(
1829 context: &ParserContext,
1830 input: &mut Parser<'i, 't>,
1831 allow_quirks: AllowQuirks,
1832 ) -> Result<Self, ParseError<'i>> {
1833 if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
1834 {
1835 return Ok(Self::LengthPercentage(l));
1836 }
1837 match input.try_parse(|i| i.expect_ident_matching("auto")) {
1838 Ok(_) => return Ok(Self::Auto),
1839 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
1840 return Err(e.into())
1841 },
1842 Err(_) => (),
1843 };
1844 Self::parse_anchor_functions_quirky(context, input, allow_quirks)
1845 }
1846
1847 fn parse_as_anchor_function_fallback<'i, 't>(
1848 context: &ParserContext,
1849 input: &mut Parser<'i, 't>,
1850 ) -> Result<Self, ParseError<'i>> {
1851 if let Ok(l) =
1852 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::No))
1853 {
1854 return Ok(Self::LengthPercentage(l));
1855 }
1856 Self::parse_anchor_functions_quirky(context, input, AllowQuirks::No)
1857 }
1858
1859 fn parse_anchor_functions_quirky<'i, 't>(
1860 context: &ParserContext,
1861 input: &mut Parser<'i, 't>,
1862 allow_quirks: AllowQuirks,
1863 ) -> Result<Self, ParseError<'i>> {
1864 debug_assert!(
1865 static_prefs::pref!("layout.css.anchor-positioning.enabled"),
1866 "How are we parsing with pref off?"
1867 );
1868 if let Ok(inner) = input.try_parse(|i| AnchorFunction::parse(context, i)) {
1869 return Ok(Self::AnchorFunction(Box::new(inner)));
1870 }
1871 if let Ok(inner) =
1872 input.try_parse(|i| GenericAnchorSizeFunction::<Inset>::parse(context, i))
1873 {
1874 return Ok(Self::AnchorSizeFunction(Box::new(inner)));
1875 }
1876 Ok(Self::AnchorContainingCalcFunction(input.try_parse(
1877 |i| LengthPercentage::parse_quirky_with_anchor_functions(context, i, allow_quirks),
1878 )?))
1879 }
1880}
1881
1882impl Parse for Inset {
1883 fn parse<'i, 't>(
1884 context: &ParserContext,
1885 input: &mut Parser<'i, 't>,
1886 ) -> Result<Self, ParseError<'i>> {
1887 Self::parse_quirky(context, input, AllowQuirks::No)
1888 }
1889}
1890
1891pub type AnchorFunction = GenericAnchorFunction<specified::Percentage, Inset>;
1893
1894impl Parse for AnchorFunction {
1895 fn parse<'i, 't>(
1896 context: &ParserContext,
1897 input: &mut Parser<'i, 't>,
1898 ) -> Result<Self, ParseError<'i>> {
1899 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
1900 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1901 }
1902 input.expect_function_matching("anchor")?;
1903 input.parse_nested_block(|i| {
1904 let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
1905 let side = GenericAnchorSide::parse(context, i)?;
1906 let target_element = if target_element.is_none() {
1907 i.try_parse(|i| DashedIdent::parse(context, i)).ok()
1908 } else {
1909 target_element
1910 };
1911 let fallback = i
1912 .try_parse(|i| {
1913 i.expect_comma()?;
1914 Inset::parse_as_anchor_function_fallback(context, i)
1915 })
1916 .ok();
1917 Ok(Self {
1918 target_element: target_element.unwrap_or_else(DashedIdent::empty),
1919 side,
1920 fallback: fallback.into(),
1921 })
1922 })
1923 }
1924}