<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Specified types for box properties.

use crate::custom_properties::Name as CustomPropertyName;
use crate::parser::{Parse, ParserContext};
use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags};
use crate::properties::{PropertyId, ShorthandId};
use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
use crate::values::generics::box_::Perspective as GenericPerspective;
use crate::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword};
use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
use crate::values::specified::{AllowQuirks, Number};
use crate::values::{CustomIdent, KeyframesName};
use crate::Atom;
use cssparser::Parser;
use num_traits::FromPrimitive;
use selectors::parser::SelectorParseErrorKind;
use std::fmt::{self, Write};
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};

#[cfg(feature = "gecko")]
fn moz_display_values_enabled(context: &ParserContext) -> bool {
    context.in_ua_or_chrome_sheet() ||
        static_prefs::pref!("layout.css.xul-display-values.content.enabled")
}

#[cfg(feature = "gecko")]
fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
    context.in_ua_or_chrome_sheet() ||
        static_prefs::pref!("layout.css.xul-box-display-values.content.enabled")
}

/// Defines an element’s display type, which consists of
/// the two basic qualities of how an element generates boxes
/// <https://drafts.csswg.org/css-display/#propdef-display>
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum DisplayOutside {
    None = 0,
    Inline,
    Block,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableCaption,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    InternalTable,
    #[cfg(feature = "gecko")]
    InternalRuby,
    #[cfg(feature = "gecko")]
    XUL,
}

#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum DisplayInside {
    None = 0,
    #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
    Contents,
    Flow,
    FlowRoot,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    Flex,
    #[cfg(feature = "gecko")]
    Grid,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    Table,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableRowGroup,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableColumn,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableColumnGroup,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableHeaderGroup,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableFooterGroup,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableRow,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableCell,
    #[cfg(feature = "gecko")]
    Ruby,
    #[cfg(feature = "gecko")]
    RubyBase,
    #[cfg(feature = "gecko")]
    RubyBaseContainer,
    #[cfg(feature = "gecko")]
    RubyText,
    #[cfg(feature = "gecko")]
    RubyTextContainer,
    #[cfg(feature = "gecko")]
    WebkitBox,
    #[cfg(feature = "gecko")]
    MozBox,
    #[cfg(feature = "gecko")]
    MozGrid,
    #[cfg(feature = "gecko")]
    MozGridGroup,
    #[cfg(feature = "gecko")]
    MozGridLine,
    #[cfg(feature = "gecko")]
    MozDeck,
    #[cfg(feature = "gecko")]
    MozPopup,
}

#[allow(missing_docs)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    FromPrimitive,
    Hash,
    MallocSizeOf,
    PartialEq,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(transparent)]
pub struct Display(u16);

/// Gecko-only impl block for Display (shared stuff later in this file):
#[allow(missing_docs)]
#[allow(non_upper_case_globals)]
impl Display {
    // Our u16 bits are used as follows:    LOOOOOOOIIIIIIII
    const LIST_ITEM_BIT: u16 = 0x8000; //^
    const DISPLAY_OUTSIDE_BITS: u16 = 7; // ^^^^^^^
    const DISPLAY_INSIDE_BITS: u16 = 8; //        ^^^^^^^^

