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