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