1use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10use crate::typed_om::{NumericType, NumericValue, ToTyped, TypedValue, UnitValue};
11use crate::values::specified;
12use crate::values::{CSSFloat, CustomIdent};
13use crate::{One, Zero};
14use cssparser::Parser;
15use std::fmt::{self, Write};
16use std::usize;
17use style_traits::values::specified::AllowedNumericType;
18use style_traits::{CssString, CssWriter, ParseError, StyleParseErrorKind, ToCss};
19use thin_vec::ThinVec;
20
21#[derive(
25 Clone,
26 Debug,
27 Default,
28 MallocSizeOf,
29 PartialEq,
30 SpecifiedValueInfo,
31 ToComputedValue,
32 ToResolvedValue,
33 ToShmem,
34 ToTyped,
35)]
36#[repr(C)]
37#[typed(todo_derive_fields)]
38pub struct GenericGridLine<Integer> {
39 pub ident: CustomIdent,
43 pub line_num: Integer,
45 pub is_span: bool,
47}
48
49pub use self::GenericGridLine as GridLine;
50
51impl<Integer> GridLine<Integer>
52where
53 Integer: PartialEq + Zero,
54{
55 pub fn auto() -> Self {
57 Self {
58 is_span: false,
59 line_num: Zero::zero(),
60 ident: CustomIdent(atom!("")),
61 }
62 }
63
64 pub fn is_auto(&self) -> bool {
66 self.ident.0 == atom!("") && self.line_num.is_zero() && !self.is_span
67 }
68
69 pub fn is_ident_only(&self) -> bool {
71 self.ident.0 != atom!("") && self.line_num.is_zero() && !self.is_span
72 }
73
74 pub fn can_omit(&self, other: &Self) -> bool {
78 if self.is_ident_only() {
79 self == other
80 } else {
81 other.is_auto()
82 }
83 }
84}
85
86impl<Integer> ToCss for GridLine<Integer>
87where
88 Integer: ToCss + PartialEq + Zero + One,
89{
90 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
91 where
92 W: Write,
93 {
94 if self.is_auto() {
96 return dest.write_str("auto");
97 }
98
99 if self.is_ident_only() {
101 return self.ident.to_css(dest);
102 }
103
104 let has_ident = self.ident.0 != atom!("");
106 if self.is_span {
107 dest.write_str("span")?;
108 debug_assert!(!self.line_num.is_zero() || has_ident);
109
110 if !self.line_num.is_zero() && !(self.line_num.is_one() && has_ident) {
115 dest.write_char(' ')?;
116 self.line_num.to_css(dest)?;
117 }
118
119 if has_ident {
120 dest.write_char(' ')?;
121 self.ident.to_css(dest)?;
122 }
123 return Ok(());
124 }
125
126 debug_assert!(!self.line_num.is_zero());
128 self.line_num.to_css(dest)?;
129 if has_ident {
130 dest.write_char(' ')?;
131 self.ident.to_css(dest)?;
132 }
133 Ok(())
134 }
135}
136
137impl Parse for GridLine<specified::Integer> {
138 fn parse<'i, 't>(
139 context: &ParserContext,
140 input: &mut Parser<'i, 't>,
141 ) -> Result<Self, ParseError<'i>> {
142 if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
143 return Ok(Self::auto());
144 }
145
146 let mut is_span = false;
147 let mut line_num: Option<specified::Integer> = None;
148 let mut ident: Option<CustomIdent> = None;
149
150 let mut val_before_span = false;
155
156 for _ in 0..3 {
157 let location = input.current_source_location();
159 if input.try_parse(|i| i.expect_ident_matching("span")).is_ok() {
160 if is_span {
161 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
162 }
163
164 if line_num.is_some() || ident.is_some() {
165 val_before_span = true;
166 }
167
168 is_span = true;
169 } else if let Ok(i) = input.try_parse(|i| specified::Integer::parse(context, i)) {
170 if val_before_span || line_num.is_some() {
171 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
172 }
173
174 if matches!(i.get(), Some(0)) {
175 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
176 }
177
178 line_num = Some(i);
179 } else if let Ok(name) = input.try_parse(|i| CustomIdent::parse(i, &["auto"])) {
180 if val_before_span || ident.is_some() {
181 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
182 }
183 ident = Some(name);
186 } else {
187 break;
188 }
189 }
190
191 if line_num.is_none() && ident.is_none() {
192 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
193 }
194
195 let mut grid_line = Self::auto();
196 grid_line.is_span = is_span;
197 if let Some(mut line_num) = line_num {
198 if is_span
199 && line_num
200 .ensure_clamping_mode(AllowedNumericType::AtLeastOne)
201 .is_err()
202 {
203 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
205 }
206 grid_line.line_num = line_num;
207 }
208 if let Some(ident) = ident {
209 grid_line.ident = ident;
210 }
211 Ok(grid_line)
212 }
213}
214
215pub struct FlexUnit;
217
218impl FlexUnit {
219 #[inline]
221 pub fn matches(unit: &str) -> bool {
222 unit.eq_ignore_ascii_case("fr")
223 }
224
225 #[inline]
227 pub fn name() -> &'static str {
228 "fr"
229 }
230}
231
232#[derive(
236 Animate,
237 Clone,
238 Copy,
239 Debug,
240 MallocSizeOf,
241 PartialEq,
242 SpecifiedValueInfo,
243 ToAnimatedValue,
244 ToComputedValue,
245 ToResolvedValue,
246 ToShmem,
247)]
248#[repr(C)]
249pub struct Flex(pub CSSFloat);
250
251impl ToCss for Flex {
252 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
253 where
254 W: Write,
255 {
256 self.0.to_css(dest)?;
257 dest.write_str(FlexUnit::name())
258 }
259}
260
261impl ToTyped for Flex {
262 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
263 let numeric_type = NumericType::flex();
264 let value = self.0;
265 let unit = CssString::from("fr");
266 dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
267 numeric_type,
268 value,
269 unit,
270 })));
271 Ok(())
272 }
273}
274
275#[derive(
280 Animate,
281 Clone,
282 Debug,
283 MallocSizeOf,
284 PartialEq,
285 SpecifiedValueInfo,
286 ToAnimatedValue,
287 ToComputedValue,
288 ToCss,
289 ToResolvedValue,
290 ToShmem,
291 ToTyped,
292)]
293#[repr(C, u8)]
294pub enum GenericTrackBreadth<L> {
295 Breadth(L),
297 Flex(Flex),
299 Auto,
301 MinContent,
303 MaxContent,
305}
306
307pub use self::GenericTrackBreadth as TrackBreadth;
308
309impl<L> TrackBreadth<L> {
310 #[inline]
314 pub fn is_fixed(&self) -> bool {
315 matches!(*self, TrackBreadth::Breadth(..))
316 }
317}
318
319#[derive(
324 Clone,
325 Debug,
326 MallocSizeOf,
327 PartialEq,
328 SpecifiedValueInfo,
329 ToAnimatedValue,
330 ToComputedValue,
331 ToResolvedValue,
332 ToShmem,
333)]
334#[repr(C, u8)]
335pub enum GenericTrackSize<L> {
336 Breadth(GenericTrackBreadth<L>),
338 #[css(function)]
343 Minmax(GenericTrackBreadth<L>, GenericTrackBreadth<L>),
344 #[css(function)]
351 FitContent(GenericTrackBreadth<L>),
352}
353
354pub use self::GenericTrackSize as TrackSize;
355
356impl<L> TrackSize<L> {
357 const INITIAL_VALUE: Self = TrackSize::Breadth(TrackBreadth::Auto);
359
360 pub const fn initial_value() -> Self {
362 Self::INITIAL_VALUE
363 }
364
365 pub fn is_initial(&self) -> bool {
367 matches!(*self, TrackSize::Breadth(TrackBreadth::Auto)) }
369
370 pub fn is_fixed(&self) -> bool {
374 match *self {
375 TrackSize::Breadth(ref breadth) => breadth.is_fixed(),
376 TrackSize::Minmax(ref breadth_1, ref breadth_2) => {
381 if breadth_1.is_fixed() {
382 return true; }
384
385 match *breadth_1 {
386 TrackBreadth::Flex(_) => false, _ => breadth_2.is_fixed(),
388 }
389 },
390 TrackSize::FitContent(_) => false,
391 }
392 }
393}
394
395impl<L> Default for TrackSize<L> {
396 fn default() -> Self {
397 Self::initial_value()
398 }
399}
400
401impl<L: ToCss> ToCss for TrackSize<L> {
402 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
403 where
404 W: Write,
405 {
406 match *self {
407 TrackSize::Breadth(ref breadth) => breadth.to_css(dest),
408 TrackSize::Minmax(ref min, ref max) => {
409 if let TrackBreadth::Auto = *min {
412 if let TrackBreadth::Flex(_) = *max {
413 return max.to_css(dest);
414 }
415 }
416
417 dest.write_str("minmax(")?;
418 min.to_css(dest)?;
419 dest.write_str(", ")?;
420 max.to_css(dest)?;
421 dest.write_char(')')
422 },
423 TrackSize::FitContent(ref lp) => {
424 dest.write_str("fit-content(")?;
425 lp.to_css(dest)?;
426 dest.write_char(')')
427 },
428 }
429 }
430}
431
432impl<L: ToTyped> ToTyped for TrackSize<L> {
433 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
434 match *self {
435 TrackSize::Breadth(ref breadth) => breadth.to_typed(dest),
436 _ => Err(()),
437 }
438 }
439}
440
441#[derive(
445 Clone,
446 Debug,
447 Default,
448 MallocSizeOf,
449 PartialEq,
450 SpecifiedValueInfo,
451 ToComputedValue,
452 ToCss,
453 ToResolvedValue,
454 ToShmem,
455 ToTyped,
456)]
457#[repr(transparent)]
458pub struct GenericImplicitGridTracks<T>(
459 #[css(if_empty = "auto", iterable)] pub crate::OwnedSlice<T>,
460);
461
462pub use self::GenericImplicitGridTracks as ImplicitGridTracks;
463
464impl<T: fmt::Debug + Default + PartialEq> ImplicitGridTracks<T> {
465 pub fn is_initial(&self) -> bool {
467 debug_assert_ne!(
468 *self,
469 ImplicitGridTracks(crate::OwnedSlice::from(vec![Default::default()]))
470 );
471 self.0.is_empty()
472 }
473}
474
475pub fn concat_serialize_idents<W>(
478 prefix: &str,
479 suffix: &str,
480 slice: &[CustomIdent],
481 sep: &str,
482 dest: &mut CssWriter<W>,
483) -> fmt::Result
484where
485 W: Write,
486{
487 if let Some((ref first, rest)) = slice.split_first() {
488 dest.write_str(prefix)?;
489 first.to_css(dest)?;
490 for thing in rest {
491 dest.write_str(sep)?;
492 thing.to_css(dest)?;
493 }
494
495 dest.write_str(suffix)?;
496 }
497
498 Ok(())
499}
500
501#[derive(
505 Clone,
506 Copy,
507 Debug,
508 MallocSizeOf,
509 PartialEq,
510 SpecifiedValueInfo,
511 ToAnimatedValue,
512 ToComputedValue,
513 ToCss,
514 ToResolvedValue,
515 ToShmem,
516)]
517#[repr(C, u8)]
518pub enum RepeatCount<Integer> {
519 Number(Integer),
521 AutoFill,
523 AutoFit,
525}
526
527impl Parse for RepeatCount<specified::Integer> {
528 fn parse<'i, 't>(
529 context: &ParserContext,
530 input: &mut Parser<'i, 't>,
531 ) -> Result<Self, ParseError<'i>> {
532 if let Ok(i) = input.try_parse(|i| specified::Integer::parse_positive(context, i)) {
533 return Ok(RepeatCount::Number(i));
534 }
535 try_match_ident_ignore_ascii_case! { input,
536 "auto-fill" => Ok(RepeatCount::AutoFill),
537 "auto-fit" => Ok(RepeatCount::AutoFit),
538 }
539 }
540}
541
542#[derive(
544 Clone,
545 Debug,
546 MallocSizeOf,
547 PartialEq,
548 SpecifiedValueInfo,
549 ToAnimatedValue,
550 ToComputedValue,
551 ToResolvedValue,
552 ToShmem,
553)]
554#[css(function = "repeat")]
555#[repr(C)]
556pub struct GenericTrackRepeat<L, I> {
557 pub count: RepeatCount<I>,
559 pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
565 pub track_sizes: crate::OwnedSlice<GenericTrackSize<L>>,
567}
568
569pub use self::GenericTrackRepeat as TrackRepeat;
570
571impl<L: ToCss, I: ToCss> ToCss for TrackRepeat<L, I> {
572 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
573 where
574 W: Write,
575 {
576 dest.write_str("repeat(")?;
577 self.count.to_css(dest)?;
578 dest.write_str(", ")?;
579
580 let mut line_names_iter = self.line_names.iter();
581 for (i, (ref size, ref names)) in self
582 .track_sizes
583 .iter()
584 .zip(&mut line_names_iter)
585 .enumerate()
586 {
587 if i > 0 {
588 dest.write_char(' ')?;
589 }
590
591 concat_serialize_idents("[", "] ", names, " ", dest)?;
592 size.to_css(dest)?;
593 }
594
595 if let Some(line_names_last) = line_names_iter.next() {
596 concat_serialize_idents(" [", "]", line_names_last, " ", dest)?;
597 }
598
599 dest.write_char(')')?;
600
601 Ok(())
602 }
603}
604
605#[derive(
607 Animate,
608 Clone,
609 Debug,
610 MallocSizeOf,
611 PartialEq,
612 SpecifiedValueInfo,
613 ToAnimatedValue,
614 ToComputedValue,
615 ToCss,
616 ToResolvedValue,
617 ToShmem,
618 ToTyped,
619)]
620#[repr(C, u8)]
621pub enum GenericTrackListValue<LengthPercentage, Integer> {
622 TrackSize(#[animation(field_bound)] GenericTrackSize<LengthPercentage>),
624 #[typed(skip)]
626 TrackRepeat(#[animation(field_bound)] GenericTrackRepeat<LengthPercentage, Integer>),
627}
628
629pub use self::GenericTrackListValue as TrackListValue;
630
631impl<L, I> TrackListValue<L, I> {
632 const INITIAL_VALUE: Self = TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto));
634
635 fn is_repeat(&self) -> bool {
636 matches!(*self, TrackListValue::TrackRepeat(..))
637 }
638
639 pub fn is_initial(&self) -> bool {
641 matches!(
642 *self,
643 TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto))
644 ) }
646}
647
648impl<L, I> Default for TrackListValue<L, I> {
649 #[inline]
650 fn default() -> Self {
651 Self::INITIAL_VALUE
652 }
653}
654
655#[derive(
659 Clone,
660 Debug,
661 MallocSizeOf,
662 PartialEq,
663 SpecifiedValueInfo,
664 ToAnimatedValue,
665 ToComputedValue,
666 ToResolvedValue,
667 ToShmem,
668)]
669#[repr(C)]
670pub struct GenericTrackList<LengthPercentage, Integer> {
671 #[css(skip)]
673 pub auto_repeat_index: usize,
674 pub values: crate::OwnedSlice<GenericTrackListValue<LengthPercentage, Integer>>,
676 pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
682}
683
684pub use self::GenericTrackList as TrackList;
685
686impl<L, I> TrackList<L, I> {
687 pub fn is_explicit(&self) -> bool {
690 !self.values.iter().any(|v| v.is_repeat())
691 }
692
693 pub fn has_auto_repeat(&self) -> bool {
695 self.auto_repeat_index < self.values.len()
696 }
697}
698
699impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
700 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
701 where
702 W: Write,
703 {
704 let mut values_iter = self.values.iter().peekable();
705 let mut line_names_iter = self.line_names.iter().peekable();
706
707 for idx in 0.. {
708 let names = line_names_iter.next().unwrap(); concat_serialize_idents("[", "]", names, " ", dest)?;
710
711 match values_iter.next() {
712 Some(value) => {
713 if !names.is_empty() {
714 dest.write_char(' ')?;
715 }
716
717 value.to_css(dest)?;
718 },
719 None => break,
720 }
721
722 if values_iter.peek().is_some()
723 || line_names_iter.peek().map_or(false, |v| !v.is_empty())
724 || (idx + 1 == self.auto_repeat_index)
725 {
726 dest.write_char(' ')?;
727 }
728 }
729
730 Ok(())
731 }
732}
733
734impl<L: ToTyped, I: ToTyped> ToTyped for TrackList<L, I> {
735 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
740 if self.values.len() != 1 {
741 return Err(());
742 }
743
744 if self.line_names.iter().any(|names| !names.is_empty()) {
745 return Err(());
746 }
747
748 self.values[0].to_typed(dest)
749 }
750}
751
752#[derive(
758 Clone,
759 Debug,
760 MallocSizeOf,
761 PartialEq,
762 SpecifiedValueInfo,
763 ToAnimatedValue,
764 ToComputedValue,
765 ToResolvedValue,
766 ToShmem,
767)]
768#[repr(C)]
769pub struct GenericNameRepeat<I> {
770 pub count: RepeatCount<I>,
773 pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
775}
776
777pub use self::GenericNameRepeat as NameRepeat;
778
779impl<I: ToCss> ToCss for NameRepeat<I> {
780 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
781 where
782 W: Write,
783 {
784 dest.write_str("repeat(")?;
785 self.count.to_css(dest)?;
786 dest.write_char(',')?;
787
788 for ref names in self.line_names.iter() {
789 if names.is_empty() {
790 dest.write_str(" []")?;
793 } else {
794 concat_serialize_idents(" [", "]", names, " ", dest)?;
795 }
796 }
797
798 dest.write_char(')')
799 }
800}
801
802impl<I> NameRepeat<I> {
803 #[inline]
805 pub fn is_auto_fill(&self) -> bool {
806 matches!(self.count, RepeatCount::AutoFill)
807 }
808}
809
810#[derive(
812 Clone,
813 Debug,
814 MallocSizeOf,
815 PartialEq,
816 SpecifiedValueInfo,
817 ToAnimatedValue,
818 ToComputedValue,
819 ToResolvedValue,
820 ToShmem,
821)]
822#[repr(C, u8)]
823pub enum GenericLineNameListValue<I> {
824 LineNames(crate::OwnedSlice<CustomIdent>),
826 Repeat(GenericNameRepeat<I>),
828}
829
830pub use self::GenericLineNameListValue as LineNameListValue;
831
832impl<I: ToCss> ToCss for LineNameListValue<I> {
833 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
834 where
835 W: Write,
836 {
837 match *self {
838 Self::Repeat(ref r) => r.to_css(dest),
839 Self::LineNames(ref names) => {
840 dest.write_char('[')?;
841
842 if let Some((ref first, rest)) = names.split_first() {
843 first.to_css(dest)?;
844 for name in rest {
845 dest.write_char(' ')?;
846 name.to_css(dest)?;
847 }
848 }
849
850 dest.write_char(']')
851 },
852 }
853 }
854}
855
856#[derive(
863 Clone,
864 Debug,
865 Default,
866 MallocSizeOf,
867 PartialEq,
868 SpecifiedValueInfo,
869 ToAnimatedValue,
870 ToComputedValue,
871 ToResolvedValue,
872 ToShmem,
873)]
874#[repr(C)]
875pub struct GenericLineNameList<I> {
876 pub expanded_line_names_length: usize,
880 pub line_names: crate::OwnedSlice<GenericLineNameListValue<I>>,
882}
883
884pub use self::GenericLineNameList as LineNameList;
885
886impl<I: ToCss> ToCss for LineNameList<I> {
887 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
888 where
889 W: Write,
890 {
891 dest.write_str("subgrid")?;
892
893 for value in self.line_names.iter() {
894 dest.write_char(' ')?;
895 value.to_css(dest)?;
896 }
897
898 Ok(())
899 }
900}
901
902#[derive(
904 Animate,
905 Clone,
906 Debug,
907 MallocSizeOf,
908 PartialEq,
909 SpecifiedValueInfo,
910 ToAnimatedValue,
911 ToComputedValue,
912 ToCss,
913 ToResolvedValue,
914 ToShmem,
915 ToTyped,
916)]
917#[value_info(other_values = "subgrid")]
918#[repr(C, u8)]
919pub enum GenericGridTemplateComponent<L, I> {
920 None,
922 TrackList(
924 #[animation(field_bound)]
925 #[compute(field_bound)]
926 #[resolve(field_bound)]
927 #[shmem(field_bound)]
928 Box<GenericTrackList<L, I>>,
929 ),
930 #[animation(error)]
933 #[typed(skip)]
934 Subgrid(Box<GenericLineNameList<I>>),
935 #[typed(skip)]
938 Masonry,
939}
940
941pub use self::GenericGridTemplateComponent as GridTemplateComponent;
942
943impl<L, I> GridTemplateComponent<L, I> {
944 const INITIAL_VALUE: Self = Self::None;
946
947 pub fn track_list_len(&self) -> usize {
949 match *self {
950 GridTemplateComponent::TrackList(ref tracklist) => tracklist.values.len(),
951 _ => 0,
952 }
953 }
954
955 pub fn is_initial(&self) -> bool {
957 matches!(*self, Self::None) }
959}
960
961impl<L, I> Default for GridTemplateComponent<L, I> {
962 #[inline]
963 fn default() -> Self {
964 Self::INITIAL_VALUE
965 }
966}