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