Skip to main content

style/values/specified/
box.rs

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