    /// https://drafts.csswg.org/css-display/#the-display-properties
    pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None);
    #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
    pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents);
    pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flow);
    pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot);
    pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Flow);
    #[cfg(feature = "gecko")]
    pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const Flex: Self = Self::new(DisplayOutside::Block, DisplayInside::Flex);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const InlineFlex: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flex);
    #[cfg(feature = "gecko")]
    pub const Grid: Self = Self::new(DisplayOutside::Block, DisplayInside::Grid);
    #[cfg(feature = "gecko")]
    pub const InlineGrid: Self = Self::new(DisplayOutside::Inline, DisplayInside::Grid);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const Table: Self = Self::new(DisplayOutside::Block, DisplayInside::Table);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Flow);
    #[cfg(feature = "gecko")]
    pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby);
    #[cfg(feature = "gecko")]
    pub const WebkitBox: Self = Self::new(DisplayOutside::Block, DisplayInside::WebkitBox);
    #[cfg(feature = "gecko")]
    pub const WebkitInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::WebkitBox);

    // Internal table boxes.

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableRowGroup: Self =
        Self::new(DisplayOutside::InternalTable, DisplayInside::TableRowGroup);

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableHeaderGroup: Self = Self::new(
        DisplayOutside::InternalTable,
        DisplayInside::TableHeaderGroup,
    );

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableFooterGroup: Self = Self::new(
        DisplayOutside::InternalTable,
        DisplayInside::TableFooterGroup,
    );

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableColumn: Self =
        Self::new(DisplayOutside::InternalTable, DisplayInside::TableColumn);

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableColumnGroup: Self = Self::new(
        DisplayOutside::InternalTable,
        DisplayInside::TableColumnGroup,
    );

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableRow: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableRow);

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableCell: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableCell);

    /// Internal ruby boxes.
    #[cfg(feature = "gecko")]
    pub const RubyBase: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyBase);
    #[cfg(feature = "gecko")]
    pub const RubyBaseContainer: Self = Self::new(
        DisplayOutside::InternalRuby,
        DisplayInside::RubyBaseContainer,
    );
    #[cfg(feature = "gecko")]
    pub const RubyText: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyText);
    #[cfg(feature = "gecko")]
    pub const RubyTextContainer: Self = Self::new(
        DisplayOutside::InternalRuby,
        DisplayInside::RubyTextContainer,
    );

    /// XUL boxes.
    #[cfg(feature = "gecko")]
    pub const MozBox: Self = Self::new(DisplayOutside::Block, DisplayInside::MozBox);
    #[cfg(feature = "gecko")]
    pub const MozInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::MozBox);
    #[cfg(feature = "gecko")]
    pub const MozGrid: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGrid);
    #[cfg(feature = "gecko")]
    pub const MozGridGroup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridGroup);
    #[cfg(feature = "gecko")]
    pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine);
    #[cfg(feature = "gecko")]
    pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
    #[cfg(feature = "gecko")]
    pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);

    /// Make a raw display value from <display-outside> and <display-inside> values.
    #[inline]
    const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
        let o: u16 = ((outside as u8) as u16) << Self::DISPLAY_INSIDE_BITS;
        let i: u16 = (inside as u8) as u16;
        Self(o | i)
    }

    /// Make a display enum value from <display-outside> and <display-inside> values.
    #[inline]
    fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
        let v = Self::new(outside, inside);
        if !list_item {
            return v;
        }
        Self(v.0 | Self::LIST_ITEM_BIT)
    }

    /// Accessor for the <display-inside> value.
    #[inline]
    pub fn inside(&self) -> DisplayInside {
        DisplayInside::from_u16(self.0 & ((1 << Self::DISPLAY_INSIDE_BITS) - 1)).unwrap()
    }

    /// Accessor for the <display-outside> value.
    #[inline]
    pub fn outside(&self) -> DisplayOutside {
        DisplayOutside::from_u16(
            (self.0 >> Self::DISPLAY_INSIDE_BITS) & ((1 << Self::DISPLAY_OUTSIDE_BITS) - 1),
        )
        .unwrap()
    }

    /// Whether this is `display: inline` (or `inline list-item`).
    #[inline]
    pub fn is_inline_flow(&self) -> bool {
        self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
    }

    /// Returns whether this `display` value is some kind of list-item.
    #[inline]
    pub const fn is_list_item(&self) -> bool {
        (self.0 & Self::LIST_ITEM_BIT) != 0
    }

    /// Returns whether this `display` value is a ruby level container.
    pub fn is_ruby_level_container(&self) -> bool {
        match *self {
            #[cfg(feature = "gecko")]
            Display::RubyBaseContainer | Display::RubyTextContainer => true,
            _ => false,
        }
    }

    /// Returns whether this `display` value is one of the types for ruby.
    pub fn is_ruby_type(&self) -> bool {
        match self.inside() {
            #[cfg(feature = "gecko")]
            DisplayInside::Ruby |
            DisplayInside::RubyBase |
            DisplayInside::RubyText |
            DisplayInside::RubyBaseContainer |
            DisplayInside::RubyTextContainer => true,
            _ => false,
        }
    }
}

/// Shared Display impl for both Gecko and Servo.
#[allow(non_upper_case_globals)]
impl Display {
    /// The initial display value.
    #[inline]
    pub fn inline() -> Self {
        Display::Inline
    }

    /// <https://drafts.csswg.org/css2/visuren.html#x13>
    #[cfg(feature = "servo")]
    #[inline]
    pub fn is_atomic_inline_level(&self) -> bool {
        match *self {
            Display::InlineBlock => true,
            #[cfg(any(feature = "servo-layout-2013"))]
            Display::InlineFlex | Display::InlineTable => true,
            _ => false,
        }
    }

    /// Returns whether this `display` value is the display of a flex or
    /// grid container.
    ///
    /// This is used to implement various style fixups.
    pub fn is_item_container(&self) -> bool {
        match self.inside() {
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            DisplayInside::Flex => true,
            #[cfg(feature = "gecko")]
            DisplayInside::Grid => true,
            _ => false,
        }
    }

    /// Returns whether an element with this display type is a line
    /// participant, which means it may lay its children on the same
    /// line as itself.
    pub fn is_line_participant(&self) -> bool {
        match *self {
            Display::Inline => true,
            #[cfg(feature = "gecko")]
            Display::Contents | Display::Ruby | Display::RubyBaseContainer => true,
            _ => false,
        }
    }

