1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 //! Values for CSS Box Alignment properties
6 //!
7 //! https://drafts.csswg.org/css-align/
8 
9 use crate::parser::{Parse, ParserContext};
10 use cssparser::Parser;
11 use std::fmt::{self, Write};
12 use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, ToCss};
13 
14 bitflags! {
15     /// Constants shared by multiple CSS Box Alignment properties
16     #[derive(MallocSizeOf, ToComputedValue, ToResolvedValue, ToShmem)]
17     #[repr(C)]
18     pub struct AlignFlags: u8 {
19         // Enumeration stored in the lower 5 bits:
20         /// {align,justify}-{content,items,self}: 'auto'
21         const AUTO = 0;
22         /// 'normal'
23         const NORMAL = 1;
24         /// 'start'
25         const START = 2;
26         /// 'end'
27         const END = 3;
28         /// 'flex-start'
29         const FLEX_START = 4;
30         /// 'flex-end'
31         const FLEX_END = 5;
32         /// 'center'
33         const CENTER = 6;
34         /// 'left'
35         const LEFT = 7;
36         /// 'right'
37         const RIGHT = 8;
38         /// 'baseline'
39         const BASELINE = 9;
40         /// 'last-baseline'
41         const LAST_BASELINE = 10;
42         /// 'stretch'
43         const STRETCH = 11;
44         /// 'self-start'
45         const SELF_START = 12;
46         /// 'self-end'
47         const SELF_END = 13;
48         /// 'space-between'
49         const SPACE_BETWEEN = 14;
50         /// 'space-around'
51         const SPACE_AROUND = 15;
52         /// 'space-evenly'
53         const SPACE_EVENLY = 16;
54 
55         // Additional flags stored in the upper bits:
56         /// 'legacy' (mutually exclusive w. SAFE & UNSAFE)
57         const LEGACY = 1 << 5;
58         /// 'safe'
59         const SAFE = 1 << 6;
60         /// 'unsafe' (mutually exclusive w. SAFE)
61         const UNSAFE = 1 << 7;
62 
63         /// Mask for the additional flags above.
64         const FLAG_BITS = 0b11100000;
65     }
66 }
67 
68 impl AlignFlags {
69     /// Returns the enumeration value stored in the lower 5 bits.
70     #[inline]
value(&self) -> Self71     fn value(&self) -> Self {
72         *self & !AlignFlags::FLAG_BITS
73     }
74 }
75 
76 impl ToCss for AlignFlags {
to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: Write,77     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
78     where
79         W: Write,
80     {
81         let extra_flags = *self & AlignFlags::FLAG_BITS;
82         let value = self.value();
83 
84         match extra_flags {
85             AlignFlags::LEGACY => {
86                 dest.write_str("legacy")?;
87                 if value.is_empty() {
88                     return Ok(());
89                 }
90                 dest.write_char(' ')?;
91             },
92             AlignFlags::SAFE => dest.write_str("safe ")?,
93             AlignFlags::UNSAFE => dest.write_str("unsafe ")?,
94             _ => {
95                 debug_assert_eq!(extra_flags, AlignFlags::empty());
96             },
97         }
98 
99         dest.write_str(match value {
100             AlignFlags::AUTO => "auto",
101             AlignFlags::NORMAL => "normal",
102             AlignFlags::START => "start",
103             AlignFlags::END => "end",
104             AlignFlags::FLEX_START => "flex-start",
105             AlignFlags::FLEX_END => "flex-end",
106             AlignFlags::CENTER => "center",
107             AlignFlags::LEFT => "left",
108             AlignFlags::RIGHT => "right",
109             AlignFlags::BASELINE => "baseline",
110             AlignFlags::LAST_BASELINE => "last baseline",
111             AlignFlags::STRETCH => "stretch",
112             AlignFlags::SELF_START => "self-start",
113             AlignFlags::SELF_END => "self-end",
114             AlignFlags::SPACE_BETWEEN => "space-between",
115             AlignFlags::SPACE_AROUND => "space-around",
116             AlignFlags::SPACE_EVENLY => "space-evenly",
117             _ => unreachable!(),
118         })
119     }
120 }
121 
122 /// An axis direction, either inline (for the `justify` properties) or block,
123 /// (for the `align` properties).
124 #[derive(Clone, Copy, PartialEq)]
125 pub enum AxisDirection {
126     /// Block direction.
127     Block,
128     /// Inline direction.
129     Inline,
130 }
131 
132 /// Shared value for the `align-content` and `justify-content` properties.
133 ///
134 /// <https://drafts.csswg.org/css-align/#content-distribution>
135 #[derive(
136     Clone,
137     Copy,
138     Debug,
139     Eq,
140     MallocSizeOf,
141     PartialEq,
142     ToComputedValue,
143     ToCss,
144     ToResolvedValue,
145     ToShmem,
146 )]
147 #[repr(C)]
148 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
149 pub struct ContentDistribution {
150     primary: AlignFlags,
151     // FIXME(https://github.com/w3c/csswg-drafts/issues/1002): This will need to
152     // accept fallback alignment, eventually.
153 }
154 
155 impl ContentDistribution {
156     /// The initial value 'normal'
157     #[inline]
normal() -> Self158     pub fn normal() -> Self {
159         Self::new(AlignFlags::NORMAL)
160     }
161 
162     /// `start`
163     #[inline]
start() -> Self164     pub fn start() -> Self {
165         Self::new(AlignFlags::START)
166     }
167 
168     /// The initial value 'normal'
169     #[inline]
new(primary: AlignFlags) -> Self170     pub fn new(primary: AlignFlags) -> Self {
171         Self { primary }
172     }
173 
174     /// Returns whether this value is a <baseline-position>.
is_baseline_position(&self) -> bool175     pub fn is_baseline_position(&self) -> bool {
176         matches!(
177             self.primary.value(),
178             AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
179         )
180     }
181 
182     /// The primary alignment
183     #[inline]
primary(self) -> AlignFlags184     pub fn primary(self) -> AlignFlags {
185         self.primary
186     }
187 
188     /// Parse a value for align-content / justify-content.
parse<'i, 't>( input: &mut Parser<'i, 't>, axis: AxisDirection, ) -> Result<Self, ParseError<'i>>189     pub fn parse<'i, 't>(
190         input: &mut Parser<'i, 't>,
191         axis: AxisDirection,
192     ) -> Result<Self, ParseError<'i>> {
193         // NOTE Please also update the `list_keywords` function below
194         //      when this function is updated.
195 
196         // Try to parse normal first
197         if input
198             .try_parse(|i| i.expect_ident_matching("normal"))
199             .is_ok()
200         {
201             return Ok(ContentDistribution::normal());
202         }
203 
204         // Parse <baseline-position>, but only on the block axis.
205         if axis == AxisDirection::Block {
206             if let Ok(value) = input.try_parse(parse_baseline) {
207                 return Ok(ContentDistribution::new(value));
208             }
209         }
210 
211         // <content-distribution>
212         if let Ok(value) = input.try_parse(parse_content_distribution) {
213             return Ok(ContentDistribution::new(value));
214         }
215 
216         // <overflow-position>? <content-position>
217         let overflow_position = input
218             .try_parse(parse_overflow_position)
219             .unwrap_or(AlignFlags::empty());
220 
221         let content_position = try_match_ident_ignore_ascii_case! { input,
222             "start" => AlignFlags::START,
223             "end" => AlignFlags::END,
224             "flex-start" => AlignFlags::FLEX_START,
225             "flex-end" => AlignFlags::FLEX_END,
226             "center" => AlignFlags::CENTER,
227             "left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
228             "right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
229         };
230 
231         Ok(ContentDistribution::new(
232             content_position | overflow_position,
233         ))
234     }
235 
list_keywords(f: KeywordsCollectFn, axis: AxisDirection)236     fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
237         f(&["normal"]);
238         if axis == AxisDirection::Block {
239             list_baseline_keywords(f);
240         }
241         list_content_distribution_keywords(f);
242         list_overflow_position_keywords(f);
243         f(&["start", "end", "flex-start", "flex-end", "center"]);
244         if axis == AxisDirection::Inline {
245             f(&["left", "right"]);
246         }
247     }
248 }
249 
250 /// Value for the `align-content` property.
251 ///
252 /// <https://drafts.csswg.org/css-align/#propdef-align-content>
253 #[derive(
254     Clone,
255     Copy,
256     Debug,
257     Eq,
258     MallocSizeOf,
259     PartialEq,
260     ToComputedValue,
261     ToCss,
262     ToResolvedValue,
263     ToShmem,
264 )]
265 #[repr(transparent)]
266 pub struct AlignContent(pub ContentDistribution);
267 
268 impl Parse for AlignContent {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>269     fn parse<'i, 't>(
270         _: &ParserContext,
271         input: &mut Parser<'i, 't>,
272     ) -> Result<Self, ParseError<'i>> {
273         // NOTE Please also update `impl SpecifiedValueInfo` below when
274         //      this function is updated.
275         Ok(AlignContent(ContentDistribution::parse(
276             input,
277             AxisDirection::Block,
278         )?))
279     }
280 }
281 
282 impl SpecifiedValueInfo for AlignContent {
collect_completion_keywords(f: KeywordsCollectFn)283     fn collect_completion_keywords(f: KeywordsCollectFn) {
284         ContentDistribution::list_keywords(f, AxisDirection::Block);
285     }
286 }
287 
288 /// Value for the `align-tracks` property.
289 ///
290 /// <https://github.com/w3c/csswg-drafts/issues/4650>
291 #[derive(
292     Clone,
293     Debug,
294     Default,
295     Eq,
296     MallocSizeOf,
297     PartialEq,
298     SpecifiedValueInfo,
299     ToComputedValue,
300     ToCss,
301     ToResolvedValue,
302     ToShmem,
303 )]
304 #[repr(transparent)]
305 #[css(comma)]
306 pub struct AlignTracks(#[css(iterable, if_empty = "normal")] pub crate::OwnedSlice<AlignContent>);
307 
308 impl Parse for AlignTracks {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>309     fn parse<'i, 't>(
310         context: &ParserContext,
311         input: &mut Parser<'i, 't>,
312     ) -> Result<Self, ParseError<'i>> {
313         let values = input.parse_comma_separated(|input| AlignContent::parse(context, input))?;
314         Ok(AlignTracks(values.into()))
315     }
316 }
317 
318 /// Value for the `justify-content` property.
319 ///
320 /// <https://drafts.csswg.org/css-align/#propdef-justify-content>
321 #[derive(
322     Clone,
323     Copy,
324     Debug,
325     Eq,
326     MallocSizeOf,
327     PartialEq,
328     ToComputedValue,
329     ToCss,
330     ToResolvedValue,
331     ToShmem,
332 )]
333 #[repr(transparent)]
334 pub struct JustifyContent(pub ContentDistribution);
335 
336 impl Parse for JustifyContent {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>337     fn parse<'i, 't>(
338         _: &ParserContext,
339         input: &mut Parser<'i, 't>,
340     ) -> Result<Self, ParseError<'i>> {
341         // NOTE Please also update `impl SpecifiedValueInfo` below when
342         //      this function is updated.
343         Ok(JustifyContent(ContentDistribution::parse(
344             input,
345             AxisDirection::Inline,
346         )?))
347     }
348 }
349 
350 impl SpecifiedValueInfo for JustifyContent {
collect_completion_keywords(f: KeywordsCollectFn)351     fn collect_completion_keywords(f: KeywordsCollectFn) {
352         ContentDistribution::list_keywords(f, AxisDirection::Inline);
353     }
354 }
355 /// Value for the `justify-tracks` property.
356 ///
357 /// <https://github.com/w3c/csswg-drafts/issues/4650>
358 #[derive(
359     Clone,
360     Debug,
361     Default,
362     Eq,
363     MallocSizeOf,
364     PartialEq,
365     SpecifiedValueInfo,
366     ToComputedValue,
367     ToCss,
368     ToResolvedValue,
369     ToShmem,
370 )]
371 #[repr(transparent)]
372 #[css(comma)]
373 pub struct JustifyTracks(
374     #[css(iterable, if_empty = "normal")] pub crate::OwnedSlice<JustifyContent>,
375 );
376 
377 impl Parse for JustifyTracks {
parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>378     fn parse<'i, 't>(
379         context: &ParserContext,
380         input: &mut Parser<'i, 't>,
381     ) -> Result<Self, ParseError<'i>> {
382         let values = input.parse_comma_separated(|input| JustifyContent::parse(context, input))?;
383         Ok(JustifyTracks(values.into()))
384     }
385 }
386 
387 /// <https://drafts.csswg.org/css-align/#self-alignment>
388 #[derive(
389     Clone,
390     Copy,
391     Debug,
392     Eq,
393     MallocSizeOf,
394     PartialEq,
395     ToComputedValue,
396     ToCss,
397     ToResolvedValue,
398     ToShmem,
399 )]
400 #[repr(transparent)]
401 pub struct SelfAlignment(pub AlignFlags);
402 
403 impl SelfAlignment {
404     /// The initial value 'auto'
405     #[inline]
auto() -> Self406     pub fn auto() -> Self {
407         SelfAlignment(AlignFlags::AUTO)
408     }
409 
410     /// Returns whether this value is valid for both axis directions.
is_valid_on_both_axes(&self) -> bool411     pub fn is_valid_on_both_axes(&self) -> bool {
412         match self.0.value() {
413             // left | right are only allowed on the inline axis.
414             AlignFlags::LEFT | AlignFlags::RIGHT => false,
415 
416             _ => true,
417         }
418     }
419 
420     /// Parse a self-alignment value on one of the axis.
parse<'i, 't>( input: &mut Parser<'i, 't>, axis: AxisDirection, ) -> Result<Self, ParseError<'i>>421     pub fn parse<'i, 't>(
422         input: &mut Parser<'i, 't>,
423         axis: AxisDirection,
424     ) -> Result<Self, ParseError<'i>> {
425         // NOTE Please also update the `list_keywords` function below
426         //      when this function is updated.
427 
428         // <baseline-position>
429         //
430         // It's weird that this accepts <baseline-position>, but not
431         // justify-content...
432         if let Ok(value) = input.try_parse(parse_baseline) {
433             return Ok(SelfAlignment(value));
434         }
435 
436         // auto | normal | stretch
437         if let Ok(value) = input.try_parse(parse_auto_normal_stretch) {
438             return Ok(SelfAlignment(value));
439         }
440 
441         // <overflow-position>? <self-position>
442         let overflow_position = input
443             .try_parse(parse_overflow_position)
444             .unwrap_or(AlignFlags::empty());
445         let self_position = parse_self_position(input, axis)?;
446         Ok(SelfAlignment(overflow_position | self_position))
447     }
448 
list_keywords(f: KeywordsCollectFn, axis: AxisDirection)449     fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
450         list_baseline_keywords(f);
451         list_auto_normal_stretch(f);
452         list_overflow_position_keywords(f);
453         list_self_position_keywords(f, axis);
454     }
455 }
456 
457 /// The specified value of the align-self property.
458 ///
459 /// <https://drafts.csswg.org/css-align/#propdef-align-self>
460 #[derive(
461     Clone,
462     Copy,
463     Debug,
464     Eq,
465     MallocSizeOf,
466     PartialEq,
467     ToComputedValue,
468     ToCss,
469     ToResolvedValue,
470     ToShmem,
471 )]
472 #[repr(C)]
473 pub struct AlignSelf(pub SelfAlignment);
474 
475 impl Parse for AlignSelf {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>476     fn parse<'i, 't>(
477         _: &ParserContext,
478         input: &mut Parser<'i, 't>,
479     ) -> Result<Self, ParseError<'i>> {
480         // NOTE Please also update `impl SpecifiedValueInfo` below when
481         //      this function is updated.
482         Ok(AlignSelf(SelfAlignment::parse(
483             input,
484             AxisDirection::Block,
485         )?))
486     }
487 }
488 
489 impl SpecifiedValueInfo for AlignSelf {
collect_completion_keywords(f: KeywordsCollectFn)490     fn collect_completion_keywords(f: KeywordsCollectFn) {
491         SelfAlignment::list_keywords(f, AxisDirection::Block);
492     }
493 }
494 
495 /// The specified value of the justify-self property.
496 ///
497 /// <https://drafts.csswg.org/css-align/#propdef-justify-self>
498 #[derive(
499     Clone,
500     Copy,
501     Debug,
502     Eq,
503     MallocSizeOf,
504     PartialEq,
505     ToComputedValue,
506     ToCss,
507     ToResolvedValue,
508     ToShmem,
509 )]
510 #[repr(C)]
511 pub struct JustifySelf(pub SelfAlignment);
512 
513 impl Parse for JustifySelf {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>514     fn parse<'i, 't>(
515         _: &ParserContext,
516         input: &mut Parser<'i, 't>,
517     ) -> Result<Self, ParseError<'i>> {
518         // NOTE Please also update `impl SpecifiedValueInfo` below when
519         //      this function is updated.
520         Ok(JustifySelf(SelfAlignment::parse(
521             input,
522             AxisDirection::Inline,
523         )?))
524     }
525 }
526 
527 impl SpecifiedValueInfo for JustifySelf {
collect_completion_keywords(f: KeywordsCollectFn)528     fn collect_completion_keywords(f: KeywordsCollectFn) {
529         SelfAlignment::list_keywords(f, AxisDirection::Inline);
530     }
531 }
532 
533 /// Value of the `align-items` property
534 ///
535 /// <https://drafts.csswg.org/css-align/#propdef-align-items>
536 #[derive(
537     Clone,
538     Copy,
539     Debug,
540     Eq,
541     MallocSizeOf,
542     PartialEq,
543     ToComputedValue,
544     ToCss,
545     ToResolvedValue,
546     ToShmem,
547 )]
548 #[repr(C)]
549 pub struct AlignItems(pub AlignFlags);
550 
551 impl AlignItems {
552     /// The initial value 'normal'
553     #[inline]
normal() -> Self554     pub fn normal() -> Self {
555         AlignItems(AlignFlags::NORMAL)
556     }
557 }
558 
559 impl Parse for AlignItems {
560     // normal | stretch | <baseline-position> |
561     // <overflow-position>? <self-position>
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>562     fn parse<'i, 't>(
563         _: &ParserContext,
564         input: &mut Parser<'i, 't>,
565     ) -> Result<Self, ParseError<'i>> {
566         // NOTE Please also update `impl SpecifiedValueInfo` below when
567         //      this function is updated.
568 
569         // <baseline-position>
570         if let Ok(baseline) = input.try_parse(parse_baseline) {
571             return Ok(AlignItems(baseline));
572         }
573 
574         // normal | stretch
575         if let Ok(value) = input.try_parse(parse_normal_stretch) {
576             return Ok(AlignItems(value));
577         }
578         // <overflow-position>? <self-position>
579         let overflow = input
580             .try_parse(parse_overflow_position)
581             .unwrap_or(AlignFlags::empty());
582         let self_position = parse_self_position(input, AxisDirection::Block)?;
583         Ok(AlignItems(self_position | overflow))
584     }
585 }
586 
587 impl SpecifiedValueInfo for AlignItems {
collect_completion_keywords(f: KeywordsCollectFn)588     fn collect_completion_keywords(f: KeywordsCollectFn) {
589         list_baseline_keywords(f);
590         list_normal_stretch(f);
591         list_overflow_position_keywords(f);
592         list_self_position_keywords(f, AxisDirection::Block);
593     }
594 }
595 
596 /// Value of the `justify-items` property
597 ///
598 /// <https://drafts.csswg.org/css-align/#justify-items-property>
599 #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
600 #[repr(C)]
601 pub struct JustifyItems(pub AlignFlags);
602 
603 impl JustifyItems {
604     /// The initial value 'legacy'
605     #[inline]
legacy() -> Self606     pub fn legacy() -> Self {
607         JustifyItems(AlignFlags::LEGACY)
608     }
609 
610     /// The value 'normal'
611     #[inline]
normal() -> Self612     pub fn normal() -> Self {
613         JustifyItems(AlignFlags::NORMAL)
614     }
615 }
616 
617 impl Parse for JustifyItems {
parse<'i, 't>( _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result<Self, ParseError<'i>>618     fn parse<'i, 't>(
619         _: &ParserContext,
620         input: &mut Parser<'i, 't>,
621     ) -> Result<Self, ParseError<'i>> {
622         // NOTE Please also update `impl SpecifiedValueInfo` below when
623         //      this function is updated.
624 
625         // <baseline-position>
626         //
627         // It's weird that this accepts <baseline-position>, but not
628         // justify-content...
629         if let Ok(baseline) = input.try_parse(parse_baseline) {
630             return Ok(JustifyItems(baseline));
631         }
632 
633         // normal | stretch
634         if let Ok(value) = input.try_parse(parse_normal_stretch) {
635             return Ok(JustifyItems(value));
636         }
637 
638         // legacy | [ legacy && [ left | right | center ] ]
639         if let Ok(value) = input.try_parse(parse_legacy) {
640             return Ok(JustifyItems(value));
641         }
642 
643         // <overflow-position>? <self-position>
644         let overflow = input
645             .try_parse(parse_overflow_position)
646             .unwrap_or(AlignFlags::empty());
647         let self_position = parse_self_position(input, AxisDirection::Inline)?;
648         Ok(JustifyItems(overflow | self_position))
649     }
650 }
651 
652 impl SpecifiedValueInfo for JustifyItems {
collect_completion_keywords(f: KeywordsCollectFn)653     fn collect_completion_keywords(f: KeywordsCollectFn) {
654         list_baseline_keywords(f);
655         list_normal_stretch(f);
656         list_legacy_keywords(f);
657         list_overflow_position_keywords(f);
658         list_self_position_keywords(f, AxisDirection::Inline);
659     }
660 }
661 
662 // auto | normal | stretch
parse_auto_normal_stretch<'i, 't>( input: &mut Parser<'i, 't>, ) -> Result<AlignFlags, ParseError<'i>>663 fn parse_auto_normal_stretch<'i, 't>(
664     input: &mut Parser<'i, 't>,
665 ) -> Result<AlignFlags, ParseError<'i>> {
666     // NOTE Please also update the `list_auto_normal_stretch` function
667     //      below when this function is updated.
668     try_match_ident_ignore_ascii_case! { input,
669         "auto" => Ok(AlignFlags::AUTO),
670         "normal" => Ok(AlignFlags::NORMAL),
671         "stretch" => Ok(AlignFlags::STRETCH),
672     }
673 }
674 
list_auto_normal_stretch(f: KeywordsCollectFn)675 fn list_auto_normal_stretch(f: KeywordsCollectFn) {
676     f(&["auto", "normal", "stretch"]);
677 }
678 
679 // normal | stretch
parse_normal_stretch<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>>680 fn parse_normal_stretch<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
681     // NOTE Please also update the `list_normal_stretch` function below
682     //      when this function is updated.
683     try_match_ident_ignore_ascii_case! { input,
684         "normal" => Ok(AlignFlags::NORMAL),
685         "stretch" => Ok(AlignFlags::STRETCH),
686     }
687 }
688 
list_normal_stretch(f: KeywordsCollectFn)689 fn list_normal_stretch(f: KeywordsCollectFn) {
690     f(&["normal", "stretch"]);
691 }
692 
693 // <baseline-position>
parse_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>>694 fn parse_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
695     // NOTE Please also update the `list_baseline_keywords` function
696     //      below when this function is updated.
697     try_match_ident_ignore_ascii_case! { input,
698         "baseline" => Ok(AlignFlags::BASELINE),
699         "first" => {
700             input.expect_ident_matching("baseline")?;
701             Ok(AlignFlags::BASELINE)
702         },
703         "last" => {
704             input.expect_ident_matching("baseline")?;
705             Ok(AlignFlags::LAST_BASELINE)
706         },
707     }
708 }
709 
list_baseline_keywords(f: KeywordsCollectFn)710 fn list_baseline_keywords(f: KeywordsCollectFn) {
711     f(&["baseline", "first baseline", "last baseline"]);
712 }
713 
714 // <content-distribution>
parse_content_distribution<'i, 't>( input: &mut Parser<'i, 't>, ) -> Result<AlignFlags, ParseError<'i>>715 fn parse_content_distribution<'i, 't>(
716     input: &mut Parser<'i, 't>,
717 ) -> Result<AlignFlags, ParseError<'i>> {
718     // NOTE Please also update the `list_content_distribution_keywords`
719     //      function below when this function is updated.
720     try_match_ident_ignore_ascii_case! { input,
721         "stretch" => Ok(AlignFlags::STRETCH),
722         "space-between" => Ok(AlignFlags::SPACE_BETWEEN),
723         "space-around" => Ok(AlignFlags::SPACE_AROUND),
724         "space-evenly" => Ok(AlignFlags::SPACE_EVENLY),
725     }
726 }
727 
list_content_distribution_keywords(f: KeywordsCollectFn)728 fn list_content_distribution_keywords(f: KeywordsCollectFn) {
729     f(&["stretch", "space-between", "space-around", "space-evenly"]);
730 }
731 
732 // <overflow-position>
parse_overflow_position<'i, 't>( input: &mut Parser<'i, 't>, ) -> Result<AlignFlags, ParseError<'i>>733 fn parse_overflow_position<'i, 't>(
734     input: &mut Parser<'i, 't>,
735 ) -> Result<AlignFlags, ParseError<'i>> {
736     // NOTE Please also update the `list_overflow_position_keywords`
737     //      function below when this function is updated.
738     try_match_ident_ignore_ascii_case! { input,
739         "safe" => Ok(AlignFlags::SAFE),
740         "unsafe" => Ok(AlignFlags::UNSAFE),
741     }
742 }
743 
list_overflow_position_keywords(f: KeywordsCollectFn)744 fn list_overflow_position_keywords(f: KeywordsCollectFn) {
745     f(&["safe", "unsafe"]);
746 }
747 
748 // <self-position> | left | right in the inline axis.
parse_self_position<'i, 't>( input: &mut Parser<'i, 't>, axis: AxisDirection, ) -> Result<AlignFlags, ParseError<'i>>749 fn parse_self_position<'i, 't>(
750     input: &mut Parser<'i, 't>,
751     axis: AxisDirection,
752 ) -> Result<AlignFlags, ParseError<'i>> {
753     // NOTE Please also update the `list_self_position_keywords`
754     //      function below when this function is updated.
755     Ok(try_match_ident_ignore_ascii_case! { input,
756         "start" => AlignFlags::START,
757         "end" => AlignFlags::END,
758         "flex-start" => AlignFlags::FLEX_START,
759         "flex-end" => AlignFlags::FLEX_END,
760         "center" => AlignFlags::CENTER,
761         "self-start" => AlignFlags::SELF_START,
762         "self-end" => AlignFlags::SELF_END,
763         "left" if axis == AxisDirection::Inline => AlignFlags::LEFT,
764         "right" if axis == AxisDirection::Inline => AlignFlags::RIGHT,
765     })
766 }
767 
list_self_position_keywords(f: KeywordsCollectFn, axis: AxisDirection)768 fn list_self_position_keywords(f: KeywordsCollectFn, axis: AxisDirection) {
769     f(&[
770         "start",
771         "end",
772         "flex-start",
773         "flex-end",
774         "center",
775         "self-start",
776         "self-end",
777     ]);
778     if axis == AxisDirection::Inline {
779         f(&["left", "right"]);
780     }
781 }
782 
parse_left_right_center<'i, 't>( input: &mut Parser<'i, 't>, ) -> Result<AlignFlags, ParseError<'i>>783 fn parse_left_right_center<'i, 't>(
784     input: &mut Parser<'i, 't>,
785 ) -> Result<AlignFlags, ParseError<'i>> {
786     // NOTE Please also update the `list_legacy_keywords` function below
787     //      when this function is updated.
788     Ok(try_match_ident_ignore_ascii_case! { input,
789         "left" => AlignFlags::LEFT,
790         "right" => AlignFlags::RIGHT,
791         "center" => AlignFlags::CENTER,
792     })
793 }
794 
795 // legacy | [ legacy && [ left | right | center ] ]
parse_legacy<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>>796 fn parse_legacy<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {
797     // NOTE Please also update the `list_legacy_keywords` function below
798     //      when this function is updated.
799     let flags = try_match_ident_ignore_ascii_case! { input,
800         "legacy" => {
801             let flags = input.try_parse(parse_left_right_center)
802                 .unwrap_or(AlignFlags::empty());
803 
804             return Ok(AlignFlags::LEGACY | flags)
805         },
806         "left" => AlignFlags::LEFT,
807         "right" => AlignFlags::RIGHT,
808         "center" => AlignFlags::CENTER,
809     };
810 
811     input.expect_ident_matching("legacy")?;
812     Ok(AlignFlags::LEGACY | flags)
813 }
814 
list_legacy_keywords(f: KeywordsCollectFn)815 fn list_legacy_keywords(f: KeywordsCollectFn) {
816     f(&["legacy", "left", "right", "center"]);
817 }
818