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