    /// Convert this display into an equivalent block display.
    ///
    /// Also used for :root style adjustments.
    pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
        #[cfg(feature = "gecko")]
        {
            // Special handling for `contents` and `list-item`s on the root element.
            if _is_root_element && (self.is_contents() || self.is_list_item()) {
                return Display::Block;
            }
        }

        match self.outside() {
            DisplayOutside::Inline => {
                let inside = match self.inside() {
                    // `inline-block` blockifies to `block` rather than
                    // `flow-root`, for legacy reasons.
                    DisplayInside::FlowRoot => DisplayInside::Flow,
                    inside => inside,
                };
                Display::from3(DisplayOutside::Block, inside, self.is_list_item())
            },
            DisplayOutside::Block | DisplayOutside::None => *self,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            _ => Display::Block,
        }
    }

    /// Convert this display into an equivalent inline-outside display.
    /// https://drafts.csswg.org/css-display/#inlinify
    #[cfg(feature = "gecko")]
    pub fn inlinify(&self) -> Self {
        match self.outside() {
            DisplayOutside::Block => {
                let inside = match self.inside() {
                    // `display: block` inlinifies to `display: inline-block`,
                    // rather than `inline`, for legacy reasons.
                    DisplayInside::Flow => DisplayInside::FlowRoot,
                    inside => inside,
                };
                Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
            },
            _ => *self,
        }
    }

    /// Returns true if the value is `Contents`
    #[inline]
    pub fn is_contents(&self) -> bool {
        match *self {
            #[cfg(feature = "gecko")]
            Display::Contents => true,
            _ => false,
        }
    }

    /// Returns true if the value is `None`
    #[inline]
    pub fn is_none(&self) -> bool {
        *self == Display::None
    }
}

impl ToCss for Display {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: fmt::Write,
    {
        let outside = self.outside();
        let inside = self.inside();
        match *self {
            Display::Block | Display::Inline => outside.to_css(dest),
            Display::InlineBlock => dest.write_str("inline-block"),
            #[cfg(feature = "gecko")]
            Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
            #[cfg(feature = "gecko")]
            Display::MozInlineBox => dest.write_str("-moz-inline-box"),
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            Display::TableCaption => dest.write_str("table-caption"),
            _ => match (outside, inside) {
                #[cfg(feature = "gecko")]
                (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
                #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
                (DisplayOutside::Inline, DisplayInside::Flex) |
                (DisplayOutside::Inline, DisplayInside::Table) => {
                    dest.write_str("inline-")?;
                    inside.to_css(dest)
                },
                #[cfg(feature = "gecko")]
                (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
                (_, inside) => {
                    if self.is_list_item() {
                        if outside != DisplayOutside::Block {
                            outside.to_css(dest)?;
                            dest.write_str(" ")?;
                        }
                        if inside != DisplayInside::Flow {
                            inside.to_css(dest)?;
                            dest.write_str(" ")?;
                        }
                        dest.write_str("list-item")
                    } else {
                        inside.to_css(dest)
                    }
                },
            },
        }
    }
}

/// <display-inside> = flow | flow-root | table | flex | grid | ruby
/// https://drafts.csswg.org/css-display/#typedef-display-inside
fn parse_display_inside<'i, 't>(
    input: &mut Parser<'i, 't>,
) -> Result<DisplayInside, ParseError<'i>> {
    Ok(try_match_ident_ignore_ascii_case! { input,
        "flow" => DisplayInside::Flow,
        #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
        "flow-root" => DisplayInside::FlowRoot,
        #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
        "table" => DisplayInside::Table,
        #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
        "flex" => DisplayInside::Flex,
        #[cfg(feature = "gecko")]
        "grid" => DisplayInside::Grid,
        #[cfg(feature = "gecko")]
        "ruby" => DisplayInside::Ruby,
    })
}

/// <display-outside> = block | inline | run-in
/// https://drafts.csswg.org/css-display/#typedef-display-outside
fn parse_display_outside<'i, 't>(
    input: &mut Parser<'i, 't>,
) -> Result<DisplayOutside, ParseError<'i>> {
    Ok(try_match_ident_ignore_ascii_case! { input,
        "block" => DisplayOutside::Block,
        "inline" => DisplayOutside::Inline,
        // FIXME(bug 2056): not supported in layout yet:
        //"run-in" => DisplayOutside::RunIn,
    })
}

/// (flow | flow-root)?
fn parse_display_inside_for_list_item<'i, 't>(
    input: &mut Parser<'i, 't>,
) -> Result<DisplayInside, ParseError<'i>> {
    Ok(try_match_ident_ignore_ascii_case! { input,
        "flow" => DisplayInside::Flow,
        #[cfg(feature = "gecko")]
        "flow-root" => DisplayInside::FlowRoot,
    })
}
/// Test a <display-inside> Result for same values as above.
fn is_valid_inside_for_list_item<'i>(inside: &Result<DisplayInside, ParseError<'i>>) -> bool {
    match inside {
        Ok(DisplayInside::Flow) => true,
        #[cfg(feature = "gecko")]
        Ok(DisplayInside::FlowRoot) => true,
        _ => false,
    }
}

