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