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 Auto,
486 Ident(DashedIdent),
488}
489
490impl PositionAnchor {
491 pub fn auto() -> Self {
493 Self::Auto
494 }
495
496 pub fn is_auto(&self) -> bool {
498 *self == Self::Auto
499 }
500}
501
502#[derive(
503 Clone,
504 Copy,
505 Debug,
506 Eq,
507 MallocSizeOf,
508 Parse,
509 PartialEq,
510 Serialize,
511 SpecifiedValueInfo,
512 ToComputedValue,
513 ToCss,
514 ToResolvedValue,
515 ToShmem,
516)]
517#[repr(u8)]
518pub enum PositionTryFallbacksTryTacticKeyword {
520 FlipBlock,
522 FlipInline,
524 FlipStart,
526 FlipX,
528 FlipY,
530}
531
532#[derive(
533 Clone,
534 Debug,
535 Default,
536 Eq,
537 MallocSizeOf,
538 PartialEq,
539 SpecifiedValueInfo,
540 ToComputedValue,
541 ToCss,
542 ToResolvedValue,
543 ToShmem,
544)]
545#[repr(transparent)]
546pub struct PositionTryFallbacksTryTactic(
551 #[css(iterable)] pub ThinVec<PositionTryFallbacksTryTacticKeyword>,
552);
553
554impl Parse for PositionTryFallbacksTryTactic {
555 fn parse<'i, 't>(
556 _context: &ParserContext,
557 input: &mut Parser<'i, 't>,
558 ) -> Result<Self, ParseError<'i>> {
559 let mut result = ThinVec::with_capacity(5);
560 for _ in 0..5 {
562 if let Ok(kw) = input.try_parse(PositionTryFallbacksTryTacticKeyword::parse) {
563 if result.contains(&kw) {
564 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
565 }
566 result.push(kw);
567 } else {
568 break;
569 }
570 }
571 if result.is_empty() {
572 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
573 }
574 Ok(Self(result))
575 }
576}
577
578impl PositionTryFallbacksTryTactic {
579 #[inline]
581 pub fn is_empty(&self) -> bool {
582 self.0.is_empty()
583 }
584
585 #[inline]
587 pub fn iter(&self) -> impl Iterator<Item = &PositionTryFallbacksTryTacticKeyword> {
588 self.0.iter()
589 }
590}
591
592#[derive(
593 Clone,
594 Debug,
595 MallocSizeOf,
596 PartialEq,
597 SpecifiedValueInfo,
598 ToComputedValue,
599 ToCss,
600 ToResolvedValue,
601 ToShmem,
602)]
603#[repr(C)]
604pub struct DashedIdentAndOrTryTactic {
607 pub ident: DashedIdent,
609 pub try_tactic: PositionTryFallbacksTryTactic,
611}
612
613impl Parse for DashedIdentAndOrTryTactic {
614 fn parse<'i, 't>(
615 context: &ParserContext,
616 input: &mut Parser<'i, 't>,
617 ) -> Result<Self, ParseError<'i>> {
618 let mut result = Self {
619 ident: DashedIdent::empty(),
620 try_tactic: PositionTryFallbacksTryTactic::default(),
621 };
622
623 loop {
624 if result.ident.is_empty() {
625 if let Ok(ident) = input.try_parse(|i| DashedIdent::parse(context, i)) {
626 result.ident = ident;
627 continue;
628 }
629 }
630 if result.try_tactic.is_empty() {
631 if let Ok(try_tactic) =
632 input.try_parse(|i| PositionTryFallbacksTryTactic::parse(context, i))
633 {
634 result.try_tactic = try_tactic;
635 continue;
636 }
637 }
638 break;
639 }
640
641 if result.ident.is_empty() && result.try_tactic.is_empty() {
642 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
643 }
644 return Ok(result);
645 }
646}
647
648#[derive(
649 Clone,
650 Debug,
651 MallocSizeOf,
652 Parse,
653 PartialEq,
654 SpecifiedValueInfo,
655 ToComputedValue,
656 ToCss,
657 ToResolvedValue,
658 ToShmem,
659)]
660#[repr(u8)]
661pub enum PositionTryFallbacksItem {
664 IdentAndOrTactic(DashedIdentAndOrTryTactic),
666 #[parse(parse_fn = "PositionArea::parse_except_none")]
667 PositionArea(PositionArea),
669}
670
671#[derive(
672 Clone,
673 Debug,
674 Default,
675 MallocSizeOf,
676 PartialEq,
677 SpecifiedValueInfo,
678 ToComputedValue,
679 ToCss,
680 ToResolvedValue,
681 ToShmem,
682 ToTyped,
683)]
684#[css(comma)]
685#[repr(C)]
686pub struct PositionTryFallbacks(
688 #[css(iterable, if_empty = "none")]
689 #[ignore_malloc_size_of = "Arc"]
690 pub crate::ArcSlice<PositionTryFallbacksItem>,
691);
692
693impl PositionTryFallbacks {
694 #[inline]
695 pub fn none() -> Self {
697 Self(Default::default())
698 }
699
700 pub fn is_none(&self) -> bool {
702 self.0.is_empty()
703 }
704}
705
706impl Parse for PositionTryFallbacks {
707 fn parse<'i, 't>(
708 context: &ParserContext,
709 input: &mut Parser<'i, 't>,
710 ) -> Result<Self, ParseError<'i>> {
711 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
712 return Ok(Self::none());
713 }
714 let mut items: SmallVec<[PositionTryFallbacksItem; 4]> =
717 smallvec![PositionTryFallbacksItem::parse(context, input)?];
718 while input.try_parse(|input| input.expect_comma()).is_ok() {
719 items.push(PositionTryFallbacksItem::parse(context, input)?);
720 }
721 Ok(Self(ArcSlice::from_iter(items.drain(..))))
722 }
723}
724
725#[derive(
727 Clone,
728 Copy,
729 Debug,
730 Default,
731 Eq,
732 MallocSizeOf,
733 Parse,
734 PartialEq,
735 SpecifiedValueInfo,
736 ToComputedValue,
737 ToCss,
738 ToResolvedValue,
739 ToShmem,
740 ToTyped,
741)]
742#[repr(u8)]
743pub enum PositionTryOrder {
744 #[default]
745 Normal,
747 MostWidth,
749 MostHeight,
751 MostBlockSize,
753 MostInlineSize,
755}
756
757impl PositionTryOrder {
758 #[inline]
759 pub fn normal() -> Self {
761 Self::Normal
762 }
763
764 pub fn is_normal(&self) -> bool {
766 *self == Self::Normal
767 }
768}
769
770#[derive(
771 Clone,
772 Copy,
773 Debug,
774 Eq,
775 MallocSizeOf,
776 Parse,
777 PartialEq,
778 Serialize,
779 SpecifiedValueInfo,
780 ToComputedValue,
781 ToCss,
782 ToResolvedValue,
783 ToShmem,
784 ToTyped,
785)]
786#[css(bitflags(single = "always", mixed = "anchors-valid,anchors-visible,no-overflow"))]
787#[repr(C)]
788pub struct PositionVisibility(u8);
790bitflags! {
791 impl PositionVisibility: u8 {
792 const ALWAYS = 0;
794 const ANCHORS_VALID = 1 << 0;
796 const ANCHORS_VISIBLE = 1 << 1;
798 const NO_OVERFLOW = 1 << 2;
800 }
801}
802
803impl Default for PositionVisibility {
804 fn default() -> Self {
805 Self::ALWAYS
806 }
807}
808
809impl PositionVisibility {
810 #[inline]
811 pub fn always() -> Self {
813 Self::ALWAYS
814 }
815}
816
817#[repr(u8)]
820#[derive(Clone, Copy, Debug, Eq, PartialEq)]
821pub enum PositionAreaType {
822 Physical,
824 Logical,
826 SelfLogical,
828 Inferred,
830 SelfInferred,
832 Common,
834 None,
836}
837
838#[repr(u8)]
845#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)]
846#[allow(missing_docs)]
847pub enum PositionAreaAxis {
848 Horizontal = 0b000,
849 Vertical = 0b001,
850
851 X = 0b010,
852 Y = 0b011,
853
854 Block = 0b110,
855 Inline = 0b111,
856
857 Inferred = 0b100,
858 None = 0b101,
859}
860
861impl PositionAreaAxis {
862 pub fn is_physical(self) -> bool {
864 (self as u8 & 0b100) == 0
865 }
866
867 fn is_flow_relative_direction(self) -> bool {
869 self == Self::Inferred || (self as u8 & 0b10) != 0
870 }
871
872 fn is_canonically_first(self) -> bool {
874 self != Self::Inferred && (self as u8) & 1 == 0
875 }
876
877 #[allow(unused)]
878 fn flip(self) -> Self {
879 if matches!(self, Self::Inferred | Self::None) {
880 return self;
881 }
882 Self::from_u8(self as u8 ^ 1u8).unwrap()
883 }
884
885 fn to_logical(self, wm: WritingMode, inferred: LogicalAxis) -> Option<LogicalAxis> {
886 Some(match self {
887 PositionAreaAxis::Horizontal | PositionAreaAxis::X => {
888 if wm.is_vertical() {
889 LogicalAxis::Block
890 } else {
891 LogicalAxis::Inline
892 }
893 },
894 PositionAreaAxis::Vertical | PositionAreaAxis::Y => {
895 if wm.is_vertical() {
896 LogicalAxis::Inline
897 } else {
898 LogicalAxis::Block
899 }
900 },
901 PositionAreaAxis::Block => LogicalAxis::Block,
902 PositionAreaAxis::Inline => LogicalAxis::Inline,
903 PositionAreaAxis::Inferred => inferred,
904 PositionAreaAxis::None => return None,
905 })
906 }
907}
908
909#[repr(u8)]
912#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)]
913pub enum PositionAreaTrack {
914 Start = 0b001,
916 SpanStart = 0b011,
918 End = 0b100,
920 SpanEnd = 0b110,
922 Center = 0b010,
924 SpanAll = 0b111,
926}
927
928impl PositionAreaTrack {
929 fn flip(self) -> Self {
930 match self {
931 Self::Start => Self::End,
932 Self::SpanStart => Self::SpanEnd,
933 Self::End => Self::Start,
934 Self::SpanEnd => Self::SpanStart,
935 Self::Center | Self::SpanAll => self,
936 }
937 }
938
939 fn start(self) -> bool {
940 self as u8 & 1 != 0
941 }
942}
943
944pub const AXIS_SHIFT: usize = 3;
946pub const AXIS_MASK: u8 = 0b111u8 << AXIS_SHIFT;
948pub const TRACK_MASK: u8 = 0b111u8;
950pub const SELF_WM: u8 = 1u8 << 6;
952
953#[derive(
954 Clone,
955 Copy,
956 Debug,
957 Default,
958 Eq,
959 MallocSizeOf,
960 Parse,
961 PartialEq,
962 SpecifiedValueInfo,
963 ToComputedValue,
964 ToCss,
965 ToResolvedValue,
966 ToShmem,
967 FromPrimitive,
968)]
969#[allow(missing_docs)]
970#[repr(u8)]
971pub enum PositionAreaKeyword {
976 #[default]
977 None = (PositionAreaAxis::None as u8) << AXIS_SHIFT,
978
979 Center = ((PositionAreaAxis::None as u8) << AXIS_SHIFT) | PositionAreaTrack::Center as u8,
981 SpanAll = ((PositionAreaAxis::None as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanAll as u8,
982
983 Start = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
985 End = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
986 SpanStart =
987 ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
988 SpanEnd = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
989
990 Left = ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
992 Right = ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
993 Top = ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
994 Bottom = ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
995
996 XStart = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
998 XEnd = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
999 YStart = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
1000 YEnd = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
1001
1002 BlockStart = ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
1004 BlockEnd = ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
1005 InlineStart = ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,
1006 InlineEnd = ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,
1007
1008 SpanLeft =
1010 ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1011 SpanRight =
1012 ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1013 SpanTop =
1014 ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1015 SpanBottom =
1016 ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1017
1018 SpanXStart = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1020 SpanXEnd = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1021 SpanYStart = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1022 SpanYEnd = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1023
1024 SpanBlockStart =
1026 ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1027 SpanBlockEnd =
1028 ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1029 SpanInlineStart =
1030 ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,
1031 SpanInlineEnd =
1032 ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,
1033
1034 SelfStart = SELF_WM | (Self::Start as u8),
1036 SelfEnd = SELF_WM | (Self::End as u8),
1037 SpanSelfStart = SELF_WM | (Self::SpanStart as u8),
1038 SpanSelfEnd = SELF_WM | (Self::SpanEnd as u8),
1039
1040 SelfXStart = SELF_WM | (Self::XStart as u8),
1041 SelfXEnd = SELF_WM | (Self::XEnd as u8),
1042 SelfYStart = SELF_WM | (Self::YStart as u8),
1043 SelfYEnd = SELF_WM | (Self::YEnd as u8),
1044 SelfBlockStart = SELF_WM | (Self::BlockStart as u8),
1045 SelfBlockEnd = SELF_WM | (Self::BlockEnd as u8),
1046 SelfInlineStart = SELF_WM | (Self::InlineStart as u8),
1047 SelfInlineEnd = SELF_WM | (Self::InlineEnd as u8),
1048
1049 SpanSelfXStart = SELF_WM | (Self::SpanXStart as u8),
1050 SpanSelfXEnd = SELF_WM | (Self::SpanXEnd as u8),
1051 SpanSelfYStart = SELF_WM | (Self::SpanYStart as u8),
1052 SpanSelfYEnd = SELF_WM | (Self::SpanYEnd as u8),
1053 SpanSelfBlockStart = SELF_WM | (Self::SpanBlockStart as u8),
1054 SpanSelfBlockEnd = SELF_WM | (Self::SpanBlockEnd as u8),
1055 SpanSelfInlineStart = SELF_WM | (Self::SpanInlineStart as u8),
1056 SpanSelfInlineEnd = SELF_WM | (Self::SpanInlineEnd as u8),
1057}
1058
1059impl PositionAreaKeyword {
1060 #[inline]
1062 pub fn none() -> Self {
1063 Self::None
1064 }
1065
1066 pub fn is_none(&self) -> bool {
1068 *self == Self::None
1069 }
1070
1071 pub fn self_wm(self) -> bool {
1073 (self as u8 & SELF_WM) != 0
1074 }
1075
1076 pub fn axis(self) -> PositionAreaAxis {
1078 PositionAreaAxis::from_u8((self as u8 >> AXIS_SHIFT) & 0b111).unwrap()
1079 }
1080
1081 pub fn with_axis(self, axis: PositionAreaAxis) -> Self {
1083 Self::from_u8(((self as u8) & !AXIS_MASK) | ((axis as u8) << AXIS_SHIFT)).unwrap()
1084 }
1085
1086 pub fn with_inferred_axis(self, axis: PositionAreaAxis) -> Self {
1088 if self.axis() == PositionAreaAxis::Inferred {
1089 self.with_axis(axis)
1090 } else {
1091 self
1092 }
1093 }
1094
1095 pub fn track(self) -> Option<PositionAreaTrack> {
1097 let result = PositionAreaTrack::from_u8(self as u8 & TRACK_MASK);
1098 debug_assert_eq!(
1099 result.is_none(),
1100 self.is_none(),
1101 "Only the none keyword has no track"
1102 );
1103 result
1104 }
1105
1106 fn group_type(self) -> PositionAreaType {
1107 let axis = self.axis();
1108 if axis == PositionAreaAxis::None {
1109 if self.is_none() {
1110 return PositionAreaType::None;
1111 }
1112 return PositionAreaType::Common;
1113 }
1114 if axis == PositionAreaAxis::Inferred {
1115 return if self.self_wm() {
1116 PositionAreaType::SelfInferred
1117 } else {
1118 PositionAreaType::Inferred
1119 };
1120 }
1121 if axis.is_physical() {
1122 return PositionAreaType::Physical;
1123 }
1124 if self.self_wm() {
1125 PositionAreaType::SelfLogical
1126 } else {
1127 PositionAreaType::Logical
1128 }
1129 }
1130
1131 fn to_physical(
1132 self,
1133 cb_wm: WritingMode,
1134 self_wm: WritingMode,
1135 inferred_axis: LogicalAxis,
1136 ) -> Self {
1137 let wm = if self.self_wm() { self_wm } else { cb_wm };
1138 let axis = self.axis();
1139 if !axis.is_flow_relative_direction() {
1140 return self;
1141 }
1142 let Some(logical_axis) = axis.to_logical(wm, inferred_axis) else {
1143 return self;
1144 };
1145 let Some(track) = self.track() else {
1146 debug_assert!(false, "How did we end up with no track here? {self:?}");
1147 return self;
1148 };
1149 let start = track.start();
1150 let logical_side = match logical_axis {
1151 LogicalAxis::Block => {
1152 if start {
1153 LogicalSide::BlockStart
1154 } else {
1155 LogicalSide::BlockEnd
1156 }
1157 },
1158 LogicalAxis::Inline => {
1159 if start {
1160 LogicalSide::InlineStart
1161 } else {
1162 LogicalSide::InlineEnd
1163 }
1164 },
1165 };
1166 let physical_side = logical_side.to_physical(wm);
1167 let physical_start = matches!(physical_side, PhysicalSide::Top | PhysicalSide::Left);
1168 let new_track = if physical_start != start {
1169 track.flip()
1170 } else {
1171 track
1172 };
1173 let new_axis = if matches!(physical_side, PhysicalSide::Top | PhysicalSide::Bottom) {
1174 PositionAreaAxis::Vertical
1175 } else {
1176 PositionAreaAxis::Horizontal
1177 };
1178 Self::from_u8(new_track as u8 | ((new_axis as u8) << AXIS_SHIFT)).unwrap()
1179 }
1180
1181 fn flip_track(self) -> Self {
1182 let Some(old_track) = self.track() else {
1183 return self;
1184 };
1185 let new_track = old_track.flip();
1186 Self::from_u8((self as u8 & !TRACK_MASK) | new_track as u8).unwrap()
1187 }
1188
1189 pub fn to_self_alignment(self) -> Option<AlignFlags> {
1194 let track = self.track()?;
1195 Some(match track {
1196 PositionAreaTrack::Center => AlignFlags::CENTER,
1198 PositionAreaTrack::SpanAll => AlignFlags::ANCHOR_CENTER,
1200 _ => {
1203 if track.start() {
1204 AlignFlags::END
1205 } else {
1206 AlignFlags::START
1207 }
1208 },
1209 })
1210 }
1211}
1212
1213#[derive(
1214 Clone,
1215 Copy,
1216 Debug,
1217 Eq,
1218 MallocSizeOf,
1219 PartialEq,
1220 SpecifiedValueInfo,
1221 ToCss,
1222 ToResolvedValue,
1223 ToShmem,
1224 ToTyped,
1225)]
1226#[repr(C)]
1227pub struct PositionArea {
1229 pub first: PositionAreaKeyword,
1231 #[css(skip_if = "PositionAreaKeyword::is_none")]
1233 pub second: PositionAreaKeyword,
1234}
1235
1236impl PositionArea {
1237 #[inline]
1239 pub fn none() -> Self {
1240 Self {
1241 first: PositionAreaKeyword::None,
1242 second: PositionAreaKeyword::None,
1243 }
1244 }
1245
1246 #[inline]
1248 pub fn is_none(&self) -> bool {
1249 self.first.is_none()
1250 }
1251
1252 pub fn parse_except_none<'i, 't>(
1254 context: &ParserContext,
1255 input: &mut Parser<'i, 't>,
1256 ) -> Result<Self, ParseError<'i>> {
1257 Self::parse_internal(context, input, false)
1258 }
1259
1260 pub fn get_type(&self) -> PositionAreaType {
1262 let first = self.first.group_type();
1263 let second = self.second.group_type();
1264 if matches!(second, PositionAreaType::None | PositionAreaType::Common) {
1265 return first;
1266 }
1267 if first == PositionAreaType::Common {
1268 return second;
1269 }
1270 if first != second {
1271 return PositionAreaType::None;
1272 }
1273 let first_axis = self.first.axis();
1274 if first_axis != PositionAreaAxis::Inferred
1275 && first_axis.is_canonically_first() == self.second.axis().is_canonically_first()
1276 {
1277 return PositionAreaType::None;
1278 }
1279 first
1280 }
1281
1282 fn parse_internal<'i, 't>(
1283 _: &ParserContext,
1284 input: &mut Parser<'i, 't>,
1285 allow_none: bool,
1286 ) -> Result<Self, ParseError<'i>> {
1287 let mut location = input.current_source_location();
1288 let mut first = PositionAreaKeyword::parse(input)?;
1289 if first.is_none() {
1290 if allow_none {
1291 return Ok(Self::none());
1292 }
1293 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1294 }
1295
1296 location = input.current_source_location();
1297 let second = input.try_parse(PositionAreaKeyword::parse);
1298 if let Ok(PositionAreaKeyword::None) = second {
1299 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1301 }
1302 let mut second = second.unwrap_or(PositionAreaKeyword::None);
1303 if second.is_none() {
1304 return Ok(Self { first, second });
1310 }
1311
1312 let pair_type = Self { first, second }.get_type();
1313 if pair_type == PositionAreaType::None {
1314 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
1316 }
1317 if matches!(
1320 pair_type,
1321 PositionAreaType::Physical | PositionAreaType::Logical | PositionAreaType::SelfLogical
1322 ) {
1323 if second == PositionAreaKeyword::SpanAll {
1324 second = PositionAreaKeyword::None;
1327 } else if first == PositionAreaKeyword::SpanAll {
1328 first = second;
1329 second = PositionAreaKeyword::None;
1330 }
1331 }
1332 if first == second {
1333 second = PositionAreaKeyword::None;
1334 }
1335 let mut result = Self { first, second };
1336 result.canonicalize_order();
1337 Ok(result)
1338 }
1339
1340 fn canonicalize_order(&mut self) {
1341 let first_axis = self.first.axis();
1342 if first_axis.is_canonically_first() || self.second.is_none() {
1343 return;
1344 }
1345 let second_axis = self.second.axis();
1346 if first_axis == second_axis {
1347 return;
1349 }
1350 if second_axis.is_canonically_first()
1351 || (second_axis == PositionAreaAxis::None && first_axis != PositionAreaAxis::Inferred)
1352 {
1353 std::mem::swap(&mut self.first, &mut self.second);
1354 }
1355 }
1356
1357 fn make_missing_second_explicit(&mut self) {
1358 if !self.second.is_none() {
1359 return;
1360 }
1361 let axis = self.first.axis();
1362 if matches!(axis, PositionAreaAxis::Inferred | PositionAreaAxis::None) {
1363 self.second = self.first;
1364 return;
1365 }
1366 self.second = PositionAreaKeyword::SpanAll;
1367 if !axis.is_canonically_first() {
1368 std::mem::swap(&mut self.first, &mut self.second);
1369 }
1370 }
1371
1372 pub fn to_physical(mut self, cb_wm: WritingMode, self_wm: WritingMode) -> Self {
1374 self.make_missing_second_explicit();
1375 self.first = self.first.to_physical(cb_wm, self_wm, LogicalAxis::Block);
1376 self.second = self.second.to_physical(cb_wm, self_wm, LogicalAxis::Inline);
1377 self.canonicalize_order();
1378 self
1379 }
1380
1381 fn flip_logical_axis(&mut self, wm: WritingMode, axis: LogicalAxis) {
1382 if self.first.axis().to_logical(wm, LogicalAxis::Block) == Some(axis) {
1383 self.first = self.first.flip_track();
1384 } else {
1385 self.second = self.second.flip_track();
1386 }
1387 }
1388
1389 fn flip_start(&mut self) {
1390 self.first = self.first.with_axis(self.first.axis().flip());
1391 self.second = self.second.with_axis(self.second.axis().flip());
1392 }
1393
1394 pub fn with_tactic(
1396 mut self,
1397 wm: WritingMode,
1398 tactic: PositionTryFallbacksTryTacticKeyword,
1399 ) -> Self {
1400 self.make_missing_second_explicit();
1401 let axis_to_flip = match tactic {
1402 PositionTryFallbacksTryTacticKeyword::FlipStart => {
1403 self.flip_start();
1404 return self;
1405 },
1406 PositionTryFallbacksTryTacticKeyword::FlipBlock => LogicalAxis::Block,
1407 PositionTryFallbacksTryTacticKeyword::FlipInline => LogicalAxis::Inline,
1408 PositionTryFallbacksTryTacticKeyword::FlipX => {
1409 if wm.is_horizontal() {
1410 LogicalAxis::Inline
1411 } else {
1412 LogicalAxis::Block
1413 }
1414 },
1415 PositionTryFallbacksTryTacticKeyword::FlipY => {
1416 if wm.is_vertical() {
1417 LogicalAxis::Inline
1418 } else {
1419 LogicalAxis::Block
1420 }
1421 },
1422 };
1423 self.flip_logical_axis(wm, axis_to_flip);
1424 self
1425 }
1426}
1427
1428impl Parse for PositionArea {
1429 fn parse<'i, 't>(
1430 context: &ParserContext,
1431 input: &mut Parser<'i, 't>,
1432 ) -> Result<Self, ParseError<'i>> {
1433 Self::parse_internal(context, input, true)
1434 }
1435}
1436
1437pub trait Side {
1439 fn start() -> Self;
1441
1442 fn is_start(&self) -> bool;
1444}
1445
1446impl Side for HorizontalPositionKeyword {
1447 #[inline]
1448 fn start() -> Self {
1449 HorizontalPositionKeyword::Left
1450 }
1451
1452 #[inline]
1453 fn is_start(&self) -> bool {
1454 *self == Self::start()
1455 }
1456}
1457
1458impl Side for VerticalPositionKeyword {
1459 #[inline]
1460 fn start() -> Self {
1461 VerticalPositionKeyword::Top
1462 }
1463
1464 #[inline]
1465 fn is_start(&self) -> bool {
1466 *self == Self::start()
1467 }
1468}
1469
1470#[derive(
1474 Clone,
1475 Copy,
1476 Debug,
1477 Eq,
1478 MallocSizeOf,
1479 Parse,
1480 PartialEq,
1481 SpecifiedValueInfo,
1482 ToComputedValue,
1483 ToResolvedValue,
1484 ToShmem,
1485 ToTyped,
1486)]
1487#[css(bitflags(
1488 mixed = "row,column,dense",
1489 validate_mixed = "Self::validate_and_simplify"
1490))]
1491#[repr(C)]
1492pub struct GridAutoFlow(u8);
1493bitflags! {
1494 impl GridAutoFlow: u8 {
1495 const ROW = 1 << 0;
1497 const COLUMN = 1 << 1;
1499 const DENSE = 1 << 2;
1501 }
1502}
1503
1504impl GridAutoFlow {
1505 fn validate_and_simplify(&mut self) -> bool {
1507 if self.contains(Self::ROW | Self::COLUMN) {
1508 return false;
1510 }
1511 if *self == Self::DENSE {
1512 self.insert(Self::ROW);
1514 }
1515 true
1516 }
1517}
1518
1519impl ToCss for GridAutoFlow {
1520 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1521 where
1522 W: Write,
1523 {
1524 let dense = self.intersects(Self::DENSE);
1525 if self.intersects(Self::ROW) {
1526 return if dense {
1527 dest.write_str("dense")
1528 } else {
1529 dest.write_str("row")
1530 };
1531 }
1532 debug_assert!(self.intersects(Self::COLUMN));
1533 if dense {
1534 dest.write_str("column dense")
1535 } else {
1536 dest.write_str("column")
1537 }
1538 }
1539}
1540
1541#[repr(u8)]
1542#[derive(
1543 Clone,
1544 Copy,
1545 Debug,
1546 Eq,
1547 MallocSizeOf,
1548 PartialEq,
1549 SpecifiedValueInfo,
1550 ToComputedValue,
1551 ToCss,
1552 ToResolvedValue,
1553 ToShmem,
1554)]
1555pub enum MasonryPlacement {
1557 Pack,
1559 Next,
1561}
1562
1563#[repr(u8)]
1564#[derive(
1565 Clone,
1566 Copy,
1567 Debug,
1568 Eq,
1569 MallocSizeOf,
1570 PartialEq,
1571 SpecifiedValueInfo,
1572 ToComputedValue,
1573 ToCss,
1574 ToResolvedValue,
1575 ToShmem,
1576)]
1577pub enum MasonryItemOrder {
1579 DefiniteFirst,
1581 Ordered,
1583}
1584
1585#[derive(
1586 Clone,
1587 Copy,
1588 Debug,
1589 Eq,
1590 MallocSizeOf,
1591 PartialEq,
1592 SpecifiedValueInfo,
1593 ToComputedValue,
1594 ToCss,
1595 ToResolvedValue,
1596 ToShmem,
1597 ToTyped,
1598)]
1599#[repr(C)]
1600pub struct MasonryAutoFlow {
1603 #[css(contextual_skip_if = "is_pack_with_non_default_order")]
1605 pub placement: MasonryPlacement,
1606 #[css(skip_if = "is_item_order_definite_first")]
1608 pub order: MasonryItemOrder,
1609}
1610
1611#[inline]
1612fn is_pack_with_non_default_order(placement: &MasonryPlacement, order: &MasonryItemOrder) -> bool {
1613 *placement == MasonryPlacement::Pack && *order != MasonryItemOrder::DefiniteFirst
1614}
1615
1616#[inline]
1617fn is_item_order_definite_first(order: &MasonryItemOrder) -> bool {
1618 *order == MasonryItemOrder::DefiniteFirst
1619}
1620
1621impl MasonryAutoFlow {
1622 #[inline]
1623 pub fn initial() -> MasonryAutoFlow {
1625 MasonryAutoFlow {
1626 placement: MasonryPlacement::Pack,
1627 order: MasonryItemOrder::DefiniteFirst,
1628 }
1629 }
1630}
1631
1632impl Parse for MasonryAutoFlow {
1633 fn parse<'i, 't>(
1635 _context: &ParserContext,
1636 input: &mut Parser<'i, 't>,
1637 ) -> Result<MasonryAutoFlow, ParseError<'i>> {
1638 let mut value = MasonryAutoFlow::initial();
1639 let mut got_placement = false;
1640 let mut got_order = false;
1641 while !input.is_exhausted() {
1642 let location = input.current_source_location();
1643 let ident = input.expect_ident()?;
1644 let success = match_ignore_ascii_case! { &ident,
1645 "pack" if !got_placement => {
1646 got_placement = true;
1647 true
1648 },
1649 "next" if !got_placement => {
1650 value.placement = MasonryPlacement::Next;
1651 got_placement = true;
1652 true
1653 },
1654 "definite-first" if !got_order => {
1655 got_order = true;
1656 true
1657 },
1658 "ordered" if !got_order => {
1659 value.order = MasonryItemOrder::Ordered;
1660 got_order = true;
1661 true
1662 },
1663 _ => false
1664 };
1665 if !success {
1666 return Err(location
1667 .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
1668 }
1669 }
1670
1671 if got_placement || got_order {
1672 Ok(value)
1673 } else {
1674 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1675 }
1676 }
1677}
1678
1679#[derive(
1680 Clone,
1681 Debug,
1682 MallocSizeOf,
1683 PartialEq,
1684 SpecifiedValueInfo,
1685 ToComputedValue,
1686 ToCss,
1687 ToResolvedValue,
1688 ToShmem,
1689)]
1690#[repr(C)]
1691pub struct TemplateAreas {
1693 #[css(skip)]
1695 pub areas: crate::OwnedSlice<NamedArea>,
1696 #[css(iterable)]
1701 pub strings: crate::OwnedSlice<crate::OwnedStr>,
1702 #[css(skip)]
1704 pub width: u32,
1705}
1706
1707#[derive(Default)]
1709pub struct TemplateAreasParser {
1710 areas: Vec<NamedArea>,
1711 area_indices: PrecomputedHashMap<Atom, usize>,
1712 strings: Vec<crate::OwnedStr>,
1713 width: u32,
1714 row: u32,
1715}
1716
1717impl TemplateAreasParser {
1718 pub fn try_parse_string<'i>(
1720 &mut self,
1721 input: &mut Parser<'i, '_>,
1722 ) -> Result<(), ParseError<'i>> {
1723 input.try_parse(|input| {
1724 self.parse_string(input.expect_string()?)
1725 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1726 })
1727 }
1728
1729 fn parse_string(&mut self, string: &str) -> Result<(), ()> {
1731 self.row += 1;
1732 let mut simplified_string = String::new();
1733 let mut current_area_index: Option<usize> = None;
1734 let mut column = 0u32;
1735 for token in TemplateAreasTokenizer(string) {
1736 column += 1;
1737 if column > 1 {
1738 simplified_string.push(' ');
1739 }
1740 let name = if let Some(token) = token? {
1741 simplified_string.push_str(token);
1742 Atom::from(token)
1743 } else {
1744 if let Some(index) = current_area_index.take() {
1745 if self.areas[index].columns.end != column {
1746 return Err(());
1747 }
1748 }
1749 simplified_string.push('.');
1750 continue;
1751 };
1752 if let Some(index) = current_area_index {
1753 if self.areas[index].name == name {
1754 if self.areas[index].rows.start == self.row {
1755 self.areas[index].columns.end += 1;
1756 }
1757 continue;
1758 }
1759 if self.areas[index].columns.end != column {
1760 return Err(());
1761 }
1762 }
1763 match self.area_indices.entry(name) {
1764 Entry::Occupied(ref e) => {
1765 let index = *e.get();
1766 if self.areas[index].columns.start != column
1767 || self.areas[index].rows.end != self.row
1768 {
1769 return Err(());
1770 }
1771 self.areas[index].rows.end += 1;
1772 current_area_index = Some(index);
1773 },
1774 Entry::Vacant(v) => {
1775 let index = self.areas.len();
1776 let name = v.key().clone();
1777 v.insert(index);
1778 self.areas.push(NamedArea {
1779 name,
1780 columns: UnsignedRange {
1781 start: column,
1782 end: column + 1,
1783 },
1784 rows: UnsignedRange {
1785 start: self.row,
1786 end: self.row + 1,
1787 },
1788 });
1789 current_area_index = Some(index);
1790 },
1791 }
1792 }
1793 if column == 0 {
1794 return Err(());
1797 }
1798 if let Some(index) = current_area_index {
1799 if self.areas[index].columns.end != column + 1 {
1800 debug_assert_ne!(self.areas[index].rows.start, self.row);
1801 return Err(());
1802 }
1803 }
1804 if self.row == 1 {
1805 self.width = column;
1806 } else if self.width != column {
1807 return Err(());
1808 }
1809
1810 self.strings.push(simplified_string.into());
1811 Ok(())
1812 }
1813
1814 pub fn finish(self) -> Result<TemplateAreas, ()> {
1816 if self.strings.is_empty() {
1817 return Err(());
1818 }
1819 Ok(TemplateAreas {
1820 areas: self.areas.into(),
1821 strings: self.strings.into(),
1822 width: self.width,
1823 })
1824 }
1825}
1826
1827impl TemplateAreas {
1828 fn parse_internal(input: &mut Parser) -> Result<Self, ()> {
1829 let mut parser = TemplateAreasParser::default();
1830 while parser.try_parse_string(input).is_ok() {}
1831 parser.finish()
1832 }
1833}
1834
1835impl Parse for TemplateAreas {
1836 fn parse<'i, 't>(
1837 _: &ParserContext,
1838 input: &mut Parser<'i, 't>,
1839 ) -> Result<Self, ParseError<'i>> {
1840 Self::parse_internal(input)
1841 .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1842 }
1843}
1844
1845#[derive(
1847 Clone,
1848 Debug,
1849 MallocSizeOf,
1850 PartialEq,
1851 SpecifiedValueInfo,
1852 ToComputedValue,
1853 ToCss,
1854 ToResolvedValue,
1855 ToShmem,
1856)]
1857#[repr(transparent)]
1858pub struct TemplateAreasArc(#[ignore_malloc_size_of = "Arc"] pub Arc<TemplateAreas>);
1859
1860impl Parse for TemplateAreasArc {
1861 fn parse<'i, 't>(
1862 context: &ParserContext,
1863 input: &mut Parser<'i, 't>,
1864 ) -> Result<Self, ParseError<'i>> {
1865 let parsed = TemplateAreas::parse(context, input)?;
1866 Ok(TemplateAreasArc(Arc::new(parsed)))
1867 }
1868}
1869
1870#[repr(C)]
1873#[derive(
1874 Clone,
1875 Debug,
1876 MallocSizeOf,
1877 PartialEq,
1878 SpecifiedValueInfo,
1879 ToComputedValue,
1880 ToResolvedValue,
1881 ToShmem,
1882)]
1883pub struct UnsignedRange {
1884 pub start: u32,
1886 pub end: u32,
1888}
1889
1890#[derive(
1891 Clone,
1892 Debug,
1893 MallocSizeOf,
1894 PartialEq,
1895 SpecifiedValueInfo,
1896 ToComputedValue,
1897 ToResolvedValue,
1898 ToShmem,
1899)]
1900#[repr(C)]
1901pub struct NamedArea {
1904 pub name: Atom,
1906 pub rows: UnsignedRange,
1908 pub columns: UnsignedRange,
1910}
1911
1912struct TemplateAreasTokenizer<'a>(&'a str);
1915
1916impl<'a> Iterator for TemplateAreasTokenizer<'a> {
1917 type Item = Result<Option<&'a str>, ()>;
1918
1919 fn next(&mut self) -> Option<Self::Item> {
1920 let rest = self.0.trim_start_matches(HTML_SPACE_CHARACTERS);
1921 if rest.is_empty() {
1922 return None;
1923 }
1924 if rest.starts_with('.') {
1925 self.0 = &rest[rest.find(|c| c != '.').unwrap_or(rest.len())..];
1926 return Some(Ok(None));
1927 }
1928 if !rest.starts_with(is_name_code_point) {
1929 return Some(Err(()));
1930 }
1931 let token_len = rest.find(|c| !is_name_code_point(c)).unwrap_or(rest.len());
1932 let token = &rest[..token_len];
1933 self.0 = &rest[token_len..];
1934 Some(Ok(Some(token)))
1935 }
1936}
1937
1938fn is_name_code_point(c: char) -> bool {
1939 c >= 'A' && c <= 'Z'
1940 || c >= 'a' && c <= 'z'
1941 || c >= '\u{80}'
1942 || c == '_'
1943 || c >= '0' && c <= '9'
1944 || c == '-'
1945}
1946
1947#[repr(C, u8)]
1953#[derive(
1954 Clone,
1955 Debug,
1956 MallocSizeOf,
1957 Parse,
1958 PartialEq,
1959 SpecifiedValueInfo,
1960 ToComputedValue,
1961 ToCss,
1962 ToResolvedValue,
1963 ToShmem,
1964 ToTyped,
1965)]
1966pub enum GridTemplateAreas {
1967 None,
1969 Areas(TemplateAreasArc),
1971}
1972
1973impl GridTemplateAreas {
1974 #[inline]
1975 pub fn none() -> GridTemplateAreas {
1977 GridTemplateAreas::None
1978 }
1979}
1980
1981pub type ZIndex = GenericZIndex<Integer>;
1983
1984pub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;
1986
1987impl Parse for AspectRatio {
1988 fn parse<'i, 't>(
1989 context: &ParserContext,
1990 input: &mut Parser<'i, 't>,
1991 ) -> Result<Self, ParseError<'i>> {
1992 use crate::values::generics::position::PreferredRatio;
1993 use crate::values::specified::Ratio;
1994
1995 let location = input.current_source_location();
1996 let mut auto = input.try_parse(|i| i.expect_ident_matching("auto"));
1997 let ratio = input.try_parse(|i| Ratio::parse(context, i));
1998 if auto.is_err() {
1999 auto = input.try_parse(|i| i.expect_ident_matching("auto"));
2000 }
2001
2002 if auto.is_err() && ratio.is_err() {
2003 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2004 }
2005
2006 Ok(AspectRatio {
2007 auto: auto.is_ok(),
2008 ratio: match ratio {
2009 Ok(ratio) => PreferredRatio::Ratio(ratio),
2010 Err(..) => PreferredRatio::None,
2011 },
2012 })
2013 }
2014}
2015
2016impl AspectRatio {
2017 pub fn from_mapped_ratio(w: f32, h: f32) -> Self {
2019 use crate::values::generics::position::PreferredRatio;
2020 use crate::values::generics::ratio::Ratio;
2021 AspectRatio {
2022 auto: true,
2023 ratio: PreferredRatio::Ratio(Ratio(
2024 NonNegativeNumber::new(w),
2025 NonNegativeNumber::new(h),
2026 )),
2027 }
2028 }
2029}
2030
2031pub type Inset = GenericInset<specified::Percentage, LengthPercentage>;
2033
2034impl Inset {
2035 #[inline]
2038 pub fn parse_quirky<'i, 't>(
2039 context: &ParserContext,
2040 input: &mut Parser<'i, 't>,
2041 allow_quirks: AllowQuirks,
2042 ) -> Result<Self, ParseError<'i>> {
2043 if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))
2044 {
2045 return Ok(Self::LengthPercentage(l));
2046 }
2047 match input.try_parse(|i| i.expect_ident_matching("auto")) {
2048 Ok(_) => return Ok(Self::Auto),
2049 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => {
2050 return Err(e.into())
2051 },
2052 Err(_) => (),
2053 };
2054 Self::parse_anchor_functions_quirky(context, input, allow_quirks)
2055 }
2056
2057 fn parse_as_anchor_function_fallback<'i, 't>(
2058 context: &ParserContext,
2059 input: &mut Parser<'i, 't>,
2060 ) -> Result<Self, ParseError<'i>> {
2061 if let Ok(l) =
2062 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::No))
2063 {
2064 return Ok(Self::LengthPercentage(l));
2065 }
2066 Self::parse_anchor_functions_quirky(context, input, AllowQuirks::No)
2067 }
2068
2069 fn parse_anchor_functions_quirky<'i, 't>(
2070 context: &ParserContext,
2071 input: &mut Parser<'i, 't>,
2072 allow_quirks: AllowQuirks,
2073 ) -> Result<Self, ParseError<'i>> {
2074 debug_assert!(
2075 static_prefs::pref!("layout.css.anchor-positioning.enabled"),
2076 "How are we parsing with pref off?"
2077 );
2078 if let Ok(inner) = input.try_parse(|i| AnchorFunction::parse(context, i)) {
2079 return Ok(Self::AnchorFunction(Box::new(inner)));
2080 }
2081 if let Ok(inner) =
2082 input.try_parse(|i| GenericAnchorSizeFunction::<Inset>::parse(context, i))
2083 {
2084 return Ok(Self::AnchorSizeFunction(Box::new(inner)));
2085 }
2086 Ok(Self::AnchorContainingCalcFunction(input.try_parse(
2087 |i| LengthPercentage::parse_quirky_with_anchor_functions(context, i, allow_quirks),
2088 )?))
2089 }
2090}
2091
2092impl Parse for Inset {
2093 fn parse<'i, 't>(
2094 context: &ParserContext,
2095 input: &mut Parser<'i, 't>,
2096 ) -> Result<Self, ParseError<'i>> {
2097 Self::parse_quirky(context, input, AllowQuirks::No)
2098 }
2099}
2100
2101pub type AnchorFunction = GenericAnchorFunction<specified::Percentage, Inset>;
2103
2104impl Parse for AnchorFunction {
2105 fn parse<'i, 't>(
2106 context: &ParserContext,
2107 input: &mut Parser<'i, 't>,
2108 ) -> Result<Self, ParseError<'i>> {
2109 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") {
2110 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
2111 }
2112 input.expect_function_matching("anchor")?;
2113 input.parse_nested_block(|i| {
2114 let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();
2115 let side = GenericAnchorSide::parse(context, i)?;
2116 let target_element = if target_element.is_none() {
2117 i.try_parse(|i| DashedIdent::parse(context, i)).ok()
2118 } else {
2119 target_element
2120 };
2121 let fallback = i
2122 .try_parse(|i| {
2123 i.expect_comma()?;
2124 Inset::parse_as_anchor_function_fallback(context, i)
2125 })
2126 .ok();
2127 Ok(Self {
2128 target_element: target_element.unwrap_or_else(DashedIdent::empty),
2129 side,
2130 fallback: fallback.into(),
2131 })
2132 })
2133 }
2134}