/// Parse `list-item`.
fn parse_list_item<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
    Ok(try_match_ident_ignore_ascii_case! { input,
        "list-item" => (),
    })
}

impl Parse for Display {
    #[allow(unused)] // `context` isn't used for servo-2020 for now
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Display, ParseError<'i>> {
        // Parse all combinations of <display-inside/outside>? and `list-item`? first.
        let mut got_list_item = input.try(parse_list_item).is_ok();
        let mut inside = if got_list_item {
            input.try(parse_display_inside_for_list_item)
        } else {
            input.try(parse_display_inside)
        };
        // <display-listitem> = <display-outside>? && [ flow | flow-root ]? && list-item
        // https://drafts.csswg.org/css-display/#typedef-display-listitem
        if !got_list_item && is_valid_inside_for_list_item(&inside) {
            got_list_item = input.try(parse_list_item).is_ok();
        }
        let outside = input.try(parse_display_outside);
        if outside.is_ok() {
            if !got_list_item && (inside.is_err() || is_valid_inside_for_list_item(&inside)) {
                got_list_item = input.try(parse_list_item).is_ok();
            }
            if inside.is_err() {
                inside = if got_list_item {
                    input.try(parse_display_inside_for_list_item)
                } else {
                    input.try(parse_display_inside)
                };
                if !got_list_item && is_valid_inside_for_list_item(&inside) {
                    got_list_item = input.try(parse_list_item).is_ok();
                }
            }
        }
        if got_list_item || inside.is_ok() || outside.is_ok() {
            let inside = inside.unwrap_or(DisplayInside::Flow);
            let outside = outside.unwrap_or(match inside {
                // "If <display-outside> is omitted, the element’s outside display type
                // defaults to block — except for ruby, which defaults to inline."
                // https://drafts.csswg.org/css-display/#inside-model
                #[cfg(feature = "gecko")]
                DisplayInside::Ruby => DisplayOutside::Inline,
                _ => DisplayOutside::Block,
            });
            return Ok(Display::from3(outside, inside, got_list_item));
        }

        // Now parse the single-keyword `display` values.
        Ok(try_match_ident_ignore_ascii_case! { input,
            "none" => Display::None,
            #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
            "contents" => Display::Contents,
            "inline-block" => Display::InlineBlock,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "inline-table" => Display::InlineTable,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "-webkit-flex" => Display::Flex,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "inline-flex" | "-webkit-inline-flex" => Display::InlineFlex,
            #[cfg(feature = "gecko")]
            "inline-grid" => Display::InlineGrid,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-caption" => Display::TableCaption,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-row-group" => Display::TableRowGroup,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-header-group" => Display::TableHeaderGroup,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-footer-group" => Display::TableFooterGroup,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-column" => Display::TableColumn,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-column-group" => Display::TableColumnGroup,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-row" => Display::TableRow,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-cell" => Display::TableCell,
            #[cfg(feature = "gecko")]
            "ruby-base" => Display::RubyBase,
            #[cfg(feature = "gecko")]
            "ruby-base-container" => Display::RubyBaseContainer,
            #[cfg(feature = "gecko")]
            "ruby-text" => Display::RubyText,
            #[cfg(feature = "gecko")]
            "ruby-text-container" => Display::RubyTextContainer,
            #[cfg(feature = "gecko")]
            "-webkit-box" => Display::WebkitBox,
            #[cfg(feature = "gecko")]
            "-webkit-inline-box" => Display::WebkitInlineBox,
            #[cfg(feature = "gecko")]
            "-moz-box" if moz_box_display_values_enabled(context) => Display::MozBox,
            #[cfg(feature = "gecko")]
            "-moz-inline-box" if moz_box_display_values_enabled(context) => Display::MozInlineBox,
            #[cfg(feature = "gecko")]
            "-moz-grid" if moz_display_values_enabled(context) => Display::MozGrid,
            #[cfg(feature = "gecko")]
            "-moz-grid-group" if moz_display_values_enabled(context) => Display::MozGridGroup,
            #[cfg(feature = "gecko")]
            "-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine,
            #[cfg(feature = "gecko")]
            "-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
            #[cfg(feature = "gecko")]
            "-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
        })
    }<commit_msg>style: Re-introduce display: -moz-stack; as <legacy-stack>.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! Specified types for box properties.

use crate::custom_properties::Name as CustomPropertyName;
use crate::parser::{Parse, ParserContext};
use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags};
use crate::properties::{PropertyId, ShorthandId};
use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
use crate::values::generics::box_::Perspective as GenericPerspective;
use crate::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword};
use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
use crate::values::specified::{AllowQuirks, Number};
use crate::values::{CustomIdent, KeyframesName};
use crate::Atom;
use cssparser::Parser;
use num_traits::FromPrimitive;
use selectors::parser::SelectorParseErrorKind;
use std::fmt::{self, Write};
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};

