Skip to main content

style/values/specified/
box.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//! Specified types for box properties.
6
7use crate::derives::*;
8pub use crate::logical_geometry::WritingModeProperty;
9use crate::parser::{Parse, ParserContext};
10use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};
11pub use crate::typed_om::{KeywordValue, ToTyped, TypedValue};
12use crate::values::CustomIdent;
13use crate::values::generics::box_::{
14    BaselineShiftKeyword, BlockEllipsis, GenericBaselineShift, GenericContainIntrinsicSize,
15    GenericLineClamp, GenericOverflowClipMargin, GenericPerspective, GenericScrollbarInset,
16    MaxLines, OverflowClipMarginBox,
17};
18use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
19use crate::values::specified::{AllowQuirks, NonNegativeNumberOrPercentage, PositiveInteger};
20use cssparser::Parser;
21use num_traits::FromPrimitive;
22use std::fmt::{self, Write};
23use style_traits::{CssWriter, KeywordsCollectFn, ParseError /*CssString*/};
24use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
25use thin_vec::ThinVec;
26
27#[inline]
28fn appearance_base_enabled(_context: &ParserContext) -> bool {
29    crate::pref!("layout.css.appearance-base.enabled")
30}
31
32#[inline]
33fn appearance_base_select_enabled(_context: &ParserContext) -> bool {
34    crate::pref!("dom.select.customizable_select.enabled")
35}
36
37#[derive(
38    Clone,
39    Copy,
40    Debug,
41    Eq,
42    MallocSizeOf,
43    PartialEq,
44    Parse,
45    ToCss,
46    SpecifiedValueInfo,
47    ToComputedValue,
48    ToResolvedValue,
49    ToShmem,
50    ToTyped,
51)]
52#[css(bitflags(
53    single = "none",
54    mixed = "block,block-start,block-end",
55    overlapping_bits
56))]
57#[repr(C)]
58/// Specified value of the `margin-trim` property, which trims the margins of a
59/// container's children where they meet the container's edges.
60/// https://drafts.csswg.org/css-box-4/#propdef-margin-trim
61/// Pending https://github.com/w3c/csswg-drafts/issues/13731, we're only targetting block
62/// trimming for now and skipping `inline-start` and `inline-end`
63pub struct MarginTrim(u8);
64bitflags! {
65    impl MarginTrim: u8 {
66        /// `none` variant, no margins are trimmed.
67        const NONE = 0;
68        /// Trim the block-start margin of the first child.
69        const BLOCK_START = 1 << 0;
70        /// Trim the block-end margin of the last child.
71        const BLOCK_END = 1 << 1;
72        /// `block` shorthand: both block-axis margins.
73        const BLOCK = MarginTrim::BLOCK_START.bits() | MarginTrim::BLOCK_END.bits();
74    }
75}
76
77/// The specified value of `overflow-clip-margin`.
78pub type OverflowClipMargin = GenericOverflowClipMargin<NonNegativeLength>;
79
80impl Parse for OverflowClipMargin {
81    // <visual-box> || <length [0,∞]>
82    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
83        use crate::Zero;
84        let mut offset = None;
85        let mut visual_box = None;
86        loop {
87            if offset.is_none() {
88                offset = input
89                    .try_parse(|i| NonNegativeLength::parse(context, i))
90                    .ok();
91            }
92            if visual_box.is_none() {
93                visual_box = input.try_parse(OverflowClipMarginBox::parse).ok();
94                if visual_box.is_some() {
95                    continue;
96                }
97            }
98            break;
99        }
100        if offset.is_none() && visual_box.is_none() {
101            return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
102        }
103        Ok(Self {
104            offset: offset.unwrap_or_else(NonNegativeLength::zero),
105            visual_box: visual_box.unwrap_or(OverflowClipMarginBox::PaddingBox),
106        })
107    }
108}
109
110/// The specified value of `-moz-scrollbar-inset-block` / `-inline`.
111pub type ScrollbarInset = GenericScrollbarInset<NonNegativeLength>;
112
113impl Parse for ScrollbarInset {
114    // <length>{1,2}
115    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
116        let start = NonNegativeLength::parse(context, input)?;
117        let end = input
118            .try_parse(|i| NonNegativeLength::parse(context, i))
119            .unwrap_or_else(|_| start.clone());
120        Ok(Self { start, end })
121    }
122}
123
124#[inline]
125fn is_display_grid_lanes_enabled() -> bool {
126    static_prefs::pref!("layout.css.display-grid-lanes.enabled")
127}
128
129/// Defines an element’s display type, which consists of
130/// the two basic qualities of how an element generates boxes
131/// <https://drafts.csswg.org/css-display/#propdef-display>
132#[allow(missing_docs)]
133#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
134#[repr(u8)]
135pub enum DisplayOutside {
136    None = 0,
137    Inline,
138    Block,
139    TableCaption,
140    InternalTable,
141    #[cfg(feature = "gecko")]
142    InternalRuby,
143}
144
145#[allow(missing_docs)]
146#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
147#[repr(u8)]
148pub enum DisplayInside {
149    None = 0,
150    Contents,
151    Flow,
152    FlowRoot,
153    Flex,
154    Grid,
155    GridLanes,
156    Table,
157    TableRowGroup,
158    TableColumn,
159    TableColumnGroup,
160    TableHeaderGroup,
161    TableFooterGroup,
162    TableRow,
163    TableCell,
164    #[cfg(feature = "gecko")]
165    Ruby,
166    #[cfg(feature = "gecko")]
167    RubyBase,
168    #[cfg(feature = "gecko")]
169    RubyBaseContainer,
170    #[cfg(feature = "gecko")]
171    RubyText,
172    #[cfg(feature = "gecko")]
173    RubyTextContainer,
174    #[cfg(feature = "gecko")]
175    WebkitBox,
176}
177
178impl DisplayInside {
179    fn is_valid_for_list_item(self) -> bool {
180        match self {
181            DisplayInside::Flow => true,
182            #[cfg(feature = "gecko")]
183            DisplayInside::FlowRoot => true,
184            _ => false,
185        }
186    }
187
188    /// https://drafts.csswg.org/css-display/#inside-model:
189    ///     If <display-outside> is omitted, the element’s outside display type defaults to block
190    ///     — except for ruby, which defaults to inline.
191    fn default_display_outside(self) -> DisplayOutside {
192        match self {
193            #[cfg(feature = "gecko")]
194            DisplayInside::Ruby => DisplayOutside::Inline,
195            _ => DisplayOutside::Block,
196        }
197    }
198}
199
200#[allow(missing_docs)]
201#[derive(
202    Clone,
203    Copy,
204    Debug,
205    Eq,
206    FromPrimitive,
207    Hash,
208    MallocSizeOf,
209    PartialEq,
210    ToAnimatedValue,
211    ToComputedValue,
212    ToResolvedValue,
213    ToShmem,
214)]
215#[repr(C)]
216pub struct Display(u16);
217
218/// Gecko-only impl block for Display (shared stuff later in this file):
219#[allow(missing_docs)]
220#[allow(non_upper_case_globals)]
221impl Display {
222    // Our u16 bits are used as follows: LOOOOOOOIIIIIIII
223    pub const LIST_ITEM_MASK: u16 = 0b1000000000000000;
224    pub const OUTSIDE_MASK: u16 = 0b0111111100000000;
225    pub const INSIDE_MASK: u16 = 0b0000000011111111;
226    pub const OUTSIDE_SHIFT: u16 = 8;
227
228    /// https://drafts.csswg.org/css-display/#the-display-properties
229    /// ::new() inlined so cbindgen can use it
230    pub const None: Self =
231        Self(((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::None as u16);
232    pub const Contents: Self = Self(
233        ((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Contents as u16,
234    );
235    pub const Inline: Self =
236        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16);
237    pub const InlineBlock: Self = Self(
238        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::FlowRoot as u16,
239    );
240    pub const Block: Self =
241        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16);
242    #[cfg(feature = "gecko")]
243    pub const FlowRoot: Self = Self(
244        ((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::FlowRoot as u16,
245    );
246    pub const Flex: Self =
247        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flex as u16);
248    pub const InlineFlex: Self =
249        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flex as u16);
250    pub const Grid: Self =
251        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);
252    pub const InlineGrid: Self =
253        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);
254    pub const GridLanes: Self = Self(
255        ((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::GridLanes as u16,
256    );
257    pub const InlineGridLanes: Self = Self(
258        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::GridLanes as u16,
259    );
260    pub const Table: Self =
261        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16);
262    pub const InlineTable: Self = Self(
263        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16,
264    );
265    pub const TableCaption: Self = Self(
266        ((DisplayOutside::TableCaption as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16,
267    );
268    #[cfg(feature = "gecko")]
269    pub const Ruby: Self =
270        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Ruby as u16);
271    #[cfg(feature = "gecko")]
272    pub const WebkitBox: Self = Self(
273        ((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::WebkitBox as u16,
274    );
275    #[cfg(feature = "gecko")]
276    pub const WebkitInlineBox: Self = Self(
277        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::WebkitBox as u16,
278    );
279
280    // Internal table boxes.
281
282    pub const TableRowGroup: Self = Self(
283        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
284            | DisplayInside::TableRowGroup as u16,
285    );
286    pub const TableHeaderGroup: Self = Self(
287        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
288            | DisplayInside::TableHeaderGroup as u16,
289    );
290    pub const TableFooterGroup: Self = Self(
291        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
292            | DisplayInside::TableFooterGroup as u16,
293    );
294    pub const TableColumn: Self = Self(
295        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
296            | DisplayInside::TableColumn as u16,
297    );
298    pub const TableColumnGroup: Self = Self(
299        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
300            | DisplayInside::TableColumnGroup as u16,
301    );
302    pub const TableRow: Self = Self(
303        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
304            | DisplayInside::TableRow as u16,
305    );
306    pub const TableCell: Self = Self(
307        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
308            | DisplayInside::TableCell as u16,
309    );
310
311    /// Internal ruby boxes.
312    #[cfg(feature = "gecko")]
313    pub const RubyBase: Self = Self(
314        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)
315            | DisplayInside::RubyBase as u16,
316    );
317    #[cfg(feature = "gecko")]
318    pub const RubyBaseContainer: Self = Self(
319        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)
320            | DisplayInside::RubyBaseContainer as u16,
321    );
322    #[cfg(feature = "gecko")]
323    pub const RubyText: Self = Self(
324        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)
325            | DisplayInside::RubyText as u16,
326    );
327    #[cfg(feature = "gecko")]
328    pub const RubyTextContainer: Self = Self(
329        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)
330            | DisplayInside::RubyTextContainer as u16,
331    );
332
333    /// Make a raw display value from <display-outside> and <display-inside> values.
334    #[inline]
335    const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
336        Self((outside as u16) << Self::OUTSIDE_SHIFT | inside as u16)
337    }
338
339    /// Make a display enum value from <display-outside> and <display-inside> values.
340    #[inline]
341    fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
342        let v = Self::new(outside, inside);
343        if !list_item {
344            return v;
345        }
346        Self(v.0 | Self::LIST_ITEM_MASK)
347    }
348
349    /// Accessor for the <display-inside> value.
350    #[inline]
351    pub fn inside(&self) -> DisplayInside {
352        DisplayInside::from_u16(self.0 & Self::INSIDE_MASK).unwrap()
353    }
354
355    /// Accessor for the <display-outside> value.
356    #[inline]
357    pub fn outside(&self) -> DisplayOutside {
358        DisplayOutside::from_u16((self.0 & Self::OUTSIDE_MASK) >> Self::OUTSIDE_SHIFT).unwrap()
359    }
360
361    /// Returns the raw underlying u16 value.
362    #[inline]
363    pub const fn to_u16(&self) -> u16 {
364        self.0
365    }
366
367    /// Whether this is `display: inline` (or `inline list-item`).
368    #[inline]
369    pub fn is_inline_flow(&self) -> bool {
370        self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
371    }
372
373    /// Returns whether this `display` value is some kind of list-item.
374    #[inline]
375    pub const fn is_list_item(&self) -> bool {
376        (self.0 & Self::LIST_ITEM_MASK) != 0
377    }
378
379    /// Returns whether this `display` value is a ruby level container.
380    pub fn is_ruby_level_container(&self) -> bool {
381        match *self {
382            #[cfg(feature = "gecko")]
383            Display::RubyBaseContainer | Display::RubyTextContainer => true,
384            _ => false,
385        }
386    }
387
388    /// Returns whether this `display` value is one of the types for ruby.
389    pub fn is_ruby_type(&self) -> bool {
390        match self.inside() {
391            #[cfg(feature = "gecko")]
392            DisplayInside::Ruby
393            | DisplayInside::RubyBase
394            | DisplayInside::RubyText
395            | DisplayInside::RubyBaseContainer
396            | DisplayInside::RubyTextContainer => true,
397            _ => false,
398        }
399    }
400}
401
402/// Shared Display impl for both Gecko and Servo.
403impl Display {
404    /// The initial display value.
405    #[inline]
406    pub fn inline() -> Self {
407        Display::Inline
408    }
409
410    /// Returns whether this `display` value is the display of a flex or
411    /// grid container.
412    ///
413    /// This is used to implement various style fixups.
414    pub fn is_item_container(&self) -> bool {
415        match self.inside() {
416            DisplayInside::Flex | DisplayInside::Grid | DisplayInside::GridLanes => true,
417            _ => false,
418        }
419    }
420
421    /// Returns whether an element with this display type is a line
422    /// participant, which means it may lay its children on the same
423    /// line as itself.
424    pub fn is_line_participant(&self) -> bool {
425        if self.is_inline_flow() {
426            return true;
427        }
428        match *self {
429            #[cfg(feature = "gecko")]
430            Display::Contents | Display::Ruby | Display::RubyBaseContainer => true,
431            _ => false,
432        }
433    }
434
435    /// Convert this display into an equivalent block display.
436    ///
437    /// Also used for :root style adjustments.
438    pub fn equivalent_block_display(&self, is_root_element: bool) -> Self {
439        // Special handling for `contents` and `list-item`s on the root element.
440        if is_root_element && (self.is_contents() || self.is_list_item()) {
441            return Display::Block;
442        }
443
444        match self.outside() {
445            DisplayOutside::Inline => {
446                let inside = match self.inside() {
447                    // `inline-block` blockifies to `block` rather than
448                    // `flow-root`, for legacy reasons.
449                    DisplayInside::FlowRoot => DisplayInside::Flow,
450                    inside => inside,
451                };
452                Display::from3(DisplayOutside::Block, inside, self.is_list_item())
453            },
454            DisplayOutside::Block | DisplayOutside::None => *self,
455            _ => Display::Block,
456        }
457    }
458
459    /// Convert this display into an equivalent inline-outside display.
460    /// https://drafts.csswg.org/css-display/#inlinify
461    #[cfg(feature = "gecko")]
462    pub fn inlinify(&self) -> Self {
463        match self.outside() {
464            DisplayOutside::Block => {
465                let inside = match self.inside() {
466                    // `display: block` inlinifies to `display: inline-block`,
467                    // rather than `inline`, for legacy reasons.
468                    DisplayInside::Flow => DisplayInside::FlowRoot,
469                    inside => inside,
470                };
471                Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
472            },
473            _ => *self,
474        }
475    }
476
477    /// Returns true if the value is `Contents`
478    #[inline]
479    pub fn is_contents(&self) -> bool {
480        match *self {
481            Display::Contents => true,
482            _ => false,
483        }
484    }
485
486    /// Returns true if the value is `None`
487    #[inline]
488    pub fn is_none(&self) -> bool {
489        *self == Display::None
490    }
491}
492
493enum DisplayKeyword {
494    Full(Display),
495    Inside(DisplayInside),
496    Outside(DisplayOutside),
497    ListItem,
498}
499
500impl DisplayKeyword {
501    fn parse(input: &mut Parser) -> Result<Self, ParseError> {
502        use self::DisplayKeyword::*;
503        Ok(try_match_ident_ignore_ascii_case! { input,
504            "none" => Full(Display::None),
505            "contents" => Full(Display::Contents),
506            "inline-block" => Full(Display::InlineBlock),
507            "inline-table" => Full(Display::InlineTable),
508            "-webkit-flex" => Full(Display::Flex),
509            "inline-flex" | "-webkit-inline-flex" => Full(Display::InlineFlex),
510            "inline-grid" => Full(Display::InlineGrid),
511            "inline-grid-lanes" if is_display_grid_lanes_enabled() => Full(Display::InlineGridLanes),
512            "table-caption" => Full(Display::TableCaption),
513            "table-row-group" => Full(Display::TableRowGroup),
514            "table-header-group" => Full(Display::TableHeaderGroup),
515            "table-footer-group" => Full(Display::TableFooterGroup),
516            "table-column" => Full(Display::TableColumn),
517            "table-column-group" => Full(Display::TableColumnGroup),
518            "table-row" => Full(Display::TableRow),
519            "table-cell" => Full(Display::TableCell),
520            #[cfg(feature = "gecko")]
521            "ruby-base" => Full(Display::RubyBase),
522            #[cfg(feature = "gecko")]
523            "ruby-base-container" => Full(Display::RubyBaseContainer),
524            #[cfg(feature = "gecko")]
525            "ruby-text" => Full(Display::RubyText),
526            #[cfg(feature = "gecko")]
527            "ruby-text-container" => Full(Display::RubyTextContainer),
528            #[cfg(feature = "gecko")]
529            "-webkit-box" => Full(Display::WebkitBox),
530            #[cfg(feature = "gecko")]
531            "-webkit-inline-box" => Full(Display::WebkitInlineBox),
532
533            /// <display-outside> = block | inline | run-in
534            /// https://drafts.csswg.org/css-display/#typedef-display-outside
535            "block" => Outside(DisplayOutside::Block),
536            "inline" => Outside(DisplayOutside::Inline),
537
538            "list-item" => ListItem,
539
540            /// <display-inside> = flow | flow-root | table | flex | grid | ruby
541            /// https://drafts.csswg.org/css-display/#typedef-display-inside
542            "flow" => Inside(DisplayInside::Flow),
543            "flex" => Inside(DisplayInside::Flex),
544            "flow-root" => Inside(DisplayInside::FlowRoot),
545            "table" => Inside(DisplayInside::Table),
546            "grid" => Inside(DisplayInside::Grid),
547            "grid-lanes" if is_display_grid_lanes_enabled() => Inside(DisplayInside::GridLanes),
548            #[cfg(feature = "gecko")]
549            "ruby" => Inside(DisplayInside::Ruby),
550        })
551    }
552}
553
554impl ToCss for Display {
555    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
556    where
557        W: fmt::Write,
558    {
559        let outside = self.outside();
560        let inside = self.inside();
561        match *self {
562            Display::Block | Display::Inline => outside.to_css(dest),
563            Display::InlineBlock => dest.write_str("inline-block"),
564            #[cfg(feature = "gecko")]
565            Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
566            Display::TableCaption => dest.write_str("table-caption"),
567            _ => match (outside, inside) {
568                (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
569                (DisplayOutside::Inline, DisplayInside::GridLanes) => {
570                    dest.write_str("inline-grid-lanes")
571                },
572                (DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"),
573                (DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"),
574                #[cfg(feature = "gecko")]
575                (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
576                (_, inside) => {
577                    if self.is_list_item() {
578                        if outside != DisplayOutside::Block {
579                            outside.to_css(dest)?;
580                            dest.write_char(' ')?;
581                        }
582                        if inside != DisplayInside::Flow {
583                            inside.to_css(dest)?;
584                            dest.write_char(' ')?;
585                        }
586                        dest.write_str("list-item")
587                    } else {
588                        inside.to_css(dest)
589                    }
590                },
591            },
592        }
593    }
594}
595
596impl ToTyped for Display {
597    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {
598        // Note: The specification does not currently define how display multi
599        // keywords should be reified into Typed OM. The current behavior
600        // follows existing WPT coverage (display.html). Syncing spec with
601        // UA/WPT behavior tracked in
602        // https://github.com/w3c/csswg-drafts/issues/13907
603
604        let outside = self.outside();
605        let inside = self.inside();
606
607        #[cfg(feature = "gecko")]
608        if outside == DisplayOutside::Block && inside == DisplayInside::Ruby {
609            return Err(());
610        }
611
612        if self.is_list_item()
613            && (outside != DisplayOutside::Block || inside != DisplayInside::Flow)
614        {
615            return Err(());
616        }
617
618        let keyword = self.to_css_cssstring();
619        debug_assert!(!AsRef::<[u8]>::as_ref(&keyword).contains(&b' '));
620
621        dest.push(TypedValue::Keyword(KeywordValue(keyword)));
622        Ok(())
623    }
624}
625
626impl Parse for Display {
627    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Display, ParseError> {
628        let mut got_list_item = false;
629        let mut inside = None;
630        let mut outside = None;
631        match DisplayKeyword::parse(input)? {
632            DisplayKeyword::Full(d) => return Ok(d),
633            DisplayKeyword::Outside(o) => {
634                outside = Some(o);
635            },
636            DisplayKeyword::Inside(i) => {
637                inside = Some(i);
638            },
639            DisplayKeyword::ListItem => {
640                got_list_item = true;
641            },
642        };
643
644        while let Ok(kw) = input.try_parse(DisplayKeyword::parse) {
645            match kw {
646                DisplayKeyword::ListItem if !got_list_item => {
647                    got_list_item = true;
648                },
649                DisplayKeyword::Outside(o) if outside.is_none() => {
650                    outside = Some(o);
651                },
652                DisplayKeyword::Inside(i) if inside.is_none() => {
653                    inside = Some(i);
654                },
655                _ => return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
656            }
657        }
658
659        let inside = inside.unwrap_or(DisplayInside::Flow);
660        let outside = outside.unwrap_or_else(|| inside.default_display_outside());
661        if got_list_item && !inside.is_valid_for_list_item() {
662            return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
663        }
664
665        Ok(Display::from3(outside, inside, got_list_item))
666    }
667}
668
669impl SpecifiedValueInfo for Display {
670    fn collect_completion_keywords(f: KeywordsCollectFn) {
671        f(&[
672            "block",
673            "contents",
674            "flex",
675            "flow-root",
676            "flow-root list-item",
677            "grid",
678            "grid-lanes",
679            "inline",
680            "inline-block",
681            "inline-flex",
682            "inline-grid",
683            "inline-grid-lanes",
684            "inline-table",
685            "inline list-item",
686            "inline flow-root list-item",
687            "list-item",
688            "none",
689            "block ruby",
690            "ruby",
691            "ruby-base",
692            "ruby-base-container",
693            "ruby-text",
694            "ruby-text-container",
695            "table",
696            "table-caption",
697            "table-cell",
698            "table-column",
699            "table-column-group",
700            "table-footer-group",
701            "table-header-group",
702            "table-row",
703            "table-row-group",
704            "-webkit-box",
705            "-webkit-inline-box",
706        ]);
707    }
708}
709
710/// A specified value for the `contain-intrinsic-size` property.
711pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
712
713/// A specified value for the `line-clamp` property.
714pub type LineClamp = GenericLineClamp<PositiveInteger>;
715
716/// A specified value for the `baseline-shift` property.
717pub type BaselineShift = GenericBaselineShift<LengthPercentage>;
718
719impl Parse for BaselineShift {
720    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
721        if let Ok(lp) =
722            input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))
723        {
724            return Ok(BaselineShift::Length(lp));
725        }
726
727        Ok(BaselineShift::Keyword(BaselineShiftKeyword::parse(input)?))
728    }
729}
730
731/// A specified value for the `dominant-baseline` property.
732/// https://drafts.csswg.org/css-inline-3/#dominant-baseline
733#[derive(
734    Clone,
735    Copy,
736    Debug,
737    Eq,
738    FromPrimitive,
739    Hash,
740    MallocSizeOf,
741    Parse,
742    PartialEq,
743    SpecifiedValueInfo,
744    ToCss,
745    ToShmem,
746    ToComputedValue,
747    ToResolvedValue,
748    ToTyped,
749)]
750#[repr(u8)]
751pub enum DominantBaseline {
752    /// Equivalent to 'alphabetic' in horizontal writing modes and in vertical writing
753    /// modes when 'text-orientation' is sideways. Equivalent to 'central' in vertical
754    /// writing modes when 'text-orientation' is 'mixed' or 'upright'.
755    Auto,
756    /// Use the text-under baseline.
757    #[parse(aliases = "text-after-edge")]
758    TextBottom,
759    /// Use the alphabetic baseline.
760    Alphabetic,
761    /// Use the ideographic-under baseline.
762    Ideographic,
763    /// In general, use the x-middle baselines; except under text-orientation: upright
764    /// (where the alphabetic and x-height baselines are essentially meaningless) use
765    /// the central baseline instead.
766    Middle,
767    /// Use the central baseline.
768    Central,
769    /// Use the math baseline.
770    Mathematical,
771    /// Use the hanging baseline.
772    Hanging,
773    /// Use the text-over baseline.
774    #[parse(aliases = "text-before-edge")]
775    TextTop,
776}
777
778/// A specified value for the `alignment-baseline` property.
779/// https://drafts.csswg.org/css-inline-3/#alignment-baseline
780#[derive(
781    Clone,
782    Copy,
783    Debug,
784    Eq,
785    FromPrimitive,
786    Hash,
787    MallocSizeOf,
788    Parse,
789    PartialEq,
790    SpecifiedValueInfo,
791    ToCss,
792    ToShmem,
793    ToComputedValue,
794    ToResolvedValue,
795    ToTyped,
796)]
797#[repr(u8)]
798pub enum AlignmentBaseline {
799    /// Use the dominant baseline choice of the parent.
800    Baseline,
801    /// Use the text-under baseline.
802    TextBottom,
803    /// Use the alphabetic baseline.
804    #[cfg(feature = "gecko")]
805    Alphabetic,
806    /// Use the ideographic-under baseline.
807    #[cfg(feature = "gecko")]
808    Ideographic,
809    /// In general, use the x-middle baselines; except under text-orientation: upright
810    /// (where the alphabetic and x-height baselines are essentially meaningless) use
811    /// the central baseline instead.
812    Middle,
813    /// Use the central baseline.
814    #[cfg(feature = "gecko")]
815    Central,
816    /// Use the math baseline.
817    #[cfg(feature = "gecko")]
818    Mathematical,
819    /// Use the hanging baseline.
820    #[cfg(feature = "gecko")]
821    Hanging,
822    /// Use the text-over baseline.
823    TextTop,
824    /// Used to implement the deprecated "align=middle" attribute for HTML img elements.
825    #[cfg(feature = "gecko")]
826    MozMiddleWithBaseline,
827}
828
829/// A specified value for the `baseline-source` property.
830/// https://drafts.csswg.org/css-inline-3/#baseline-source
831#[derive(
832    Clone,
833    Copy,
834    Debug,
835    Eq,
836    Hash,
837    MallocSizeOf,
838    Parse,
839    PartialEq,
840    SpecifiedValueInfo,
841    ToCss,
842    ToShmem,
843    ToComputedValue,
844    ToResolvedValue,
845    ToTyped,
846)]
847#[repr(u8)]
848pub enum BaselineSource {
849    /// `Last` for `inline-block`, `First` otherwise.
850    Auto,
851    /// Use first baseline for alignment.
852    First,
853    /// Use last baseline for alignment.
854    Last,
855}
856
857impl BaselineSource {
858    /// Parse baseline source, but without the auto keyword, for the shorthand.
859    pub fn parse_non_auto(input: &mut Parser) -> Result<Self, ParseError> {
860        Ok(try_match_ident_ignore_ascii_case! { input,
861            "first" => Self::First,
862            "last" => Self::Last,
863        })
864    }
865}
866
867/// https://drafts.csswg.org/css-scroll-snap-1/#snap-axis
868#[allow(missing_docs)]
869#[derive(
870    Clone,
871    Copy,
872    Debug,
873    Deserialize,
874    Eq,
875    MallocSizeOf,
876    Parse,
877    PartialEq,
878    Serialize,
879    SpecifiedValueInfo,
880    ToComputedValue,
881    ToCss,
882    ToResolvedValue,
883    ToShmem,
884)]
885#[repr(u8)]
886pub enum ScrollSnapAxis {
887    X,
888    Y,
889    Block,
890    Inline,
891    Both,
892}
893
894/// https://drafts.csswg.org/css-scroll-snap-1/#snap-strictness
895#[allow(missing_docs)]
896#[derive(
897    Clone,
898    Copy,
899    Debug,
900    Deserialize,
901    Eq,
902    MallocSizeOf,
903    Parse,
904    PartialEq,
905    Serialize,
906    SpecifiedValueInfo,
907    ToComputedValue,
908    ToCss,
909    ToResolvedValue,
910    ToShmem,
911)]
912#[repr(u8)]
913pub enum ScrollSnapStrictness {
914    #[css(skip)]
915    None, // Used to represent scroll-snap-type: none.  It's not parsed.
916    Mandatory,
917    Proximity,
918}
919
920/// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type
921#[allow(missing_docs)]
922#[derive(
923    Clone,
924    Copy,
925    Debug,
926    Deserialize,
927    Eq,
928    MallocSizeOf,
929    PartialEq,
930    Serialize,
931    SpecifiedValueInfo,
932    ToComputedValue,
933    ToResolvedValue,
934    ToShmem,
935    ToTyped,
936)]
937#[repr(C)]
938#[typed(todo_derive_fields)]
939pub struct ScrollSnapType {
940    axis: ScrollSnapAxis,
941    strictness: ScrollSnapStrictness,
942}
943
944impl ScrollSnapType {
945    /// Returns `none`.
946    #[inline]
947    pub fn none() -> Self {
948        Self {
949            axis: ScrollSnapAxis::Both,
950            strictness: ScrollSnapStrictness::None,
951        }
952    }
953}
954
955impl Parse for ScrollSnapType {
956    /// none | [ x | y | block | inline | both ] [ mandatory | proximity ]?
957    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
958        if input
959            .try_parse(|input| input.expect_ident_matching("none"))
960            .is_ok()
961        {
962            return Ok(ScrollSnapType::none());
963        }
964
965        let axis = ScrollSnapAxis::parse(input)?;
966        let strictness = input
967            .try_parse(ScrollSnapStrictness::parse)
968            .unwrap_or(ScrollSnapStrictness::Proximity);
969        Ok(Self { axis, strictness })
970    }
971}
972
973impl ToCss for ScrollSnapType {
974    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
975    where
976        W: Write,
977    {
978        if self.strictness == ScrollSnapStrictness::None {
979            return dest.write_str("none");
980        }
981        self.axis.to_css(dest)?;
982        if self.strictness != ScrollSnapStrictness::Proximity {
983            dest.write_char(' ')?;
984            self.strictness.to_css(dest)?;
985        }
986        Ok(())
987    }
988}
989
990/// Specified value of scroll-snap-align keyword value.
991#[allow(missing_docs)]
992#[derive(
993    Clone,
994    Copy,
995    Debug,
996    Eq,
997    FromPrimitive,
998    Hash,
999    MallocSizeOf,
1000    Parse,
1001    PartialEq,
1002    SpecifiedValueInfo,
1003    ToComputedValue,
1004    ToCss,
1005    ToResolvedValue,
1006    ToShmem,
1007)]
1008#[repr(u8)]
1009pub enum ScrollSnapAlignKeyword {
1010    None,
1011    Start,
1012    End,
1013    Center,
1014}
1015
1016/// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align
1017#[allow(missing_docs)]
1018#[derive(
1019    Clone,
1020    Copy,
1021    Debug,
1022    Eq,
1023    MallocSizeOf,
1024    PartialEq,
1025    SpecifiedValueInfo,
1026    ToComputedValue,
1027    ToResolvedValue,
1028    ToShmem,
1029    ToTyped,
1030)]
1031#[repr(C)]
1032#[typed(todo_derive_fields)]
1033pub struct ScrollSnapAlign {
1034    block: ScrollSnapAlignKeyword,
1035    inline: ScrollSnapAlignKeyword,
1036}
1037
1038impl ScrollSnapAlign {
1039    /// Returns `none`.
1040    #[inline]
1041    pub fn none() -> Self {
1042        ScrollSnapAlign {
1043            block: ScrollSnapAlignKeyword::None,
1044            inline: ScrollSnapAlignKeyword::None,
1045        }
1046    }
1047}
1048
1049impl Parse for ScrollSnapAlign {
1050    /// [ none | start | end | center ]{1,2}
1051    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<ScrollSnapAlign, ParseError> {
1052        let block = ScrollSnapAlignKeyword::parse(input)?;
1053        let inline = input
1054            .try_parse(ScrollSnapAlignKeyword::parse)
1055            .unwrap_or(block);
1056        Ok(ScrollSnapAlign { block, inline })
1057    }
1058}
1059
1060impl ToCss for ScrollSnapAlign {
1061    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1062    where
1063        W: Write,
1064    {
1065        self.block.to_css(dest)?;
1066        if self.block != self.inline {
1067            dest.write_char(' ')?;
1068            self.inline.to_css(dest)?;
1069        }
1070        Ok(())
1071    }
1072}
1073
1074#[allow(missing_docs)]
1075#[derive(
1076    Clone,
1077    Copy,
1078    Debug,
1079    Deserialize,
1080    Eq,
1081    MallocSizeOf,
1082    Parse,
1083    PartialEq,
1084    Serialize,
1085    SpecifiedValueInfo,
1086    ToComputedValue,
1087    ToCss,
1088    ToResolvedValue,
1089    ToShmem,
1090    ToTyped,
1091)]
1092#[repr(u8)]
1093pub enum ScrollSnapStop {
1094    Normal,
1095    Always,
1096}
1097
1098#[allow(missing_docs)]
1099#[derive(
1100    Clone,
1101    Copy,
1102    Debug,
1103    Deserialize,
1104    Eq,
1105    MallocSizeOf,
1106    Parse,
1107    PartialEq,
1108    Serialize,
1109    SpecifiedValueInfo,
1110    ToComputedValue,
1111    ToCss,
1112    ToResolvedValue,
1113    ToShmem,
1114    ToTyped,
1115)]
1116#[repr(u8)]
1117pub enum OverscrollBehavior {
1118    Auto,
1119    Contain,
1120    Chain,
1121    None,
1122}
1123
1124#[allow(missing_docs)]
1125#[derive(
1126    Clone,
1127    Copy,
1128    Debug,
1129    Deserialize,
1130    Eq,
1131    MallocSizeOf,
1132    Parse,
1133    PartialEq,
1134    Serialize,
1135    SpecifiedValueInfo,
1136    ToComputedValue,
1137    ToCss,
1138    ToResolvedValue,
1139    ToShmem,
1140    ToTyped,
1141)]
1142#[repr(u8)]
1143pub enum OverflowAnchor {
1144    Auto,
1145    None,
1146}
1147
1148#[derive(
1149    Clone,
1150    Debug,
1151    Default,
1152    MallocSizeOf,
1153    PartialEq,
1154    SpecifiedValueInfo,
1155    ToComputedValue,
1156    ToCss,
1157    ToResolvedValue,
1158    ToShmem,
1159    ToTyped,
1160)]
1161#[css(comma)]
1162#[repr(C)]
1163#[typed(no_multiple_values)]
1164/// Provides a rendering hint to the user agent, stating what kinds of changes
1165/// the author expects to perform on the element.
1166///
1167/// `auto` is represented by an empty `features` list.
1168///
1169/// <https://drafts.csswg.org/css-will-change/#will-change>
1170pub struct WillChange {
1171    /// The features that are supposed to change.
1172    ///
1173    /// TODO(emilio): Consider using ArcSlice since we just clone them from the
1174    /// specified value? That'd save an allocation, which could be worth it.
1175    #[css(iterable, if_empty = "auto")]
1176    features: crate::OwnedSlice<CustomIdent>,
1177    /// A bitfield with the kind of change that the value will create, based
1178    /// on the above field.
1179    #[css(skip)]
1180    pub bits: WillChangeBits,
1181}
1182
1183impl WillChange {
1184    #[inline]
1185    /// Get default value of `will-change` as `auto`
1186    pub fn auto() -> Self {
1187        Self::default()
1188    }
1189}
1190
1191/// The change bits that we care about.
1192#[derive(
1193    Clone,
1194    Copy,
1195    Debug,
1196    Default,
1197    Eq,
1198    MallocSizeOf,
1199    PartialEq,
1200    SpecifiedValueInfo,
1201    ToComputedValue,
1202    ToResolvedValue,
1203    ToShmem,
1204)]
1205#[repr(C)]
1206pub struct WillChangeBits(u16);
1207bitflags! {
1208    impl WillChangeBits: u16 {
1209        /// Whether a property which can create a stacking context **on any
1210        /// box** will change.
1211        const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;
1212        /// Whether `transform` or related properties will change.
1213        const TRANSFORM = 1 << 1;
1214        /// Whether `scroll-position` will change.
1215        const SCROLL = 1 << 2;
1216        /// Whether `contain` will change.
1217        const CONTAIN = 1 << 3;
1218        /// Whether `opacity` will change.
1219        const OPACITY = 1 << 4;
1220        /// Whether `perspective` will change.
1221        const PERSPECTIVE = 1 << 5;
1222        /// Whether `z-index` will change.
1223        const Z_INDEX = 1 << 6;
1224        /// Whether any property which creates a containing block for non-svg
1225        /// text frames will change.
1226        const FIXPOS_CB_NON_SVG = 1 << 7;
1227        /// Whether the position property will change.
1228        const POSITION = 1 << 8;
1229        /// Whether the view-transition-name property will change.
1230        const VIEW_TRANSITION_NAME = 1 << 9;
1231        /// Whether any property which establishes a backdrop-root will change.
1232        /// See https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty
1233        const BACKDROP_ROOT = 1 << 10;
1234    }
1235}
1236
1237fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
1238    match longhand {
1239        LonghandId::Opacity => WillChangeBits::OPACITY | WillChangeBits::BACKDROP_ROOT,
1240        LonghandId::Contain => WillChangeBits::CONTAIN,
1241        LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
1242        LonghandId::Position => {
1243            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
1244        },
1245        LonghandId::ZIndex => WillChangeBits::Z_INDEX,
1246        LonghandId::Transform
1247        | LonghandId::TransformStyle
1248        | LonghandId::Translate
1249        | LonghandId::Rotate
1250        | LonghandId::Scale
1251        | LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
1252        LonghandId::Filter | LonghandId::BackdropFilter => {
1253            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
1254                | WillChangeBits::BACKDROP_ROOT
1255                | WillChangeBits::FIXPOS_CB_NON_SVG
1256        },
1257        LonghandId::ViewTransitionName => {
1258            WillChangeBits::VIEW_TRANSITION_NAME | WillChangeBits::BACKDROP_ROOT
1259        },
1260        LonghandId::MixBlendMode => {
1261            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
1262        },
1263        LonghandId::Isolation | LonghandId::MaskImage => {
1264            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
1265        },
1266        LonghandId::ClipPath => {
1267            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
1268        },
1269        _ => WillChangeBits::empty(),
1270    }
1271}
1272
1273fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
1274    let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
1275        Ok(id) => id,
1276        Err(..) => return WillChangeBits::empty(),
1277    };
1278
1279    match id.as_shorthand() {
1280        Ok(shorthand) => shorthand
1281            .longhands()
1282            .fold(WillChangeBits::empty(), |flags, p| {
1283                flags | change_bits_for_longhand(p)
1284            }),
1285        Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
1286        Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
1287    }
1288}
1289
1290impl Parse for WillChange {
1291    /// auto | <animateable-feature>#
1292    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1293        if input
1294            .try_parse(|input| input.expect_ident_matching("auto"))
1295            .is_ok()
1296        {
1297            return Ok(Self::default());
1298        }
1299
1300        let mut bits = WillChangeBits::empty();
1301        let custom_idents = input.parse_comma_separated(|i| {
1302            let parser_ident = i.expect_ident()?;
1303            let ident =
1304                CustomIdent::from_ident(parser_ident, &["will-change", "none", "all", "auto"])?;
1305
1306            if context.in_ua_sheet() && ident.0 == atom!("-moz-fixed-pos-containing-block") {
1307                bits |= WillChangeBits::FIXPOS_CB_NON_SVG;
1308            } else if ident.0 == atom!("scroll-position") {
1309                bits |= WillChangeBits::SCROLL;
1310            } else {
1311                bits |= change_bits_for_maybe_property(parser_ident, context);
1312            }
1313            Ok(ident)
1314        })?;
1315
1316        Ok(Self {
1317            features: custom_idents.into(),
1318            bits,
1319        })
1320    }
1321}
1322
1323/// Values for the `touch-action` property.
1324#[derive(
1325    Clone,
1326    Copy,
1327    Debug,
1328    Eq,
1329    MallocSizeOf,
1330    Parse,
1331    PartialEq,
1332    SpecifiedValueInfo,
1333    ToComputedValue,
1334    ToCss,
1335    ToResolvedValue,
1336    ToShmem,
1337    ToTyped,
1338)]
1339#[css(bitflags(single = "none,auto,manipulation", mixed = "pan-x,pan-y,pinch-zoom"))]
1340#[repr(C)]
1341pub struct TouchAction(u8);
1342bitflags! {
1343    impl TouchAction: u8 {
1344        /// `none` variant
1345        const NONE = 1 << 0;
1346        /// `auto` variant
1347        const AUTO = 1 << 1;
1348        /// `pan-x` variant
1349        const PAN_X = 1 << 2;
1350        /// `pan-y` variant
1351        const PAN_Y = 1 << 3;
1352        /// `manipulation` variant
1353        const MANIPULATION = 1 << 4;
1354        /// `pinch-zoom` variant
1355        const PINCH_ZOOM = 1 << 5;
1356    }
1357}
1358
1359impl TouchAction {
1360    #[inline]
1361    /// Get default `touch-action` as `auto`
1362    pub fn auto() -> TouchAction {
1363        TouchAction::AUTO
1364    }
1365}
1366
1367#[derive(
1368    Clone,
1369    Copy,
1370    Debug,
1371    Eq,
1372    MallocSizeOf,
1373    Parse,
1374    PartialEq,
1375    SpecifiedValueInfo,
1376    ToComputedValue,
1377    ToCss,
1378    ToResolvedValue,
1379    ToShmem,
1380    ToTyped,
1381)]
1382#[css(bitflags(
1383    single = "none,strict,content",
1384    mixed = "size,layout,style,paint,inline-size",
1385    overlapping_bits
1386))]
1387#[repr(C)]
1388/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
1389pub struct Contain(u8);
1390bitflags! {
1391    impl Contain: u8 {
1392        /// `none` variant, just for convenience.
1393        const NONE = 0;
1394        /// `inline-size` variant, turns on single-axis inline size containment
1395        const INLINE_SIZE = 1 << 0;
1396        /// `block-size` variant, turns on single-axis block size containment, internal only
1397        const BLOCK_SIZE = 1 << 1;
1398        /// `layout` variant, turns on layout containment
1399        const LAYOUT = 1 << 2;
1400        /// `style` variant, turns on style containment
1401        const STYLE = 1 << 3;
1402        /// `paint` variant, turns on paint containment
1403        const PAINT = 1 << 4;
1404        /// 'size' variant, turns on size containment
1405        const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits() | Contain::BLOCK_SIZE.bits();
1406        /// `content` variant, turns on layout and paint containment
1407        const CONTENT = 1 << 6 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits();
1408        /// `strict` variant, turns on all types of containment
1409        const STRICT = 1 << 7 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits() | Contain::SIZE.bits();
1410    }
1411}
1412
1413impl Parse for ContainIntrinsicSize {
1414    /// none | <length> | auto <length>
1415    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1416        if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
1417            return Ok(Self::Length(l));
1418        }
1419
1420        if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
1421            if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1422                return Ok(Self::AutoNone);
1423            }
1424
1425            let l = NonNegativeLength::parse(context, input)?;
1426            return Ok(Self::AutoLength(l));
1427        }
1428
1429        input.expect_ident_matching("none")?;
1430        Ok(Self::None)
1431    }
1432}
1433
1434impl Parse for MaxLines<PositiveInteger> {
1435    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1436        let mut lines = None;
1437        let mut auto = false;
1438
1439        loop {
1440            if lines.is_none()
1441                && let Ok(value) = input.try_parse(|i| PositiveInteger::parse(context, i))
1442            {
1443                lines = Some(value);
1444                continue;
1445            }
1446
1447            if !auto && input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
1448                auto = true;
1449                continue;
1450            }
1451
1452            break;
1453        }
1454
1455        match (lines, auto) {
1456            (Some(value), true) => Ok(MaxLines::lines(value, true)),
1457            (Some(value), false) => Ok(MaxLines::lines(value, false)),
1458            (None, true) => Ok(MaxLines::auto()),
1459            (None, false) => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
1460        }
1461    }
1462}
1463
1464impl Parse for LineClamp {
1465    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1466        if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1467            return Ok(Self::none());
1468        }
1469
1470        let mut max_lines = None;
1471        let mut block_ellipsis = None;
1472
1473        loop {
1474            if max_lines.is_none()
1475                && let Ok(value) = input.try_parse(|i| MaxLines::parse(context, i))
1476            {
1477                max_lines = Some(value);
1478                continue;
1479            }
1480            if block_ellipsis.is_none()
1481                && let Ok(value) = input.try_parse(|i| BlockEllipsis::parse(context, i))
1482            {
1483                block_ellipsis = Some(value);
1484                continue;
1485            }
1486
1487            break;
1488        }
1489
1490        if max_lines.is_none() && block_ellipsis.is_none() {
1491            return Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError));
1492        }
1493
1494        let webkit_legacy = input
1495            .try_parse(|i| i.expect_ident_matching("-webkit-legacy"))
1496            .is_ok();
1497
1498        let block_ellipsis = block_ellipsis.unwrap_or(BlockEllipsis::Ellipsis);
1499        let max_lines = max_lines.unwrap_or_else(MaxLines::auto);
1500
1501        Ok(Self {
1502            max_lines,
1503            block_ellipsis,
1504            webkit_legacy,
1505        })
1506    }
1507}
1508
1509impl LineClamp {
1510    /// Parses the legacy `-webkit-line-clamp` syntax.
1511    pub fn parse_legacy(context: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1512        if let Ok(value) =
1513            input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))
1514        {
1515            return Ok(Self {
1516                max_lines: MaxLines::lines(value, false),
1517                block_ellipsis: BlockEllipsis::Ellipsis,
1518                webkit_legacy: true,
1519            });
1520        }
1521        input.expect_ident_matching("none")?;
1522        Ok(Self::none())
1523    }
1524
1525    /// Serializes the legacy `-webkit-line-clamp` syntax.
1526    #[cfg_attr(feature = "servo", allow(unused))]
1527    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1528    where
1529        W: fmt::Write,
1530    {
1531        if self.is_none() {
1532            return dest.write_str("none");
1533        }
1534
1535        if !self.webkit_legacy || !self.block_ellipsis.is_ellipsis() {
1536            return Ok(());
1537        }
1538
1539        let Some(lines) = self.max_lines.lines_value() else {
1540            return Ok(());
1541        };
1542
1543        lines.to_css(dest)
1544    }
1545}
1546
1547/// https://drafts.csswg.org/css-contain-2/#content-visibility
1548#[derive(
1549    Clone,
1550    Copy,
1551    Debug,
1552    Deserialize,
1553    Eq,
1554    FromPrimitive,
1555    MallocSizeOf,
1556    Parse,
1557    PartialEq,
1558    Serialize,
1559    SpecifiedValueInfo,
1560    ToAnimatedValue,
1561    ToComputedValue,
1562    ToCss,
1563    ToResolvedValue,
1564    ToShmem,
1565    ToTyped,
1566)]
1567#[repr(u8)]
1568pub enum ContentVisibility {
1569    /// `auto` variant, the element turns on layout containment, style containment, and paint
1570    /// containment. In addition, if the element is not relevant to the user (such as by being
1571    /// offscreen) it also skips its content
1572    Auto,
1573    /// `hidden` variant, the element skips its content
1574    Hidden,
1575    /// 'visible' variant, no effect
1576    Visible,
1577}
1578
1579#[derive(
1580    Clone,
1581    Copy,
1582    Debug,
1583    PartialEq,
1584    Eq,
1585    MallocSizeOf,
1586    SpecifiedValueInfo,
1587    ToComputedValue,
1588    ToCss,
1589    Parse,
1590    ToResolvedValue,
1591    ToShmem,
1592    ToTyped,
1593)]
1594#[css(bitflags(
1595    single = "normal",
1596    mixed = "size,inline-size,scroll-state",
1597    validate_mixed = "Self::validate_mixed_flags",
1598))]
1599#[repr(C)]
1600/// Specified keyword values for the container-type property.
1601/// Spec: normal | [ [ size | inline-size ] || scroll-state ]
1602///
1603/// Container Queries are moved from css-contain-3 to css-conditional-5 in August 2022:
1604/// https://drafts.csswg.org/css-contain-3/#container-type
1605/// https://drafts.csswg.org/css-conditional-5/#container-type
1606pub struct ContainerType(u8);
1607bitflags! {
1608    impl ContainerType: u8 {
1609        /// The `normal` variant.
1610        const NORMAL = 0;
1611        /// The `inline-size` variant.
1612        const INLINE_SIZE = 1 << 0;
1613        /// The `size` variant.
1614        const SIZE = 1 << 1;
1615        /// The `scroll-state` variant.
1616        const SCROLL_STATE = 1 << 2;
1617    }
1618}
1619
1620impl ContainerType {
1621    fn validate_mixed_flags(&self) -> bool {
1622        // size and inline-size can't be mixed together.
1623        if self.contains(Self::SIZE | Self::INLINE_SIZE) {
1624            return false;
1625        }
1626        if self.contains(Self::SCROLL_STATE) && !crate::pref!("layout.css.scroll-state.enabled") {
1627            return false;
1628        }
1629        true
1630    }
1631
1632    /// Is this container-type: normal?
1633    pub fn is_normal(self) -> bool {
1634        self == Self::NORMAL
1635    }
1636
1637    /// Is this type containing size in any way?
1638    pub fn is_size_container_type(self) -> bool {
1639        self.intersects(Self::SIZE | Self::INLINE_SIZE)
1640    }
1641}
1642
1643/// https://drafts.csswg.org/css-contain-3/#container-name
1644#[repr(transparent)]
1645#[derive(
1646    Clone,
1647    Debug,
1648    MallocSizeOf,
1649    PartialEq,
1650    SpecifiedValueInfo,
1651    ToComputedValue,
1652    ToCss,
1653    ToResolvedValue,
1654    ToShmem,
1655    ToTyped,
1656)]
1657pub struct ContainerName(#[css(iterable, if_empty = "none")] pub crate::OwnedSlice<CustomIdent>);
1658
1659impl ContainerName {
1660    /// Return the `none` value.
1661    pub fn none() -> Self {
1662        Self(Default::default())
1663    }
1664
1665    /// Returns whether this is the `none` value.
1666    pub fn is_none(&self) -> bool {
1667        self.0.is_empty()
1668    }
1669
1670    fn parse_internal(input: &mut Parser, for_query: bool) -> Result<Self, ParseError> {
1671        let mut idents = vec![];
1672        let first = input.expect_ident()?;
1673        if !for_query && first.eq_ignore_ascii_case("none") {
1674            return Ok(Self::none());
1675        }
1676        const DISALLOWED_CONTAINER_NAMES: &[&str] = &["none", "not", "or", "and"];
1677        idents.push(CustomIdent::from_ident(first, DISALLOWED_CONTAINER_NAMES)?);
1678        if !for_query {
1679            while let Ok(name) =
1680                input.try_parse(|input| CustomIdent::parse(input, DISALLOWED_CONTAINER_NAMES))
1681            {
1682                idents.push(name);
1683            }
1684        }
1685        Ok(ContainerName(idents.into()))
1686    }
1687
1688    /// https://github.com/w3c/csswg-drafts/issues/7203
1689    /// Only a single name allowed in @container rule.
1690    /// Disallow none for container-name in @container rule.
1691    pub fn parse_for_query(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1692        Self::parse_internal(input, /* for_query = */ true)
1693    }
1694}
1695
1696impl Parse for ContainerName {
1697    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
1698        Self::parse_internal(input, /* for_query = */ false)
1699    }
1700}
1701
1702/// A specified value for the `perspective` property.
1703pub type Perspective = GenericPerspective<NonNegativeLength>;
1704
1705impl Perspective {
1706    /// Parses a `-webkit-perspective` value.
1707    pub(crate) fn parse_legacy(
1708        context: &ParserContext,
1709        input: &mut Parser,
1710    ) -> Result<Self, ParseError> {
1711        use crate::values::generics::NonNegative;
1712        use crate::values::specified::{AllowQuirks, Length};
1713        if let Ok(l) = input.try_parse(|input| {
1714            Length::parse_non_negative_quirky(context, input, AllowQuirks::Always)
1715        }) {
1716            return Ok(Self::Length(NonNegative(l)));
1717        }
1718        Self::parse(context, input)
1719    }
1720}
1721
1722/// https://drafts.csswg.org/css-box/#propdef-float
1723#[allow(missing_docs)]
1724#[derive(
1725    Clone,
1726    Copy,
1727    Debug,
1728    Deserialize,
1729    Eq,
1730    FromPrimitive,
1731    Hash,
1732    MallocSizeOf,
1733    Parse,
1734    PartialEq,
1735    Serialize,
1736    SpecifiedValueInfo,
1737    ToComputedValue,
1738    ToCss,
1739    ToResolvedValue,
1740    ToShmem,
1741    ToTyped,
1742)]
1743#[repr(u8)]
1744pub enum Float {
1745    Left,
1746    Right,
1747    None,
1748    // https://drafts.csswg.org/css-logical-props/#float-clear
1749    InlineStart,
1750    InlineEnd,
1751}
1752
1753impl Float {
1754    /// Returns true if `self` is not `None`.
1755    pub fn is_floating(self) -> bool {
1756        self != Self::None
1757    }
1758}
1759
1760/// https://drafts.csswg.org/css2/#propdef-clear
1761#[allow(missing_docs)]
1762#[derive(
1763    Clone,
1764    Copy,
1765    Debug,
1766    Deserialize,
1767    Eq,
1768    FromPrimitive,
1769    Hash,
1770    MallocSizeOf,
1771    Parse,
1772    PartialEq,
1773    Serialize,
1774    SpecifiedValueInfo,
1775    ToComputedValue,
1776    ToCss,
1777    ToResolvedValue,
1778    ToShmem,
1779    ToTyped,
1780)]
1781#[repr(u8)]
1782pub enum Clear {
1783    None,
1784    Left,
1785    Right,
1786    Both,
1787    // https://drafts.csswg.org/css-logical-props/#float-clear
1788    InlineStart,
1789    InlineEnd,
1790}
1791
1792/// https://drafts.csswg.org/css-ui/#propdef-resize
1793#[allow(missing_docs)]
1794#[derive(
1795    Clone,
1796    Copy,
1797    Debug,
1798    Deserialize,
1799    Eq,
1800    Hash,
1801    MallocSizeOf,
1802    Parse,
1803    PartialEq,
1804    Serialize,
1805    SpecifiedValueInfo,
1806    ToCss,
1807    ToShmem,
1808    ToTyped,
1809)]
1810pub enum Resize {
1811    None,
1812    Both,
1813    Horizontal,
1814    Vertical,
1815    // https://drafts.csswg.org/css-logical-1/#resize
1816    Inline,
1817    Block,
1818}
1819
1820/// The value for the `appearance` property.
1821///
1822/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
1823#[allow(missing_docs)]
1824#[derive(
1825    Clone,
1826    Copy,
1827    Debug,
1828    Eq,
1829    Hash,
1830    MallocSizeOf,
1831    Parse,
1832    PartialEq,
1833    SpecifiedValueInfo,
1834    ToCss,
1835    ToComputedValue,
1836    ToResolvedValue,
1837    ToShmem,
1838    ToTyped,
1839)]
1840#[repr(u8)]
1841pub enum Appearance {
1842    /// No appearance at all.
1843    None,
1844    /// Default appearance for the element.
1845    ///
1846    /// This value doesn't make sense for -moz-default-appearance, but we don't bother to guard
1847    /// against parsing it.
1848    Auto,
1849    /// A searchfield.
1850    Searchfield,
1851    /// A multi-line text field, e.g. HTML <textarea>.
1852    Textarea,
1853    /// A checkbox element.
1854    Checkbox,
1855    /// A radio element within a radio group.
1856    Radio,
1857    /// A dropdown list.
1858    Menulist,
1859    /// List boxes.
1860    Listbox,
1861    /// A horizontal meter bar.
1862    Meter,
1863    /// A horizontal progress bar.
1864    ProgressBar,
1865    /// A typical dialog button.
1866    Button,
1867    /// A single-line text field, e.g. HTML <input type=text>.
1868    Textfield,
1869    /// The dropdown button(s) that open up a dropdown list.
1870    MenulistButton,
1871    /// https://drafts.csswg.org/css-forms/#appearance
1872    #[parse(condition = "appearance_base_enabled")]
1873    Base,
1874    /// Only relevant to the <select> element and ::picker(select) pseudo-element, allowing them to
1875    /// be styled.
1876    #[parse(condition = "appearance_base_select_enabled")]
1877    BaseSelect,
1878    /// Menu Popup background.
1879    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1880    Menupopup,
1881    /// The "arrowed" part of the dropdown button that open up a dropdown list.
1882    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1883    MozMenulistArrowButton,
1884    /// For HTML's <input type=number>
1885    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1886    NumberInput,
1887    /// For HTML's <input type=password>
1888    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1889    PasswordInput,
1890    /// nsRangeFrame and its subparts
1891    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1892    Range,
1893    /// The scrollbar slider
1894    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1895    ScrollbarHorizontal,
1896    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1897    ScrollbarVertical,
1898    /// A scrollbar button (up/down/left/right).
1899    /// Keep these in order (some code casts these values to `int` in order to
1900    /// compare them against each other).
1901    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1902    ScrollbarbuttonUp,
1903    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1904    ScrollbarbuttonDown,
1905    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1906    ScrollbarbuttonLeft,
1907    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1908    ScrollbarbuttonRight,
1909    /// The scrollbar thumb.
1910    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1911    ScrollbarthumbHorizontal,
1912    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1913    ScrollbarthumbVertical,
1914    /// The scroll corner
1915    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1916    Scrollcorner,
1917    /// The up button of a spin control.
1918    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1919    SpinnerUpbutton,
1920    /// The down button of a spin control.
1921    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1922    SpinnerDownbutton,
1923    /// A single toolbar button (with no associated dropdown).
1924    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1925    Toolbarbutton,
1926    /// A tooltip.
1927    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1928    Tooltip,
1929
1930    /// Sidebar appearance.
1931    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1932    MozSidebar,
1933
1934    /// Mac help button.
1935    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1936    MozMacHelpButton,
1937
1938    /// An appearance value for the root, so that we can get tinting and unified toolbar looks
1939    /// (which require a transparent gecko background) without really using the whole transparency
1940    /// set-up which otherwise loses window borders, see bug 1870481.
1941    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1942    MozMacWindow,
1943
1944    /// Windows themed window frame elements.
1945    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1946    MozWindowButtonBox,
1947    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1948    MozWindowButtonClose,
1949    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1950    MozWindowButtonMaximize,
1951    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1952    MozWindowButtonMinimize,
1953    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1954    MozWindowButtonRestore,
1955    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1956    MozWindowTitlebar,
1957    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1958    MozWindowTitlebarMaximized,
1959    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1960    MozWindowDecorations,
1961
1962    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1963    MozMacDisclosureButtonClosed,
1964    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1965    MozMacDisclosureButtonOpen,
1966
1967    /// A themed focus outline (for outline:auto).
1968    ///
1969    /// This isn't exposed to CSS at all, just here for convenience.
1970    #[css(skip)]
1971    FocusOutline,
1972
1973    /// A dummy variant that should be last to let the GTK widget do hackery.
1974    #[css(skip)]
1975    Count,
1976}
1977
1978impl Appearance {
1979    /// Parses a `-moz-appearance` value.
1980    #[cfg_attr(feature = "servo", allow(unused))]
1981    pub(crate) fn parse_legacy(
1982        context: &ParserContext,
1983        input: &mut Parser,
1984    ) -> Result<Self, ParseError> {
1985        // TODO: Restrict the values we parse in the prefixed version.
1986        Self::parse(context, input)
1987    }
1988}
1989
1990/// A kind of break between two boxes.
1991///
1992/// https://drafts.csswg.org/css-break/#break-between
1993#[allow(missing_docs)]
1994#[derive(
1995    Clone,
1996    Copy,
1997    Debug,
1998    Eq,
1999    Hash,
2000    MallocSizeOf,
2001    Parse,
2002    PartialEq,
2003    SpecifiedValueInfo,
2004    ToCss,
2005    ToComputedValue,
2006    ToResolvedValue,
2007    ToShmem,
2008    ToTyped,
2009)]
2010#[repr(u8)]
2011pub enum BreakBetween {
2012    Always,
2013    Auto,
2014    Page,
2015    Avoid,
2016    Left,
2017    Right,
2018}
2019
2020impl BreakBetween {
2021    /// Parse a legacy break-between value for `page-break-{before,after}`.
2022    ///
2023    /// See https://drafts.csswg.org/css-break/#page-break-properties.
2024    #[cfg_attr(feature = "servo", allow(unused))]
2025    #[inline]
2026    pub(crate) fn parse_legacy(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
2027        let break_value = BreakBetween::parse(input)?;
2028        match break_value {
2029            BreakBetween::Always => Ok(BreakBetween::Page),
2030            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
2031                Ok(break_value)
2032            },
2033            BreakBetween::Page => Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError)),
2034        }
2035    }
2036
2037    /// Serialize a legacy break-between value for `page-break-*`.
2038    ///
2039    /// See https://drafts.csswg.org/css-break/#page-break-properties.
2040    #[cfg_attr(feature = "servo", allow(unused))]
2041    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2042    where
2043        W: Write,
2044    {
2045        match *self {
2046            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
2047                self.to_css(dest)
2048            },
2049            BreakBetween::Page => dest.write_str("always"),
2050            BreakBetween::Always => Ok(()),
2051        }
2052    }
2053}
2054
2055/// A kind of break within a box.
2056///
2057/// https://drafts.csswg.org/css-break/#break-within
2058#[allow(missing_docs)]
2059#[derive(
2060    Clone,
2061    Copy,
2062    Debug,
2063    Eq,
2064    Hash,
2065    MallocSizeOf,
2066    Parse,
2067    PartialEq,
2068    SpecifiedValueInfo,
2069    ToCss,
2070    ToComputedValue,
2071    ToResolvedValue,
2072    ToShmem,
2073    ToTyped,
2074)]
2075#[repr(u8)]
2076pub enum BreakWithin {
2077    Auto,
2078    Avoid,
2079    AvoidPage,
2080    AvoidColumn,
2081}
2082
2083impl BreakWithin {
2084    /// Parse a legacy break-between value for `page-break-inside`.
2085    ///
2086    /// See https://drafts.csswg.org/css-break/#page-break-properties.
2087    #[cfg_attr(feature = "servo", allow(unused))]
2088    #[inline]
2089    pub(crate) fn parse_legacy(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
2090        let break_value = BreakWithin::parse(input)?;
2091        match break_value {
2092            BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
2093            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
2094                Err(ParseError::custom(StyleParseErrorKind::UnspecifiedError))
2095            },
2096        }
2097    }
2098
2099    /// Serialize a legacy break-between value for `page-break-inside`.
2100    ///
2101    /// See https://drafts.csswg.org/css-break/#page-break-properties.
2102    #[cfg_attr(feature = "servo", allow(unused))]
2103    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
2104    where
2105        W: Write,
2106    {
2107        match *self {
2108            BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
2109            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
2110        }
2111    }
2112}
2113
2114/// The value for the `overflow-x` / `overflow-y` properties.
2115#[allow(missing_docs)]
2116#[derive(
2117    Clone,
2118    Copy,
2119    Debug,
2120    Eq,
2121    Hash,
2122    MallocSizeOf,
2123    PartialEq,
2124    SpecifiedValueInfo,
2125    ToCss,
2126    ToComputedValue,
2127    ToResolvedValue,
2128    ToShmem,
2129    ToTyped,
2130)]
2131#[repr(u8)]
2132pub enum Overflow {
2133    Visible,
2134    Hidden,
2135    Scroll,
2136    Auto,
2137    Clip,
2138}
2139
2140// This can be derived once we remove or keep `-moz-hidden-unscrollable`
2141// indefinitely.
2142impl Parse for Overflow {
2143    fn parse(_: &ParserContext, input: &mut Parser) -> Result<Self, ParseError> {
2144        Ok(try_match_ident_ignore_ascii_case! { input,
2145            "visible" => Self::Visible,
2146            "hidden" => Self::Hidden,
2147            "scroll" => Self::Scroll,
2148            "auto" | "overlay" => Self::Auto,
2149            "clip" => Self::Clip,
2150            "-moz-hidden-unscrollable" if crate::pref!("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
2151                Overflow::Clip
2152            },
2153        })
2154    }
2155}
2156
2157impl Overflow {
2158    /// Return true if the value will create a scrollable box.
2159    #[inline]
2160    pub fn is_scrollable(&self) -> bool {
2161        matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)
2162    }
2163    /// Convert the value to a scrollable value if it's not already scrollable.
2164    /// This maps `visible` to `auto` and `clip` to `hidden`.
2165    #[inline]
2166    pub fn to_scrollable(&self) -> Self {
2167        match *self {
2168            Self::Hidden | Self::Scroll | Self::Auto => *self,
2169            Self::Visible => Self::Auto,
2170            Self::Clip => Self::Hidden,
2171        }
2172    }
2173}
2174
2175#[derive(
2176    Clone,
2177    Copy,
2178    Debug,
2179    Eq,
2180    MallocSizeOf,
2181    Parse,
2182    PartialEq,
2183    SpecifiedValueInfo,
2184    ToComputedValue,
2185    ToCss,
2186    ToResolvedValue,
2187    ToShmem,
2188    ToTyped,
2189)]
2190#[repr(C)]
2191#[css(bitflags(
2192    single = "auto",
2193    mixed = "stable,both-edges",
2194    validate_mixed = "Self::has_stable"
2195))]
2196/// Values for scrollbar-gutter:
2197/// <https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property>
2198pub struct ScrollbarGutter(u8);
2199bitflags! {
2200    impl ScrollbarGutter: u8 {
2201        /// `auto` variant. Just for convenience if there is no flag set.
2202        const AUTO = 0;
2203        /// `stable` variant.
2204        const STABLE = 1 << 0;
2205        /// `both-edges` variant.
2206        const BOTH_EDGES = 1 << 1;
2207    }
2208}
2209
2210impl ScrollbarGutter {
2211    #[inline]
2212    fn has_stable(&self) -> bool {
2213        self.intersects(Self::STABLE)
2214    }
2215}
2216
2217/// A specified value for the zoom property.
2218#[derive(
2219    Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
2220)]
2221#[allow(missing_docs)]
2222#[typed(todo_derive_fields)]
2223pub enum Zoom {
2224    Normal,
2225    /// An internal value that resets the effective zoom to 1. Used for scrollbar parts, which
2226    /// disregard zoom. We use this name because WebKit has this value exposed to the web.
2227    #[parse(condition = "ParserContext::in_ua_sheet")]
2228    Document,
2229    Value(NonNegativeNumberOrPercentage),
2230}
2231
2232impl Zoom {
2233    /// Return a particular number value of the zoom property.
2234    #[inline]
2235    pub fn new_number(n: f32) -> Self {
2236        Self::Value(NonNegativeNumberOrPercentage::new_number(n))
2237    }
2238}
2239
2240pub use crate::values::generics::box_::PositionProperty;
2241
2242/// Whether an element lives in the top layer. Set by UA sheets.
2243#[allow(missing_docs)]
2244#[derive(
2245    Clone,
2246    Copy,
2247    Debug,
2248    Deserialize,
2249    Eq,
2250    FromPrimitive,
2251    Hash,
2252    MallocSizeOf,
2253    Parse,
2254    PartialEq,
2255    Serialize,
2256    SpecifiedValueInfo,
2257    ToComputedValue,
2258    ToCss,
2259    ToResolvedValue,
2260    ToShmem,
2261    ToTyped,
2262)]
2263#[repr(u8)]
2264pub enum TopLayer {
2265    None,
2266    Auto,
2267}
2268
2269/// https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior
2270#[allow(missing_docs)]
2271#[derive(
2272    Clone,
2273    Copy,
2274    Debug,
2275    Deserialize,
2276    Eq,
2277    FromPrimitive,
2278    Hash,
2279    MallocSizeOf,
2280    Parse,
2281    PartialEq,
2282    Serialize,
2283    SpecifiedValueInfo,
2284    ToComputedValue,
2285    ToCss,
2286    ToResolvedValue,
2287    ToShmem,
2288    ToTyped,
2289)]
2290#[repr(u8)]
2291pub enum ScrollBehavior {
2292    Auto,
2293    Smooth,
2294}
2295
2296/// https://drafts.fxtf.org/compositing/#isolation
2297#[allow(missing_docs)]
2298#[derive(
2299    Clone,
2300    Copy,
2301    Debug,
2302    Deserialize,
2303    Eq,
2304    FromPrimitive,
2305    Hash,
2306    MallocSizeOf,
2307    Parse,
2308    PartialEq,
2309    Serialize,
2310    SpecifiedValueInfo,
2311    ToComputedValue,
2312    ToCss,
2313    ToResolvedValue,
2314    ToShmem,
2315    ToTyped,
2316)]
2317#[repr(u8)]
2318pub enum Isolation {
2319    Auto,
2320    Isolate,
2321}
2322
2323/// https://drafts.csswg.org/css-transforms/#backface-visibility-property
2324#[allow(missing_docs)]
2325#[derive(
2326    Clone,
2327    Copy,
2328    Debug,
2329    Deserialize,
2330    Eq,
2331    FromPrimitive,
2332    Hash,
2333    MallocSizeOf,
2334    Parse,
2335    PartialEq,
2336    Serialize,
2337    SpecifiedValueInfo,
2338    ToComputedValue,
2339    ToCss,
2340    ToResolvedValue,
2341    ToShmem,
2342    ToTyped,
2343)]
2344#[repr(u8)]
2345pub enum BackfaceVisibility {
2346    Visible,
2347    Hidden,
2348}
2349
2350/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)
2351#[allow(missing_docs)]
2352#[derive(
2353    Clone,
2354    Copy,
2355    Debug,
2356    Deserialize,
2357    Eq,
2358    FromPrimitive,
2359    Hash,
2360    MallocSizeOf,
2361    Parse,
2362    PartialEq,
2363    Serialize,
2364    SpecifiedValueInfo,
2365    ToComputedValue,
2366    ToCss,
2367    ToResolvedValue,
2368    ToShmem,
2369    ToTyped,
2370)]
2371#[repr(u8)]
2372pub enum Orient {
2373    Inline,
2374    Block,
2375    Horizontal,
2376    Vertical,
2377}
2378
2379/// https://drafts.csswg.org/css-box/#propdef-visibility
2380#[allow(missing_docs)]
2381#[derive(
2382    Clone,
2383    Copy,
2384    Debug,
2385    Deserialize,
2386    Eq,
2387    FromPrimitive,
2388    Hash,
2389    MallocSizeOf,
2390    Parse,
2391    PartialEq,
2392    Serialize,
2393    SpecifiedValueInfo,
2394    ToAnimatedValue,
2395    ToComputedValue,
2396    ToCss,
2397    ToResolvedValue,
2398    ToShmem,
2399    ToTyped,
2400)]
2401#[repr(u8)]
2402pub enum Visibility {
2403    Visible,
2404    Hidden,
2405    Collapse,
2406}
2407
2408/// Whether flexbox visibility: collapse items use legacy -moz-box behavior or not.
2409#[allow(missing_docs)]
2410#[derive(
2411    Clone,
2412    Copy,
2413    Debug,
2414    Deserialize,
2415    Eq,
2416    FromPrimitive,
2417    Hash,
2418    MallocSizeOf,
2419    Parse,
2420    PartialEq,
2421    Serialize,
2422    SpecifiedValueInfo,
2423    ToComputedValue,
2424    ToCss,
2425    ToResolvedValue,
2426    ToShmem,
2427    ToTyped,
2428)]
2429#[repr(u8)]
2430pub enum BoxCollapse {
2431    Flex,
2432    Legacy,
2433}
2434
2435/// https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation
2436#[allow(missing_docs)]
2437#[derive(
2438    Clone,
2439    Copy,
2440    Debug,
2441    Deserialize,
2442    Eq,
2443    FromPrimitive,
2444    Hash,
2445    MallocSizeOf,
2446    Parse,
2447    PartialEq,
2448    Serialize,
2449    SpecifiedValueInfo,
2450    ToComputedValue,
2451    ToCss,
2452    ToResolvedValue,
2453    ToShmem,
2454    ToTyped,
2455)]
2456#[repr(u8)]
2457pub enum TextOrientation {
2458    Mixed,
2459    Upright,
2460    #[cfg_attr(feature = "gecko", parse(aliases = "sideways-right"))]
2461    Sideways,
2462}
2463
2464/// https://drafts.csswg.org/css-images/#propdef-image-orientation
2465#[allow(missing_docs)]
2466#[derive(
2467    Clone,
2468    Copy,
2469    Debug,
2470    Deserialize,
2471    Eq,
2472    FromPrimitive,
2473    Hash,
2474    MallocSizeOf,
2475    Parse,
2476    PartialEq,
2477    Serialize,
2478    SpecifiedValueInfo,
2479    ToComputedValue,
2480    ToCss,
2481    ToResolvedValue,
2482    ToShmem,
2483    ToTyped,
2484)]
2485#[repr(u8)]
2486pub enum ImageOrientation {
2487    FromImage,
2488    None,
2489}
2490
2491/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)
2492#[allow(missing_docs)]
2493#[derive(
2494    Clone,
2495    Copy,
2496    Debug,
2497    Deserialize,
2498    Eq,
2499    FromPrimitive,
2500    Hash,
2501    MallocSizeOf,
2502    Parse,
2503    PartialEq,
2504    Serialize,
2505    SpecifiedValueInfo,
2506    ToComputedValue,
2507    ToCss,
2508    ToResolvedValue,
2509    ToShmem,
2510    ToTyped,
2511)]
2512#[repr(u8)]
2513pub enum BoxAlign {
2514    Stretch,
2515    Start,
2516    Center,
2517    Baseline,
2518    End,
2519}
2520
2521/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)
2522#[allow(missing_docs)]
2523#[derive(
2524    Clone,
2525    Copy,
2526    Debug,
2527    Deserialize,
2528    Eq,
2529    FromPrimitive,
2530    Hash,
2531    MallocSizeOf,
2532    Parse,
2533    PartialEq,
2534    Serialize,
2535    SpecifiedValueInfo,
2536    ToComputedValue,
2537    ToCss,
2538    ToResolvedValue,
2539    ToShmem,
2540    ToTyped,
2541)]
2542#[repr(u8)]
2543pub enum BoxDirection {
2544    Normal,
2545    Reverse,
2546}
2547
2548/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)
2549#[allow(missing_docs)]
2550#[derive(
2551    Clone,
2552    Copy,
2553    Debug,
2554    Deserialize,
2555    Eq,
2556    FromPrimitive,
2557    Hash,
2558    MallocSizeOf,
2559    Parse,
2560    PartialEq,
2561    Serialize,
2562    SpecifiedValueInfo,
2563    ToComputedValue,
2564    ToCss,
2565    ToResolvedValue,
2566    ToShmem,
2567    ToTyped,
2568)]
2569#[repr(u8)]
2570pub enum BoxOrient {
2571    #[cfg_attr(feature = "gecko", parse(aliases = "inline-axis"))]
2572    Horizontal,
2573    #[cfg_attr(feature = "gecko", parse(aliases = "block-axis"))]
2574    Vertical,
2575}
2576
2577/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)
2578#[allow(missing_docs)]
2579#[derive(
2580    Clone,
2581    Copy,
2582    Debug,
2583    Deserialize,
2584    Eq,
2585    FromPrimitive,
2586    Hash,
2587    MallocSizeOf,
2588    Parse,
2589    PartialEq,
2590    Serialize,
2591    SpecifiedValueInfo,
2592    ToComputedValue,
2593    ToCss,
2594    ToResolvedValue,
2595    ToShmem,
2596    ToTyped,
2597)]
2598#[repr(u8)]
2599pub enum BoxPack {
2600    Start,
2601    Center,
2602    End,
2603    Justify,
2604}
2605
2606/// https://drafts.csswg.org/css-writing-modes/#propdef-direction
2607#[allow(missing_docs)]
2608#[derive(
2609    Clone,
2610    Copy,
2611    Debug,
2612    Deserialize,
2613    Eq,
2614    FromPrimitive,
2615    Hash,
2616    MallocSizeOf,
2617    Parse,
2618    PartialEq,
2619    Serialize,
2620    SpecifiedValueInfo,
2621    ToComputedValue,
2622    ToCss,
2623    ToResolvedValue,
2624    ToShmem,
2625    ToTyped,
2626)]
2627#[repr(u8)]
2628pub enum DirectionProperty {
2629    Ltr,
2630    Rtl,
2631}