style/values/specified/
box.rs

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