#[cfg(feature = "gecko")]
fn moz_display_values_enabled(context: &ParserContext) -> bool {
    context.in_ua_or_chrome_sheet() ||
        static_prefs::pref!("layout.css.xul-display-values.content.enabled")
}

#[cfg(feature = "gecko")]
fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
    context.in_ua_or_chrome_sheet() ||
        static_prefs::pref!("layout.css.xul-box-display-values.content.enabled")
}

/// Defines an element’s display type, which consists of
/// the two basic qualities of how an element generates boxes
/// <https://drafts.csswg.org/css-display/#propdef-display>
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum DisplayOutside {
    None = 0,
    Inline,
    Block,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableCaption,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    InternalTable,
    #[cfg(feature = "gecko")]
    InternalRuby,
    #[cfg(feature = "gecko")]
    XUL,
}

#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
#[repr(u8)]
pub enum DisplayInside {
    None = 0,
    #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
    Contents,
    Flow,
    FlowRoot,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    Flex,
    #[cfg(feature = "gecko")]
    Grid,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    Table,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableRowGroup,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableColumn,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableColumnGroup,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableHeaderGroup,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableFooterGroup,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableRow,
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    TableCell,
    #[cfg(feature = "gecko")]
    Ruby,
    #[cfg(feature = "gecko")]
    RubyBase,
    #[cfg(feature = "gecko")]
    RubyBaseContainer,
    #[cfg(feature = "gecko")]
    RubyText,
    #[cfg(feature = "gecko")]
    RubyTextContainer,
    #[cfg(feature = "gecko")]
    WebkitBox,
    #[cfg(feature = "gecko")]
    MozBox,
    #[cfg(feature = "gecko")]
    MozGrid,
    #[cfg(feature = "gecko")]
    MozGridGroup,
    #[cfg(feature = "gecko")]
    MozGridLine,
    #[cfg(feature = "gecko")]
    MozStack,
    #[cfg(feature = "gecko")]
    MozDeck,
    #[cfg(feature = "gecko")]
    MozPopup,
}

#[allow(missing_docs)]
#[derive(
    Clone,
    Copy,
    Debug,
    Eq,
    FromPrimitive,
    Hash,
    MallocSizeOf,
    PartialEq,
    ToComputedValue,
    ToResolvedValue,
    ToShmem,
)]
#[repr(transparent)]
pub struct Display(u16);

/// Gecko-only impl block for Display (shared stuff later in this file):
#[allow(missing_docs)]
#[allow(non_upper_case_globals)]
impl Display {
    // Our u16 bits are used as follows:    LOOOOOOOIIIIIIII
    const LIST_ITEM_BIT: u16 = 0x8000; //^
    const DISPLAY_OUTSIDE_BITS: u16 = 7; // ^^^^^^^
    const DISPLAY_INSIDE_BITS: u16 = 8; //        ^^^^^^^^

