style/values/specified/
box.rs

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