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#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
815#[derive(
816    Clone,
817    Copy,
818    Debug,
819    Eq,
820    MallocSizeOf,
821    Parse,
822    PartialEq,
823    SpecifiedValueInfo,
824    ToComputedValue,
825    ToCss,
826    ToResolvedValue,
827    ToShmem,
828)]
829#[repr(u8)]
830pub enum ScrollSnapAxis {
831    X,
832    Y,
833    Block,
834    Inline,
835    Both,
836}
837
838/// https://drafts.csswg.org/css-scroll-snap-1/#snap-strictness
839#[allow(missing_docs)]
840#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
841#[derive(
842    Clone,
843    Copy,
844    Debug,
845    Eq,
846    MallocSizeOf,
847    Parse,
848    PartialEq,
849    SpecifiedValueInfo,
850    ToComputedValue,
851    ToCss,
852    ToResolvedValue,
853    ToShmem,
854)]
855#[repr(u8)]
856pub enum ScrollSnapStrictness {
857    #[css(skip)]
858    None, // Used to represent scroll-snap-type: none.  It's not parsed.
859    Mandatory,
860    Proximity,
861}
862
863/// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type
864#[allow(missing_docs)]
865#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
866#[derive(
867    Clone,
868    Copy,
869    Debug,
870    Eq,
871    MallocSizeOf,
872    PartialEq,
873    SpecifiedValueInfo,
874    ToComputedValue,
875    ToResolvedValue,
876    ToShmem,
877    ToTyped,
878)]
879#[repr(C)]
880#[typed(todo_derive_fields)]
881pub struct ScrollSnapType {
882    axis: ScrollSnapAxis,
883    strictness: ScrollSnapStrictness,
884}
885
886impl ScrollSnapType {
887    /// Returns `none`.
888    #[inline]
889    pub fn none() -> Self {
890        Self {
891            axis: ScrollSnapAxis::Both,
892            strictness: ScrollSnapStrictness::None,
893        }
894    }
895}
896
897impl Parse for ScrollSnapType {
898    /// none | [ x | y | block | inline | both ] [ mandatory | proximity ]?
899    fn parse<'i, 't>(
900        _context: &ParserContext,
901        input: &mut Parser<'i, 't>,
902    ) -> Result<Self, ParseError<'i>> {
903        if input
904            .try_parse(|input| input.expect_ident_matching("none"))
905            .is_ok()
906        {
907            return Ok(ScrollSnapType::none());
908        }
909
910        let axis = ScrollSnapAxis::parse(input)?;
911        let strictness = input
912            .try_parse(ScrollSnapStrictness::parse)
913            .unwrap_or(ScrollSnapStrictness::Proximity);
914        Ok(Self { axis, strictness })
915    }
916}
917
918impl ToCss for ScrollSnapType {
919    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
920    where
921        W: Write,
922    {
923        if self.strictness == ScrollSnapStrictness::None {
924            return dest.write_str("none");
925        }
926        self.axis.to_css(dest)?;
927        if self.strictness != ScrollSnapStrictness::Proximity {
928            dest.write_char(' ')?;
929            self.strictness.to_css(dest)?;
930        }
931        Ok(())
932    }
933}
934
935/// Specified value of scroll-snap-align keyword value.
936#[allow(missing_docs)]
937#[derive(
938    Clone,
939    Copy,
940    Debug,
941    Eq,
942    FromPrimitive,
943    Hash,
944    MallocSizeOf,
945    Parse,
946    PartialEq,
947    SpecifiedValueInfo,
948    ToComputedValue,
949    ToCss,
950    ToResolvedValue,
951    ToShmem,
952)]
953#[repr(u8)]
954pub enum ScrollSnapAlignKeyword {
955    None,
956    Start,
957    End,
958    Center,
959}
960
961/// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align
962#[allow(missing_docs)]
963#[derive(
964    Clone,
965    Copy,
966    Debug,
967    Eq,
968    MallocSizeOf,
969    PartialEq,
970    SpecifiedValueInfo,
971    ToComputedValue,
972    ToResolvedValue,
973    ToShmem,
974    ToTyped,
975)]
976#[repr(C)]
977#[typed(todo_derive_fields)]
978pub struct ScrollSnapAlign {
979    block: ScrollSnapAlignKeyword,
980    inline: ScrollSnapAlignKeyword,
981}
982
983impl ScrollSnapAlign {
984    /// Returns `none`.
985    #[inline]
986    pub fn none() -> Self {
987        ScrollSnapAlign {
988            block: ScrollSnapAlignKeyword::None,
989            inline: ScrollSnapAlignKeyword::None,
990        }
991    }
992}
993
994impl Parse for ScrollSnapAlign {
995    /// [ none | start | end | center ]{1,2}
996    fn parse<'i, 't>(
997        _context: &ParserContext,
998        input: &mut Parser<'i, 't>,
999    ) -> Result<ScrollSnapAlign, ParseError<'i>> {
1000        let block = ScrollSnapAlignKeyword::parse(input)?;
1001        let inline = input
1002            .try_parse(ScrollSnapAlignKeyword::parse)
1003            .unwrap_or(block);
1004        Ok(ScrollSnapAlign { block, inline })
1005    }
1006}
1007
1008impl ToCss for ScrollSnapAlign {
1009    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1010    where
1011        W: Write,
1012    {
1013        self.block.to_css(dest)?;
1014        if self.block != self.inline {
1015            dest.write_char(' ')?;
1016            self.inline.to_css(dest)?;
1017        }
1018        Ok(())
1019    }
1020}
1021
1022#[allow(missing_docs)]
1023#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1024#[derive(
1025    Clone,
1026    Copy,
1027    Debug,
1028    Eq,
1029    MallocSizeOf,
1030    Parse,
1031    PartialEq,
1032    SpecifiedValueInfo,
1033    ToComputedValue,
1034    ToCss,
1035    ToResolvedValue,
1036    ToShmem,
1037    ToTyped,
1038)]
1039#[repr(u8)]
1040pub enum ScrollSnapStop {
1041    Normal,
1042    Always,
1043}
1044
1045#[allow(missing_docs)]
1046#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1047#[derive(
1048    Clone,
1049    Copy,
1050    Debug,
1051    Eq,
1052    MallocSizeOf,
1053    Parse,
1054    PartialEq,
1055    SpecifiedValueInfo,
1056    ToComputedValue,
1057    ToCss,
1058    ToResolvedValue,
1059    ToShmem,
1060    ToTyped,
1061)]
1062#[repr(u8)]
1063pub enum OverscrollBehavior {
1064    Auto,
1065    Contain,
1066    None,
1067}
1068
1069#[allow(missing_docs)]
1070#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1071#[derive(
1072    Clone,
1073    Copy,
1074    Debug,
1075    Eq,
1076    MallocSizeOf,
1077    Parse,
1078    PartialEq,
1079    SpecifiedValueInfo,
1080    ToComputedValue,
1081    ToCss,
1082    ToResolvedValue,
1083    ToShmem,
1084    ToTyped,
1085)]
1086#[repr(u8)]
1087pub enum OverflowAnchor {
1088    Auto,
1089    None,
1090}
1091
1092#[derive(
1093    Clone,
1094    Debug,
1095    Default,
1096    MallocSizeOf,
1097    PartialEq,
1098    SpecifiedValueInfo,
1099    ToComputedValue,
1100    ToCss,
1101    ToResolvedValue,
1102    ToShmem,
1103    ToTyped,
1104)]
1105#[css(comma)]
1106#[repr(C)]
1107#[typed(no_multiple_values)]
1108/// Provides a rendering hint to the user agent, stating what kinds of changes
1109/// the author expects to perform on the element.
1110///
1111/// `auto` is represented by an empty `features` list.
1112///
1113/// <https://drafts.csswg.org/css-will-change/#will-change>
1114pub struct WillChange {
1115    /// The features that are supposed to change.
1116    ///
1117    /// TODO(emilio): Consider using ArcSlice since we just clone them from the
1118    /// specified value? That'd save an allocation, which could be worth it.
1119    #[css(iterable, if_empty = "auto")]
1120    features: crate::OwnedSlice<CustomIdent>,
1121    /// A bitfield with the kind of change that the value will create, based
1122    /// on the above field.
1123    #[css(skip)]
1124    pub bits: WillChangeBits,
1125}
1126
1127impl WillChange {
1128    #[inline]
1129    /// Get default value of `will-change` as `auto`
1130    pub fn auto() -> Self {
1131        Self::default()
1132    }
1133}
1134
1135/// The change bits that we care about.
1136#[derive(
1137    Clone,
1138    Copy,
1139    Debug,
1140    Default,
1141    Eq,
1142    MallocSizeOf,
1143    PartialEq,
1144    SpecifiedValueInfo,
1145    ToComputedValue,
1146    ToResolvedValue,
1147    ToShmem,
1148)]
1149#[repr(C)]
1150pub struct WillChangeBits(u16);
1151bitflags! {
1152    impl WillChangeBits: u16 {
1153        /// Whether a property which can create a stacking context **on any
1154        /// box** will change.
1155        const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;
1156        /// Whether `transform` or related properties will change.
1157        const TRANSFORM = 1 << 1;
1158        /// Whether `scroll-position` will change.
1159        const SCROLL = 1 << 2;
1160        /// Whether `contain` will change.
1161        const CONTAIN = 1 << 3;
1162        /// Whether `opacity` will change.
1163        const OPACITY = 1 << 4;
1164        /// Whether `perspective` will change.
1165        const PERSPECTIVE = 1 << 5;
1166        /// Whether `z-index` will change.
1167        const Z_INDEX = 1 << 6;
1168        /// Whether any property which creates a containing block for non-svg
1169        /// text frames will change.
1170        const FIXPOS_CB_NON_SVG = 1 << 7;
1171        /// Whether the position property will change.
1172        const POSITION = 1 << 8;
1173        /// Whether the view-transition-name property will change.
1174        const VIEW_TRANSITION_NAME = 1 << 9;
1175        /// Whether any property which establishes a backdrop-root will change.
1176        /// See https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty
1177        const BACKDROP_ROOT = 1 << 10;
1178    }
1179}
1180
1181fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
1182    match longhand {
1183        LonghandId::Opacity => WillChangeBits::OPACITY | WillChangeBits::BACKDROP_ROOT,
1184        LonghandId::Contain => WillChangeBits::CONTAIN,
1185        LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
1186        LonghandId::Position => {
1187            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
1188        },
1189        LonghandId::ZIndex => WillChangeBits::Z_INDEX,
1190        LonghandId::Transform
1191        | LonghandId::TransformStyle
1192        | LonghandId::Translate
1193        | LonghandId::Rotate
1194        | LonghandId::Scale
1195        | LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
1196        LonghandId::Filter | LonghandId::BackdropFilter => {
1197            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
1198                | WillChangeBits::BACKDROP_ROOT
1199                | WillChangeBits::FIXPOS_CB_NON_SVG
1200        },
1201        LonghandId::ViewTransitionName => {
1202            WillChangeBits::VIEW_TRANSITION_NAME | WillChangeBits::BACKDROP_ROOT
1203        },
1204        LonghandId::MixBlendMode => {
1205            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
1206        },
1207        LonghandId::Isolation | LonghandId::MaskImage => {
1208            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
1209        },
1210        LonghandId::ClipPath => {
1211            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
1212        },
1213        _ => WillChangeBits::empty(),
1214    }
1215}
1216
1217fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
1218    let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
1219        Ok(id) => id,
1220        Err(..) => return WillChangeBits::empty(),
1221    };
1222
1223    match id.as_shorthand() {
1224        Ok(shorthand) => shorthand
1225            .longhands()
1226            .fold(WillChangeBits::empty(), |flags, p| {
1227                flags | change_bits_for_longhand(p)
1228            }),
1229        Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
1230        Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
1231    }
1232}
1233
1234impl Parse for WillChange {
1235    /// auto | <animateable-feature>#
1236    fn parse<'i, 't>(
1237        context: &ParserContext,
1238        input: &mut Parser<'i, 't>,
1239    ) -> Result<Self, ParseError<'i>> {
1240        if input
1241            .try_parse(|input| input.expect_ident_matching("auto"))
1242            .is_ok()
1243        {
1244            return Ok(Self::default());
1245        }
1246
1247        let mut bits = WillChangeBits::empty();
1248        let custom_idents = input.parse_comma_separated(|i| {
1249            let location = i.current_source_location();
1250            let parser_ident = i.expect_ident()?;
1251            let ident = CustomIdent::from_ident(
1252                location,
1253                parser_ident,
1254                &["will-change", "none", "all", "auto"],
1255            )?;
1256
1257            if context.in_ua_sheet() && ident.0 == atom!("-moz-fixed-pos-containing-block") {
1258                bits |= WillChangeBits::FIXPOS_CB_NON_SVG;
1259            } else if ident.0 == atom!("scroll-position") {
1260                bits |= WillChangeBits::SCROLL;
1261            } else {
1262                bits |= change_bits_for_maybe_property(&parser_ident, context);
1263            }
1264            Ok(ident)
1265        })?;
1266
1267        Ok(Self {
1268            features: custom_idents.into(),
1269            bits,
1270        })
1271    }
1272}
1273
1274/// Values for the `touch-action` property.
1275#[derive(
1276    Clone,
1277    Copy,
1278    Debug,
1279    Eq,
1280    MallocSizeOf,
1281    Parse,
1282    PartialEq,
1283    SpecifiedValueInfo,
1284    ToComputedValue,
1285    ToCss,
1286    ToResolvedValue,
1287    ToShmem,
1288    ToTyped,
1289)]
1290#[css(bitflags(single = "none,auto,manipulation", mixed = "pan-x,pan-y,pinch-zoom"))]
1291#[repr(C)]
1292pub struct TouchAction(u8);
1293bitflags! {
1294    impl TouchAction: u8 {
1295        /// `none` variant
1296        const NONE = 1 << 0;
1297        /// `auto` variant
1298        const AUTO = 1 << 1;
1299        /// `pan-x` variant
1300        const PAN_X = 1 << 2;
1301        /// `pan-y` variant
1302        const PAN_Y = 1 << 3;
1303        /// `manipulation` variant
1304        const MANIPULATION = 1 << 4;
1305        /// `pinch-zoom` variant
1306        const PINCH_ZOOM = 1 << 5;
1307    }
1308}
1309
1310impl TouchAction {
1311    #[inline]
1312    /// Get default `touch-action` as `auto`
1313    pub fn auto() -> TouchAction {
1314        TouchAction::AUTO
1315    }
1316}
1317
1318#[derive(
1319    Clone,
1320    Copy,
1321    Debug,
1322    Eq,
1323    MallocSizeOf,
1324    Parse,
1325    PartialEq,
1326    SpecifiedValueInfo,
1327    ToComputedValue,
1328    ToCss,
1329    ToResolvedValue,
1330    ToShmem,
1331    ToTyped,
1332)]
1333#[css(bitflags(
1334    single = "none,strict,content",
1335    mixed = "size,layout,style,paint,inline-size",
1336    overlapping_bits
1337))]
1338#[repr(C)]
1339/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
1340pub struct Contain(u8);
1341bitflags! {
1342    impl Contain: u8 {
1343        /// `none` variant, just for convenience.
1344        const NONE = 0;
1345        /// `inline-size` variant, turns on single-axis inline size containment
1346        const INLINE_SIZE = 1 << 0;
1347        /// `block-size` variant, turns on single-axis block size containment, internal only
1348        const BLOCK_SIZE = 1 << 1;
1349        /// `layout` variant, turns on layout containment
1350        const LAYOUT = 1 << 2;
1351        /// `style` variant, turns on style containment
1352        const STYLE = 1 << 3;
1353        /// `paint` variant, turns on paint containment
1354        const PAINT = 1 << 4;
1355        /// 'size' variant, turns on size containment
1356        const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits() | Contain::BLOCK_SIZE.bits();
1357        /// `content` variant, turns on layout and paint containment
1358        const CONTENT = 1 << 6 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits();
1359        /// `strict` variant, turns on all types of containment
1360        const STRICT = 1 << 7 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits() | Contain::SIZE.bits();
1361    }
1362}
1363
1364impl Parse for ContainIntrinsicSize {
1365    /// none | <length> | auto <length>
1366    fn parse<'i, 't>(
1367        context: &ParserContext,
1368        input: &mut Parser<'i, 't>,
1369    ) -> Result<Self, ParseError<'i>> {
1370        if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
1371            return Ok(Self::Length(l));
1372        }
1373
1374        if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
1375            if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1376                return Ok(Self::AutoNone);
1377            }
1378
1379            let l = NonNegativeLength::parse(context, input)?;
1380            return Ok(Self::AutoLength(l));
1381        }
1382
1383        input.expect_ident_matching("none")?;
1384        Ok(Self::None)
1385    }
1386}
1387
1388impl Parse for LineClamp {
1389    /// none | <positive-integer>
1390    fn parse<'i, 't>(
1391        context: &ParserContext,
1392        input: &mut Parser<'i, 't>,
1393    ) -> Result<Self, ParseError<'i>> {
1394        if let Ok(i) =
1395            input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))
1396        {
1397            return Ok(Self(i.0));
1398        }
1399        input.expect_ident_matching("none")?;
1400        Ok(Self::none())
1401    }
1402}
1403
1404/// https://drafts.csswg.org/css-contain-2/#content-visibility
1405#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1406#[derive(
1407    Clone,
1408    Copy,
1409    Debug,
1410    Eq,
1411    FromPrimitive,
1412    MallocSizeOf,
1413    Parse,
1414    PartialEq,
1415    SpecifiedValueInfo,
1416    ToAnimatedValue,
1417    ToComputedValue,
1418    ToCss,
1419    ToResolvedValue,
1420    ToShmem,
1421    ToTyped,
1422)]
1423#[repr(u8)]
1424pub enum ContentVisibility {
1425    /// `auto` variant, the element turns on layout containment, style containment, and paint
1426    /// containment. In addition, if the element is not relevant to the user (such as by being
1427    /// offscreen) it also skips its content
1428    Auto,
1429    /// `hidden` variant, the element skips its content
1430    Hidden,
1431    /// 'visible' variant, no effect
1432    Visible,
1433}
1434
1435#[derive(
1436    Clone,
1437    Copy,
1438    Debug,
1439    PartialEq,
1440    Eq,
1441    MallocSizeOf,
1442    SpecifiedValueInfo,
1443    ToComputedValue,
1444    ToCss,
1445    Parse,
1446    ToResolvedValue,
1447    ToShmem,
1448    ToTyped,
1449)]
1450#[css(bitflags(
1451    single = "normal",
1452    mixed = "size,inline-size,scroll-state",
1453    validate_mixed = "Self::validate_mixed_flags",
1454))]
1455#[repr(C)]
1456/// Specified keyword values for the container-type property.
1457/// Spec: normal | [ [ size | inline-size ] || scroll-state ]
1458///
1459/// Container Queries are moved from css-contain-3 to css-conditional-5 in August 2022:
1460/// https://drafts.csswg.org/css-contain-3/#container-type
1461/// https://drafts.csswg.org/css-conditional-5/#container-type
1462pub struct ContainerType(u8);
1463bitflags! {
1464    impl ContainerType: u8 {
1465        /// The `normal` variant.
1466        const NORMAL = 0;
1467        /// The `inline-size` variant.
1468        const INLINE_SIZE = 1 << 0;
1469        /// The `size` variant.
1470        const SIZE = 1 << 1;
1471        /// The `scroll-state` variant.
1472        const SCROLL_STATE = 1 << 2;
1473    }
1474}
1475
1476impl ContainerType {
1477    fn validate_mixed_flags(&self) -> bool {
1478        // size and inline-size can't be mixed together.
1479        if self.contains(Self::SIZE | Self::INLINE_SIZE) {
1480            return false;
1481        }
1482        if self.contains(Self::SCROLL_STATE)
1483            && !static_prefs::pref!("layout.css.scroll-state.enabled")
1484        {
1485            return false;
1486        }
1487        true
1488    }
1489
1490    /// Is this container-type: normal?
1491    pub fn is_normal(self) -> bool {
1492        self == Self::NORMAL
1493    }
1494
1495    /// Is this type containing size in any way?
1496    pub fn is_size_container_type(self) -> bool {
1497        self.intersects(Self::SIZE | Self::INLINE_SIZE)
1498    }
1499}
1500
1501/// https://drafts.csswg.org/css-contain-3/#container-name
1502#[repr(transparent)]
1503#[derive(
1504    Clone,
1505    Debug,
1506    MallocSizeOf,
1507    PartialEq,
1508    SpecifiedValueInfo,
1509    ToComputedValue,
1510    ToCss,
1511    ToResolvedValue,
1512    ToShmem,
1513    ToTyped,
1514)]
1515pub struct ContainerName(#[css(iterable, if_empty = "none")] pub crate::OwnedSlice<CustomIdent>);
1516
1517impl ContainerName {
1518    /// Return the `none` value.
1519    pub fn none() -> Self {
1520        Self(Default::default())
1521    }
1522
1523    /// Returns whether this is the `none` value.
1524    pub fn is_none(&self) -> bool {
1525        self.0.is_empty()
1526    }
1527
1528    fn parse_internal<'i>(
1529        input: &mut Parser<'i, '_>,
1530        for_query: bool,
1531    ) -> Result<Self, ParseError<'i>> {
1532        let mut idents = vec![];
1533        let location = input.current_source_location();
1534        let first = input.expect_ident()?;
1535        if !for_query && first.eq_ignore_ascii_case("none") {
1536            return Ok(Self::none());
1537        }
1538        const DISALLOWED_CONTAINER_NAMES: &'static [&'static str] = &["none", "not", "or", "and"];
1539        idents.push(CustomIdent::from_ident(
1540            location,
1541            first,
1542            DISALLOWED_CONTAINER_NAMES,
1543        )?);
1544        if !for_query {
1545            while let Ok(name) =
1546                input.try_parse(|input| CustomIdent::parse(input, DISALLOWED_CONTAINER_NAMES))
1547            {
1548                idents.push(name);
1549            }
1550        }
1551        Ok(ContainerName(idents.into()))
1552    }
1553
1554    /// https://github.com/w3c/csswg-drafts/issues/7203
1555    /// Only a single name allowed in @container rule.
1556    /// Disallow none for container-name in @container rule.
1557    pub fn parse_for_query<'i, 't>(
1558        _: &ParserContext,
1559        input: &mut Parser<'i, 't>,
1560    ) -> Result<Self, ParseError<'i>> {
1561        Self::parse_internal(input, /* for_query = */ true)
1562    }
1563}
1564
1565impl Parse for ContainerName {
1566    fn parse<'i, 't>(
1567        _: &ParserContext,
1568        input: &mut Parser<'i, 't>,
1569    ) -> Result<Self, ParseError<'i>> {
1570        Self::parse_internal(input, /* for_query = */ false)
1571    }
1572}
1573
1574/// A specified value for the `perspective` property.
1575pub type Perspective = GenericPerspective<NonNegativeLength>;
1576
1577/// https://drafts.csswg.org/css-box/#propdef-float
1578#[allow(missing_docs)]
1579#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1580#[derive(
1581    Clone,
1582    Copy,
1583    Debug,
1584    Eq,
1585    FromPrimitive,
1586    Hash,
1587    MallocSizeOf,
1588    Parse,
1589    PartialEq,
1590    SpecifiedValueInfo,
1591    ToComputedValue,
1592    ToCss,
1593    ToResolvedValue,
1594    ToShmem,
1595    ToTyped,
1596)]
1597#[repr(u8)]
1598pub enum Float {
1599    Left,
1600    Right,
1601    None,
1602    // https://drafts.csswg.org/css-logical-props/#float-clear
1603    InlineStart,
1604    InlineEnd,
1605}
1606
1607impl Float {
1608    /// Returns true if `self` is not `None`.
1609    pub fn is_floating(self) -> bool {
1610        self != Self::None
1611    }
1612}
1613
1614/// https://drafts.csswg.org/css2/#propdef-clear
1615#[allow(missing_docs)]
1616#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1617#[derive(
1618    Clone,
1619    Copy,
1620    Debug,
1621    Eq,
1622    FromPrimitive,
1623    Hash,
1624    MallocSizeOf,
1625    Parse,
1626    PartialEq,
1627    SpecifiedValueInfo,
1628    ToComputedValue,
1629    ToCss,
1630    ToResolvedValue,
1631    ToShmem,
1632    ToTyped,
1633)]
1634#[repr(u8)]
1635pub enum Clear {
1636    None,
1637    Left,
1638    Right,
1639    Both,
1640    // https://drafts.csswg.org/css-logical-props/#float-clear
1641    InlineStart,
1642    InlineEnd,
1643}
1644
1645/// https://drafts.csswg.org/css-ui/#propdef-resize
1646#[allow(missing_docs)]
1647#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1648#[derive(
1649    Clone,
1650    Copy,
1651    Debug,
1652    Eq,
1653    Hash,
1654    MallocSizeOf,
1655    Parse,
1656    PartialEq,
1657    SpecifiedValueInfo,
1658    ToCss,
1659    ToShmem,
1660    ToTyped,
1661)]
1662pub enum Resize {
1663    None,
1664    Both,
1665    Horizontal,
1666    Vertical,
1667    // https://drafts.csswg.org/css-logical-1/#resize
1668    Inline,
1669    Block,
1670}
1671
1672/// The value for the `appearance` property.
1673///
1674/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
1675#[allow(missing_docs)]
1676#[derive(
1677    Clone,
1678    Copy,
1679    Debug,
1680    Eq,
1681    Hash,
1682    MallocSizeOf,
1683    Parse,
1684    PartialEq,
1685    SpecifiedValueInfo,
1686    ToCss,
1687    ToComputedValue,
1688    ToResolvedValue,
1689    ToShmem,
1690    ToTyped,
1691)]
1692#[repr(u8)]
1693pub enum Appearance {
1694    /// No appearance at all.
1695    None,
1696    /// Default appearance for the element.
1697    ///
1698    /// This value doesn't make sense for -moz-default-appearance, but we don't bother to guard
1699    /// against parsing it.
1700    Auto,
1701    /// A searchfield.
1702    Searchfield,
1703    /// A multi-line text field, e.g. HTML <textarea>.
1704    Textarea,
1705    /// A checkbox element.
1706    Checkbox,
1707    /// A radio element within a radio group.
1708    Radio,
1709    /// A dropdown list.
1710    Menulist,
1711    /// List boxes.
1712    Listbox,
1713    /// A horizontal meter bar.
1714    Meter,
1715    /// A horizontal progress bar.
1716    ProgressBar,
1717    /// A typical dialog button.
1718    Button,
1719    /// A single-line text field, e.g. HTML <input type=text>.
1720    Textfield,
1721    /// The dropdown button(s) that open up a dropdown list.
1722    MenulistButton,
1723    /// https://drafts.csswg.org/css-forms/#appearance
1724    #[parse(condition = "appearance_base_enabled")]
1725    Base,
1726    /// Only relevant to the <select> element and ::picker(select) pseudo-element, allowing them to
1727    /// be styled.
1728    #[parse(condition = "appearance_base_select_enabled")]
1729    BaseSelect,
1730    /// Menu Popup background.
1731    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1732    Menupopup,
1733    /// The "arrowed" part of the dropdown button that open up a dropdown list.
1734    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1735    MozMenulistArrowButton,
1736    /// For HTML's <input type=number>
1737    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1738    NumberInput,
1739    /// For HTML's <input type=password>
1740    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1741    PasswordInput,
1742    /// nsRangeFrame and its subparts
1743    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1744    Range,
1745    /// The scrollbar slider
1746    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1747    ScrollbarHorizontal,
1748    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1749    ScrollbarVertical,
1750    /// A scrollbar button (up/down/left/right).
1751    /// Keep these in order (some code casts these values to `int` in order to
1752    /// compare them against each other).
1753    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1754    ScrollbarbuttonUp,
1755    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1756    ScrollbarbuttonDown,
1757    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1758    ScrollbarbuttonLeft,
1759    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1760    ScrollbarbuttonRight,
1761    /// The scrollbar thumb.
1762    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1763    ScrollbarthumbHorizontal,
1764    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1765    ScrollbarthumbVertical,
1766    /// The scroll corner
1767    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1768    Scrollcorner,
1769    /// The up button of a spin control.
1770    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1771    SpinnerUpbutton,
1772    /// The down button of a spin control.
1773    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1774    SpinnerDownbutton,
1775    /// A single toolbar button (with no associated dropdown).
1776    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1777    Toolbarbutton,
1778    /// A tooltip.
1779    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1780    Tooltip,
1781
1782    /// Sidebar appearance.
1783    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1784    MozSidebar,
1785
1786    /// Mac help button.
1787    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1788    MozMacHelpButton,
1789
1790    /// An appearance value for the root, so that we can get tinting and unified toolbar looks
1791    /// (which require a transparent gecko background) without really using the whole transparency
1792    /// set-up which otherwise loses window borders, see bug 1870481.
1793    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1794    MozMacWindow,
1795
1796    /// Windows themed window frame elements.
1797    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1798    MozWindowButtonBox,
1799    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1800    MozWindowButtonClose,
1801    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1802    MozWindowButtonMaximize,
1803    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1804    MozWindowButtonMinimize,
1805    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1806    MozWindowButtonRestore,
1807    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1808    MozWindowTitlebar,
1809    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1810    MozWindowTitlebarMaximized,
1811    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1812    MozWindowDecorations,
1813
1814    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1815    MozMacDisclosureButtonClosed,
1816    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1817    MozMacDisclosureButtonOpen,
1818
1819    /// A themed focus outline (for outline:auto).
1820    ///
1821    /// This isn't exposed to CSS at all, just here for convenience.
1822    #[css(skip)]
1823    FocusOutline,
1824
1825    /// A dummy variant that should be last to let the GTK widget do hackery.
1826    #[css(skip)]
1827    Count,
1828}
1829
1830/// A kind of break between two boxes.
1831///
1832/// https://drafts.csswg.org/css-break/#break-between
1833#[allow(missing_docs)]
1834#[derive(
1835    Clone,
1836    Copy,
1837    Debug,
1838    Eq,
1839    Hash,
1840    MallocSizeOf,
1841    Parse,
1842    PartialEq,
1843    SpecifiedValueInfo,
1844    ToCss,
1845    ToComputedValue,
1846    ToResolvedValue,
1847    ToShmem,
1848    ToTyped,
1849)]
1850#[repr(u8)]
1851pub enum BreakBetween {
1852    Always,
1853    Auto,
1854    Page,
1855    Avoid,
1856    Left,
1857    Right,
1858}
1859
1860impl BreakBetween {
1861    /// Parse a legacy break-between value for `page-break-{before,after}`.
1862    ///
1863    /// See https://drafts.csswg.org/css-break/#page-break-properties.
1864    #[cfg_attr(feature = "servo", allow(unused))]
1865    #[inline]
1866    pub(crate) fn parse_legacy<'i>(
1867        _: &ParserContext,
1868        input: &mut Parser<'i, '_>,
1869    ) -> Result<Self, ParseError<'i>> {
1870        let break_value = BreakBetween::parse(input)?;
1871        match break_value {
1872            BreakBetween::Always => Ok(BreakBetween::Page),
1873            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1874                Ok(break_value)
1875            },
1876            BreakBetween::Page => {
1877                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1878            },
1879        }
1880    }
1881
1882    /// Serialize a legacy break-between value for `page-break-*`.
1883    ///
1884    /// See https://drafts.csswg.org/css-break/#page-break-properties.
1885    #[cfg_attr(feature = "servo", allow(unused))]
1886    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1887    where
1888        W: Write,
1889    {
1890        match *self {
1891            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1892                self.to_css(dest)
1893            },
1894            BreakBetween::Page => dest.write_str("always"),
1895            BreakBetween::Always => Ok(()),
1896        }
1897    }
1898}
1899
1900/// A kind of break within a box.
1901///
1902/// https://drafts.csswg.org/css-break/#break-within
1903#[allow(missing_docs)]
1904#[derive(
1905    Clone,
1906    Copy,
1907    Debug,
1908    Eq,
1909    Hash,
1910    MallocSizeOf,
1911    Parse,
1912    PartialEq,
1913    SpecifiedValueInfo,
1914    ToCss,
1915    ToComputedValue,
1916    ToResolvedValue,
1917    ToShmem,
1918    ToTyped,
1919)]
1920#[repr(u8)]
1921pub enum BreakWithin {
1922    Auto,
1923    Avoid,
1924    AvoidPage,
1925    AvoidColumn,
1926}
1927
1928impl BreakWithin {
1929    /// Parse a legacy break-between value for `page-break-inside`.
1930    ///
1931    /// See https://drafts.csswg.org/css-break/#page-break-properties.
1932    #[cfg_attr(feature = "servo", allow(unused))]
1933    #[inline]
1934    pub(crate) fn parse_legacy<'i>(
1935        _: &ParserContext,
1936        input: &mut Parser<'i, '_>,
1937    ) -> Result<Self, ParseError<'i>> {
1938        let break_value = BreakWithin::parse(input)?;
1939        match break_value {
1940            BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
1941            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
1942                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1943            },
1944        }
1945    }
1946
1947    /// Serialize a legacy break-between value for `page-break-inside`.
1948    ///
1949    /// See https://drafts.csswg.org/css-break/#page-break-properties.
1950    #[cfg_attr(feature = "servo", allow(unused))]
1951    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1952    where
1953        W: Write,
1954    {
1955        match *self {
1956            BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
1957            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
1958        }
1959    }
1960}
1961
1962/// The value for the `overflow-x` / `overflow-y` properties.
1963#[allow(missing_docs)]
1964#[derive(
1965    Clone,
1966    Copy,
1967    Debug,
1968    Eq,
1969    Hash,
1970    MallocSizeOf,
1971    PartialEq,
1972    SpecifiedValueInfo,
1973    ToCss,
1974    ToComputedValue,
1975    ToResolvedValue,
1976    ToShmem,
1977    ToTyped,
1978)]
1979#[repr(u8)]
1980pub enum Overflow {
1981    Visible,
1982    Hidden,
1983    Scroll,
1984    Auto,
1985    Clip,
1986}
1987
1988// This can be derived once we remove or keep `-moz-hidden-unscrollable`
1989// indefinitely.
1990impl Parse for Overflow {
1991    fn parse<'i, 't>(
1992        _: &ParserContext,
1993        input: &mut Parser<'i, 't>,
1994    ) -> Result<Self, ParseError<'i>> {
1995        Ok(try_match_ident_ignore_ascii_case! { input,
1996            "visible" => Self::Visible,
1997            "hidden" => Self::Hidden,
1998            "scroll" => Self::Scroll,
1999            "auto" | "overlay" => Self::Auto,
2000            "clip" => Self::Clip,
2001            #[cfg(feature = "gecko")]
2002            "-moz-hidden-unscrollable" if static_prefs::pref!("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
2003                Overflow::Clip
2004            },
2005        })
2006    }
2007}
2008
2009impl Overflow {
2010    /// Return true if the value will create a scrollable box.
2011    #[inline]
2012    pub fn is_scrollable(&self) -> bool {
2013        matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)
2014    }
2015    /// Convert the value to a scrollable value if it's not already scrollable.
2016    /// This maps `visible` to `auto` and `clip` to `hidden`.
2017    #[inline]
2018    pub fn to_scrollable(&self) -> Self {
2019        match *self {
2020            Self::Hidden | Self::Scroll | Self::Auto => *self,
2021            Self::Visible => Self::Auto,
2022            Self::Clip => Self::Hidden,
2023        }
2024    }
2025}
2026
2027#[derive(
2028    Clone,
2029    Copy,
2030    Debug,
2031    Eq,
2032    MallocSizeOf,
2033    Parse,
2034    PartialEq,
2035    SpecifiedValueInfo,
2036    ToComputedValue,
2037    ToCss,
2038    ToResolvedValue,
2039    ToShmem,
2040    ToTyped,
2041)]
2042#[repr(C)]
2043#[css(bitflags(
2044    single = "auto",
2045    mixed = "stable,both-edges",
2046    validate_mixed = "Self::has_stable"
2047))]
2048/// Values for scrollbar-gutter:
2049/// <https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property>
2050pub struct ScrollbarGutter(u8);
2051bitflags! {
2052    impl ScrollbarGutter: u8 {
2053        /// `auto` variant. Just for convenience if there is no flag set.
2054        const AUTO = 0;
2055        /// `stable` variant.
2056        const STABLE = 1 << 0;
2057        /// `both-edges` variant.
2058        const BOTH_EDGES = 1 << 1;
2059    }
2060}
2061
2062impl ScrollbarGutter {
2063    #[inline]
2064    fn has_stable(&self) -> bool {
2065        self.intersects(Self::STABLE)
2066    }
2067}
2068
2069/// A specified value for the zoom property.
2070#[derive(
2071    Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,
2072)]
2073#[allow(missing_docs)]
2074#[typed(todo_derive_fields)]
2075pub enum Zoom {
2076    Normal,
2077    /// An internal value that resets the effective zoom to 1. Used for scrollbar parts, which
2078    /// disregard zoom. We use this name because WebKit has this value exposed to the web.
2079    #[parse(condition = "ParserContext::in_ua_sheet")]
2080    Document,
2081    Value(NonNegativeNumberOrPercentage),
2082}
2083
2084impl Zoom {
2085    /// Return a particular number value of the zoom property.
2086    #[inline]
2087    pub fn new_number(n: f32) -> Self {
2088        Self::Value(NonNegativeNumberOrPercentage::new_number(n))
2089    }
2090}
2091
2092pub use crate::values::generics::box_::PositionProperty;