1use crate::logical_geometry::{LogicalAxis, LogicalSide, PhysicalSide, WritingMode};
11use crate::parser::{Parse, ParserContext};
12use crate::selector_map::PrecomputedHashMap;
13use crate::str::HTML_SPACE_CHARACTERS;
14use crate::values::computed::LengthPercentage as ComputedLengthPercentage;
15use crate::values::computed::{Context, Percentage, ToComputedValue};
16use crate::values::generics::length::GenericAnchorSizeFunction;
17use crate::values::generics::position::Position as GenericPosition;
18use crate::values::generics::position::PositionComponent as GenericPositionComponent;
19use crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;
20use crate::values::generics::position::ZIndex as GenericZIndex;
21use crate::values::generics::position::{AspectRatio as GenericAspectRatio, GenericAnchorSide};
22use crate::values::generics::position::{GenericAnchorFunction, GenericInset};
23use crate::values::specified;
24use crate::values::specified::align::AlignFlags;
25use crate::values::specified::{AllowQuirks, Integer, LengthPercentage, NonNegativeNumber};
26use crate::values::DashedIdent;
27use crate::{Atom, Zero};
28use cssparser::Parser;
29use num_traits::FromPrimitive;
30use selectors::parser::SelectorParseErrorKind;
31use servo_arc::Arc;
32use smallvec::{smallvec, SmallVec};
33use std::collections::hash_map::Entry;
34use std::fmt::{self, Write};
35use style_traits::arc_slice::ArcSlice;
36use style_traits::values::specified::AllowedNumericType;
37use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
38use thin_vec::ThinVec;
39
40pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
42
43pub type PositionOrAuto = GenericPositionOrAuto<Position>;
45
46pub type HorizontalPosition = PositionComponent<HorizontalPositionKeyword>;
48
49pub type VerticalPosition = PositionComponent<VerticalPositionKeyword>;
51
52#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
54pub enum PositionComponent<S> {
55 Center,
57 Length(LengthPercentage),
59 Side(S, Option<LengthPercentage>),
61}
62
63#[derive(
65 Clone,
66 Copy,
67 Debug,
68 Eq,
69 Hash,
70 MallocSizeOf,
71 Parse,
72 PartialEq,
73 SpecifiedValueInfo,
74 ToComputedValue,
75 ToCss,
76 ToResolvedValue,
77 ToShmem,
78)]
79#[allow(missing_docs)]
80#[repr(u8)]
81pub enum HorizontalPositionKeyword {
82 Left,
83 Right,
84}
85
86#[derive(
88 Clone,
89 Copy,
90 Debug,
91 Eq,
92 Hash,
93 MallocSizeOf,
94 Parse,
95 PartialEq,
96 SpecifiedValueInfo,
97 ToComputedValue,
98 ToCss,
99 ToResolvedValue,
100 ToShmem,
101)]
102#[allow(missing_docs)]
103#[repr(u8)]
104pub enum VerticalPositionKeyword {
105 Top,
106 Bottom,
107}
108
109impl Parse for Position {
110 fn parse<'i, 't>(
111 context: &ParserContext,
112 input: &mut Parser<'i, 't>,
113 ) -> Result<Self, ParseError<'i>> {
114 let position = Self::parse_three_value_quirky(context, input, AllowQuirks::No)?;
115 if position.is_three_value_syntax() {
116 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
117 }
118 Ok(position)
119 }
120}
121
122impl Position {
123 pub fn parse_three_value_quirky<'i, 't>(
125 context: &ParserContext,
126 input: &mut Parser<'i, 't>,
127 allow_quirks: AllowQuirks,
128 ) -> Result<Self, ParseError<'i>> {
129 match input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) {
130 Ok(x_pos @ PositionComponent::Center) => {
131 if let Ok(y_pos) =
132 input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))
133 {
134 return Ok(Self::new(x_pos, y_pos));
135 }
136 let x_pos = input
137 .try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))
138 .unwrap_or(x_pos);
139 let y_pos = PositionComponent::Center;
140 return Ok(Self::new(x_pos, y_pos));
141 },
142 Ok(PositionComponent::Side(x_keyword, lp)) => {
143 if input
144 .try_parse(|i| i.expect_ident_matching("center"))
145 .is_ok()
146 {
147 let x_pos = PositionComponent::Side(x_keyword, lp);
148 let y_pos = PositionComponent::Center;
149 return Ok(Self::new(x_pos, y_pos));
150 }
151 if let Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) {
152 let y_lp = input
153 .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
154 .ok();
155 let x_pos = PositionComponent::Side(x_keyword, lp);
156 let y_pos = PositionComponent::Side(y_keyword, y_lp);
157 return Ok(Self::new(x_pos, y_pos));
158 }
159 let x_pos = PositionComponent::Side(x_keyword, None);
160 let y_pos = lp.map_or(PositionComponent::Center, PositionComponent::Length);
161 return Ok(Self::new(x_pos, y_pos));
162 },
163 Ok(x_pos @ PositionComponent::Length(_)) => {
164 if let Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) {
165 let y_pos = PositionComponent::Side(y_keyword, None);
166 return Ok(Self::new(x_pos, y_pos));
167 }
168 if let Ok(y_lp) =
169 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
170 {
171 let y_pos = PositionComponent::Length(y_lp);
172 return Ok(Self::new(x_pos, y_pos));
173 }
174 let y_pos = PositionComponent::Center;
175 let _ = input.try_parse(|i| i.expect_ident_matching("center"));
176 return Ok(Self::new(x_pos, y_pos));
177 },
178 Err(_) => {},
179 }
180 let y_keyword = VerticalPositionKeyword::parse(input)?;
181 let lp_and_x_pos: Result<_, ParseError> = input.try_parse(|i| {
182 let y_lp = i
183 .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
184 .ok();
185 if let Ok(x_keyword) = i.try_parse(HorizontalPositionKeyword::parse) {
186 let x_lp = i
187 .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
188 .ok();
189 let x_pos = PositionComponent::Side(x_keyword, x_lp);
190 return Ok((y_lp, x_pos));
191 };
192 i.expect_ident_matching("center")?;
193 let x_pos = PositionComponent::Center;
194 Ok((y_lp, x_pos))
195 });
196 if let Ok((y_lp, x_pos)) = lp_and_x_pos {
197 let y_pos = PositionComponent::Side(y_keyword, y_lp);
198 return Ok(Self::new(x_pos, y_pos));
199 }
200 let x_pos = PositionComponent::Center;
201 let y_pos = PositionComponent::Side(y_keyword, None);
202 Ok(Self::new(x_pos, y_pos))
203 }
204
205 #[inline]
207 pub fn center() -> Self {
208 Self::new(PositionComponent::Center, PositionComponent::Center)
209 }
210
211 #[inline]
213 fn is_three_value_syntax(&self) -> bool {
214 self.horizontal.component_count() != self.vertical.component_count()
215 }
216}
217
218impl ToCss for Position {
219 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
220 where
221 W: Write,
222 {
223 match (&self.horizontal, &self.vertical) {
224 (
225 x_pos @ &PositionComponent::Side(_, Some(_)),
226 &PositionComponent::Length(ref y_lp),
227 ) => {
228 x_pos.to_css(dest)?;
229 dest.write_str(" top ")?;
230 y_lp.to_css(dest)
231 },
232 (
233 &PositionComponent::Length(ref x_lp),
234 y_pos @ &PositionComponent::Side(_, Some(_)),
235 ) => {
236 dest.write_str("left ")?;
237 x_lp.to_css(dest)?;
238 dest.write_char(' ')?;
239 y_pos.to_css(dest)
240 },
241 (x_pos, y_pos) => {
242 x_pos.to_css(dest)?;
243 dest.write_char(' ')?;
244 y_pos.to_css(dest)
245 },
246 }
247 }
248}
249
250impl<S: Parse> Parse for PositionComponent<S> {
251 fn parse<'i, 't>(
252 context: &ParserContext,
253 input: &mut Parser<'i, 't>,
254 ) -> Result<Self, ParseError<'i>> {
255 Self::parse_quirky(context, input, AllowQuirks::No)
256 }
257}
258
259impl<S: Parse> PositionComponent<S> {
260 pub fn parse_quirky<'i, 't>(
262 context: &ParserContext,
263 input: &mut Parser<'i, 't>,
264 allow_quirks: AllowQuirks,
265 ) -> Result<Self, ParseError<'i>> {
266 if input
267 .try_parse(|i| i.expect_ident_matching("center"))
268 .is_ok()
269 {
270 return Ok(PositionComponent::Center);
271 }
272 if let Ok(lp) =
273 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
274 {
275 return Ok(PositionComponent::Length(lp));
276 }
277 let keyword = S::parse(context, input)?;
278 let lp = input
279 .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
280 .ok();
281 Ok(PositionComponent::Side(keyword, lp))
282 }
283}
284
285impl<S> GenericPositionComponent for PositionComponent<S> {
286 fn is_center(&self) -> bool {
287 match *self {
288 PositionComponent::Center => true,
289 PositionComponent::Length(LengthPercentage::Percentage(ref per)) => per.0 == 0.5,
290 PositionComponent::Side(_, Some(LengthPercentage::Percentage(ref per))) => per.0 == 0.5,
292 _ => false,
293 }
294 }
295}
296
297impl<S> PositionComponent<S> {
298 pub fn zero() -> Self {
300 PositionComponent::Length(LengthPercentage::Percentage(Percentage::zero()))
301 }
302
303 fn component_count(&self) -> usize {
305 match *self {
306 PositionComponent::Length(..) | PositionComponent::Center => 1,
307 PositionComponent::Side(_, ref lp) => {
308 if lp.is_some() {
309 2
310 } else {
311 1
312 }
313 },
314 }
315 }
316}
317
318impl<S: Side> ToComputedValue for PositionComponent<S> {
319 type ComputedValue = ComputedLengthPercentage;
320
321 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
322 match *self {
323 PositionComponent::Center => ComputedLengthPercentage::new_percent(Percentage(0.5)),
324 PositionComponent::Side(ref keyword, None) => {
325 let p = Percentage(if keyword.is_start() { 0. } else { 1. });
326 ComputedLengthPercentage::new_percent(p)
327 },
328 PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {
329 let length = length.to_computed_value(context);
330 ComputedLengthPercentage::hundred_percent_minus(length, AllowedNumericType::All)
332 },
333 PositionComponent::Side(_, Some(ref length))
334 | PositionComponent::Length(ref length) => length.to_computed_value(context),
335 }
336 }
337
338 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
339 PositionComponent::Length(ToComputedValue::from_computed_value(computed))
340 }
341}
342
343impl<S: Side> PositionComponent<S> {
344 pub fn initial_specified_value() -> Self {
346 PositionComponent::Side(S::start(), None)
347 }
348}
349
350#[repr(transparent)]
352#[derive(
353 Clone,
354 Debug,
355 MallocSizeOf,
356 PartialEq,
357 SpecifiedValueInfo,
358 ToComputedValue,
359 ToCss,
360 ToResolvedValue,
361 ToShmem,
362 ToTyped,
363)]
364#[css(comma)]
365pub struct AnchorName(
366 #[css(iterable, if_empty = "none")]
367 #[ignore_malloc_size_of = "Arc"]
368 pub crate::ArcSlice<DashedIdent>,
369);
370
371impl AnchorName {
372 pub fn none() -> Self {
374 Self(Default::default())
375 }
376
377 pub fn is_none(&self) -> bool {
379 self.0.is_empty()
380 }
381}
382
383impl Parse for AnchorName {
384 fn parse<'i, 't>(
385 context: &ParserContext,
386 input: &mut Parser<'i, 't>,
387 ) -> Result<Self, ParseError<'i>> {
388 let location = input.current_source_location();
389 let first = input.expect_ident()?;
390 if first.eq_ignore_ascii_case("none") {
391 return Ok(Self::none());
392 }
393 let mut idents: SmallVec<[DashedIdent; 4]> =
396 smallvec![DashedIdent::from_ident(location, first,)?];
397 while input.try_parse(|input| input.expect_comma()).is_ok() {
398 idents.push(DashedIdent::parse(context, input)?);
399 }
400 Ok(AnchorName(ArcSlice::from_iter(idents.drain(..))))
401 }
402}
403
404#[derive(
406 Clone,
407 Debug,
408 MallocSizeOf,
409 PartialEq,
410 SpecifiedValueInfo,
411 ToComputedValue,
412 ToCss,
413 ToResolvedValue,
414 ToShmem,
415 ToTyped,
416)]
417#[repr(u8)]
418pub enum AnchorScope {
419 None,
421 All,
423 #[css(comma)]
425 Idents(
426 #[css(iterable)]
427 #[ignore_malloc_size_of = "Arc"]
428 crate::ArcSlice<DashedIdent>,
429 ),
430}
431
432impl AnchorScope {
433 pub fn none() -> Self {
435 Self::None
436 }
437
438 pub fn is_none(&self) -> bool {
440 *self == Self::None
441 }
442}
443
444impl Parse for AnchorScope {
445 fn parse<'i, 't>(
446 context: &ParserContext,
447 input: &mut Parser<'i, 't>,
448 ) -> Result<Self, ParseError<'i>> {
449 let location = input.current_source_location();
450 let first = input.expect_ident()?;
451 if first.eq_ignore_ascii_case("none") {
452 return Ok(Self::None);
453 }
454 if first.eq_ignore_ascii_case("all") {
455 return Ok(Self::All);
456 }
457 let mut idents: SmallVec<[DashedIdent; 8]> =
460 smallvec![DashedIdent::from_ident(location, first,)?];
461 while input.try_parse(|input| input.expect_comma()).is_ok() {
462 idents.push(DashedIdent::parse(context, input)?);
463 }
464 Ok(AnchorScope::Idents(ArcSlice::from_iter(idents.drain(..))))
465 }
466}
467
468#[derive(
470 Clone,
471 Debug,
472 MallocSizeOf,
473 Parse,
474 PartialEq,
475 SpecifiedValueInfo,
476 ToComputedValue,
477 ToCss,
478 ToResolvedValue,
479 ToShmem,
480 ToTyped,
481)]
482#[repr(u8)]
483pub enum PositionAnchor {
484 None,
486 Auto,
488 Ident(DashedIdent),
490}
491
492#[derive(
493 Clone,
494 Copy,
495 Debug,
496 Eq,
497 MallocSizeOf,
498 Parse,
499 PartialEq,
500 Serialize,
501 SpecifiedValueInfo,
502 ToComputedValue,
503 ToCss,
504 ToResolvedValue,
505 ToShmem,
506)]
507#[repr(u8)]
508pub enum PositionTryFallbacksTryTacticKeyword {
510 FlipBlock,
512 FlipInline,
514 FlipStart,
516 FlipX,
518 FlipY,
520}
521
522#[derive(
523 Clone,
524 Debug,
525 Default,
526 Eq,
527 MallocSizeOf,
528 PartialEq,
529 SpecifiedValueInfo,
530 ToComputedValue,
531 ToCss,
532 ToResolvedValue,
533 ToShmem,
534)]
535#[repr(transparent)]
536pub struct PositionTryFallbacksTryTactic(
541 #[css(iterable)] pub ThinVec<PositionTryFallbacksTryTacticKeyword>,
542);
543
544impl Parse for PositionTryFallbacksTryTactic {
545 fn parse<'i, 't>(
546 _context: &ParserContext,
547 input: &mut Parser<'i, 't>,
548 ) -> Result<Self, ParseError<'i>> {
549 let mut result = ThinVec::with_capacity(5);
550 for _ in 0..5 {
552 if let Ok(kw) = input.try_parse(PositionTryFallbacksTryTacticKeyword::parse) {
553 if result.contains(&kw) {
554 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
555 }
556 result.push(kw);
557 } else {
558 break;
559 }
560 }
561 if result.is_empty() {
562 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
563 }
564 Ok(Self(result))
565 }
566}
567
568impl PositionTryFallbacksTryTactic {
569 #[inline]
571 pub fn is_empty(&self) -> bool {
572 self.0.is_empty()
573 }
574
575 #[inline]
577 pub fn iter(&self) -> impl Iterator<Item = &PositionTryFallbacksTryTacticKeyword> {
578 self.0.iter()
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 ToTyped,
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 ToTyped,
731)]
732#[repr(u8)]
733pub enum PositionTryOrder {
734 #[default]
735 Normal,
737 MostWidth,
739 MostHeight,
741 MostBlockSize,
743 MostInlineSize,
745}
746
747impl PositionTryOrder {
748 #[inline]
749 pub fn normal() -> Self {
751 Self::Normal
752 }
753
754 pub fn is_normal(&self) -> bool {
756 *self == Self::Normal
757 }
758}
759
760#[derive(
761 Clone,
762 Copy,
763 Debug,
764 Eq,
765 MallocSizeOf,
766 Parse,
767 PartialEq,
768 Serialize,
769 SpecifiedValueInfo,
770 ToComputedValue,
771 ToCss,
772 ToResolvedValue,
773 ToShmem,
774 ToTyped,
775)]
776#[css(bitflags(single = "always", mixed = "anchors-valid,anchors-visible,no-overflow"))]
777#[repr(C)]
778pub struct PositionVisibility(u8);
780bitflags! {
781 impl PositionVisibility: u8 {
782 const ALWAYS = 0;
784 const ANCHORS_VALID = 1 << 0;
786 const ANCHORS_VISIBLE = 1 << 1;
788 const NO_OVERFLOW = 1 << 2;
790 }
791}
792
793impl Default for PositionVisibility {
794 fn default() -> Self {
795 Self::ALWAYS
796 }
797}
798
799impl PositionVisibility {
800 #[inline]
801 pub fn always() -> Self {
803 Self::ALWAYS
804 }
805}
806
807#[repr(u8)]
810#[derive(Clone, Copy, Debug, Eq, PartialEq)]
811pub enum PositionAreaType {
812 Physical,
814 Logical,
816 SelfLogical,
818 Inferred,
820 SelfInferred,
822 Common,
824 None,
826}
827
828#[repr(u8)]
835#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)]
836#[allow(missing_docs)]
837pub enum PositionAreaAxis {
838 Horizontal = 0b000,
839 Vertical = 0b001,
840
841 X = 0b010,
842 Y = 0b011,
843
844 Block = 0b110,
845 Inline = 0b111,
846
847 Inferred = 0b100,
848 None = 0b101,
849}
850
851impl PositionAreaAxis {
852 pub fn is_physical(self) -> bool {
854 (self as u8 & 0b100) == 0
855 }
856
857 fn is_flow_relative_direction(self) -> bool {
859 self == Self::Inferred || (self as u8 & 0b10) != 0
860 }
861
862 fn is_canonically_first(self) -> bool {
864 self != Self::Inferred && (self as u8) & 1 == 0
865 }
866
867 #[allow(unused)]
868 fn flip(self) -> Self {
869 if matches!(self, Self::Inferred | Self::None) {
870 return self;
871 }
872 Self::from_u8(self as u8 ^ 1u8).unwrap()
873 }
874
875 fn to_logical(self, wm: WritingMode, inferred: LogicalAxis) -> Option<LogicalAxis> {
876 Some(match self {
877 PositionAreaAxis::Horizontal | PositionAreaAxis::X => {
878 if wm.is_vertical() {
879 LogicalAxis::Block
880 } else {
881 LogicalAxis::Inline
882 }
883 },
884 PositionAreaAxis::Vertical | PositionAreaAxis::Y => {
885 if wm.is_vertical() {
886 LogicalAxis::Inline
887 } else {
888 LogicalAxis::Block
889 }
890 },
891 PositionAreaAxis::Block => LogicalAxis::Block,
892 PositionAreaAxis::Inline => LogicalAxis::Inline,
893 PositionAreaAxis::Inferred => inferred,
894 PositionAreaAxis::None => return None,
895 })
896 }
897}
898
899#[repr(u8)]
902#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)]
903pub enum PositionAreaTrack {
904 Start = 0b001,
906 SpanStart = 0b011,
908 End = 0b100,
910 SpanEnd = 0b110,
912 Center = 0b010,
914 SpanAll = 0b111,
916}
917
918impl PositionAreaTrack {
919 fn flip(self) -> Self {
920 match self {
921 Self::Start => Self::End,
922 Self::SpanStart => Self::SpanEnd,
923 Self::End => Self::Start,
924 Self::SpanEnd => Self::SpanStart,
925 Self::Center | Self::SpanAll => self,
926 }
927 }
928
929 fn start(self) -> bool {
930 self as u8 & 1 != 0
931 }
932}
933
934pub const AXIS_SHIFT: usize = 3;
936pub const AXIS_MASK: u8 = 0b111u8 << AXIS_SHIFT;
938pub const TRACK_MASK: u8 = 0b111u8;
940pub const SELF_WM: u8 = 1u8 << 6;
942
943#[derive(
944 Clone,
945 Copy,
946 Debug,
947 Default,
948 Eq,
949 MallocSizeOf,
950 Parse,
951 PartialEq,
952 SpecifiedValueInfo,
953 ToComputedValue,
954 ToCss,
955 ToResolvedValue,
956 ToShmem,
957 FromPrimitive,
958)]
959#[allow(missing_docs)]
960#[repr(u8)]
961pub enum PositionAreaKeyword {
966 #[default]
967 None = (PositionAreaAxis::None as u8) << AXIS_SHIFT,
968
969 Center = ((PositionAreaAxis::None as u8) << AXIS_SHIFT) | PositionAreaTrack::Center as u8,
971 SpanAll = ((PositionAreaAxis::None as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanAll as u8,
972
973 Start = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
975 End = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
976 SpanStart =
977 ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
978 SpanEnd = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
979
980 Left = ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
982 Right = ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
983 Top = ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
984 Bottom = ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
985
986 XStart = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
988 XEnd = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
989 YStart = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
990 YEnd = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
991
992 BlockStart = ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
994 BlockEnd = ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
995 InlineStart = ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
996 InlineEnd = ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
997
998 SpanLeft =
1000 ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1001 SpanRight =
1002 ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1003 SpanTop =
1004 ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1005 SpanBottom =
1006 ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1007
1008 SpanXStart = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1010 SpanXEnd = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1011 SpanYStart = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1012 SpanYEnd = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1013
1014 SpanBlockStart =
1016 ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1017 SpanBlockEnd =
1018 ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1019 SpanInlineStart =
1020 ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1021 SpanInlineEnd =
1022 ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1023
1024 SelfStart = SELF_WM | (Self::Start as u8),
1026 SelfEnd = SELF_WM | (Self::End as u8),
1027 SpanSelfStart = SELF_WM | (Self::SpanStart as u8),
1028 SpanSelfEnd = SELF_WM | (Self::SpanEnd as u8),
1029
1030 SelfXStart = SELF_WM | (Self::XStart as u8),
1031 SelfXEnd = SELF_WM | (Self::XEnd as u8),
1032 SelfYStart = SELF_WM | (Self::YStart as u8),
1033 SelfYEnd = SELF_WM | (Self::YEnd as u8),
1034 SelfBlockStart = SELF_WM | (Self::BlockStart as u8),
1035 SelfBlockEnd = SELF_WM | (Self::BlockEnd as u8),
1036 SelfInlineStart = SELF_WM | (Self::InlineStart as u8),
1037 SelfInlineEnd = SELF_WM | (Self::InlineEnd as u8),
1038
1039 SpanSelfXStart = SELF_WM | (Self::SpanXStart as u8),
1040 SpanSelfXEnd = SELF_WM | (Self::SpanXEnd as u8),
1041 SpanSelfYStart = SELF_WM | (Self::SpanYStart as u8),
1042 SpanSelfYEnd = SELF_WM | (Self::SpanYEnd as u8),
1043 SpanSelfBlockStart = SELF_WM | (Self::SpanBlockStart as u8),
1044 SpanSelfBlockEnd = SELF_WM | (Self::SpanBlockEnd as u8),
1045 SpanSelfInlineStart = SELF_WM | (Self::SpanInlineStart as u8),
1046 SpanSelfInlineEnd = SELF_WM | (Self::SpanInlineEnd as u8),
1047}
1048
1049impl PositionAreaKeyword {
1050 #[inline]
1052 pub fn none() -> Self {
1053 Self::None
1054 }
1055
1056 pub fn is_none(&self) -> bool {
1058 *self == Self::None
1059 }
1060
1061 pub fn self_wm(self) -> bool {
1063 (self as u8 & SELF_WM) != 0
1064 }
1065
1066 pub fn axis(self) -> PositionAreaAxis {
1068 PositionAreaAxis::from_u8((self as u8 >> AXIS_SHIFT) & 0b111).unwrap()
1069 }
1070
1071 pub fn with_axis(self, axis: PositionAreaAxis) -> Self {
1073 Self::from_u8(((self as u8) & !AXIS_MASK) | ((axis as u8) << AXIS_SHIFT)).unwrap()
1074 }
1075
1076 pub fn with_inferred_axis(self, axis: PositionAreaAxis) -> Self {
1078 if self.axis() == PositionAreaAxis::Inferred {
1079 self.with_axis(axis)
1080 } else {
1081 self
1082 }
1083 }
1084
1085 pub fn track(self) -> Option<PositionAreaTrack> {
1087 let result = PositionAreaTrack::from_u8(self as u8 & TRACK_MASK);
1088 debug_assert_eq!(
1089 result.is_none(),
1090 self.is_none(),
1091 "Only the none keyword has no track"
1092 );
1093 result
1094 }
1095
1096 fn group_type(self) -> PositionAreaType {
1097 let axis = self.axis();
1098 if axis == PositionAreaAxis::None {
1099 if self.is_none() {
1100 return PositionAreaType::None;
1101 }
1102 return PositionAreaType::Common;
1103 }
1104 if axis == PositionAreaAxis::Inferred {
1105 return if self.self_wm() {
1106 PositionAreaType::SelfInferred
1107 } else {
1108 PositionAreaType::Inferred
1109 };
1110 }
1111 if axis.is_physical() {
1112 return PositionAreaType::Physical;
1113 }
1114 if self.self_wm() {
1115 PositionAreaType::SelfLogical
1116 } else {
1117 PositionAreaType::Logical
1118 }
1119 }
1120
1121 fn to_physical(
1122 self,
1123 cb_wm: WritingMode,
1124 self_wm: WritingMode,
1125 inferred_axis: LogicalAxis,
1126 ) -> Self {
1127 let wm = if self.self_wm() { self_wm } else { cb_wm };
1128 let axis = self.axis();
1129 if !axis.is_flow_relative_direction() {
1130 return self;
1131 }
1132 let Some(logical_axis) = axis.to_logical(wm, inferred_axis) else {
1133 return self;
1134 };
1135 let Some(track) = self.track() else {
1136 debug_assert!(false, "How did we end up with no track here? {self:?}");
1137 return self;
1138 };
1139 let start = track.start();
1140 let logical_side = match logical_axis {
1141 LogicalAxis::Block => {
1142 if start {
1143 LogicalSide::BlockStart
1144 } else {
1145 LogicalSide::BlockEnd
1146 }
1147 },
1148 LogicalAxis::Inline => {
1149 if start {
1150 LogicalSide::InlineStart
1151 } else {
1152 LogicalSide::InlineEnd
1153 }
1154 },
1155 };
1156 let physical_side = logical_side.to_physical(wm);
1157 let physical_start = matches!(physical_side, PhysicalSide::Top | PhysicalSide::Left);
1158 let new_track = if physical_start != start {
1159 track.flip()
1160 } else {
1161 track
1162 };
1163 let new_axis = if matches!(physical_side, PhysicalSide::Top | PhysicalSide::Bottom) {
1164 PositionAreaAxis::Vertical
1165 } else {
1166 PositionAreaAxis::Horizontal
1167 };
1168 Self::from_u8(new_track as u8 | ((new_axis as u8) << AXIS_SHIFT)).unwrap()
1169 }
1170
1171 fn flip_track(self) -> Self {
1172 let Some(old_track) = self.track() else {
1173 return self;
1174 };
1175 let new_track = old_track.flip();
1176 Self::from_u8((self as u8 & !TRACK_MASK) | new_track as u8).unwrap()
1177 }
1178
1179 pub fn to_self_alignment(self) -> Option<AlignFlags> {
1184 let track = self.track()?;
1185 Some(match track {
1186 PositionAreaTrack::Center => AlignFlags::CENTER,
1188 PositionAreaTrack::SpanAll => AlignFlags::ANCHOR_CENTER,
1190 _ => {
1193 if track.start() {
1194 AlignFlags::END
1195 } else {
1196 AlignFlags::START
1197 }
1198 },
1199 })
1200 }
1201}
1202
1203#[derive(
1204 Clone,
1205 Copy,
1206 Debug,
1207 Eq,
1208 MallocSizeOf,
1209 PartialEq,
1210 SpecifiedValueInfo,
1211 ToCss,
1212 ToResolvedValue,
1213 ToShmem,
1214 ToTyped,
1215)]
1216#[repr(C)]
1217pub struct PositionArea {
1219 pub first: PositionAreaKeyword,
1221 #[css(skip_if = "PositionAreaKeyword::is_none")]
1223 pub second: PositionAreaKeyword,
1224}
1225
1226impl PositionArea {
1227 #[inline]
1229 pub fn none() -> Self {
1230 Self {
1231 first: PositionAreaKeyword::None,
1232 second: PositionAreaKeyword::None,
1233 }
1234 }
1235
1236 #[inline]
1238 pub fn is_none(&self) -> bool {
1239 self.first.is_none()
1240 }
1241
1242 pub fn parse_except_none<'i, 't>(
1244 context: &ParserContext,
1245 input: &mut Parser<'i, 't>,
1246 ) -> Result<Self, ParseError<'i>> {
1247 Self::parse_internal(context, input, false)
1248 }
1249
1250 pub fn get_type(&self) -> PositionAreaType {
1252 let first = self.first.group_type();
1253 let second = self.second.group_type();
1254 if matches!(second, PositionAreaType::None | PositionAreaType::Common) {
1255 return first;
1256 }
1257 if first == PositionAreaType::Common {
1258 return second;
1259 }
1260 if first != second {
1261 return PositionAreaType::None;
1262 }
1263 let first_axis = self.first.axis();
1264 if first_axis != PositionAreaAxis::Inferred
1265 && first_axis.is_canonically_first() == self.second.axis().is_canonically_first()
1266 {
1267 return PositionAreaType::None;
1268 }
1269 first
1270 }
1271
1272 fn parse_internal<'i, 't>(
1273 _: &ParserContext,
1274 input: &mut Parser<'i, 't>,
1275 allow_none: bool,
1276 ) -> Result<Self, ParseError<'i>> {
1277 let mut location = input.current_source_location();
1278 let mut first = PositionAreaKeyword::parse(input)?;
1279 if first.is_none() {
1280 if allow_none {
1281 return Ok(Self::none());
1282 }
1283 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1284 }
1285
1286 location = input.current_source_location();
1287 let second = input.try_parse(PositionAreaKeyword::parse);
1288 if let Ok(PositionAreaKeyword::None) = second {
1289 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1291 }
1292 let mut second = second.unwrap_or(PositionAreaKeyword::None);
1293 if second.is_none() {
1294 return Ok(Self { first, second });
1300 }
1301
1302 let pair_type = Self { first, second }.get_type();
1303 if pair_type == PositionAreaType::None {
1304 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1306 }
1307 if matches!(
1310 pair_type,
1311 PositionAreaType::Physical | PositionAreaType::Logical | PositionAreaType::SelfLogical
1312 ) {
1313 if second == PositionAreaKeyword::SpanAll {
1314 second = PositionAreaKeyword::None;
1317 } else if first == PositionAreaKeyword::SpanAll {
1318 first = second;
1319 second = PositionAreaKeyword::None;
1320 }
1321 }
1322 if first == second {
1323 second = PositionAreaKeyword::None;
1324 }
1325 let mut result = Self { first, second };
1326 result.canonicalize_order();
1327 Ok(result)
1328 }
1329
1330 fn canonicalize_order(&mut self) {
1331 let first_axis = self.first.axis();
1332 if first_axis.is_canonically_first() || self.second.is_none() {
1333 return;
1334 }
1335 let second_axis = self.second.axis();
1336 if first_axis == second_axis {
1337 return;
1339 }
1340 if second_axis.is_canonically_first()
1341 || (second_axis == PositionAreaAxis::None && first_axis != PositionAreaAxis::Inferred)
1342 {
1343 std::mem::swap(&mut self.first, &mut self.second);
1344 }
1345 }
1346
1347 fn make_missing_second_explicit(&mut self) {
1348 if !self.second.is_none() {
1349 return;
1350 }
1351 let axis = self.first.axis();
1352 if matches!(axis, PositionAreaAxis::Inferred | PositionAreaAxis::None) {
1353 self.second = self.first;
1354 return;
1355 }
1356 self.second = PositionAreaKeyword::SpanAll;
1357 if !axis.is_canonically_first() {
1358 std::mem::swap(&mut self.first, &mut self.second);
1359 }
1360 }
1361
1362 pub fn to_physical(mut self, cb_wm: WritingMode, self_wm: WritingMode) -> Self {
1364 self.make_missing_second_explicit();
1365 self.first = self.first.to_physical(cb_wm, self_wm, LogicalAxis::Block);
1366 self.second = self.second.to_physical(cb_wm, self_wm, LogicalAxis::Inline);
1367 self.canonicalize_order();
1368 self
1369 }
1370
1371 fn flip_logical_axis(&mut self, wm: WritingMode, axis: LogicalAxis) {
1372 if self.first.axis().to_logical(wm, LogicalAxis::Block) == Some(axis) {
1373 self.first = self.first.flip_track();
1374 } else {
1375 self.second = self.second.flip_track();
1376 }
1377 }
1378
1379 fn flip_start(&mut self) {
1380 self.first = self.first.with_axis(self.first.axis().flip());
1381 self.second = self.second.with_axis(self.second.axis().flip());
1382 }
1383
1384 pub fn with_tactic(
1386 mut self,
1387 wm: WritingMode,
1388 tactic: PositionTryFallbacksTryTacticKeyword,
1389 ) -> Self {
1390 self.make_missing_second_explicit();
1391 let axis_to_flip = match tactic {
1392 PositionTryFallbacksTryTacticKeyword::FlipStart => {
1393 self.flip_start();
1394 return self;
1395 },
1396 PositionTryFallbacksTryTacticKeyword::FlipBlock => LogicalAxis::Block,
1397 PositionTryFallbacksTryTacticKeyword::FlipInline => LogicalAxis::Inline,
1398 PositionTryFallbacksTryTacticKeyword::FlipX => {
1399 if wm.is_horizontal() {
1400 LogicalAxis::Inline
1401 } else {
1402 LogicalAxis::Block
1403 }
1404 },
1405 PositionTryFallbacksTryTacticKeyword::FlipY => {
1406 if wm.is_vertical() {
1407 LogicalAxis::Inline
1408 } else {
1409 LogicalAxis::Block
1410 }
1411 },
1412 };
1413 self.flip_logical_axis(wm, axis_to_flip);
1414 self
1415 }
1416}
1417
1418impl Parse for PositionArea {
1419 fn parse<'i, 't>(
1420 context: &ParserContext,
1421 input: &mut Parser<'i, 't>,
1422 ) -> Result<Self, ParseError<'i>> {
1423 Self::parse_internal(context, input, true)
1424 }
1425}
1426
1427pub trait Side {
1429 fn start() -> Self;
1431
1432 fn is_start(&self) -> bool;
1434}
1435
1436impl Side for HorizontalPositionKeyword {
1437 #[inline]
1438 fn start() -> Self {
1439 HorizontalPositionKeyword::Left
1440 }
1441
1442 #[inline]
1443 fn is_start(&self) -> bool {
1444 *self == Self::start()
1445 }
1446}
1447
1448impl Side for VerticalPositionKeyword {
1449 #[inline]
1450 fn start() -> Self {
1451 VerticalPositionKeyword::Top
1452 }
1453
1454 #[inline]
1455 fn is_start(&self) -> bool {
1456 *self == Self::start()
1457 }
1458}
1459
1460#[derive(
1464 Clone,
1465 Copy,
1466 Debug,
1467 Eq,
1468 MallocSizeOf,
1469 Parse,
1470 PartialEq,
1471 SpecifiedValueInfo,
1472 ToComputedValue,
1473 ToResolvedValue,
1474 ToShmem,
1475 ToTyped,
1476)]
1477#[css(bitflags(
1478 mixed = "row,column,dense",
1479 validate_mixed = "Self::validate_and_simplify"
1480))]
1481#[repr(C)]
1482pub struct GridAutoFlow(u8);
1483bitflags! {
1484 impl GridAutoFlow: u8 {
1485 const ROW = 1 << 0;
1487 const COLUMN = 1 << 1;
1489 const DENSE = 1 << 2;
1491 }
1492}
1493
1494impl GridAutoFlow {
1495 fn validate_and_simplify(&mut self) -> bool {
1497 if self.contains(Self::ROW | Self::COLUMN) {
1498 return false;
1500 }
1501 if *self == Self::DENSE {
1502 self.insert(Self::ROW);
1504 }
1505 true
1506 }
1507}
1508
1509impl ToCss for GridAutoFlow {
1510 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1511 where
1512 W: Write,
1513 {
1514 let dense = self.intersects(Self::DENSE);
1515 if self.intersects(Self::ROW) {
1516 return if dense {
1517 dest.write_str("dense")
1518 } else {
1519 dest.write_str("row")
1520 };
1521 }
1522 debug_assert!(self.intersects(Self::COLUMN));
1523 if dense {
1524 dest.write_str("column dense")
1525 } else {
1526 dest.write_str("column")
1527 }
1528 }
1529}
1530
1531#[repr(u8)]
1532#[derive(
1533 Clone,
1534 Copy,
1535 Debug,
1536 Eq,
1537 MallocSizeOf,
1538 PartialEq,
1539 SpecifiedValueInfo,
1540 ToComputedValue,
1541 ToCss,
1542 ToResolvedValue,
1543 ToShmem,
1544)]
1545pub enum MasonryPlacement {
1547 Pack,
1549 Next,
1551}
1552
1553#[repr(u8)]
1554#[derive(
1555 Clone,
1556 Copy,
1557 Debug,
1558 Eq,
1559 MallocSizeOf,
1560 PartialEq,
1561 SpecifiedValueInfo,
1562 ToComputedValue,
1563 ToCss,
1564 ToResolvedValue,
1565 ToShmem,
1566)]
1567pub enum MasonryItemOrder {
1569 DefiniteFirst,
1571 Ordered,
1573}
1574
1575#[derive(
1576 Clone,
1577 Copy,
1578 Debug,
1579 Eq,
1580 MallocSizeOf,
1581 PartialEq,
1582 SpecifiedValueInfo,
1583 ToComputedValue,
1584 ToCss,
1585 ToResolvedValue,
1586 ToShmem,
1587 ToTyped,
1588)]
1589#[repr(C)]
1590pub struct MasonryAutoFlow {
1593 #[css(contextual_skip_if = "is_pack_with_non_default_order")]
1595 pub placement: MasonryPlacement,
1596 #[css(skip_if = "is_item_order_definite_first")]
1598 pub order: MasonryItemOrder,
1599}
1600
1601#[inline]
1602fn is_pack_with_non_default_order(placement: &MasonryPlacement, order: &MasonryItemOrder) -> bool {
1603 *placement == MasonryPlacement::Pack && *order != MasonryItemOrder::DefiniteFirst
1604}
1605
1606#[inline]
1607fn is_item_order_definite_first(order: &MasonryItemOrder) -> bool {
1608 *order == MasonryItemOrder::DefiniteFirst
1609}
1610
1611impl MasonryAutoFlow {
1612 #[inline]
1613 pub fn initial() -> MasonryAutoFlow {
1615 MasonryAutoFlow {
1616 placement: MasonryPlacement::Pack,
1617 order: MasonryItemOrder::DefiniteFirst,
1618 }
1619 }
1620}
1621
1622impl Parse for MasonryAutoFlow {
1623 fn parse<'i, 't>(
1625 _context: &ParserContext,
1626 input: &mut Parser<'i, 't>,
1627 ) -> Result<MasonryAutoFlow, ParseError<'i>> {
1628 let mut value = MasonryAutoFlow::initial();
1629 let mut got_placement = false;
1630 let mut got_order = false;
1631 while !input.is_exhausted() {
1632 let location = input.current_source_location();
1633 let ident = input.expect_ident()?;
1634 let success = match_ignore_ascii_case! { &ident,
1635 "pack" if !got_placement => {
1636 got_placement = true;
1637 true
1638 },
1639 "next" if !got_placement => {
1640 value.placement = MasonryPlacement::Next;
1641 got_placement = true;
1642 true
1643 },
1644 "definite-first" if !got_order => {
1645 got_order = true;
1646 true
1647 },
1648 "ordered" if !got_order => {
1649 value.order = MasonryItemOrder::Ordered;
1650 got_order = true;
1651 true
1652 },
1653 _ => false
1654 };
1655 if !success {
1656 return Err(location
1657 .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
1658 }
1659 }
1660
1661 if got_placement || got_order {
1662 Ok(value)
1663 } else {
1664 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1665 }
1666 }
1667}
1668
1669#[derive(
1670 Clone,
1671 Debug,
1672 MallocSizeOf,
1673 PartialEq,
1674 SpecifiedValueInfo,
1675 ToComputedValue,
1676 ToCss,
1677 ToResolvedValue,
1678 ToShmem,
1679)]
1680#[repr(C)]
1681pub struct TemplateAreas {
1683 #[css(skip)]
1685 pub areas: crate::OwnedSlice<NamedArea>,
1686 #[css(iterable)]
1691 pub strings: crate::OwnedSlice<crate::OwnedStr>,
1692 #[css(skip)]
1694 pub width: u32,
1695}
1696
1697#[derive(Default)]
1699pub struct TemplateAreasParser {
1700 areas: Vec<NamedArea>,
1701 area_indices: PrecomputedHashMap<Atom, usize>,
1702 strings: Vec<crate::OwnedStr>,
1703 width: u32,
1704 row: u32,
1705}
1706
1707impl TemplateAreasParser {
1708 pub fn try_parse_string<'i>(
1710 &mut self,
1711 input: &mut Parser<'i, '_>,
1712 ) -> Result<(), ParseError<'i>> {
1713 input.try_parse(|input| {
1714 self.parse_string(input.expect_string()?)
1715 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1716 })
1717 }
1718
1719 fn parse_string(&mut self, string: &str) -> Result<(), ()> {
1721 self.row += 1;
1722 let mut simplified_string = String::new();
1723 let mut current_area_index: Option<usize> = None;
1724 let mut column = 0u32;
1725 for token in TemplateAreasTokenizer(string) {
1726 column += 1;
1727 if column > 1 {
1728 simplified_string.push(' ');
1729 }
1730 let name = if let Some(token) = token? {
1731 simplified_string.push_str(token);
1732 Atom::from(token)
1733 } else {
1734 if let Some(index) = current_area_index.take() {
1735 if self.areas[index].columns.end != column {
1736 return Err(());
1737 }
1738 }
1739 simplified_string.push('.');
1740 continue;
1741 };
1742 if let Some(index) = current_area_index {
1743 if self.areas[index].name == name {
1744 if self.areas[index].rows.start == self.row {
1745 self.areas[index].columns.end += 1;
1746 }
1747 continue;
1748 }
1749 if self.areas[index].columns.end != column {
1750 return Err(());
1751 }
1752 }
1753 match self.area_indices.entry(name) {
1754 Entry::Occupied(ref e) => {
1755 let index = *e.get();
1756 if self.areas[index].columns.start != column
1757 || self.areas[index].rows.end != self.row
1758 {
1759 return Err(());
1760 }
1761 self.areas[index].rows.end += 1;
1762 current_area_index = Some(index);
1763 },
1764 Entry::Vacant(v) => {
1765 let index = self.areas.len();
1766 let name = v.key().clone();
1767 v.insert(index);
1768 self.areas.push(NamedArea {
1769 name,
1770 columns: UnsignedRange {
1771 start: column,
1772 end: column + 1,
1773 },
1774 rows: UnsignedRange {
1775 start: self.row,
1776 end: self.row + 1,
1777 },
1778 });
1779 current_area_index = Some(index);
1780 },
1781 }
1782 }
1783 if column == 0 {
1784 return Err(());
1787 }
1788 if let Some(index) = current_area_index {
1789 if self.areas[index].columns.end != column + 1 {
1790 debug_assert_ne!(self.areas[index].rows.start, self.row);
1791 return Err(());
1792 }
1793 }
1794 if self.row == 1 {
1795 self.width = column;
1796 } else if self.width != column {
1797 return Err(());
1798 }
1799
1800 self.strings.push(simplified_string.into());
1801 Ok(())
1802 }
1803
1804 pub fn finish(self) -> Result<TemplateAreas, ()> {
1806 if self.strings.is_empty() {
1807 return Err(());
1808 }
1809 Ok(TemplateAreas {
1810 areas: self.areas.into(),
1811 strings: self.strings.into(),
1812 width: self.width,
1813 })
1814 }
1815}
1816
1817impl TemplateAreas {
1818 fn parse_internal(input: &mut Parser) -> Result<Self, ()> {
1819 let mut parser = TemplateAreasParser::default();
1820 while parser.try_parse_string(input).is_ok() {}
1821 parser.finish()
1822 }
1823}
1824
1825impl Parse for TemplateAreas {
1826 fn parse<'i, 't>(
1827 _: &ParserContext,
1828 input: &mut Parser<'i, 't>,
1829 ) -> Result<Self, ParseError<'i>> {
1830 Self::parse_internal(input)
1831 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1832 }
1833}
1834
1835#[derive(
1837 Clone,
1838 Debug,
1839 MallocSizeOf,
1840 PartialEq,
1841 SpecifiedValueInfo,
1842 ToComputedValue,
1843 ToCss,
1844 ToResolvedValue,
1845 ToShmem,
1846)]
1847#[repr(transparent)]
1848pub struct TemplateAreasArc(#[ignore_malloc_size_of = "Arc"] pub Arc<TemplateAreas>);
1849
1850impl Parse for TemplateAreasArc {
1851 fn parse<'i, 't>(
1852 context: &ParserContext,
1853 input: &mut Parser<'i, 't>,
1854 ) -> Result<Self, ParseError<'i>> {
1855 let parsed = TemplateAreas::parse(context, input)?;
1856 Ok(TemplateAreasArc(Arc::new(parsed)))
1857 }
1858}
1859
1860#[repr(C)]
1863#[derive(
1864 Clone,
1865 Debug,
1866 MallocSizeOf,
1867 PartialEq,
1868 SpecifiedValueInfo,
1869 ToComputedValue,
1870 ToResolvedValue,
1871 ToShmem,
1872)]
1873pub struct UnsignedRange {
1874 pub start: u32,
1876 pub end: u32,
1878}
1879
1880#[derive(
1881 Clone,
1882 Debug,
1883 MallocSizeOf,
1884 PartialEq,
1885 SpecifiedValueInfo,
1886 ToComputedValue,
1887 ToResolvedValue,
1888 ToShmem,
1889)]
1890#[repr(C)]
1891pub struct NamedArea {
1894 pub name: Atom,
1896 pub rows: UnsignedRange,
1898 pub columns: UnsignedRange,
1900}
1901
1902struct TemplateAreasTokenizer<'a>(&'a str);
1905
1906impl<'a> Iterator for TemplateAreasTokenizer<'a> {
1907 type Item = Result<Option<&'a str>, ()>;
1908
1909 fn next(&mut self) -> Option<Self::Item> {
1910 let rest = self.0.trim_start_matches(HTML_SPACE_CHARACTERS);
1911 if rest.is_empty() {
1912 return None;
1913 }
1914 if rest.starts_with('.') {
1915 self.0 = &rest[rest.find(|c| c != '.').unwrap_or(rest.len())..];
1916 return Some(Ok(None));
1917 }
1918 if !rest.starts_with(is_name_code_point) {
1919 return Some(Err(()));
1920 }
1921 let token_len = rest.find(|c| !is_name_code_point(c)).unwrap_or(rest.len());
1922 let token = &rest[..token_len];
1923 self.0 = &rest[token_len..];
1924 Some(Ok(Some(token)))
1925 }
1926}
1927
1928fn is_name_code_point(c: char) -> bool {
1929 c >= 'A' && c <= 'Z'
1930 || c >= 'a' && c <= 'z'
1931 || c >= '\u{80}'
1932 || c == '_'
1933 || c >= '0' && c <= '9'
1934 || c == '-'
1935}
1936
1937#[repr(C, u8)]
1943#[derive(
1944 Clone,
1945 Debug,
1946 MallocSizeOf,
1947 Parse,
1948 PartialEq,
1949 SpecifiedValueInfo,
1950 ToComputedValue,
1951 ToCss,
1952 ToResolvedValue,
1953 ToShmem,
1954 ToTyped,
1955)]
1956pub enum GridTemplateAreas {
1957 None,
1959 Areas(TemplateAreasArc),
1961}
1962
1963impl GridTemplateAreas {
1964 #[inline]
1965 pub fn none() -> GridTemplateAreas {
1967 GridTemplateAreas::None
1968 }
1969}
1970
1971pub type ZIndex = GenericZIndex<Integer>;
1973
1974pub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;
1976
1977impl Parse for AspectRatio {
1978 fn parse<'i, 't>(
1979 context: &ParserContext,
1980 input: &mut Parser<'i, 't>,
1981 ) -> Result<Self, ParseError<'i>> {
1982 use crate::values::generics::position::PreferredRatio;
1983 use crate::values::specified::Ratio;
1984
1985 let location = input.current_source_location();
1986 let mut auto = input.try_parse(|i| i.expect_ident_matching("auto"));
1987 let ratio = input.try_parse(|i| Ratio::parse(context, i));
1988 if auto.is_err() {
1989 auto = input.try_parse(|i| i.expect_ident_matching("auto"));
1990 }
1991
1992 if auto.is_err() && ratio.is_err() {
1993 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1994 }
1995
1996 Ok(AspectRatio {
1997 auto: auto.is_ok(),
1998 ratio: match ratio {
1999 Ok(ratio) => PreferredRatio::Ratio(ratio),
2000 Err(..) => PreferredRatio::None,
2001 },
2002 })
2003 }
2004}
2005
2006impl AspectRatio {
2007 pub fn from_mapped_ratio(w: f32, h: f32) -> Self {
2009 use crate::values::generics::position::PreferredRatio;
2010 use crate::values::generics::ratio::Ratio;
2011 AspectRatio {
2012 auto: true,
2013 ratio: PreferredRatio::Ratio(Ratio(
2014 NonNegativeNumber::new(w),
2015 NonNegativeNumber::new(h),
2016 )),
2017 }
2018 }
2019}
2020
2021pub type Inset = GenericInset<specified::Percentage, LengthPercentage>;
2023
2024impl Inset {
2025 #[inline]
2028 pub fn parse_quirky<'i, 't>(
2029 context: &ParserContext,
2030 input: &mut Parser<'i, 't>,
2031 allow_quirks: AllowQuirks,
2032 ) -> Result<Self, ParseError<'i>> {
2033 if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2034 {
2035 return Ok(Self::LengthPercentage(l));
2036 }
2037 match input.try_parse(|i| i.expect_ident_matching("auto")) {
2038 Ok(_) => return Ok(Self::Auto),
2039 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2040 return Err(e.into())
2041 },
2042 Err(_) => (),
2043 };
2044 Self::parse_anchor_functions_quirky(context, input, allow_quirks)
2045 }
2046
2047 fn parse_as_anchor_function_fallback<'i, 't>(
2048 context: &ParserContext,
2049 input: &mut Parser<'i, 't>,
2050 ) -> Result<Self, ParseError<'i>> {
2051 if let Ok(l) =
2052 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::No))
2053 {
2054 return Ok(Self::LengthPercentage(l));
2055 }
2056 Self::parse_anchor_functions_quirky(context, input, AllowQuirks::No)
2057 }
2058
2059 fn parse_anchor_functions_quirky<'i, 't>(
2060 context: &ParserContext,
2061 input: &mut Parser<'i, 't>,
2062 allow_quirks: AllowQuirks,
2063 ) -> Result<Self, ParseError<'i>> {
2064 debug_assert!(
2065 static_prefs::pref!("layout.css.anchor-positioning.enabled"),
2066 "How are we parsing with pref off?"
2067 );
2068 if let Ok(inner) = input.try_parse(|i| AnchorFunction::parse(context, i)) {
2069 return Ok(Self::AnchorFunction(Box::new(inner)));
2070 }
2071 if let Ok(inner) =
2072 input.try_parse(|i| GenericAnchorSizeFunction::<Inset>::parse(context, i))
2073 {
2074 return Ok(Self::AnchorSizeFunction(Box::new(inner)));
2075 }
2076 Ok(Self::AnchorContainingCalcFunction(input.try_parse(
2077 |i| LengthPercentage::parse_quirky_with_anchor_functions(context, i, allow_quirks),
2078 )?))
2079 }
2080}
2081
2082impl Parse for Inset {
2083 fn parse<'i, 't>(
2084 context: &ParserContext,
2085 input: &mut Parser<'i, 't>,
2086 ) -> Result<Self, ParseError<'i>> {
2087 Self::parse_quirky(context, input, AllowQuirks::No)
2088 }
2089}
2090
2091pub type AnchorFunction = GenericAnchorFunction<specified::Percentage, Inset>;
2093
2094impl Parse for AnchorFunction {
2095 fn parse<'i, 't>(
2096 context: &ParserContext,
2097 input: &mut Parser<'i, 't>,
2098 ) -> Result<Self, ParseError<'i>> {
2099 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
2100 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2101 }
2102 input.expect_function_matching("anchor")?;
2103 input.parse_nested_block(|i| {
2104 let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
2105 let side = GenericAnchorSide::parse(context, i)?;
2106 let target_element = if target_element.is_none() {
2107 i.try_parse(|i| DashedIdent::parse(context, i)).ok()
2108 } else {
2109 target_element
2110 };
2111 let fallback = i
2112 .try_parse(|i| {
2113 i.expect_comma()?;
2114 Inset::parse_as_anchor_function_fallback(context, i)
2115 })
2116 .ok();
2117 Ok(Self {
2118 target_element: target_element.unwrap_or_else(DashedIdent::empty),
2119 side,
2120 fallback: fallback.into(),
2121 })
2122 })
2123 }
2124}