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