tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

box.rs (56576B)


      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 
      7 use crate::derives::*;
      8 pub use crate::logical_geometry::WritingModeProperty;
      9 use crate::parser::{Parse, ParserContext};
     10 use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};
     11 use crate::values::generics::box_::{
     12    GenericContainIntrinsicSize, GenericLineClamp, GenericOverflowClipMargin, GenericPerspective,
     13    GenericVerticalAlign, OverflowClipMarginBox, VerticalAlignKeyword,
     14 };
     15 use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
     16 use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumberOrPercentage};
     17 use crate::values::CustomIdent;
     18 use cssparser::Parser;
     19 use num_traits::FromPrimitive;
     20 use std::fmt::{self, Write};
     21 use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
     22 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
     23 
     24 #[cfg(not(feature = "servo"))]
     25 fn grid_enabled() -> bool {
     26    true
     27 }
     28 
     29 #[cfg(feature = "servo")]
     30 fn grid_enabled() -> bool {
     31    style_config::get_bool("layout.grid.enabled")
     32 }
     33 
     34 /// The specified value of `overflow-clip-margin`.
     35 pub type OverflowClipMargin = GenericOverflowClipMargin<NonNegativeLength>;
     36 
     37 impl Parse for OverflowClipMargin {
     38    // <visual-box> || <length [0,∞]>
     39    fn parse<'i>(
     40        context: &ParserContext,
     41        input: &mut Parser<'i, '_>,
     42    ) -> Result<Self, ParseError<'i>> {
     43        use crate::Zero;
     44        let mut offset = None;
     45        let mut visual_box = None;
     46        loop {
     47            if offset.is_none() {
     48                offset = input
     49                    .try_parse(|i| NonNegativeLength::parse(context, i))
     50                    .ok();
     51            }
     52            if visual_box.is_none() {
     53                visual_box = input.try_parse(OverflowClipMarginBox::parse).ok();
     54                if visual_box.is_some() {
     55                    continue;
     56                }
     57            }
     58            break;
     59        }
     60        if offset.is_none() && visual_box.is_none() {
     61            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
     62        }
     63        Ok(Self {
     64            offset: offset.unwrap_or_else(NonNegativeLength::zero),
     65            visual_box: visual_box.unwrap_or(OverflowClipMarginBox::PaddingBox),
     66        })
     67    }
     68 }
     69 
     70 /// Defines an element’s display type, which consists of
     71 /// the two basic qualities of how an element generates boxes
     72 /// <https://drafts.csswg.org/css-display/#propdef-display>
     73 #[allow(missing_docs)]
     74 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
     75 #[repr(u8)]
     76 pub enum DisplayOutside {
     77    None = 0,
     78    Inline,
     79    Block,
     80    TableCaption,
     81    InternalTable,
     82    #[cfg(feature = "gecko")]
     83    InternalRuby,
     84 }
     85 
     86 #[allow(missing_docs)]
     87 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
     88 #[repr(u8)]
     89 pub enum DisplayInside {
     90    None = 0,
     91    Contents,
     92    Flow,
     93    FlowRoot,
     94    Flex,
     95    Grid,
     96    Table,
     97    TableRowGroup,
     98    TableColumn,
     99    TableColumnGroup,
    100    TableHeaderGroup,
    101    TableFooterGroup,
    102    TableRow,
    103    TableCell,
    104    #[cfg(feature = "gecko")]
    105    Ruby,
    106    #[cfg(feature = "gecko")]
    107    RubyBase,
    108    #[cfg(feature = "gecko")]
    109    RubyBaseContainer,
    110    #[cfg(feature = "gecko")]
    111    RubyText,
    112    #[cfg(feature = "gecko")]
    113    RubyTextContainer,
    114    #[cfg(feature = "gecko")]
    115    WebkitBox,
    116 }
    117 
    118 impl DisplayInside {
    119    fn is_valid_for_list_item(self) -> bool {
    120        match self {
    121            DisplayInside::Flow => true,
    122            #[cfg(feature = "gecko")]
    123            DisplayInside::FlowRoot => true,
    124            _ => false,
    125        }
    126    }
    127 
    128    /// https://drafts.csswg.org/css-display/#inside-model:
    129    ///     If <display-outside> is omitted, the element’s outside display type defaults to block
    130    ///     — except for ruby, which defaults to inline.
    131    fn default_display_outside(self) -> DisplayOutside {
    132        match self {
    133            #[cfg(feature = "gecko")]
    134            DisplayInside::Ruby => DisplayOutside::Inline,
    135            _ => DisplayOutside::Block,
    136        }
    137    }
    138 }
    139 
    140 #[allow(missing_docs)]
    141 #[derive(
    142    Clone,
    143    Copy,
    144    Debug,
    145    Eq,
    146    FromPrimitive,
    147    Hash,
    148    MallocSizeOf,
    149    PartialEq,
    150    ToComputedValue,
    151    ToResolvedValue,
    152    ToShmem,
    153    ToTyped,
    154 )]
    155 #[repr(C)]
    156 pub struct Display(u16);
    157 
    158 /// Gecko-only impl block for Display (shared stuff later in this file):
    159 #[allow(missing_docs)]
    160 #[allow(non_upper_case_globals)]
    161 impl Display {
    162    // Our u16 bits are used as follows: LOOOOOOOIIIIIIII
    163    pub const LIST_ITEM_MASK: u16 = 0b1000000000000000;
    164    pub const OUTSIDE_MASK: u16 = 0b0111111100000000;
    165    pub const INSIDE_MASK: u16 = 0b0000000011111111;
    166    pub const OUTSIDE_SHIFT: u16 = 8;
    167 
    168    /// https://drafts.csswg.org/css-display/#the-display-properties
    169    /// ::new() inlined so cbindgen can use it
    170    pub const None: Self =
    171        Self(((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::None as u16);
    172    pub const Contents: Self = Self(
    173        ((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Contents as u16,
    174    );
    175    pub const Inline: Self =
    176        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16);
    177    pub const InlineBlock: Self = Self(
    178        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::FlowRoot as u16,
    179    );
    180    pub const Block: Self =
    181        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16);
    182    #[cfg(feature = "gecko")]
    183    pub const FlowRoot: Self = Self(
    184        ((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::FlowRoot as u16,
    185    );
    186    pub const Flex: Self =
    187        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flex as u16);
    188    pub const InlineFlex: Self =
    189        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flex as u16);
    190    pub const Grid: Self =
    191        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);
    192    pub const InlineGrid: Self =
    193        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);
    194    pub const Table: Self =
    195        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16);
    196    pub const InlineTable: Self = Self(
    197        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16,
    198    );
    199    pub const TableCaption: Self = Self(
    200        ((DisplayOutside::TableCaption as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16,
    201    );
    202    #[cfg(feature = "gecko")]
    203    pub const Ruby: Self =
    204        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Ruby as u16);
    205    #[cfg(feature = "gecko")]
    206    pub const WebkitBox: Self = Self(
    207        ((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::WebkitBox as u16,
    208    );
    209    #[cfg(feature = "gecko")]
    210    pub const WebkitInlineBox: Self = Self(
    211        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::WebkitBox as u16,
    212    );
    213 
    214    // Internal table boxes.
    215 
    216    pub const TableRowGroup: Self = Self(
    217        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
    218            | DisplayInside::TableRowGroup as u16,
    219    );
    220    pub const TableHeaderGroup: Self = Self(
    221        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
    222            | DisplayInside::TableHeaderGroup as u16,
    223    );
    224    pub const TableFooterGroup: Self = Self(
    225        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
    226            | DisplayInside::TableFooterGroup as u16,
    227    );
    228    pub const TableColumn: Self = Self(
    229        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
    230            | DisplayInside::TableColumn as u16,
    231    );
    232    pub const TableColumnGroup: Self = Self(
    233        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
    234            | DisplayInside::TableColumnGroup as u16,
    235    );
    236    pub const TableRow: Self = Self(
    237        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
    238            | DisplayInside::TableRow as u16,
    239    );
    240    pub const TableCell: Self = Self(
    241        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)
    242            | DisplayInside::TableCell as u16,
    243    );
    244 
    245    /// Internal ruby boxes.
    246    #[cfg(feature = "gecko")]
    247    pub const RubyBase: Self = Self(
    248        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)
    249            | DisplayInside::RubyBase as u16,
    250    );
    251    #[cfg(feature = "gecko")]
    252    pub const RubyBaseContainer: Self = Self(
    253        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)
    254            | DisplayInside::RubyBaseContainer as u16,
    255    );
    256    #[cfg(feature = "gecko")]
    257    pub const RubyText: Self = Self(
    258        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)
    259            | DisplayInside::RubyText as u16,
    260    );
    261    #[cfg(feature = "gecko")]
    262    pub const RubyTextContainer: Self = Self(
    263        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)
    264            | DisplayInside::RubyTextContainer as u16,
    265    );
    266 
    267    /// Make a raw display value from <display-outside> and <display-inside> values.
    268    #[inline]
    269    const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
    270        Self((outside as u16) << Self::OUTSIDE_SHIFT | inside as u16)
    271    }
    272 
    273    /// Make a display enum value from <display-outside> and <display-inside> values.
    274    #[inline]
    275    fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
    276        let v = Self::new(outside, inside);
    277        if !list_item {
    278            return v;
    279        }
    280        Self(v.0 | Self::LIST_ITEM_MASK)
    281    }
    282 
    283    /// Accessor for the <display-inside> value.
    284    #[inline]
    285    pub fn inside(&self) -> DisplayInside {
    286        DisplayInside::from_u16(self.0 & Self::INSIDE_MASK).unwrap()
    287    }
    288 
    289    /// Accessor for the <display-outside> value.
    290    #[inline]
    291    pub fn outside(&self) -> DisplayOutside {
    292        DisplayOutside::from_u16((self.0 & Self::OUTSIDE_MASK) >> Self::OUTSIDE_SHIFT).unwrap()
    293    }
    294 
    295    /// Returns the raw underlying u16 value.
    296    #[inline]
    297    pub const fn to_u16(&self) -> u16 {
    298        self.0
    299    }
    300 
    301    /// Whether this is `display: inline` (or `inline list-item`).
    302    #[inline]
    303    pub fn is_inline_flow(&self) -> bool {
    304        self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
    305    }
    306 
    307    /// Returns whether this `display` value is some kind of list-item.
    308    #[inline]
    309    pub const fn is_list_item(&self) -> bool {
    310        (self.0 & Self::LIST_ITEM_MASK) != 0
    311    }
    312 
    313    /// Returns whether this `display` value is a ruby level container.
    314    pub fn is_ruby_level_container(&self) -> bool {
    315        match *self {
    316            #[cfg(feature = "gecko")]
    317            Display::RubyBaseContainer | Display::RubyTextContainer => true,
    318            _ => false,
    319        }
    320    }
    321 
    322    /// Returns whether this `display` value is one of the types for ruby.
    323    pub fn is_ruby_type(&self) -> bool {
    324        match self.inside() {
    325            #[cfg(feature = "gecko")]
    326            DisplayInside::Ruby
    327            | DisplayInside::RubyBase
    328            | DisplayInside::RubyText
    329            | DisplayInside::RubyBaseContainer
    330            | DisplayInside::RubyTextContainer => true,
    331            _ => false,
    332        }
    333    }
    334 }
    335 
    336 /// Shared Display impl for both Gecko and Servo.
    337 impl Display {
    338    /// The initial display value.
    339    #[inline]
    340    pub fn inline() -> Self {
    341        Display::Inline
    342    }
    343 
    344    /// Returns whether this `display` value is the display of a flex or
    345    /// grid container.
    346    ///
    347    /// This is used to implement various style fixups.
    348    pub fn is_item_container(&self) -> bool {
    349        match self.inside() {
    350            DisplayInside::Flex => true,
    351            DisplayInside::Grid => true,
    352            _ => false,
    353        }
    354    }
    355 
    356    /// Returns whether an element with this display type is a line
    357    /// participant, which means it may lay its children on the same
    358    /// line as itself.
    359    pub fn is_line_participant(&self) -> bool {
    360        if self.is_inline_flow() {
    361            return true;
    362        }
    363        match *self {
    364            #[cfg(feature = "gecko")]
    365            Display::Contents | Display::Ruby | Display::RubyBaseContainer => true,
    366            _ => false,
    367        }
    368    }
    369 
    370    /// Convert this display into an equivalent block display.
    371    ///
    372    /// Also used for :root style adjustments.
    373    pub fn equivalent_block_display(&self, is_root_element: bool) -> Self {
    374        // Special handling for `contents` and `list-item`s on the root element.
    375        if is_root_element && (self.is_contents() || self.is_list_item()) {
    376            return Display::Block;
    377        }
    378 
    379        match self.outside() {
    380            DisplayOutside::Inline => {
    381                let inside = match self.inside() {
    382                    // `inline-block` blockifies to `block` rather than
    383                    // `flow-root`, for legacy reasons.
    384                    DisplayInside::FlowRoot => DisplayInside::Flow,
    385                    inside => inside,
    386                };
    387                Display::from3(DisplayOutside::Block, inside, self.is_list_item())
    388            },
    389            DisplayOutside::Block | DisplayOutside::None => *self,
    390            _ => Display::Block,
    391        }
    392    }
    393 
    394    /// Convert this display into an equivalent inline-outside display.
    395    /// https://drafts.csswg.org/css-display/#inlinify
    396    #[cfg(feature = "gecko")]
    397    pub fn inlinify(&self) -> Self {
    398        match self.outside() {
    399            DisplayOutside::Block => {
    400                let inside = match self.inside() {
    401                    // `display: block` inlinifies to `display: inline-block`,
    402                    // rather than `inline`, for legacy reasons.
    403                    DisplayInside::Flow => DisplayInside::FlowRoot,
    404                    inside => inside,
    405                };
    406                Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
    407            },
    408            _ => *self,
    409        }
    410    }
    411 
    412    /// Returns true if the value is `Contents`
    413    #[inline]
    414    pub fn is_contents(&self) -> bool {
    415        match *self {
    416            Display::Contents => true,
    417            _ => false,
    418        }
    419    }
    420 
    421    /// Returns true if the value is `None`
    422    #[inline]
    423    pub fn is_none(&self) -> bool {
    424        *self == Display::None
    425    }
    426 }
    427 
    428 enum DisplayKeyword {
    429    Full(Display),
    430    Inside(DisplayInside),
    431    Outside(DisplayOutside),
    432    ListItem,
    433 }
    434 
    435 impl DisplayKeyword {
    436    fn parse<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
    437        use self::DisplayKeyword::*;
    438        Ok(try_match_ident_ignore_ascii_case! { input,
    439            "none" => Full(Display::None),
    440            "contents" => Full(Display::Contents),
    441            "inline-block" => Full(Display::InlineBlock),
    442            "inline-table" => Full(Display::InlineTable),
    443            "-webkit-flex" => Full(Display::Flex),
    444            "inline-flex" | "-webkit-inline-flex" => Full(Display::InlineFlex),
    445            "inline-grid" if grid_enabled() => Full(Display::InlineGrid),
    446            "table-caption" => Full(Display::TableCaption),
    447            "table-row-group" => Full(Display::TableRowGroup),
    448            "table-header-group" => Full(Display::TableHeaderGroup),
    449            "table-footer-group" => Full(Display::TableFooterGroup),
    450            "table-column" => Full(Display::TableColumn),
    451            "table-column-group" => Full(Display::TableColumnGroup),
    452            "table-row" => Full(Display::TableRow),
    453            "table-cell" => Full(Display::TableCell),
    454            #[cfg(feature = "gecko")]
    455            "ruby-base" => Full(Display::RubyBase),
    456            #[cfg(feature = "gecko")]
    457            "ruby-base-container" => Full(Display::RubyBaseContainer),
    458            #[cfg(feature = "gecko")]
    459            "ruby-text" => Full(Display::RubyText),
    460            #[cfg(feature = "gecko")]
    461            "ruby-text-container" => Full(Display::RubyTextContainer),
    462            #[cfg(feature = "gecko")]
    463            "-webkit-box" => Full(Display::WebkitBox),
    464            #[cfg(feature = "gecko")]
    465            "-webkit-inline-box" => Full(Display::WebkitInlineBox),
    466 
    467            /// <display-outside> = block | inline | run-in
    468            /// https://drafts.csswg.org/css-display/#typedef-display-outside
    469            "block" => Outside(DisplayOutside::Block),
    470            "inline" => Outside(DisplayOutside::Inline),
    471 
    472            "list-item" => ListItem,
    473 
    474            /// <display-inside> = flow | flow-root | table | flex | grid | ruby
    475            /// https://drafts.csswg.org/css-display/#typedef-display-inside
    476            "flow" => Inside(DisplayInside::Flow),
    477            "flex" => Inside(DisplayInside::Flex),
    478            "flow-root" => Inside(DisplayInside::FlowRoot),
    479            "table" => Inside(DisplayInside::Table),
    480            "grid" if grid_enabled() => Inside(DisplayInside::Grid),
    481            #[cfg(feature = "gecko")]
    482            "ruby" => Inside(DisplayInside::Ruby),
    483        })
    484    }
    485 }
    486 
    487 impl ToCss for Display {
    488    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    489    where
    490        W: fmt::Write,
    491    {
    492        let outside = self.outside();
    493        let inside = self.inside();
    494        match *self {
    495            Display::Block | Display::Inline => outside.to_css(dest),
    496            Display::InlineBlock => dest.write_str("inline-block"),
    497            #[cfg(feature = "gecko")]
    498            Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
    499            Display::TableCaption => dest.write_str("table-caption"),
    500            _ => match (outside, inside) {
    501                (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
    502                (DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"),
    503                (DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"),
    504                #[cfg(feature = "gecko")]
    505                (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
    506                (_, inside) => {
    507                    if self.is_list_item() {
    508                        if outside != DisplayOutside::Block {
    509                            outside.to_css(dest)?;
    510                            dest.write_char(' ')?;
    511                        }
    512                        if inside != DisplayInside::Flow {
    513                            inside.to_css(dest)?;
    514                            dest.write_char(' ')?;
    515                        }
    516                        dest.write_str("list-item")
    517                    } else {
    518                        inside.to_css(dest)
    519                    }
    520                },
    521            },
    522        }
    523    }
    524 }
    525 
    526 impl Parse for Display {
    527    fn parse<'i, 't>(
    528        _: &ParserContext,
    529        input: &mut Parser<'i, 't>,
    530    ) -> Result<Display, ParseError<'i>> {
    531        let mut got_list_item = false;
    532        let mut inside = None;
    533        let mut outside = None;
    534        match DisplayKeyword::parse(input)? {
    535            DisplayKeyword::Full(d) => return Ok(d),
    536            DisplayKeyword::Outside(o) => {
    537                outside = Some(o);
    538            },
    539            DisplayKeyword::Inside(i) => {
    540                inside = Some(i);
    541            },
    542            DisplayKeyword::ListItem => {
    543                got_list_item = true;
    544            },
    545        };
    546 
    547        while let Ok(kw) = input.try_parse(DisplayKeyword::parse) {
    548            match kw {
    549                DisplayKeyword::ListItem if !got_list_item => {
    550                    got_list_item = true;
    551                },
    552                DisplayKeyword::Outside(o) if outside.is_none() => {
    553                    outside = Some(o);
    554                },
    555                DisplayKeyword::Inside(i) if inside.is_none() => {
    556                    inside = Some(i);
    557                },
    558                _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
    559            }
    560        }
    561 
    562        let inside = inside.unwrap_or(DisplayInside::Flow);
    563        let outside = outside.unwrap_or_else(|| inside.default_display_outside());
    564        if got_list_item && !inside.is_valid_for_list_item() {
    565            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
    566        }
    567 
    568        return Ok(Display::from3(outside, inside, got_list_item));
    569    }
    570 }
    571 
    572 impl SpecifiedValueInfo for Display {
    573    fn collect_completion_keywords(f: KeywordsCollectFn) {
    574        f(&[
    575            "block",
    576            "contents",
    577            "flex",
    578            "flow-root",
    579            "flow-root list-item",
    580            "grid",
    581            "inline",
    582            "inline-block",
    583            "inline-flex",
    584            "inline-grid",
    585            "inline-table",
    586            "inline list-item",
    587            "inline flow-root list-item",
    588            "list-item",
    589            "none",
    590            "block ruby",
    591            "ruby",
    592            "ruby-base",
    593            "ruby-base-container",
    594            "ruby-text",
    595            "ruby-text-container",
    596            "table",
    597            "table-caption",
    598            "table-cell",
    599            "table-column",
    600            "table-column-group",
    601            "table-footer-group",
    602            "table-header-group",
    603            "table-row",
    604            "table-row-group",
    605            "-webkit-box",
    606            "-webkit-inline-box",
    607        ]);
    608    }
    609 }
    610 
    611 /// A specified value for the `contain-intrinsic-size` property.
    612 pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
    613 
    614 /// A specified value for the `line-clamp` property.
    615 pub type LineClamp = GenericLineClamp<Integer>;
    616 
    617 /// A specified value for the `vertical-align` property.
    618 pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;
    619 
    620 impl Parse for VerticalAlign {
    621    fn parse<'i, 't>(
    622        context: &ParserContext,
    623        input: &mut Parser<'i, 't>,
    624    ) -> Result<Self, ParseError<'i>> {
    625        if let Ok(lp) =
    626            input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))
    627        {
    628            return Ok(GenericVerticalAlign::Length(lp));
    629        }
    630 
    631        Ok(GenericVerticalAlign::Keyword(VerticalAlignKeyword::parse(
    632            input,
    633        )?))
    634    }
    635 }
    636 
    637 /// A specified value for the `baseline-source` property.
    638 /// https://drafts.csswg.org/css-inline-3/#baseline-source
    639 #[derive(
    640    Clone,
    641    Copy,
    642    Debug,
    643    Eq,
    644    Hash,
    645    MallocSizeOf,
    646    Parse,
    647    PartialEq,
    648    SpecifiedValueInfo,
    649    ToCss,
    650    ToShmem,
    651    ToComputedValue,
    652    ToResolvedValue,
    653    ToTyped,
    654 )]
    655 #[repr(u8)]
    656 pub enum BaselineSource {
    657    /// `Last` for `inline-block`, `First` otherwise.
    658    Auto,
    659    /// Use first baseline for alignment.
    660    First,
    661    /// Use last baseline for alignment.
    662    Last,
    663 }
    664 
    665 /// https://drafts.csswg.org/css-scroll-snap-1/#snap-axis
    666 #[allow(missing_docs)]
    667 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    668 #[derive(
    669    Clone,
    670    Copy,
    671    Debug,
    672    Eq,
    673    MallocSizeOf,
    674    Parse,
    675    PartialEq,
    676    SpecifiedValueInfo,
    677    ToComputedValue,
    678    ToCss,
    679    ToResolvedValue,
    680    ToShmem,
    681 )]
    682 #[repr(u8)]
    683 pub enum ScrollSnapAxis {
    684    X,
    685    Y,
    686    Block,
    687    Inline,
    688    Both,
    689 }
    690 
    691 /// https://drafts.csswg.org/css-scroll-snap-1/#snap-strictness
    692 #[allow(missing_docs)]
    693 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    694 #[derive(
    695    Clone,
    696    Copy,
    697    Debug,
    698    Eq,
    699    MallocSizeOf,
    700    Parse,
    701    PartialEq,
    702    SpecifiedValueInfo,
    703    ToComputedValue,
    704    ToCss,
    705    ToResolvedValue,
    706    ToShmem,
    707 )]
    708 #[repr(u8)]
    709 pub enum ScrollSnapStrictness {
    710    #[css(skip)]
    711    None, // Used to represent scroll-snap-type: none.  It's not parsed.
    712    Mandatory,
    713    Proximity,
    714 }
    715 
    716 /// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type
    717 #[allow(missing_docs)]
    718 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    719 #[derive(
    720    Clone,
    721    Copy,
    722    Debug,
    723    Eq,
    724    MallocSizeOf,
    725    PartialEq,
    726    SpecifiedValueInfo,
    727    ToComputedValue,
    728    ToResolvedValue,
    729    ToShmem,
    730    ToTyped,
    731 )]
    732 #[repr(C)]
    733 pub struct ScrollSnapType {
    734    axis: ScrollSnapAxis,
    735    strictness: ScrollSnapStrictness,
    736 }
    737 
    738 impl ScrollSnapType {
    739    /// Returns `none`.
    740    #[inline]
    741    pub fn none() -> Self {
    742        Self {
    743            axis: ScrollSnapAxis::Both,
    744            strictness: ScrollSnapStrictness::None,
    745        }
    746    }
    747 }
    748 
    749 impl Parse for ScrollSnapType {
    750    /// none | [ x | y | block | inline | both ] [ mandatory | proximity ]?
    751    fn parse<'i, 't>(
    752        _context: &ParserContext,
    753        input: &mut Parser<'i, 't>,
    754    ) -> Result<Self, ParseError<'i>> {
    755        if input
    756            .try_parse(|input| input.expect_ident_matching("none"))
    757            .is_ok()
    758        {
    759            return Ok(ScrollSnapType::none());
    760        }
    761 
    762        let axis = ScrollSnapAxis::parse(input)?;
    763        let strictness = input
    764            .try_parse(ScrollSnapStrictness::parse)
    765            .unwrap_or(ScrollSnapStrictness::Proximity);
    766        Ok(Self { axis, strictness })
    767    }
    768 }
    769 
    770 impl ToCss for ScrollSnapType {
    771    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    772    where
    773        W: Write,
    774    {
    775        if self.strictness == ScrollSnapStrictness::None {
    776            return dest.write_str("none");
    777        }
    778        self.axis.to_css(dest)?;
    779        if self.strictness != ScrollSnapStrictness::Proximity {
    780            dest.write_char(' ')?;
    781            self.strictness.to_css(dest)?;
    782        }
    783        Ok(())
    784    }
    785 }
    786 
    787 /// Specified value of scroll-snap-align keyword value.
    788 #[allow(missing_docs)]
    789 #[derive(
    790    Clone,
    791    Copy,
    792    Debug,
    793    Eq,
    794    FromPrimitive,
    795    Hash,
    796    MallocSizeOf,
    797    Parse,
    798    PartialEq,
    799    SpecifiedValueInfo,
    800    ToComputedValue,
    801    ToCss,
    802    ToResolvedValue,
    803    ToShmem,
    804 )]
    805 #[repr(u8)]
    806 pub enum ScrollSnapAlignKeyword {
    807    None,
    808    Start,
    809    End,
    810    Center,
    811 }
    812 
    813 /// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align
    814 #[allow(missing_docs)]
    815 #[derive(
    816    Clone,
    817    Copy,
    818    Debug,
    819    Eq,
    820    MallocSizeOf,
    821    PartialEq,
    822    SpecifiedValueInfo,
    823    ToComputedValue,
    824    ToResolvedValue,
    825    ToShmem,
    826    ToTyped,
    827 )]
    828 #[repr(C)]
    829 pub struct ScrollSnapAlign {
    830    block: ScrollSnapAlignKeyword,
    831    inline: ScrollSnapAlignKeyword,
    832 }
    833 
    834 impl ScrollSnapAlign {
    835    /// Returns `none`.
    836    #[inline]
    837    pub fn none() -> Self {
    838        ScrollSnapAlign {
    839            block: ScrollSnapAlignKeyword::None,
    840            inline: ScrollSnapAlignKeyword::None,
    841        }
    842    }
    843 }
    844 
    845 impl Parse for ScrollSnapAlign {
    846    /// [ none | start | end | center ]{1,2}
    847    fn parse<'i, 't>(
    848        _context: &ParserContext,
    849        input: &mut Parser<'i, 't>,
    850    ) -> Result<ScrollSnapAlign, ParseError<'i>> {
    851        let block = ScrollSnapAlignKeyword::parse(input)?;
    852        let inline = input
    853            .try_parse(ScrollSnapAlignKeyword::parse)
    854            .unwrap_or(block);
    855        Ok(ScrollSnapAlign { block, inline })
    856    }
    857 }
    858 
    859 impl ToCss for ScrollSnapAlign {
    860    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    861    where
    862        W: Write,
    863    {
    864        self.block.to_css(dest)?;
    865        if self.block != self.inline {
    866            dest.write_char(' ')?;
    867            self.inline.to_css(dest)?;
    868        }
    869        Ok(())
    870    }
    871 }
    872 
    873 #[allow(missing_docs)]
    874 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    875 #[derive(
    876    Clone,
    877    Copy,
    878    Debug,
    879    Eq,
    880    MallocSizeOf,
    881    Parse,
    882    PartialEq,
    883    SpecifiedValueInfo,
    884    ToComputedValue,
    885    ToCss,
    886    ToResolvedValue,
    887    ToShmem,
    888    ToTyped,
    889 )]
    890 #[repr(u8)]
    891 pub enum ScrollSnapStop {
    892    Normal,
    893    Always,
    894 }
    895 
    896 #[allow(missing_docs)]
    897 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    898 #[derive(
    899    Clone,
    900    Copy,
    901    Debug,
    902    Eq,
    903    MallocSizeOf,
    904    Parse,
    905    PartialEq,
    906    SpecifiedValueInfo,
    907    ToComputedValue,
    908    ToCss,
    909    ToResolvedValue,
    910    ToShmem,
    911    ToTyped,
    912 )]
    913 #[repr(u8)]
    914 pub enum OverscrollBehavior {
    915    Auto,
    916    Contain,
    917    None,
    918 }
    919 
    920 #[allow(missing_docs)]
    921 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
    922 #[derive(
    923    Clone,
    924    Copy,
    925    Debug,
    926    Eq,
    927    MallocSizeOf,
    928    Parse,
    929    PartialEq,
    930    SpecifiedValueInfo,
    931    ToComputedValue,
    932    ToCss,
    933    ToResolvedValue,
    934    ToShmem,
    935    ToTyped,
    936 )]
    937 #[repr(u8)]
    938 pub enum OverflowAnchor {
    939    Auto,
    940    None,
    941 }
    942 
    943 #[derive(
    944    Clone,
    945    Debug,
    946    Default,
    947    MallocSizeOf,
    948    PartialEq,
    949    SpecifiedValueInfo,
    950    ToComputedValue,
    951    ToCss,
    952    ToResolvedValue,
    953    ToShmem,
    954    ToTyped,
    955 )]
    956 #[css(comma)]
    957 #[repr(C)]
    958 /// Provides a rendering hint to the user agent, stating what kinds of changes
    959 /// the author expects to perform on the element.
    960 ///
    961 /// `auto` is represented by an empty `features` list.
    962 ///
    963 /// <https://drafts.csswg.org/css-will-change/#will-change>
    964 pub struct WillChange {
    965    /// The features that are supposed to change.
    966    ///
    967    /// TODO(emilio): Consider using ArcSlice since we just clone them from the
    968    /// specified value? That'd save an allocation, which could be worth it.
    969    #[css(iterable, if_empty = "auto")]
    970    features: crate::OwnedSlice<CustomIdent>,
    971    /// A bitfield with the kind of change that the value will create, based
    972    /// on the above field.
    973    #[css(skip)]
    974    pub bits: WillChangeBits,
    975 }
    976 
    977 impl WillChange {
    978    #[inline]
    979    /// Get default value of `will-change` as `auto`
    980    pub fn auto() -> Self {
    981        Self::default()
    982    }
    983 }
    984 
    985 /// The change bits that we care about.
    986 #[derive(
    987    Clone,
    988    Copy,
    989    Debug,
    990    Default,
    991    Eq,
    992    MallocSizeOf,
    993    PartialEq,
    994    SpecifiedValueInfo,
    995    ToComputedValue,
    996    ToResolvedValue,
    997    ToShmem,
    998 )]
    999 #[repr(C)]
   1000 pub struct WillChangeBits(u16);
   1001 bitflags! {
   1002    impl WillChangeBits: u16 {
   1003        /// Whether a property which can create a stacking context **on any
   1004        /// box** will change.
   1005        const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;
   1006        /// Whether `transform` or related properties will change.
   1007        const TRANSFORM = 1 << 1;
   1008        /// Whether `scroll-position` will change.
   1009        const SCROLL = 1 << 2;
   1010        /// Whether `contain` will change.
   1011        const CONTAIN = 1 << 3;
   1012        /// Whether `opacity` will change.
   1013        const OPACITY = 1 << 4;
   1014        /// Whether `perspective` will change.
   1015        const PERSPECTIVE = 1 << 5;
   1016        /// Whether `z-index` will change.
   1017        const Z_INDEX = 1 << 6;
   1018        /// Whether any property which creates a containing block for non-svg
   1019        /// text frames will change.
   1020        const FIXPOS_CB_NON_SVG = 1 << 7;
   1021        /// Whether the position property will change.
   1022        const POSITION = 1 << 8;
   1023        /// Whether the view-transition-name property will change.
   1024        const VIEW_TRANSITION_NAME = 1 << 9;
   1025        /// Whether any property which establishes a backdrop-root will change.
   1026        /// See https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty
   1027        const BACKDROP_ROOT = 1 << 10;
   1028    }
   1029 }
   1030 
   1031 fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
   1032    match longhand {
   1033        LonghandId::Opacity => WillChangeBits::OPACITY | WillChangeBits::BACKDROP_ROOT,
   1034        LonghandId::Contain => WillChangeBits::CONTAIN,
   1035        LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
   1036        LonghandId::Position => {
   1037            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
   1038        },
   1039        LonghandId::ZIndex => WillChangeBits::Z_INDEX,
   1040        LonghandId::Transform
   1041        | LonghandId::TransformStyle
   1042        | LonghandId::Translate
   1043        | LonghandId::Rotate
   1044        | LonghandId::Scale
   1045        | LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
   1046        LonghandId::Filter | LonghandId::BackdropFilter => {
   1047            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
   1048                | WillChangeBits::BACKDROP_ROOT
   1049                | WillChangeBits::FIXPOS_CB_NON_SVG
   1050        },
   1051        LonghandId::ViewTransitionName => {
   1052            WillChangeBits::VIEW_TRANSITION_NAME | WillChangeBits::BACKDROP_ROOT
   1053        },
   1054        LonghandId::MixBlendMode => {
   1055            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
   1056        },
   1057        LonghandId::Isolation | LonghandId::MaskImage => {
   1058            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
   1059        },
   1060        LonghandId::ClipPath => {
   1061            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
   1062        },
   1063        _ => WillChangeBits::empty(),
   1064    }
   1065 }
   1066 
   1067 fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
   1068    let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
   1069        Ok(id) => id,
   1070        Err(..) => return WillChangeBits::empty(),
   1071    };
   1072 
   1073    match id.as_shorthand() {
   1074        Ok(shorthand) => shorthand
   1075            .longhands()
   1076            .fold(WillChangeBits::empty(), |flags, p| {
   1077                flags | change_bits_for_longhand(p)
   1078            }),
   1079        Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
   1080        Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
   1081    }
   1082 }
   1083 
   1084 impl Parse for WillChange {
   1085    /// auto | <animateable-feature>#
   1086    fn parse<'i, 't>(
   1087        context: &ParserContext,
   1088        input: &mut Parser<'i, 't>,
   1089    ) -> Result<Self, ParseError<'i>> {
   1090        if input
   1091            .try_parse(|input| input.expect_ident_matching("auto"))
   1092            .is_ok()
   1093        {
   1094            return Ok(Self::default());
   1095        }
   1096 
   1097        let mut bits = WillChangeBits::empty();
   1098        let custom_idents = input.parse_comma_separated(|i| {
   1099            let location = i.current_source_location();
   1100            let parser_ident = i.expect_ident()?;
   1101            let ident = CustomIdent::from_ident(
   1102                location,
   1103                parser_ident,
   1104                &["will-change", "none", "all", "auto"],
   1105            )?;
   1106 
   1107            if context.in_ua_sheet() && ident.0 == atom!("-moz-fixed-pos-containing-block") {
   1108                bits |= WillChangeBits::FIXPOS_CB_NON_SVG;
   1109            } else if ident.0 == atom!("scroll-position") {
   1110                bits |= WillChangeBits::SCROLL;
   1111            } else {
   1112                bits |= change_bits_for_maybe_property(&parser_ident, context);
   1113            }
   1114            Ok(ident)
   1115        })?;
   1116 
   1117        Ok(Self {
   1118            features: custom_idents.into(),
   1119            bits,
   1120        })
   1121    }
   1122 }
   1123 
   1124 /// Values for the `touch-action` property.
   1125 #[derive(
   1126    Clone,
   1127    Copy,
   1128    Debug,
   1129    Eq,
   1130    MallocSizeOf,
   1131    Parse,
   1132    PartialEq,
   1133    SpecifiedValueInfo,
   1134    ToComputedValue,
   1135    ToCss,
   1136    ToResolvedValue,
   1137    ToShmem,
   1138    ToTyped,
   1139 )]
   1140 #[css(bitflags(single = "none,auto,manipulation", mixed = "pan-x,pan-y,pinch-zoom"))]
   1141 #[repr(C)]
   1142 pub struct TouchAction(u8);
   1143 bitflags! {
   1144    impl TouchAction: u8 {
   1145        /// `none` variant
   1146        const NONE = 1 << 0;
   1147        /// `auto` variant
   1148        const AUTO = 1 << 1;
   1149        /// `pan-x` variant
   1150        const PAN_X = 1 << 2;
   1151        /// `pan-y` variant
   1152        const PAN_Y = 1 << 3;
   1153        /// `manipulation` variant
   1154        const MANIPULATION = 1 << 4;
   1155        /// `pinch-zoom` variant
   1156        const PINCH_ZOOM = 1 << 5;
   1157    }
   1158 }
   1159 
   1160 impl TouchAction {
   1161    #[inline]
   1162    /// Get default `touch-action` as `auto`
   1163    pub fn auto() -> TouchAction {
   1164        TouchAction::AUTO
   1165    }
   1166 }
   1167 
   1168 #[derive(
   1169    Clone,
   1170    Copy,
   1171    Debug,
   1172    Eq,
   1173    MallocSizeOf,
   1174    Parse,
   1175    PartialEq,
   1176    SpecifiedValueInfo,
   1177    ToComputedValue,
   1178    ToCss,
   1179    ToResolvedValue,
   1180    ToShmem,
   1181    ToTyped,
   1182 )]
   1183 #[css(bitflags(
   1184    single = "none,strict,content",
   1185    mixed = "size,layout,style,paint,inline-size",
   1186    overlapping_bits
   1187 ))]
   1188 #[repr(C)]
   1189 /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
   1190 pub struct Contain(u8);
   1191 bitflags! {
   1192    impl Contain: u8 {
   1193        /// `none` variant, just for convenience.
   1194        const NONE = 0;
   1195        /// `inline-size` variant, turns on single-axis inline size containment
   1196        const INLINE_SIZE = 1 << 0;
   1197        /// `block-size` variant, turns on single-axis block size containment, internal only
   1198        const BLOCK_SIZE = 1 << 1;
   1199        /// `layout` variant, turns on layout containment
   1200        const LAYOUT = 1 << 2;
   1201        /// `style` variant, turns on style containment
   1202        const STYLE = 1 << 3;
   1203        /// `paint` variant, turns on paint containment
   1204        const PAINT = 1 << 4;
   1205        /// 'size' variant, turns on size containment
   1206        const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits() | Contain::BLOCK_SIZE.bits();
   1207        /// `content` variant, turns on layout and paint containment
   1208        const CONTENT = 1 << 6 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits();
   1209        /// `strict` variant, turns on all types of containment
   1210        const STRICT = 1 << 7 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits() | Contain::SIZE.bits();
   1211    }
   1212 }
   1213 
   1214 impl Parse for ContainIntrinsicSize {
   1215    /// none | <length> | auto <length>
   1216    fn parse<'i, 't>(
   1217        context: &ParserContext,
   1218        input: &mut Parser<'i, 't>,
   1219    ) -> Result<Self, ParseError<'i>> {
   1220        if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
   1221            return Ok(Self::Length(l));
   1222        }
   1223 
   1224        if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
   1225            if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
   1226                return Ok(Self::AutoNone);
   1227            }
   1228 
   1229            let l = NonNegativeLength::parse(context, input)?;
   1230            return Ok(Self::AutoLength(l));
   1231        }
   1232 
   1233        input.expect_ident_matching("none")?;
   1234        Ok(Self::None)
   1235    }
   1236 }
   1237 
   1238 impl Parse for LineClamp {
   1239    /// none | <positive-integer>
   1240    fn parse<'i, 't>(
   1241        context: &ParserContext,
   1242        input: &mut Parser<'i, 't>,
   1243    ) -> Result<Self, ParseError<'i>> {
   1244        if let Ok(i) =
   1245            input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))
   1246        {
   1247            return Ok(Self(i.0));
   1248        }
   1249        input.expect_ident_matching("none")?;
   1250        Ok(Self::none())
   1251    }
   1252 }
   1253 
   1254 /// https://drafts.csswg.org/css-contain-2/#content-visibility
   1255 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
   1256 #[derive(
   1257    Clone,
   1258    Copy,
   1259    Debug,
   1260    Eq,
   1261    FromPrimitive,
   1262    MallocSizeOf,
   1263    Parse,
   1264    PartialEq,
   1265    SpecifiedValueInfo,
   1266    ToAnimatedValue,
   1267    ToComputedValue,
   1268    ToCss,
   1269    ToResolvedValue,
   1270    ToShmem,
   1271    ToTyped,
   1272 )]
   1273 #[repr(u8)]
   1274 pub enum ContentVisibility {
   1275    /// `auto` variant, the element turns on layout containment, style containment, and paint
   1276    /// containment. In addition, if the element is not relevant to the user (such as by being
   1277    /// offscreen) it also skips its content
   1278    Auto,
   1279    /// `hidden` variant, the element skips its content
   1280    Hidden,
   1281    /// 'visible' variant, no effect
   1282    Visible,
   1283 }
   1284 
   1285 #[derive(
   1286    Clone,
   1287    Copy,
   1288    Debug,
   1289    PartialEq,
   1290    Eq,
   1291    MallocSizeOf,
   1292    SpecifiedValueInfo,
   1293    ToComputedValue,
   1294    ToCss,
   1295    Parse,
   1296    ToResolvedValue,
   1297    ToShmem,
   1298    ToTyped,
   1299 )]
   1300 #[css(bitflags(
   1301    single = "normal",
   1302    mixed = "size,inline-size,scroll-state",
   1303    validate_mixed = "Self::validate_mixed_flags",
   1304 ))]
   1305 #[repr(C)]
   1306 /// Specified keyword values for the container-type property.
   1307 /// Spec: normal | [ [ size | inline-size ] || scroll-state ]
   1308 ///
   1309 /// Container Queries are moved from css-contain-3 to css-conditional-5 in August 2022:
   1310 /// https://drafts.csswg.org/css-contain-3/#container-type
   1311 /// https://drafts.csswg.org/css-conditional-5/#container-type
   1312 pub struct ContainerType(u8);
   1313 bitflags! {
   1314    impl ContainerType: u8 {
   1315        /// The `normal` variant.
   1316        const NORMAL = 0;
   1317        /// The `inline-size` variant.
   1318        const INLINE_SIZE = 1 << 0;
   1319        /// The `size` variant.
   1320        const SIZE = 1 << 1;
   1321        /// The `scroll-state` variant.
   1322        const SCROLL_STATE = 1 << 2;
   1323    }
   1324 }
   1325 
   1326 impl ContainerType {
   1327    fn validate_mixed_flags(&self) -> bool {
   1328        // size and inline-size can't be mixed together.
   1329        if self.contains(Self::SIZE | Self::INLINE_SIZE) {
   1330            return false;
   1331        }
   1332        if self.contains(Self::SCROLL_STATE)
   1333            && !static_prefs::pref!("layout.css.scroll-state.enabled")
   1334        {
   1335            return false;
   1336        }
   1337        true
   1338    }
   1339 
   1340    /// Is this container-type: normal?
   1341    pub fn is_normal(self) -> bool {
   1342        self == Self::NORMAL
   1343    }
   1344 
   1345    /// Is this type containing size in any way?
   1346    pub fn is_size_container_type(self) -> bool {
   1347        self.intersects(Self::SIZE | Self::INLINE_SIZE)
   1348    }
   1349 }
   1350 
   1351 /// https://drafts.csswg.org/css-contain-3/#container-name
   1352 #[repr(transparent)]
   1353 #[derive(
   1354    Clone,
   1355    Debug,
   1356    MallocSizeOf,
   1357    PartialEq,
   1358    SpecifiedValueInfo,
   1359    ToComputedValue,
   1360    ToCss,
   1361    ToResolvedValue,
   1362    ToShmem,
   1363    ToTyped,
   1364 )]
   1365 pub struct ContainerName(#[css(iterable, if_empty = "none")] pub crate::OwnedSlice<CustomIdent>);
   1366 
   1367 impl ContainerName {
   1368    /// Return the `none` value.
   1369    pub fn none() -> Self {
   1370        Self(Default::default())
   1371    }
   1372 
   1373    /// Returns whether this is the `none` value.
   1374    pub fn is_none(&self) -> bool {
   1375        self.0.is_empty()
   1376    }
   1377 
   1378    fn parse_internal<'i>(
   1379        input: &mut Parser<'i, '_>,
   1380        for_query: bool,
   1381    ) -> Result<Self, ParseError<'i>> {
   1382        let mut idents = vec![];
   1383        let location = input.current_source_location();
   1384        let first = input.expect_ident()?;
   1385        if !for_query && first.eq_ignore_ascii_case("none") {
   1386            return Ok(Self::none());
   1387        }
   1388        const DISALLOWED_CONTAINER_NAMES: &'static [&'static str] = &["none", "not", "or", "and"];
   1389        idents.push(CustomIdent::from_ident(
   1390            location,
   1391            first,
   1392            DISALLOWED_CONTAINER_NAMES,
   1393        )?);
   1394        if !for_query {
   1395            while let Ok(name) =
   1396                input.try_parse(|input| CustomIdent::parse(input, DISALLOWED_CONTAINER_NAMES))
   1397            {
   1398                idents.push(name);
   1399            }
   1400        }
   1401        Ok(ContainerName(idents.into()))
   1402    }
   1403 
   1404    /// https://github.com/w3c/csswg-drafts/issues/7203
   1405    /// Only a single name allowed in @container rule.
   1406    /// Disallow none for container-name in @container rule.
   1407    pub fn parse_for_query<'i, 't>(
   1408        _: &ParserContext,
   1409        input: &mut Parser<'i, 't>,
   1410    ) -> Result<Self, ParseError<'i>> {
   1411        Self::parse_internal(input, /* for_query = */ true)
   1412    }
   1413 }
   1414 
   1415 impl Parse for ContainerName {
   1416    fn parse<'i, 't>(
   1417        _: &ParserContext,
   1418        input: &mut Parser<'i, 't>,
   1419    ) -> Result<Self, ParseError<'i>> {
   1420        Self::parse_internal(input, /* for_query = */ false)
   1421    }
   1422 }
   1423 
   1424 /// A specified value for the `perspective` property.
   1425 pub type Perspective = GenericPerspective<NonNegativeLength>;
   1426 
   1427 /// https://drafts.csswg.org/css-box/#propdef-float
   1428 #[allow(missing_docs)]
   1429 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
   1430 #[derive(
   1431    Clone,
   1432    Copy,
   1433    Debug,
   1434    Eq,
   1435    FromPrimitive,
   1436    Hash,
   1437    MallocSizeOf,
   1438    Parse,
   1439    PartialEq,
   1440    SpecifiedValueInfo,
   1441    ToComputedValue,
   1442    ToCss,
   1443    ToResolvedValue,
   1444    ToShmem,
   1445    ToTyped,
   1446 )]
   1447 #[repr(u8)]
   1448 pub enum Float {
   1449    Left,
   1450    Right,
   1451    None,
   1452    // https://drafts.csswg.org/css-logical-props/#float-clear
   1453    InlineStart,
   1454    InlineEnd,
   1455 }
   1456 
   1457 impl Float {
   1458    /// Returns true if `self` is not `None`.
   1459    pub fn is_floating(self) -> bool {
   1460        self != Self::None
   1461    }
   1462 }
   1463 
   1464 /// https://drafts.csswg.org/css2/#propdef-clear
   1465 #[allow(missing_docs)]
   1466 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
   1467 #[derive(
   1468    Clone,
   1469    Copy,
   1470    Debug,
   1471    Eq,
   1472    FromPrimitive,
   1473    Hash,
   1474    MallocSizeOf,
   1475    Parse,
   1476    PartialEq,
   1477    SpecifiedValueInfo,
   1478    ToComputedValue,
   1479    ToCss,
   1480    ToResolvedValue,
   1481    ToShmem,
   1482    ToTyped,
   1483 )]
   1484 #[repr(u8)]
   1485 pub enum Clear {
   1486    None,
   1487    Left,
   1488    Right,
   1489    Both,
   1490    // https://drafts.csswg.org/css-logical-props/#float-clear
   1491    InlineStart,
   1492    InlineEnd,
   1493 }
   1494 
   1495 /// https://drafts.csswg.org/css-ui/#propdef-resize
   1496 #[allow(missing_docs)]
   1497 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
   1498 #[derive(
   1499    Clone,
   1500    Copy,
   1501    Debug,
   1502    Eq,
   1503    Hash,
   1504    MallocSizeOf,
   1505    Parse,
   1506    PartialEq,
   1507    SpecifiedValueInfo,
   1508    ToCss,
   1509    ToShmem,
   1510    ToTyped,
   1511 )]
   1512 pub enum Resize {
   1513    None,
   1514    Both,
   1515    Horizontal,
   1516    Vertical,
   1517    // https://drafts.csswg.org/css-logical-1/#resize
   1518    Inline,
   1519    Block,
   1520 }
   1521 
   1522 /// The value for the `appearance` property.
   1523 ///
   1524 /// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
   1525 #[allow(missing_docs)]
   1526 #[derive(
   1527    Clone,
   1528    Copy,
   1529    Debug,
   1530    Eq,
   1531    Hash,
   1532    MallocSizeOf,
   1533    Parse,
   1534    PartialEq,
   1535    SpecifiedValueInfo,
   1536    ToCss,
   1537    ToComputedValue,
   1538    ToResolvedValue,
   1539    ToShmem,
   1540    ToTyped,
   1541 )]
   1542 #[repr(u8)]
   1543 pub enum Appearance {
   1544    /// No appearance at all.
   1545    None,
   1546    /// Default appearance for the element.
   1547    ///
   1548    /// This value doesn't make sense for -moz-default-appearance, but we don't bother to guard
   1549    /// against parsing it.
   1550    Auto,
   1551    /// A searchfield.
   1552    Searchfield,
   1553    /// A multi-line text field, e.g. HTML <textarea>.
   1554    Textarea,
   1555    /// A checkbox element.
   1556    Checkbox,
   1557    /// A radio element within a radio group.
   1558    Radio,
   1559    /// A dropdown list.
   1560    Menulist,
   1561    /// List boxes.
   1562    Listbox,
   1563    /// A horizontal meter bar.
   1564    Meter,
   1565    /// A horizontal progress bar.
   1566    ProgressBar,
   1567    /// A typical dialog button.
   1568    Button,
   1569    /// A single-line text field, e.g. HTML <input type=text>.
   1570    Textfield,
   1571    /// The dropdown button(s) that open up a dropdown list.
   1572    MenulistButton,
   1573    /// Menu Popup background.
   1574    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1575    Menupopup,
   1576    /// The "arrowed" part of the dropdown button that open up a dropdown list.
   1577    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1578    MozMenulistArrowButton,
   1579    /// For HTML's <input type=number>
   1580    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1581    NumberInput,
   1582    /// For HTML's <input type=password>
   1583    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1584    PasswordInput,
   1585    /// nsRangeFrame and its subparts
   1586    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1587    Range,
   1588    /// The scrollbar slider
   1589    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1590    ScrollbarHorizontal,
   1591    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1592    ScrollbarVertical,
   1593    /// A scrollbar button (up/down/left/right).
   1594    /// Keep these in order (some code casts these values to `int` in order to
   1595    /// compare them against each other).
   1596    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1597    ScrollbarbuttonUp,
   1598    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1599    ScrollbarbuttonDown,
   1600    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1601    ScrollbarbuttonLeft,
   1602    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1603    ScrollbarbuttonRight,
   1604    /// The scrollbar thumb.
   1605    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1606    ScrollbarthumbHorizontal,
   1607    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1608    ScrollbarthumbVertical,
   1609    /// The scroll corner
   1610    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1611    Scrollcorner,
   1612    /// The up button of a spin control.
   1613    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1614    SpinnerUpbutton,
   1615    /// The down button of a spin control.
   1616    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1617    SpinnerDownbutton,
   1618    /// A single toolbar button (with no associated dropdown).
   1619    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1620    Toolbarbutton,
   1621    /// A tooltip.
   1622    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1623    Tooltip,
   1624 
   1625    /// Sidebar appearance.
   1626    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1627    MozSidebar,
   1628 
   1629    /// Mac help button.
   1630    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1631    MozMacHelpButton,
   1632 
   1633    /// An appearance value for the root, so that we can get tinting and unified toolbar looks
   1634    /// (which require a transparent gecko background) without really using the whole transparency
   1635    /// set-up which otherwise loses window borders, see bug 1870481.
   1636    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1637    MozMacWindow,
   1638 
   1639    /// Windows themed window frame elements.
   1640    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1641    MozWindowButtonBox,
   1642    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1643    MozWindowButtonClose,
   1644    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1645    MozWindowButtonMaximize,
   1646    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1647    MozWindowButtonMinimize,
   1648    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1649    MozWindowButtonRestore,
   1650    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1651    MozWindowTitlebar,
   1652    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1653    MozWindowTitlebarMaximized,
   1654    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1655    MozWindowDecorations,
   1656 
   1657    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1658    MozMacDisclosureButtonClosed,
   1659    #[parse(condition = "ParserContext::chrome_rules_enabled")]
   1660    MozMacDisclosureButtonOpen,
   1661 
   1662    /// A themed focus outline (for outline:auto).
   1663    ///
   1664    /// This isn't exposed to CSS at all, just here for convenience.
   1665    #[css(skip)]
   1666    FocusOutline,
   1667 
   1668    /// A dummy variant that should be last to let the GTK widget do hackery.
   1669    #[css(skip)]
   1670    Count,
   1671 }
   1672 
   1673 /// A kind of break between two boxes.
   1674 ///
   1675 /// https://drafts.csswg.org/css-break/#break-between
   1676 #[allow(missing_docs)]
   1677 #[derive(
   1678    Clone,
   1679    Copy,
   1680    Debug,
   1681    Eq,
   1682    Hash,
   1683    MallocSizeOf,
   1684    Parse,
   1685    PartialEq,
   1686    SpecifiedValueInfo,
   1687    ToCss,
   1688    ToComputedValue,
   1689    ToResolvedValue,
   1690    ToShmem,
   1691    ToTyped,
   1692 )]
   1693 #[repr(u8)]
   1694 pub enum BreakBetween {
   1695    Always,
   1696    Auto,
   1697    Page,
   1698    Avoid,
   1699    Left,
   1700    Right,
   1701 }
   1702 
   1703 impl BreakBetween {
   1704    /// Parse a legacy break-between value for `page-break-{before,after}`.
   1705    ///
   1706    /// See https://drafts.csswg.org/css-break/#page-break-properties.
   1707    #[cfg_attr(feature = "servo", allow(unused))]
   1708    #[inline]
   1709    pub(crate) fn parse_legacy<'i>(
   1710        _: &ParserContext,
   1711        input: &mut Parser<'i, '_>,
   1712    ) -> Result<Self, ParseError<'i>> {
   1713        let break_value = BreakBetween::parse(input)?;
   1714        match break_value {
   1715            BreakBetween::Always => Ok(BreakBetween::Page),
   1716            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
   1717                Ok(break_value)
   1718            },
   1719            BreakBetween::Page => {
   1720                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1721            },
   1722        }
   1723    }
   1724 
   1725    /// Serialize a legacy break-between value for `page-break-*`.
   1726    ///
   1727    /// See https://drafts.csswg.org/css-break/#page-break-properties.
   1728    #[cfg_attr(feature = "servo", allow(unused))]
   1729    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1730    where
   1731        W: Write,
   1732    {
   1733        match *self {
   1734            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
   1735                self.to_css(dest)
   1736            },
   1737            BreakBetween::Page => dest.write_str("always"),
   1738            BreakBetween::Always => Ok(()),
   1739        }
   1740    }
   1741 }
   1742 
   1743 /// A kind of break within a box.
   1744 ///
   1745 /// https://drafts.csswg.org/css-break/#break-within
   1746 #[allow(missing_docs)]
   1747 #[derive(
   1748    Clone,
   1749    Copy,
   1750    Debug,
   1751    Eq,
   1752    Hash,
   1753    MallocSizeOf,
   1754    Parse,
   1755    PartialEq,
   1756    SpecifiedValueInfo,
   1757    ToCss,
   1758    ToComputedValue,
   1759    ToResolvedValue,
   1760    ToShmem,
   1761    ToTyped,
   1762 )]
   1763 #[repr(u8)]
   1764 pub enum BreakWithin {
   1765    Auto,
   1766    Avoid,
   1767    AvoidPage,
   1768    AvoidColumn,
   1769 }
   1770 
   1771 impl BreakWithin {
   1772    /// Parse a legacy break-between value for `page-break-inside`.
   1773    ///
   1774    /// See https://drafts.csswg.org/css-break/#page-break-properties.
   1775    #[cfg_attr(feature = "servo", allow(unused))]
   1776    #[inline]
   1777    pub(crate) fn parse_legacy<'i>(
   1778        _: &ParserContext,
   1779        input: &mut Parser<'i, '_>,
   1780    ) -> Result<Self, ParseError<'i>> {
   1781        let break_value = BreakWithin::parse(input)?;
   1782        match break_value {
   1783            BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
   1784            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
   1785                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
   1786            },
   1787        }
   1788    }
   1789 
   1790    /// Serialize a legacy break-between value for `page-break-inside`.
   1791    ///
   1792    /// See https://drafts.csswg.org/css-break/#page-break-properties.
   1793    #[cfg_attr(feature = "servo", allow(unused))]
   1794    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
   1795    where
   1796        W: Write,
   1797    {
   1798        match *self {
   1799            BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
   1800            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
   1801        }
   1802    }
   1803 }
   1804 
   1805 /// The value for the `overflow-x` / `overflow-y` properties.
   1806 #[allow(missing_docs)]
   1807 #[derive(
   1808    Clone,
   1809    Copy,
   1810    Debug,
   1811    Eq,
   1812    Hash,
   1813    MallocSizeOf,
   1814    PartialEq,
   1815    SpecifiedValueInfo,
   1816    ToCss,
   1817    ToComputedValue,
   1818    ToResolvedValue,
   1819    ToShmem,
   1820    ToTyped,
   1821 )]
   1822 #[repr(u8)]
   1823 pub enum Overflow {
   1824    Visible,
   1825    Hidden,
   1826    Scroll,
   1827    Auto,
   1828    Clip,
   1829 }
   1830 
   1831 // This can be derived once we remove or keep `-moz-hidden-unscrollable`
   1832 // indefinitely.
   1833 impl Parse for Overflow {
   1834    fn parse<'i, 't>(
   1835        _: &ParserContext,
   1836        input: &mut Parser<'i, 't>,
   1837    ) -> Result<Self, ParseError<'i>> {
   1838        Ok(try_match_ident_ignore_ascii_case! { input,
   1839            "visible" => Self::Visible,
   1840            "hidden" => Self::Hidden,
   1841            "scroll" => Self::Scroll,
   1842            "auto" | "overlay" => Self::Auto,
   1843            "clip" => Self::Clip,
   1844            #[cfg(feature = "gecko")]
   1845            "-moz-hidden-unscrollable" if static_prefs::pref!("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
   1846                Overflow::Clip
   1847            },
   1848        })
   1849    }
   1850 }
   1851 
   1852 impl Overflow {
   1853    /// Return true if the value will create a scrollable box.
   1854    #[inline]
   1855    pub fn is_scrollable(&self) -> bool {
   1856        matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)
   1857    }
   1858    /// Convert the value to a scrollable value if it's not already scrollable.
   1859    /// This maps `visible` to `auto` and `clip` to `hidden`.
   1860    #[inline]
   1861    pub fn to_scrollable(&self) -> Self {
   1862        match *self {
   1863            Self::Hidden | Self::Scroll | Self::Auto => *self,
   1864            Self::Visible => Self::Auto,
   1865            Self::Clip => Self::Hidden,
   1866        }
   1867    }
   1868 }
   1869 
   1870 #[derive(
   1871    Clone,
   1872    Copy,
   1873    Debug,
   1874    Eq,
   1875    MallocSizeOf,
   1876    Parse,
   1877    PartialEq,
   1878    SpecifiedValueInfo,
   1879    ToComputedValue,
   1880    ToCss,
   1881    ToResolvedValue,
   1882    ToShmem,
   1883    ToTyped,
   1884 )]
   1885 #[repr(C)]
   1886 #[css(bitflags(
   1887    single = "auto",
   1888    mixed = "stable,both-edges",
   1889    validate_mixed = "Self::has_stable"
   1890 ))]
   1891 /// Values for scrollbar-gutter:
   1892 /// <https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property>
   1893 pub struct ScrollbarGutter(u8);
   1894 bitflags! {
   1895    impl ScrollbarGutter: u8 {
   1896        /// `auto` variant. Just for convenience if there is no flag set.
   1897        const AUTO = 0;
   1898        /// `stable` variant.
   1899        const STABLE = 1 << 0;
   1900        /// `both-edges` variant.
   1901        const BOTH_EDGES = 1 << 1;
   1902    }
   1903 }
   1904 
   1905 impl ScrollbarGutter {
   1906    #[inline]
   1907    fn has_stable(&self) -> bool {
   1908        self.intersects(Self::STABLE)
   1909    }
   1910 }
   1911 
   1912 /// A specified value for the zoom property.
   1913 #[derive(
   1914    Clone, Copy, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
   1915 )]
   1916 #[allow(missing_docs)]
   1917 pub enum Zoom {
   1918    Normal,
   1919    /// An internal value that resets the effective zoom to 1. Used for scrollbar parts, which
   1920    /// disregard zoom. We use this name because WebKit has this value exposed to the web.
   1921    #[parse(condition = "ParserContext::in_ua_sheet")]
   1922    Document,
   1923    Value(NonNegativeNumberOrPercentage),
   1924 }
   1925 
   1926 impl Zoom {
   1927    /// Return a particular number value of the zoom property.
   1928    #[inline]
   1929    pub fn new_number(n: f32) -> Self {
   1930        Self::Value(NonNegativeNumberOrPercentage::new_number(n))
   1931    }
   1932 }
   1933 
   1934 pub use crate::values::generics::box_::PositionProperty;