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