style/values/specified/
align.rs

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