Skip to main content

style/values/specified/
box.rs

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