1use crate::derives::*;
9use crate::parser::{Parse, ParserContext};
10use crate::typed_om::{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
215#[derive(
219 Animate,
220 Clone,
221 Copy,
222 Debug,
223 MallocSizeOf,
224 PartialEq,
225 SpecifiedValueInfo,
226 ToAnimatedValue,
227 ToComputedValue,
228 ToResolvedValue,
229 ToShmem,
230)]
231#[repr(C)]
232pub struct Flex(pub CSSFloat);
233
234impl ToCss for Flex {
235 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
236 where
237 W: Write,
238 {
239 self.0.to_css(dest)?;
240 dest.write_str("fr")
241 }
242}
243
244impl ToTyped for Flex {
245 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
246 let value = self.0;
247 let unit = CssString::from("fr");
248 dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {
249 value,
250 unit,
251 })));
252 Ok(())
253 }
254}
255
256#[derive(
261 Animate,
262 Clone,
263 Debug,
264 MallocSizeOf,
265 PartialEq,
266 SpecifiedValueInfo,
267 ToAnimatedValue,
268 ToComputedValue,
269 ToCss,
270 ToResolvedValue,
271 ToShmem,
272 ToTyped,
273)]
274#[repr(C, u8)]
275pub enum GenericTrackBreadth<L> {
276 Breadth(L),
278 Flex(Flex),
280 Auto,
282 MinContent,
284 MaxContent,
286}
287
288pub use self::GenericTrackBreadth as TrackBreadth;
289
290impl<L> TrackBreadth<L> {
291 #[inline]
295 pub fn is_fixed(&self) -> bool {
296 matches!(*self, TrackBreadth::Breadth(..))
297 }
298}
299
300#[derive(
305 Clone,
306 Debug,
307 MallocSizeOf,
308 PartialEq,
309 SpecifiedValueInfo,
310 ToAnimatedValue,
311 ToComputedValue,
312 ToResolvedValue,
313 ToShmem,
314)]
315#[repr(C, u8)]
316pub enum GenericTrackSize<L> {
317 Breadth(GenericTrackBreadth<L>),
319 #[css(function)]
324 Minmax(GenericTrackBreadth<L>, GenericTrackBreadth<L>),
325 #[css(function)]
332 FitContent(GenericTrackBreadth<L>),
333}
334
335pub use self::GenericTrackSize as TrackSize;
336
337impl<L> TrackSize<L> {
338 const INITIAL_VALUE: Self = TrackSize::Breadth(TrackBreadth::Auto);
340
341 pub const fn initial_value() -> Self {
343 Self::INITIAL_VALUE
344 }
345
346 pub fn is_initial(&self) -> bool {
348 matches!(*self, TrackSize::Breadth(TrackBreadth::Auto)) }
350
351 pub fn is_fixed(&self) -> bool {
355 match *self {
356 TrackSize::Breadth(ref breadth) => breadth.is_fixed(),
357 TrackSize::Minmax(ref breadth_1, ref breadth_2) => {
362 if breadth_1.is_fixed() {
363 return true; }
365
366 match *breadth_1 {
367 TrackBreadth::Flex(_) => false, _ => breadth_2.is_fixed(),
369 }
370 },
371 TrackSize::FitContent(_) => false,
372 }
373 }
374}
375
376impl<L> Default for TrackSize<L> {
377 fn default() -> Self {
378 Self::initial_value()
379 }
380}
381
382impl<L: ToCss> ToCss for TrackSize<L> {
383 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
384 where
385 W: Write,
386 {
387 match *self {
388 TrackSize::Breadth(ref breadth) => breadth.to_css(dest),
389 TrackSize::Minmax(ref min, ref max) => {
390 if let TrackBreadth::Auto = *min {
393 if let TrackBreadth::Flex(_) = *max {
394 return max.to_css(dest);
395 }
396 }
397
398 dest.write_str("minmax(")?;
399 min.to_css(dest)?;
400 dest.write_str(", ")?;
401 max.to_css(dest)?;
402 dest.write_char(')')
403 },
404 TrackSize::FitContent(ref lp) => {
405 dest.write_str("fit-content(")?;
406 lp.to_css(dest)?;
407 dest.write_char(')')
408 },
409 }
410 }
411}
412
413impl<L: ToTyped> ToTyped for TrackSize<L> {
414 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
415 match *self {
416 TrackSize::Breadth(ref breadth) => breadth.to_typed(dest),
417 _ => Err(()),
418 }
419 }
420}
421
422#[derive(
426 Clone,
427 Debug,
428 Default,
429 MallocSizeOf,
430 PartialEq,
431 SpecifiedValueInfo,
432 ToComputedValue,
433 ToCss,
434 ToResolvedValue,
435 ToShmem,
436 ToTyped,
437)]
438#[repr(transparent)]
439pub struct GenericImplicitGridTracks<T>(
440 #[css(if_empty = "auto", iterable)] pub crate::OwnedSlice<T>,
441);
442
443pub use self::GenericImplicitGridTracks as ImplicitGridTracks;
444
445impl<T: fmt::Debug + Default + PartialEq> ImplicitGridTracks<T> {
446 pub fn is_initial(&self) -> bool {
448 debug_assert_ne!(
449 *self,
450 ImplicitGridTracks(crate::OwnedSlice::from(vec![Default::default()]))
451 );
452 self.0.is_empty()
453 }
454}
455
456pub fn concat_serialize_idents<W>(
459 prefix: &str,
460 suffix: &str,
461 slice: &[CustomIdent],
462 sep: &str,
463 dest: &mut CssWriter<W>,
464) -> fmt::Result
465where
466 W: Write,
467{
468 if let Some((ref first, rest)) = slice.split_first() {
469 dest.write_str(prefix)?;
470 first.to_css(dest)?;
471 for thing in rest {
472 dest.write_str(sep)?;
473 thing.to_css(dest)?;
474 }
475
476 dest.write_str(suffix)?;
477 }
478
479 Ok(())
480}
481
482#[derive(
486 Clone,
487 Copy,
488 Debug,
489 MallocSizeOf,
490 PartialEq,
491 SpecifiedValueInfo,
492 ToAnimatedValue,
493 ToComputedValue,
494 ToCss,
495 ToResolvedValue,
496 ToShmem,
497)]
498#[repr(C, u8)]
499pub enum RepeatCount<Integer> {
500 Number(Integer),
502 AutoFill,
504 AutoFit,
506}
507
508impl Parse for RepeatCount<specified::Integer> {
509 fn parse<'i, 't>(
510 context: &ParserContext,
511 input: &mut Parser<'i, 't>,
512 ) -> Result<Self, ParseError<'i>> {
513 if let Ok(i) = input.try_parse(|i| specified::Integer::parse_positive(context, i)) {
514 return Ok(RepeatCount::Number(i));
515 }
516 try_match_ident_ignore_ascii_case! { input,
517 "auto-fill" => Ok(RepeatCount::AutoFill),
518 "auto-fit" => Ok(RepeatCount::AutoFit),
519 }
520 }
521}
522
523#[derive(
525 Clone,
526 Debug,
527 MallocSizeOf,
528 PartialEq,
529 SpecifiedValueInfo,
530 ToAnimatedValue,
531 ToComputedValue,
532 ToResolvedValue,
533 ToShmem,
534)]
535#[css(function = "repeat")]
536#[repr(C)]
537pub struct GenericTrackRepeat<L, I> {
538 pub count: RepeatCount<I>,
540 pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
546 pub track_sizes: crate::OwnedSlice<GenericTrackSize<L>>,
548}
549
550pub use self::GenericTrackRepeat as TrackRepeat;
551
552impl<L: ToCss, I: ToCss> ToCss for TrackRepeat<L, I> {
553 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
554 where
555 W: Write,
556 {
557 dest.write_str("repeat(")?;
558 self.count.to_css(dest)?;
559 dest.write_str(", ")?;
560
561 let mut line_names_iter = self.line_names.iter();
562 for (i, (ref size, ref names)) in self
563 .track_sizes
564 .iter()
565 .zip(&mut line_names_iter)
566 .enumerate()
567 {
568 if i > 0 {
569 dest.write_char(' ')?;
570 }
571
572 concat_serialize_idents("[", "] ", names, " ", dest)?;
573 size.to_css(dest)?;
574 }
575
576 if let Some(line_names_last) = line_names_iter.next() {
577 concat_serialize_idents(" [", "]", line_names_last, " ", dest)?;
578 }
579
580 dest.write_char(')')?;
581
582 Ok(())
583 }
584}
585
586#[derive(
588 Animate,
589 Clone,
590 Debug,
591 MallocSizeOf,
592 PartialEq,
593 SpecifiedValueInfo,
594 ToAnimatedValue,
595 ToComputedValue,
596 ToCss,
597 ToResolvedValue,
598 ToShmem,
599 ToTyped,
600)]
601#[repr(C, u8)]
602pub enum GenericTrackListValue<LengthPercentage, Integer> {
603 TrackSize(#[animation(field_bound)] GenericTrackSize<LengthPercentage>),
605 #[typed(skip)]
607 TrackRepeat(#[animation(field_bound)] GenericTrackRepeat<LengthPercentage, Integer>),
608}
609
610pub use self::GenericTrackListValue as TrackListValue;
611
612impl<L, I> TrackListValue<L, I> {
613 const INITIAL_VALUE: Self = TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto));
615
616 fn is_repeat(&self) -> bool {
617 matches!(*self, TrackListValue::TrackRepeat(..))
618 }
619
620 pub fn is_initial(&self) -> bool {
622 matches!(
623 *self,
624 TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto))
625 ) }
627}
628
629impl<L, I> Default for TrackListValue<L, I> {
630 #[inline]
631 fn default() -> Self {
632 Self::INITIAL_VALUE
633 }
634}
635
636#[derive(
640 Clone,
641 Debug,
642 MallocSizeOf,
643 PartialEq,
644 SpecifiedValueInfo,
645 ToAnimatedValue,
646 ToComputedValue,
647 ToResolvedValue,
648 ToShmem,
649)]
650#[repr(C)]
651pub struct GenericTrackList<LengthPercentage, Integer> {
652 #[css(skip)]
654 pub auto_repeat_index: usize,
655 pub values: crate::OwnedSlice<GenericTrackListValue<LengthPercentage, Integer>>,
657 pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
663}
664
665pub use self::GenericTrackList as TrackList;
666
667impl<L, I> TrackList<L, I> {
668 pub fn is_explicit(&self) -> bool {
671 !self.values.iter().any(|v| v.is_repeat())
672 }
673
674 pub fn has_auto_repeat(&self) -> bool {
676 self.auto_repeat_index < self.values.len()
677 }
678}
679
680impl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {
681 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
682 where
683 W: Write,
684 {
685 let mut values_iter = self.values.iter().peekable();
686 let mut line_names_iter = self.line_names.iter().peekable();
687
688 for idx in 0.. {
689 let names = line_names_iter.next().unwrap(); concat_serialize_idents("[", "]", names, " ", dest)?;
691
692 match values_iter.next() {
693 Some(value) => {
694 if !names.is_empty() {
695 dest.write_char(' ')?;
696 }
697
698 value.to_css(dest)?;
699 },
700 None => break,
701 }
702
703 if values_iter.peek().is_some()
704 || line_names_iter.peek().map_or(false, |v| !v.is_empty())
705 || (idx + 1 == self.auto_repeat_index)
706 {
707 dest.write_char(' ')?;
708 }
709 }
710
711 Ok(())
712 }
713}
714
715impl<L: ToTyped, I: ToTyped> ToTyped for TrackList<L, I> {
716 fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
721 if self.values.len() != 1 {
722 return Err(());
723 }
724
725 if self.line_names.iter().any(|names| !names.is_empty()) {
726 return Err(());
727 }
728
729 self.values[0].to_typed(dest)
730 }
731}
732
733#[derive(
739 Clone,
740 Debug,
741 MallocSizeOf,
742 PartialEq,
743 SpecifiedValueInfo,
744 ToAnimatedValue,
745 ToComputedValue,
746 ToResolvedValue,
747 ToShmem,
748)]
749#[repr(C)]
750pub struct GenericNameRepeat<I> {
751 pub count: RepeatCount<I>,
754 pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,
756}
757
758pub use self::GenericNameRepeat as NameRepeat;
759
760impl<I: ToCss> ToCss for NameRepeat<I> {
761 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
762 where
763 W: Write,
764 {
765 dest.write_str("repeat(")?;
766 self.count.to_css(dest)?;
767 dest.write_char(',')?;
768
769 for ref names in self.line_names.iter() {
770 if names.is_empty() {
771 dest.write_str(" []")?;
774 } else {
775 concat_serialize_idents(" [", "]", names, " ", dest)?;
776 }
777 }
778
779 dest.write_char(')')
780 }
781}
782
783impl<I> NameRepeat<I> {
784 #[inline]
786 pub fn is_auto_fill(&self) -> bool {
787 matches!(self.count, RepeatCount::AutoFill)
788 }
789}
790
791#[derive(
793 Clone,
794 Debug,
795 MallocSizeOf,
796 PartialEq,
797 SpecifiedValueInfo,
798 ToAnimatedValue,
799 ToComputedValue,
800 ToResolvedValue,
801 ToShmem,
802)]
803#[repr(C, u8)]
804pub enum GenericLineNameListValue<I> {
805 LineNames(crate::OwnedSlice<CustomIdent>),
807 Repeat(GenericNameRepeat<I>),
809}
810
811pub use self::GenericLineNameListValue as LineNameListValue;
812
813impl<I: ToCss> ToCss for LineNameListValue<I> {
814 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
815 where
816 W: Write,
817 {
818 match *self {
819 Self::Repeat(ref r) => r.to_css(dest),
820 Self::LineNames(ref names) => {
821 dest.write_char('[')?;
822
823 if let Some((ref first, rest)) = names.split_first() {
824 first.to_css(dest)?;
825 for name in rest {
826 dest.write_char(' ')?;
827 name.to_css(dest)?;
828 }
829 }
830
831 dest.write_char(']')
832 },
833 }
834 }
835}
836
837#[derive(
844 Clone,
845 Debug,
846 Default,
847 MallocSizeOf,
848 PartialEq,
849 SpecifiedValueInfo,
850 ToAnimatedValue,
851 ToComputedValue,
852 ToResolvedValue,
853 ToShmem,
854)]
855#[repr(C)]
856pub struct GenericLineNameList<I> {
857 pub expanded_line_names_length: usize,
861 pub line_names: crate::OwnedSlice<GenericLineNameListValue<I>>,
863}
864
865pub use self::GenericLineNameList as LineNameList;
866
867impl<I: ToCss> ToCss for LineNameList<I> {
868 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
869 where
870 W: Write,
871 {
872 dest.write_str("subgrid")?;
873
874 for value in self.line_names.iter() {
875 dest.write_char(' ')?;
876 value.to_css(dest)?;
877 }
878
879 Ok(())
880 }
881}
882
883#[derive(
885 Animate,
886 Clone,
887 Debug,
888 MallocSizeOf,
889 PartialEq,
890 SpecifiedValueInfo,
891 ToAnimatedValue,
892 ToComputedValue,
893 ToCss,
894 ToResolvedValue,
895 ToShmem,
896 ToTyped,
897)]
898#[value_info(other_values = "subgrid")]
899#[repr(C, u8)]
900pub enum GenericGridTemplateComponent<L, I> {
901 None,
903 TrackList(
905 #[animation(field_bound)]
906 #[compute(field_bound)]
907 #[resolve(field_bound)]
908 #[shmem(field_bound)]
909 Box<GenericTrackList<L, I>>,
910 ),
911 #[animation(error)]
914 #[typed(skip)]
915 Subgrid(Box<GenericLineNameList<I>>),
916 #[typed(skip)]
919 Masonry,
920}
921
922pub use self::GenericGridTemplateComponent as GridTemplateComponent;
923
924impl<L, I> GridTemplateComponent<L, I> {
925 const INITIAL_VALUE: Self = Self::None;
927
928 pub fn track_list_len(&self) -> usize {
930 match *self {
931 GridTemplateComponent::TrackList(ref tracklist) => tracklist.values.len(),
932 _ => 0,
933 }
934 }
935
936 pub fn is_initial(&self) -> bool {
938 matches!(*self, Self::None) }
940}
941
942impl<L, I> Default for GridTemplateComponent<L, I> {
943 #[inline]
944 fn default() -> Self {
945 Self::INITIAL_VALUE
946 }
947}