    /// https://drafts.csswg.org/css-display/#the-display-properties
    pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None);
    #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
    pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents);
    pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flow);
    pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot);
    pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Flow);
    #[cfg(feature = "gecko")]
    pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const Flex: Self = Self::new(DisplayOutside::Block, DisplayInside::Flex);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const InlineFlex: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flex);
    #[cfg(feature = "gecko")]
    pub const Grid: Self = Self::new(DisplayOutside::Block, DisplayInside::Grid);
    #[cfg(feature = "gecko")]
    pub const InlineGrid: Self = Self::new(DisplayOutside::Inline, DisplayInside::Grid);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const Table: Self = Self::new(DisplayOutside::Block, DisplayInside::Table);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table);
    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Flow);
    #[cfg(feature = "gecko")]
    pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby);
    #[cfg(feature = "gecko")]
    pub const WebkitBox: Self = Self::new(DisplayOutside::Block, DisplayInside::WebkitBox);
    #[cfg(feature = "gecko")]
    pub const WebkitInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::WebkitBox);

    // Internal table boxes.

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableRowGroup: Self =
        Self::new(DisplayOutside::InternalTable, DisplayInside::TableRowGroup);

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableHeaderGroup: Self = Self::new(
        DisplayOutside::InternalTable,
        DisplayInside::TableHeaderGroup,
    );

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableFooterGroup: Self = Self::new(
        DisplayOutside::InternalTable,
        DisplayInside::TableFooterGroup,
    );

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableColumn: Self =
        Self::new(DisplayOutside::InternalTable, DisplayInside::TableColumn);

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableColumnGroup: Self = Self::new(
        DisplayOutside::InternalTable,
        DisplayInside::TableColumnGroup,
    );

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableRow: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableRow);

    #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
    pub const TableCell: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableCell);

    /// Internal ruby boxes.
    #[cfg(feature = "gecko")]
    pub const RubyBase: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyBase);
    #[cfg(feature = "gecko")]
    pub const RubyBaseContainer: Self = Self::new(
        DisplayOutside::InternalRuby,
        DisplayInside::RubyBaseContainer,
    );
    #[cfg(feature = "gecko")]
    pub const RubyText: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyText);
    #[cfg(feature = "gecko")]
    pub const RubyTextContainer: Self = Self::new(
        DisplayOutside::InternalRuby,
        DisplayInside::RubyTextContainer,
    );

    /// XUL boxes.
    #[cfg(feature = "gecko")]
    pub const MozBox: Self = Self::new(DisplayOutside::Block, DisplayInside::MozBox);
    #[cfg(feature = "gecko")]
    pub const MozInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::MozBox);
    #[cfg(feature = "gecko")]
    pub const MozGrid: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGrid);
    #[cfg(feature = "gecko")]
    pub const MozGridGroup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridGroup);
    #[cfg(feature = "gecko")]
    pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine);
    #[cfg(feature = "gecko")]
    pub const MozStack: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozStack);
    #[cfg(feature = "gecko")]
    pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
    #[cfg(feature = "gecko")]
    pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);

    /// Make a raw display value from <display-outside> and <display-inside> values.
    #[inline]
    const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
        let o: u16 = ((outside as u8) as u16) << Self::DISPLAY_INSIDE_BITS;
        let i: u16 = (inside as u8) as u16;
        Self(o | i)
    }

    /// Make a display enum value from <display-outside> and <display-inside> values.
    #[inline]
    fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
        let v = Self::new(outside, inside);
        if !list_item {
            return v;
        }
        Self(v.0 | Self::LIST_ITEM_BIT)
    }

    /// Accessor for the <display-inside> value.
    #[inline]
    pub fn inside(&self) -> DisplayInside {
        DisplayInside::from_u16(self.0 & ((1 << Self::DISPLAY_INSIDE_BITS) - 1)).unwrap()
    }

    /// Accessor for the <display-outside> value.
    #[inline]
    pub fn outside(&self) -> DisplayOutside {
        DisplayOutside::from_u16(
            (self.0 >> Self::DISPLAY_INSIDE_BITS) & ((1 << Self::DISPLAY_OUTSIDE_BITS) - 1),
        )
        .unwrap()
    }

    /// Whether this is `display: inline` (or `inline list-item`).
    #[inline]
    pub fn is_inline_flow(&self) -> bool {
        self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
    }

    /// Returns whether this `display` value is some kind of list-item.
    #[inline]
    pub const fn is_list_item(&self) -> bool {
        (self.0 & Self::LIST_ITEM_BIT) != 0
    }

    /// Returns whether this `display` value is a ruby level container.
    pub fn is_ruby_level_container(&self) -> bool {
        match *self {
            #[cfg(feature = "gecko")]
            Display::RubyBaseContainer | Display::RubyTextContainer => true,
            _ => false,
        }
    }

    /// Returns whether this `display` value is one of the types for ruby.
    pub fn is_ruby_type(&self) -> bool {
        match self.inside() {
            #[cfg(feature = "gecko")]
            DisplayInside::Ruby |
            DisplayInside::RubyBase |
            DisplayInside::RubyText |
            DisplayInside::RubyBaseContainer |
            DisplayInside::RubyTextContainer => true,
            _ => false,
        }
    }
}

/// Shared Display impl for both Gecko and Servo.
#[allow(non_upper_case_globals)]
impl Display {
    /// The initial display value.
    #[inline]
    pub fn inline() -> Self {
        Display::Inline
    }

    /// <https://drafts.csswg.org/css2/visuren.html#x13>
    #[cfg(feature = "servo")]
    #[inline]
    pub fn is_atomic_inline_level(&self) -> bool {
        match *self {
            Display::InlineBlock => true,
            #[cfg(any(feature = "servo-layout-2013"))]
            Display::InlineFlex | Display::InlineTable => true,
            _ => false,
        }
    }

    /// Returns whether this `display` value is the display of a flex or
    /// grid container.
    ///
    /// This is used to implement various style fixups.
    pub fn is_item_container(&self) -> bool {
        match self.inside() {
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            DisplayInside::Flex => true,
            #[cfg(feature = "gecko")]
            DisplayInside::Grid => true,
            _ => false,
        }
    }

    /// Returns whether an element with this display type is a line
    /// participant, which means it may lay its children on the same
    /// line as itself.
    pub fn is_line_participant(&self) -> bool {
        match *self {
            Display::Inline => true,
            #[cfg(feature = "gecko")]
            Display::Contents | Display::Ruby | Display::RubyBaseContainer => true,
            _ => false,
        }
    }

