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
7pub use crate::logical_geometry::WritingModeProperty;
8use crate::parser::{Parse, ParserContext};
9use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};
10use crate::values::generics::box_::{
11    GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign,
12    VerticalAlignKeyword,
13};
14use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
15use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumberOrPercentage};
16use crate::values::CustomIdent;
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 => WillChangeBits::OPACITY | WillChangeBits::BACKDROP_ROOT,
1011        LonghandId::Contain => WillChangeBits::CONTAIN,
1012        LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
1013        LonghandId::Position => {
1014            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
1015        },
1016        LonghandId::ZIndex => WillChangeBits::Z_INDEX,
1017        LonghandId::Transform
1018        | LonghandId::TransformStyle
1019        | LonghandId::Translate
1020        | LonghandId::Rotate
1021        | LonghandId::Scale
1022        | LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
1023        LonghandId::Filter | LonghandId::BackdropFilter => {
1024            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
1025                | WillChangeBits::BACKDROP_ROOT
1026                | WillChangeBits::FIXPOS_CB_NON_SVG
1027        },
1028        LonghandId::ViewTransitionName => {
1029            WillChangeBits::VIEW_TRANSITION_NAME | WillChangeBits::BACKDROP_ROOT
1030        },
1031        LonghandId::MixBlendMode => {
1032            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
1033        },
1034        LonghandId::Isolation | LonghandId::MaskImage => {
1035            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
1036        },
1037        LonghandId::ClipPath => {
1038            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
1039        },
1040        _ => WillChangeBits::empty(),
1041    }
1042}
1043
1044fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
1045    let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
1046        Ok(id) => id,
1047        Err(..) => return WillChangeBits::empty(),
1048    };
1049
1050    match id.as_shorthand() {
1051        Ok(shorthand) => shorthand
1052            .longhands()
1053            .fold(WillChangeBits::empty(), |flags, p| {
1054                flags | change_bits_for_longhand(p)
1055            }),
1056        Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
1057        Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
1058    }
1059}
1060
1061impl Parse for WillChange {
1062    /// auto | <animateable-feature>#
1063    fn parse<'i, 't>(
1064        context: &ParserContext,
1065        input: &mut Parser<'i, 't>,
1066    ) -> Result<Self, ParseError<'i>> {
1067        if input
1068            .try_parse(|input| input.expect_ident_matching("auto"))
1069            .is_ok()
1070        {
1071            return Ok(Self::default());
1072        }
1073
1074        let mut bits = WillChangeBits::empty();
1075        let custom_idents = input.parse_comma_separated(|i| {
1076            let location = i.current_source_location();
1077            let parser_ident = i.expect_ident()?;
1078            let ident = CustomIdent::from_ident(
1079                location,
1080                parser_ident,
1081                &["will-change", "none", "all", "auto"],
1082            )?;
1083
1084            if context.in_ua_sheet() && ident.0 == atom!("-moz-fixed-pos-containing-block") {
1085                bits |= WillChangeBits::FIXPOS_CB_NON_SVG;
1086            } else if ident.0 == atom!("scroll-position") {
1087                bits |= WillChangeBits::SCROLL;
1088            } else {
1089                bits |= change_bits_for_maybe_property(&parser_ident, context);
1090            }
1091            Ok(ident)
1092        })?;
1093
1094        Ok(Self {
1095            features: custom_idents.into(),
1096            bits,
1097        })
1098    }
1099}
1100
1101/// Values for the `touch-action` property.
1102#[derive(
1103    Clone,
1104    Copy,
1105    Debug,
1106    Eq,
1107    MallocSizeOf,
1108    Parse,
1109    PartialEq,
1110    SpecifiedValueInfo,
1111    ToComputedValue,
1112    ToCss,
1113    ToResolvedValue,
1114    ToShmem,
1115)]
1116#[css(bitflags(single = "none,auto,manipulation", mixed = "pan-x,pan-y,pinch-zoom"))]
1117#[repr(C)]
1118pub struct TouchAction(u8);
1119bitflags! {
1120    impl TouchAction: u8 {
1121        /// `none` variant
1122        const NONE = 1 << 0;
1123        /// `auto` variant
1124        const AUTO = 1 << 1;
1125        /// `pan-x` variant
1126        const PAN_X = 1 << 2;
1127        /// `pan-y` variant
1128        const PAN_Y = 1 << 3;
1129        /// `manipulation` variant
1130        const MANIPULATION = 1 << 4;
1131        /// `pinch-zoom` variant
1132        const PINCH_ZOOM = 1 << 5;
1133    }
1134}
1135
1136impl TouchAction {
1137    #[inline]
1138    /// Get default `touch-action` as `auto`
1139    pub fn auto() -> TouchAction {
1140        TouchAction::AUTO
1141    }
1142}
1143
1144#[derive(
1145    Clone,
1146    Copy,
1147    Debug,
1148    Eq,
1149    MallocSizeOf,
1150    Parse,
1151    PartialEq,
1152    SpecifiedValueInfo,
1153    ToComputedValue,
1154    ToCss,
1155    ToResolvedValue,
1156    ToShmem,
1157)]
1158#[css(bitflags(
1159    single = "none,strict,content",
1160    mixed = "size,layout,style,paint,inline-size",
1161    overlapping_bits
1162))]
1163#[repr(C)]
1164/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
1165pub struct Contain(u8);
1166bitflags! {
1167    impl Contain: u8 {
1168        /// `none` variant, just for convenience.
1169        const NONE = 0;
1170        /// `inline-size` variant, turns on single-axis inline size containment
1171        const INLINE_SIZE = 1 << 0;
1172        /// `block-size` variant, turns on single-axis block size containment, internal only
1173        const BLOCK_SIZE = 1 << 1;
1174        /// `layout` variant, turns on layout containment
1175        const LAYOUT = 1 << 2;
1176        /// `style` variant, turns on style containment
1177        const STYLE = 1 << 3;
1178        /// `paint` variant, turns on paint containment
1179        const PAINT = 1 << 4;
1180        /// 'size' variant, turns on size containment
1181        const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits() | Contain::BLOCK_SIZE.bits();
1182        /// `content` variant, turns on layout and paint containment
1183        const CONTENT = 1 << 6 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits();
1184        /// `strict` variant, turns on all types of containment
1185        const STRICT = 1 << 7 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits() | Contain::SIZE.bits();
1186    }
1187}
1188
1189impl Parse for ContainIntrinsicSize {
1190    /// none | <length> | auto <length>
1191    fn parse<'i, 't>(
1192        context: &ParserContext,
1193        input: &mut Parser<'i, 't>,
1194    ) -> Result<Self, ParseError<'i>> {
1195        if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
1196            return Ok(Self::Length(l));
1197        }
1198
1199        if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
1200            if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1201                return Ok(Self::AutoNone);
1202            }
1203
1204            let l = NonNegativeLength::parse(context, input)?;
1205            return Ok(Self::AutoLength(l));
1206        }
1207
1208        input.expect_ident_matching("none")?;
1209        Ok(Self::None)
1210    }
1211}
1212
1213impl Parse for LineClamp {
1214    /// none | <positive-integer>
1215    fn parse<'i, 't>(
1216        context: &ParserContext,
1217        input: &mut Parser<'i, 't>,
1218    ) -> Result<Self, ParseError<'i>> {
1219        if let Ok(i) =
1220            input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))
1221        {
1222            return Ok(Self(i.0));
1223        }
1224        input.expect_ident_matching("none")?;
1225        Ok(Self::none())
1226    }
1227}
1228
1229/// https://drafts.csswg.org/css-contain-2/#content-visibility
1230#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1231#[derive(
1232    Clone,
1233    Copy,
1234    Debug,
1235    Eq,
1236    FromPrimitive,
1237    MallocSizeOf,
1238    Parse,
1239    PartialEq,
1240    SpecifiedValueInfo,
1241    ToAnimatedValue,
1242    ToComputedValue,
1243    ToCss,
1244    ToResolvedValue,
1245    ToShmem,
1246)]
1247#[repr(u8)]
1248pub enum ContentVisibility {
1249    /// `auto` variant, the element turns on layout containment, style containment, and paint
1250    /// containment. In addition, if the element is not relevant to the user (such as by being
1251    /// offscreen) it also skips its content
1252    Auto,
1253    /// `hidden` variant, the element skips its content
1254    Hidden,
1255    /// 'visible' variant, no effect
1256    Visible,
1257}
1258
1259#[derive(
1260    Clone,
1261    Copy,
1262    Debug,
1263    PartialEq,
1264    Eq,
1265    MallocSizeOf,
1266    SpecifiedValueInfo,
1267    ToComputedValue,
1268    ToCss,
1269    Parse,
1270    ToResolvedValue,
1271    ToShmem,
1272)]
1273#[css(bitflags(
1274    single = "normal",
1275    mixed = "size,inline-size,scroll-state",
1276    validate_mixed = "Self::validate_mixed_flags",
1277))]
1278#[repr(C)]
1279/// Specified keyword values for the container-type property.
1280/// Spec: normal | [ [ size | inline-size ] || scroll-state ]
1281///
1282/// Container Queries are moved from css-contain-3 to css-conditional-5 in August 2022:
1283/// https://drafts.csswg.org/css-contain-3/#container-type
1284/// https://drafts.csswg.org/css-conditional-5/#container-type
1285pub struct ContainerType(u8);
1286bitflags! {
1287    impl ContainerType: u8 {
1288        /// The `normal` variant.
1289        const NORMAL = 0;
1290        /// The `inline-size` variant.
1291        const INLINE_SIZE = 1 << 0;
1292        /// The `size` variant.
1293        const SIZE = 1 << 1;
1294        /// The `scroll-state` variant.
1295        const SCROLL_STATE = 1 << 2;
1296    }
1297}
1298
1299impl ContainerType {
1300    fn validate_mixed_flags(&self) -> bool {
1301        // size and inline-size can't be mixed together.
1302        if self.contains(Self::SIZE | Self::INLINE_SIZE) {
1303            return false;
1304        }
1305        if self.contains(Self::SCROLL_STATE)
1306            && !static_prefs::pref!("layout.css.scroll-state.enabled")
1307        {
1308            return false;
1309        }
1310        true
1311    }
1312
1313    /// Is this container-type: normal?
1314    pub fn is_normal(self) -> bool {
1315        self == Self::NORMAL
1316    }
1317
1318    /// Is this type containing size in any way?
1319    pub fn is_size_container_type(self) -> bool {
1320        self.intersects(Self::SIZE | Self::INLINE_SIZE)
1321    }
1322}
1323
1324/// https://drafts.csswg.org/css-contain-3/#container-name
1325#[repr(transparent)]
1326#[derive(
1327    Clone,
1328    Debug,
1329    MallocSizeOf,
1330    PartialEq,
1331    SpecifiedValueInfo,
1332    ToComputedValue,
1333    ToCss,
1334    ToResolvedValue,
1335    ToShmem,
1336)]
1337pub struct ContainerName(#[css(iterable, if_empty = "none")] pub crate::OwnedSlice<CustomIdent>);
1338
1339impl ContainerName {
1340    /// Return the `none` value.
1341    pub fn none() -> Self {
1342        Self(Default::default())
1343    }
1344
1345    /// Returns whether this is the `none` value.
1346    pub fn is_none(&self) -> bool {
1347        self.0.is_empty()
1348    }
1349
1350    fn parse_internal<'i>(
1351        input: &mut Parser<'i, '_>,
1352        for_query: bool,
1353    ) -> Result<Self, ParseError<'i>> {
1354        let mut idents = vec![];
1355        let location = input.current_source_location();
1356        let first = input.expect_ident()?;
1357        if !for_query && first.eq_ignore_ascii_case("none") {
1358            return Ok(Self::none());
1359        }
1360        const DISALLOWED_CONTAINER_NAMES: &'static [&'static str] = &["none", "not", "or", "and"];
1361        idents.push(CustomIdent::from_ident(
1362            location,
1363            first,
1364            DISALLOWED_CONTAINER_NAMES,
1365        )?);
1366        if !for_query {
1367            while let Ok(name) =
1368                input.try_parse(|input| CustomIdent::parse(input, DISALLOWED_CONTAINER_NAMES))
1369            {
1370                idents.push(name);
1371            }
1372        }
1373        Ok(ContainerName(idents.into()))
1374    }
1375
1376    /// https://github.com/w3c/csswg-drafts/issues/7203
1377    /// Only a single name allowed in @container rule.
1378    /// Disallow none for container-name in @container rule.
1379    pub fn parse_for_query<'i, 't>(
1380        _: &ParserContext,
1381        input: &mut Parser<'i, 't>,
1382    ) -> Result<Self, ParseError<'i>> {
1383        Self::parse_internal(input, /* for_query = */ true)
1384    }
1385}
1386
1387impl Parse for ContainerName {
1388    fn parse<'i, 't>(
1389        _: &ParserContext,
1390        input: &mut Parser<'i, 't>,
1391    ) -> Result<Self, ParseError<'i>> {
1392        Self::parse_internal(input, /* for_query = */ false)
1393    }
1394}
1395
1396/// A specified value for the `perspective` property.
1397pub type Perspective = GenericPerspective<NonNegativeLength>;
1398
1399/// https://drafts.csswg.org/css-box/#propdef-float
1400#[allow(missing_docs)]
1401#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1402#[derive(
1403    Clone,
1404    Copy,
1405    Debug,
1406    Eq,
1407    FromPrimitive,
1408    Hash,
1409    MallocSizeOf,
1410    Parse,
1411    PartialEq,
1412    SpecifiedValueInfo,
1413    ToComputedValue,
1414    ToCss,
1415    ToResolvedValue,
1416    ToShmem,
1417)]
1418#[repr(u8)]
1419pub enum Float {
1420    Left,
1421    Right,
1422    None,
1423    // https://drafts.csswg.org/css-logical-props/#float-clear
1424    InlineStart,
1425    InlineEnd,
1426}
1427
1428impl Float {
1429    /// Returns true if `self` is not `None`.
1430    pub fn is_floating(self) -> bool {
1431        self != Self::None
1432    }
1433}
1434
1435/// https://drafts.csswg.org/css2/#propdef-clear
1436#[allow(missing_docs)]
1437#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1438#[derive(
1439    Clone,
1440    Copy,
1441    Debug,
1442    Eq,
1443    FromPrimitive,
1444    Hash,
1445    MallocSizeOf,
1446    Parse,
1447    PartialEq,
1448    SpecifiedValueInfo,
1449    ToComputedValue,
1450    ToCss,
1451    ToResolvedValue,
1452    ToShmem,
1453)]
1454#[repr(u8)]
1455pub enum Clear {
1456    None,
1457    Left,
1458    Right,
1459    Both,
1460    // https://drafts.csswg.org/css-logical-props/#float-clear
1461    InlineStart,
1462    InlineEnd,
1463}
1464
1465/// https://drafts.csswg.org/css-ui/#propdef-resize
1466#[allow(missing_docs)]
1467#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1468#[derive(
1469    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1470)]
1471pub enum Resize {
1472    None,
1473    Both,
1474    Horizontal,
1475    Vertical,
1476    // https://drafts.csswg.org/css-logical-1/#resize
1477    Inline,
1478    Block,
1479}
1480
1481/// The value for the `appearance` property.
1482///
1483/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
1484#[allow(missing_docs)]
1485#[derive(
1486    Clone,
1487    Copy,
1488    Debug,
1489    Eq,
1490    Hash,
1491    MallocSizeOf,
1492    Parse,
1493    PartialEq,
1494    SpecifiedValueInfo,
1495    ToCss,
1496    ToComputedValue,
1497    ToResolvedValue,
1498    ToShmem,
1499)]
1500#[repr(u8)]
1501pub enum Appearance {
1502    /// No appearance at all.
1503    None,
1504    /// Default appearance for the element.
1505    ///
1506    /// This value doesn't make sense for -moz-default-appearance, but we don't bother to guard
1507    /// against parsing it.
1508    Auto,
1509    /// A searchfield.
1510    Searchfield,
1511    /// A multi-line text field, e.g. HTML <textarea>.
1512    Textarea,
1513    /// A checkbox element.
1514    Checkbox,
1515    /// A radio element within a radio group.
1516    Radio,
1517    /// A dropdown list.
1518    Menulist,
1519    /// List boxes.
1520    Listbox,
1521    /// A horizontal meter bar.
1522    Meter,
1523    /// A horizontal progress bar.
1524    ProgressBar,
1525    /// A typical dialog button.
1526    Button,
1527    /// A single-line text field, e.g. HTML <input type=text>.
1528    Textfield,
1529    /// The dropdown button(s) that open up a dropdown list.
1530    MenulistButton,
1531    /// Menu Popup background.
1532    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1533    Menupopup,
1534    /// The "arrowed" part of the dropdown button that open up a dropdown list.
1535    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1536    MozMenulistArrowButton,
1537    /// For HTML's <input type=number>
1538    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1539    NumberInput,
1540    /// For HTML's <input type=password>
1541    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1542    PasswordInput,
1543    /// nsRangeFrame and its subparts
1544    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1545    Range,
1546    /// The scrollbar slider
1547    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1548    ScrollbarHorizontal,
1549    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1550    ScrollbarVertical,
1551    /// A scrollbar button (up/down/left/right).
1552    /// Keep these in order (some code casts these values to `int` in order to
1553    /// compare them against each other).
1554    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1555    ScrollbarbuttonUp,
1556    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1557    ScrollbarbuttonDown,
1558    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1559    ScrollbarbuttonLeft,
1560    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1561    ScrollbarbuttonRight,
1562    /// The scrollbar thumb.
1563    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1564    ScrollbarthumbHorizontal,
1565    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1566    ScrollbarthumbVertical,
1567    /// The scroll corner
1568    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1569    Scrollcorner,
1570    /// The up button of a spin control.
1571    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1572    SpinnerUpbutton,
1573    /// The down button of a spin control.
1574    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1575    SpinnerDownbutton,
1576    /// A single toolbar button (with no associated dropdown).
1577    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1578    Toolbarbutton,
1579    /// A tooltip.
1580    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1581    Tooltip,
1582
1583    /// Sidebar appearance.
1584    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1585    MozSidebar,
1586
1587    /// Mac help button.
1588    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1589    MozMacHelpButton,
1590
1591    /// An appearance value for the root, so that we can get tinting and unified toolbar looks
1592    /// (which require a transparent gecko background) without really using the whole transparency
1593    /// set-up which otherwise loses window borders, see bug 1870481.
1594    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1595    MozMacWindow,
1596
1597    /// Windows themed window frame elements.
1598    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1599    MozWindowButtonBox,
1600    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1601    MozWindowButtonClose,
1602    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1603    MozWindowButtonMaximize,
1604    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1605    MozWindowButtonMinimize,
1606    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1607    MozWindowButtonRestore,
1608    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1609    MozWindowTitlebar,
1610    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1611    MozWindowTitlebarMaximized,
1612    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1613    MozWindowDecorations,
1614
1615    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1616    MozMacDisclosureButtonClosed,
1617    #[parse(condition = "ParserContext::chrome_rules_enabled")]
1618    MozMacDisclosureButtonOpen,
1619
1620    /// A themed focus outline (for outline:auto).
1621    ///
1622    /// This isn't exposed to CSS at all, just here for convenience.
1623    #[css(skip)]
1624    FocusOutline,
1625
1626    /// A dummy variant that should be last to let the GTK widget do hackery.
1627    #[css(skip)]
1628    Count,
1629}
1630
1631/// A kind of break between two boxes.
1632///
1633/// https://drafts.csswg.org/css-break/#break-between
1634#[allow(missing_docs)]
1635#[derive(
1636    Clone,
1637    Copy,
1638    Debug,
1639    Eq,
1640    Hash,
1641    MallocSizeOf,
1642    Parse,
1643    PartialEq,
1644    SpecifiedValueInfo,
1645    ToCss,
1646    ToComputedValue,
1647    ToResolvedValue,
1648    ToShmem,
1649)]
1650#[repr(u8)]
1651pub enum BreakBetween {
1652    Always,
1653    Auto,
1654    Page,
1655    Avoid,
1656    Left,
1657    Right,
1658}
1659
1660impl BreakBetween {
1661    /// Parse a legacy break-between value for `page-break-{before,after}`.
1662    ///
1663    /// See https://drafts.csswg.org/css-break/#page-break-properties.
1664    #[cfg_attr(feature = "servo", allow(unused))]
1665    #[inline]
1666    pub(crate) fn parse_legacy<'i>(
1667        _: &ParserContext,
1668        input: &mut Parser<'i, '_>,
1669    ) -> Result<Self, ParseError<'i>> {
1670        let break_value = BreakBetween::parse(input)?;
1671        match break_value {
1672            BreakBetween::Always => Ok(BreakBetween::Page),
1673            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1674                Ok(break_value)
1675            },
1676            BreakBetween::Page => {
1677                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1678            },
1679        }
1680    }
1681
1682    /// Serialize a legacy break-between value for `page-break-*`.
1683    ///
1684    /// See https://drafts.csswg.org/css-break/#page-break-properties.
1685    #[cfg_attr(feature = "servo", allow(unused))]
1686    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1687    where
1688        W: Write,
1689    {
1690        match *self {
1691            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1692                self.to_css(dest)
1693            },
1694            BreakBetween::Page => dest.write_str("always"),
1695            BreakBetween::Always => Ok(()),
1696        }
1697    }
1698}
1699
1700/// A kind of break within a box.
1701///
1702/// https://drafts.csswg.org/css-break/#break-within
1703#[allow(missing_docs)]
1704#[derive(
1705    Clone,
1706    Copy,
1707    Debug,
1708    Eq,
1709    Hash,
1710    MallocSizeOf,
1711    Parse,
1712    PartialEq,
1713    SpecifiedValueInfo,
1714    ToCss,
1715    ToComputedValue,
1716    ToResolvedValue,
1717    ToShmem,
1718)]
1719#[repr(u8)]
1720pub enum BreakWithin {
1721    Auto,
1722    Avoid,
1723    AvoidPage,
1724    AvoidColumn,
1725}
1726
1727impl BreakWithin {
1728    /// Parse a legacy break-between value for `page-break-inside`.
1729    ///
1730    /// See https://drafts.csswg.org/css-break/#page-break-properties.
1731    #[cfg_attr(feature = "servo", allow(unused))]
1732    #[inline]
1733    pub(crate) fn parse_legacy<'i>(
1734        _: &ParserContext,
1735        input: &mut Parser<'i, '_>,
1736    ) -> Result<Self, ParseError<'i>> {
1737        let break_value = BreakWithin::parse(input)?;
1738        match break_value {
1739            BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
1740            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
1741                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1742            },
1743        }
1744    }
1745
1746    /// Serialize a legacy break-between value for `page-break-inside`.
1747    ///
1748    /// See https://drafts.csswg.org/css-break/#page-break-properties.
1749    #[cfg_attr(feature = "servo", allow(unused))]
1750    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1751    where
1752        W: Write,
1753    {
1754        match *self {
1755            BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
1756            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
1757        }
1758    }
1759}
1760
1761/// The value for the `overflow-x` / `overflow-y` properties.
1762#[allow(missing_docs)]
1763#[derive(
1764    Clone,
1765    Copy,
1766    Debug,
1767    Eq,
1768    Hash,
1769    MallocSizeOf,
1770    PartialEq,
1771    SpecifiedValueInfo,
1772    ToCss,
1773    ToComputedValue,
1774    ToResolvedValue,
1775    ToShmem,
1776)]
1777#[repr(u8)]
1778pub enum Overflow {
1779    Visible,
1780    Hidden,
1781    Scroll,
1782    Auto,
1783    Clip,
1784}
1785
1786// This can be derived once we remove or keep `-moz-hidden-unscrollable`
1787// indefinitely.
1788impl Parse for Overflow {
1789    fn parse<'i, 't>(
1790        _: &ParserContext,
1791        input: &mut Parser<'i, 't>,
1792    ) -> Result<Self, ParseError<'i>> {
1793        Ok(try_match_ident_ignore_ascii_case! { input,
1794            "visible" => Self::Visible,
1795            "hidden" => Self::Hidden,
1796            "scroll" => Self::Scroll,
1797            "auto" | "overlay" => Self::Auto,
1798            "clip" => Self::Clip,
1799            #[cfg(feature = "gecko")]
1800            "-moz-hidden-unscrollable" if static_prefs::pref!("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
1801                Overflow::Clip
1802            },
1803        })
1804    }
1805}
1806
1807impl Overflow {
1808    /// Return true if the value will create a scrollable box.
1809    #[inline]
1810    pub fn is_scrollable(&self) -> bool {
1811        matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)
1812    }
1813    /// Convert the value to a scrollable value if it's not already scrollable.
1814    /// This maps `visible` to `auto` and `clip` to `hidden`.
1815    #[inline]
1816    pub fn to_scrollable(&self) -> Self {
1817        match *self {
1818            Self::Hidden | Self::Scroll | Self::Auto => *self,
1819            Self::Visible => Self::Auto,
1820            Self::Clip => Self::Hidden,
1821        }
1822    }
1823}
1824
1825#[derive(
1826    Clone,
1827    Copy,
1828    Debug,
1829    Eq,
1830    MallocSizeOf,
1831    Parse,
1832    PartialEq,
1833    SpecifiedValueInfo,
1834    ToComputedValue,
1835    ToCss,
1836    ToResolvedValue,
1837    ToShmem,
1838)]
1839#[repr(C)]
1840#[css(bitflags(
1841    single = "auto",
1842    mixed = "stable,both-edges",
1843    validate_mixed = "Self::has_stable"
1844))]
1845/// Values for scrollbar-gutter:
1846/// <https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property>
1847pub struct ScrollbarGutter(u8);
1848bitflags! {
1849    impl ScrollbarGutter: u8 {
1850        /// `auto` variant. Just for convenience if there is no flag set.
1851        const AUTO = 0;
1852        /// `stable` variant.
1853        const STABLE = 1 << 0;
1854        /// `both-edges` variant.
1855        const BOTH_EDGES = 1 << 1;
1856    }
1857}
1858
1859impl ScrollbarGutter {
1860    #[inline]
1861    fn has_stable(&self) -> bool {
1862        self.intersects(Self::STABLE)
1863    }
1864}
1865
1866/// A specified value for the zoom property.
1867#[derive(
1868    Clone, Copy, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem,
1869)]
1870#[allow(missing_docs)]
1871pub enum Zoom {
1872    Normal,
1873    /// An internal value that resets the effective zoom to 1. Used for scrollbar parts, which
1874    /// disregard zoom. We use this name because WebKit has this value exposed to the web.
1875    #[parse(condition = "ParserContext::in_ua_sheet")]
1876    Document,
1877    Value(NonNegativeNumberOrPercentage),
1878}
1879
1880impl Zoom {
1881    /// Return a particular number value of the zoom property.
1882    #[inline]
1883    pub fn new_number(n: f32) -> Self {
1884        Self::Value(NonNegativeNumberOrPercentage::new_number(n))
1885    }
1886}
1887
1888pub use crate::values::generics::box_::PositionProperty;