    /// Convert this display into an equivalent block display.
    ///
    /// Also used for :root style adjustments.
    pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
        #[cfg(feature = "gecko")]
        {
            // Special handling for `contents` and `list-item`s on the root element.
            if _is_root_element && (self.is_contents() || self.is_list_item()) {
                return Display::Block;
            }
        }

        match self.outside() {
            DisplayOutside::Inline => {
                let inside = match self.inside() {
                    // `inline-block` blockifies to `block` rather than
                    // `flow-root`, for legacy reasons.
                    DisplayInside::FlowRoot => DisplayInside::Flow,
                    inside => inside,
                };
                Display::from3(DisplayOutside::Block, inside, self.is_list_item())
            },
            DisplayOutside::Block | DisplayOutside::None => *self,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            _ => Display::Block,
        }
    }

    /// Convert this display into an equivalent inline-outside display.
    /// https://drafts.csswg.org/css-display/#inlinify
    #[cfg(feature = "gecko")]
    pub fn inlinify(&self) -> Self {
        match self.outside() {
            DisplayOutside::Block => {
                let inside = match self.inside() {
                    // `display: block` inlinifies to `display: inline-block`,
                    // rather than `inline`, for legacy reasons.
                    DisplayInside::Flow => DisplayInside::FlowRoot,
                    inside => inside,
                };
                Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
            },
            _ => *self,
        }
    }

    /// Returns true if the value is `Contents`
    #[inline]
    pub fn is_contents(&self) -> bool {
        match *self {
            #[cfg(feature = "gecko")]
            Display::Contents => true,
            _ => false,
        }
    }

    /// Returns true if the value is `None`
    #[inline]
    pub fn is_none(&self) -> bool {
        *self == Display::None
    }
}

impl ToCss for Display {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: fmt::Write,
    {
        let outside = self.outside();
        let inside = self.inside();
        match *self {
            Display::Block | Display::Inline => outside.to_css(dest),
            Display::InlineBlock => dest.write_str("inline-block"),
            #[cfg(feature = "gecko")]
            Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
            #[cfg(feature = "gecko")]
            Display::MozInlineBox => dest.write_str("-moz-inline-box"),
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            Display::TableCaption => dest.write_str("table-caption"),
            _ => match (outside, inside) {
                #[cfg(feature = "gecko")]
                (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
                #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
                (DisplayOutside::Inline, DisplayInside::Flex) |
                (DisplayOutside::Inline, DisplayInside::Table) => {
                    dest.write_str("inline-")?;
                    inside.to_css(dest)
                },
                #[cfg(feature = "gecko")]
                (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
                (_, inside) => {
                    if self.is_list_item() {
                        if outside != DisplayOutside::Block {
                            outside.to_css(dest)?;
                            dest.write_str(" ")?;
                        }
                        if inside != DisplayInside::Flow {
                            inside.to_css(dest)?;
                            dest.write_str(" ")?;
                        }
                        dest.write_str("list-item")
                    } else {
                        inside.to_css(dest)
                    }
                },
            },
        }
    }
}

/// <display-inside> = flow | flow-root | table | flex | grid | ruby
/// https://drafts.csswg.org/css-display/#typedef-display-inside
fn parse_display_inside<'i, 't>(
    input: &mut Parser<'i, 't>,
) -> Result<DisplayInside, ParseError<'i>> {
    Ok(try_match_ident_ignore_ascii_case! { input,
        "flow" => DisplayInside::Flow,
        #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
        "flow-root" => DisplayInside::FlowRoot,
        #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
        "table" => DisplayInside::Table,
        #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
        "flex" => DisplayInside::Flex,
        #[cfg(feature = "gecko")]
        "grid" => DisplayInside::Grid,
        #[cfg(feature = "gecko")]
        "ruby" => DisplayInside::Ruby,
    })
}

/// <display-outside> = block | inline | run-in
/// https://drafts.csswg.org/css-display/#typedef-display-outside
fn parse_display_outside<'i, 't>(
    input: &mut Parser<'i, 't>,
) -> Result<DisplayOutside, ParseError<'i>> {
    Ok(try_match_ident_ignore_ascii_case! { input,
        "block" => DisplayOutside::Block,
        "inline" => DisplayOutside::Inline,
        // FIXME(bug 2056): not supported in layout yet:
        //"run-in" => DisplayOutside::RunIn,
    })
}

/// (flow | flow-root)?
fn parse_display_inside_for_list_item<'i, 't>(
    input: &mut Parser<'i, 't>,
) -> Result<DisplayInside, ParseError<'i>> {
    Ok(try_match_ident_ignore_ascii_case! { input,
        "flow" => DisplayInside::Flow,
        #[cfg(feature = "gecko")]
        "flow-root" => DisplayInside::FlowRoot,
    })
}
/// Test a <display-inside> Result for same values as above.
fn is_valid_inside_for_list_item<'i>(inside: &Result<DisplayInside, ParseError<'i>>) -> bool {
    match inside {
        Ok(DisplayInside::Flow) => true,
        #[cfg(feature = "gecko")]
        Ok(DisplayInside::FlowRoot) => true,
        _ => false,
    }
}

/// Parse `list-item`.
fn parse_list_item<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
    Ok(try_match_ident_ignore_ascii_case! { input,
        "list-item" => (),
    })
}

impl Parse for Display {
    #[allow(unused)] // `context` isn't used for servo-2020 for now
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Display, ParseError<'i>> {
        // Parse all combinations of <display-inside/outside>? and `list-item`? first.
        let mut got_list_item = input.try(parse_list_item).is_ok();
        let mut inside = if got_list_item {
            input.try(parse_display_inside_for_list_item)
        } else {
            input.try(parse_display_inside)
        };
        // <display-listitem> = <display-outside>? && [ flow | flow-root ]? && list-item
        // https://drafts.csswg.org/css-display/#typedef-display-listitem
        if !got_list_item && is_valid_inside_for_list_item(&inside) {
            got_list_item = input.try(parse_list_item).is_ok();
        }
        let outside = input.try(parse_display_outside);
        if outside.is_ok() {
            if !got_list_item && (inside.is_err() || is_valid_inside_for_list_item(&inside)) {
                got_list_item = input.try(parse_list_item).is_ok();
            }
            if inside.is_err() {
                inside = if got_list_item {
                    input.try(parse_display_inside_for_list_item)
                } else {
                    input.try(parse_display_inside)
                };
                if !got_list_item && is_valid_inside_for_list_item(&inside) {
                    got_list_item = input.try(parse_list_item).is_ok();
                }
            }
        }
        if got_list_item || inside.is_ok() || outside.is_ok() {
            let inside = inside.unwrap_or(DisplayInside::Flow);
            let outside = outside.unwrap_or(match inside {
                // "If <display-outside> is omitted, the element’s outside display type
                // defaults to block — except for ruby, which defaults to inline."
                // https://drafts.csswg.org/css-display/#inside-model
                #[cfg(feature = "gecko")]
                DisplayInside::Ruby => DisplayOutside::Inline,
                _ => DisplayOutside::Block,
            });
            return Ok(Display::from3(outside, inside, got_list_item));
        }

        // Now parse the single-keyword `display` values.
        Ok(try_match_ident_ignore_ascii_case! { input,
            "none" => Display::None,
            #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
            "contents" => Display::Contents,
            "inline-block" => Display::InlineBlock,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "inline-table" => Display::InlineTable,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "-webkit-flex" => Display::Flex,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "inline-flex" | "-webkit-inline-flex" => Display::InlineFlex,
            #[cfg(feature = "gecko")]
            "inline-grid" => Display::InlineGrid,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-caption" => Display::TableCaption,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-row-group" => Display::TableRowGroup,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-header-group" => Display::TableHeaderGroup,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-footer-group" => Display::TableFooterGroup,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-column" => Display::TableColumn,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-column-group" => Display::TableColumnGroup,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-row" => Display::TableRow,
            #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
            "table-cell" => Display::TableCell,
            #[cfg(feature = "gecko")]
            "ruby-base" => Display::RubyBase,
            #[cfg(feature = "gecko")]
            "ruby-base-container" => Display::RubyBaseContainer,
            #[cfg(feature = "gecko")]
            "ruby-text" => Display::RubyText,
            #[cfg(feature = "gecko")]
            "ruby-text-container" => Display::RubyTextContainer,
            #[cfg(feature = "gecko")]
            "-webkit-box" => Display::WebkitBox,
            #[cfg(feature = "gecko")]
            "-webkit-inline-box" => Display::WebkitInlineBox,
            #[cfg(feature = "gecko")]
            "-moz-box" if moz_box_display_values_enabled(context) => Display::MozBox,
            #[cfg(feature = "gecko")]
            "-moz-inline-box" if moz_box_display_values_enabled(context) => Display::MozInlineBox,
            #[cfg(feature = "gecko")]
            "-moz-grid" if moz_display_values_enabled(context) => Display::MozGrid,
            #[cfg(feature = "gecko")]
            "-moz-grid-group" if moz_display_values_enabled(context) => Display::MozGridGroup,
            #[cfg(feature = "gecko")]
            "-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine,
            #[cfg(feature = "gecko")]
            "-moz-stack" if moz_display_values_enabled(context) => Display::MozStack,
            #[cfg(feature = "gecko")]
            "-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
            #[cfg(feature = "gecko")]
            "-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
        })
    }
{'fraction_non_alphanumeric': 0.15354112142120707, 'fraction_numerical': 0.009635974304068522, 'mean_word_length': 2.643502131040959, 'pattern_counts': {'":': 0, '<': 96, '<?xml version=': 0, '>': 297, 'https://': 18, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4878769', 'n_tokens_mistral': 16214, 'n_tokens_neox': 15362, 'n_words': 3136}