Bug 1620467 - Part 5: Remove appearance values not used by the browser or Web content...
[gecko.git] / servo / components / style / values / specified / box.rs
blob3658a791d8c337cd11789c93ab37d811fc5232e2
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/. */
5 //! Specified types for box properties.
7 use crate::custom_properties::Name as CustomPropertyName;
8 use crate::parser::{Parse, ParserContext};
9 use crate::properties::{LonghandId, PropertyDeclarationId, PropertyFlags};
10 use crate::properties::{PropertyId, ShorthandId};
11 use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
12 use crate::values::generics::box_::Perspective as GenericPerspective;
13 use crate::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword};
14 use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
15 use crate::values::specified::{AllowQuirks, Number};
16 use crate::values::{CustomIdent, KeyframesName};
17 use crate::Atom;
18 use cssparser::Parser;
19 use num_traits::FromPrimitive;
20 use selectors::parser::SelectorParseErrorKind;
21 use std::fmt::{self, Write};
22 use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
23 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
25 #[cfg(feature = "gecko")]
26 fn moz_display_values_enabled(context: &ParserContext) -> bool {
27     context.in_ua_or_chrome_sheet() ||
28         static_prefs::pref!("layout.css.xul-display-values.content.enabled")
31 #[cfg(feature = "gecko")]
32 fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
33     context.in_ua_or_chrome_sheet() ||
34         static_prefs::pref!("layout.css.xul-box-display-values.content.enabled")
37 fn flexbox_enabled() -> bool {
38     #[cfg(feature = "servo-layout-2020")]
39     {
40         return servo_config::prefs::pref_map()
41             .get("layout.flexbox.enabled")
42             .as_bool()
43             .unwrap_or(false)
44     }
46     true
49 /// Defines an element’s display type, which consists of
50 /// the two basic qualities of how an element generates boxes
51 /// <https://drafts.csswg.org/css-display/#propdef-display>
52 #[allow(missing_docs)]
53 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
54 #[repr(u8)]
55 pub enum DisplayOutside {
56     None = 0,
57     Inline,
58     Block,
59     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
60     TableCaption,
61     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
62     InternalTable,
63     #[cfg(feature = "gecko")]
64     InternalRuby,
65     #[cfg(feature = "gecko")]
66     XUL,
69 #[allow(missing_docs)]
70 #[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
71 #[repr(u8)]
72 pub enum DisplayInside {
73     None = 0,
74     #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
75     Contents,
76     Flow,
77     FlowRoot,
78     Flex,
79     #[cfg(feature = "gecko")]
80     Grid,
81     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
82     Table,
83     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
84     TableRowGroup,
85     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
86     TableColumn,
87     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
88     TableColumnGroup,
89     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
90     TableHeaderGroup,
91     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
92     TableFooterGroup,
93     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
94     TableRow,
95     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
96     TableCell,
97     #[cfg(feature = "gecko")]
98     Ruby,
99     #[cfg(feature = "gecko")]
100     RubyBase,
101     #[cfg(feature = "gecko")]
102     RubyBaseContainer,
103     #[cfg(feature = "gecko")]
104     RubyText,
105     #[cfg(feature = "gecko")]
106     RubyTextContainer,
107     #[cfg(feature = "gecko")]
108     WebkitBox,
109     #[cfg(feature = "gecko")]
110     MozBox,
111     #[cfg(feature = "gecko")]
112     MozGrid,
113     #[cfg(feature = "gecko")]
114     MozGridGroup,
115     #[cfg(feature = "gecko")]
116     MozGridLine,
117     #[cfg(feature = "gecko")]
118     MozStack,
119     #[cfg(feature = "gecko")]
120     MozDeck,
121     #[cfg(feature = "gecko")]
122     MozPopup,
125 #[allow(missing_docs)]
126 #[derive(
127     Clone,
128     Copy,
129     Debug,
130     Eq,
131     FromPrimitive,
132     Hash,
133     MallocSizeOf,
134     PartialEq,
135     ToComputedValue,
136     ToResolvedValue,
137     ToShmem,
139 #[repr(transparent)]
140 pub struct Display(u16);
142 /// Gecko-only impl block for Display (shared stuff later in this file):
143 #[allow(missing_docs)]
144 #[allow(non_upper_case_globals)]
145 impl Display {
146     // Our u16 bits are used as follows:    LOOOOOOOIIIIIIII
147     const LIST_ITEM_BIT: u16 = 0x8000; //^
148     const DISPLAY_OUTSIDE_BITS: u16 = 7; // ^^^^^^^
149     const DISPLAY_INSIDE_BITS: u16 = 8; //        ^^^^^^^^
151     /// https://drafts.csswg.org/css-display/#the-display-properties
152     pub const None: Self = Self::new(DisplayOutside::None, DisplayInside::None);
153     #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
154     pub const Contents: Self = Self::new(DisplayOutside::None, DisplayInside::Contents);
155     pub const Inline: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flow);
156     pub const InlineBlock: Self = Self::new(DisplayOutside::Inline, DisplayInside::FlowRoot);
157     pub const Block: Self = Self::new(DisplayOutside::Block, DisplayInside::Flow);
158     #[cfg(feature = "gecko")]
159     pub const FlowRoot: Self = Self::new(DisplayOutside::Block, DisplayInside::FlowRoot);
160     pub const Flex: Self = Self::new(DisplayOutside::Block, DisplayInside::Flex);
161     pub const InlineFlex: Self = Self::new(DisplayOutside::Inline, DisplayInside::Flex);
162     #[cfg(feature = "gecko")]
163     pub const Grid: Self = Self::new(DisplayOutside::Block, DisplayInside::Grid);
164     #[cfg(feature = "gecko")]
165     pub const InlineGrid: Self = Self::new(DisplayOutside::Inline, DisplayInside::Grid);
166     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
167     pub const Table: Self = Self::new(DisplayOutside::Block, DisplayInside::Table);
168     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
169     pub const InlineTable: Self = Self::new(DisplayOutside::Inline, DisplayInside::Table);
170     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
171     pub const TableCaption: Self = Self::new(DisplayOutside::TableCaption, DisplayInside::Flow);
172     #[cfg(feature = "gecko")]
173     pub const Ruby: Self = Self::new(DisplayOutside::Inline, DisplayInside::Ruby);
174     #[cfg(feature = "gecko")]
175     pub const WebkitBox: Self = Self::new(DisplayOutside::Block, DisplayInside::WebkitBox);
176     #[cfg(feature = "gecko")]
177     pub const WebkitInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::WebkitBox);
179     // Internal table boxes.
181     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
182     pub const TableRowGroup: Self =
183         Self::new(DisplayOutside::InternalTable, DisplayInside::TableRowGroup);
185     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
186     pub const TableHeaderGroup: Self = Self::new(
187         DisplayOutside::InternalTable,
188         DisplayInside::TableHeaderGroup,
189     );
191     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
192     pub const TableFooterGroup: Self = Self::new(
193         DisplayOutside::InternalTable,
194         DisplayInside::TableFooterGroup,
195     );
197     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
198     pub const TableColumn: Self =
199         Self::new(DisplayOutside::InternalTable, DisplayInside::TableColumn);
201     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
202     pub const TableColumnGroup: Self = Self::new(
203         DisplayOutside::InternalTable,
204         DisplayInside::TableColumnGroup,
205     );
207     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
208     pub const TableRow: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableRow);
210     #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
211     pub const TableCell: Self = Self::new(DisplayOutside::InternalTable, DisplayInside::TableCell);
213     /// Internal ruby boxes.
214     #[cfg(feature = "gecko")]
215     pub const RubyBase: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyBase);
216     #[cfg(feature = "gecko")]
217     pub const RubyBaseContainer: Self = Self::new(
218         DisplayOutside::InternalRuby,
219         DisplayInside::RubyBaseContainer,
220     );
221     #[cfg(feature = "gecko")]
222     pub const RubyText: Self = Self::new(DisplayOutside::InternalRuby, DisplayInside::RubyText);
223     #[cfg(feature = "gecko")]
224     pub const RubyTextContainer: Self = Self::new(
225         DisplayOutside::InternalRuby,
226         DisplayInside::RubyTextContainer,
227     );
229     /// XUL boxes.
230     #[cfg(feature = "gecko")]
231     pub const MozBox: Self = Self::new(DisplayOutside::Block, DisplayInside::MozBox);
232     #[cfg(feature = "gecko")]
233     pub const MozInlineBox: Self = Self::new(DisplayOutside::Inline, DisplayInside::MozBox);
234     #[cfg(feature = "gecko")]
235     pub const MozGrid: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGrid);
236     #[cfg(feature = "gecko")]
237     pub const MozGridGroup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridGroup);
238     #[cfg(feature = "gecko")]
239     pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine);
240     #[cfg(feature = "gecko")]
241     pub const MozStack: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozStack);
242     #[cfg(feature = "gecko")]
243     pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
244     #[cfg(feature = "gecko")]
245     pub const MozPopup: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozPopup);
247     /// Make a raw display value from <display-outside> and <display-inside> values.
248     #[inline]
249     const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
250         let o: u16 = ((outside as u8) as u16) << Self::DISPLAY_INSIDE_BITS;
251         let i: u16 = (inside as u8) as u16;
252         Self(o | i)
253     }
255     /// Make a display enum value from <display-outside> and <display-inside> values.
256     #[inline]
257     fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {
258         let v = Self::new(outside, inside);
259         if !list_item {
260             return v;
261         }
262         Self(v.0 | Self::LIST_ITEM_BIT)
263     }
265     /// Accessor for the <display-inside> value.
266     #[inline]
267     pub fn inside(&self) -> DisplayInside {
268         DisplayInside::from_u16(self.0 & ((1 << Self::DISPLAY_INSIDE_BITS) - 1)).unwrap()
269     }
271     /// Accessor for the <display-outside> value.
272     #[inline]
273     pub fn outside(&self) -> DisplayOutside {
274         DisplayOutside::from_u16(
275             (self.0 >> Self::DISPLAY_INSIDE_BITS) & ((1 << Self::DISPLAY_OUTSIDE_BITS) - 1),
276         )
277         .unwrap()
278     }
280     /// Whether this is `display: inline` (or `inline list-item`).
281     #[inline]
282     pub fn is_inline_flow(&self) -> bool {
283         self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
284     }
286     /// Returns whether this `display` value is some kind of list-item.
287     #[inline]
288     pub const fn is_list_item(&self) -> bool {
289         (self.0 & Self::LIST_ITEM_BIT) != 0
290     }
292     /// Returns whether this `display` value is a ruby level container.
293     pub fn is_ruby_level_container(&self) -> bool {
294         match *self {
295             #[cfg(feature = "gecko")]
296             Display::RubyBaseContainer | Display::RubyTextContainer => true,
297             _ => false,
298         }
299     }
301     /// Returns whether this `display` value is one of the types for ruby.
302     pub fn is_ruby_type(&self) -> bool {
303         match self.inside() {
304             #[cfg(feature = "gecko")]
305             DisplayInside::Ruby |
306             DisplayInside::RubyBase |
307             DisplayInside::RubyText |
308             DisplayInside::RubyBaseContainer |
309             DisplayInside::RubyTextContainer => true,
310             _ => false,
311         }
312     }
315 /// Shared Display impl for both Gecko and Servo.
316 #[allow(non_upper_case_globals)]
317 impl Display {
318     /// The initial display value.
319     #[inline]
320     pub fn inline() -> Self {
321         Display::Inline
322     }
324     /// <https://drafts.csswg.org/css2/visuren.html#x13>
325     #[cfg(feature = "servo")]
326     #[inline]
327     pub fn is_atomic_inline_level(&self) -> bool {
328         match *self {
329             Display::InlineBlock | Display::InlineFlex => true,
330             #[cfg(any(feature = "servo-layout-2013"))]
331             Display::InlineTable => true,
332             _ => false,
333         }
334     }
336     /// Returns whether this `display` value is the display of a flex or
337     /// grid container.
338     ///
339     /// This is used to implement various style fixups.
340     pub fn is_item_container(&self) -> bool {
341         match self.inside() {
342             DisplayInside::Flex => true,
343             #[cfg(feature = "gecko")]
344             DisplayInside::Grid => true,
345             _ => false,
346         }
347     }
349     /// Returns whether an element with this display type is a line
350     /// participant, which means it may lay its children on the same
351     /// line as itself.
352     pub fn is_line_participant(&self) -> bool {
353         match *self {
354             Display::Inline => true,
355             #[cfg(feature = "gecko")]
356             Display::Contents | Display::Ruby | Display::RubyBaseContainer => true,
357             _ => false,
358         }
359     }
361     /// Convert this display into an equivalent block display.
362     ///
363     /// Also used for :root style adjustments.
364     pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
365         #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
366         {
367             // Special handling for `contents` and `list-item`s on the root element.
368             if _is_root_element && (self.is_contents() || self.is_list_item()) {
369                 return Display::Block;
370             }
371         }
373         match self.outside() {
374             DisplayOutside::Inline => {
375                 let inside = match self.inside() {
376                     // `inline-block` blockifies to `block` rather than
377                     // `flow-root`, for legacy reasons.
378                     DisplayInside::FlowRoot => DisplayInside::Flow,
379                     inside => inside,
380                 };
381                 Display::from3(DisplayOutside::Block, inside, self.is_list_item())
382             },
383             DisplayOutside::Block | DisplayOutside::None => *self,
384             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
385             _ => Display::Block,
386         }
387     }
389     /// Convert this display into an equivalent inline-outside display.
390     /// https://drafts.csswg.org/css-display/#inlinify
391     #[cfg(feature = "gecko")]
392     pub fn inlinify(&self) -> Self {
393         match self.outside() {
394             DisplayOutside::Block => {
395                 let inside = match self.inside() {
396                     // `display: block` inlinifies to `display: inline-block`,
397                     // rather than `inline`, for legacy reasons.
398                     DisplayInside::Flow => DisplayInside::FlowRoot,
399                     inside => inside,
400                 };
401                 Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
402             },
403             _ => *self,
404         }
405     }
407     /// Returns true if the value is `Contents`
408     #[inline]
409     pub fn is_contents(&self) -> bool {
410         match *self {
411             #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
412             Display::Contents => true,
413             _ => false,
414         }
415     }
417     /// Returns true if the value is `None`
418     #[inline]
419     pub fn is_none(&self) -> bool {
420         *self == Display::None
421     }
424 impl ToCss for Display {
425     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
426     where
427         W: fmt::Write,
428     {
429         let outside = self.outside();
430         let inside = self.inside();
431         match *self {
432             Display::Block | Display::Inline => outside.to_css(dest),
433             Display::InlineBlock => dest.write_str("inline-block"),
434             #[cfg(feature = "gecko")]
435             Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
436             #[cfg(feature = "gecko")]
437             Display::MozInlineBox => dest.write_str("-moz-inline-box"),
438             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
439             Display::TableCaption => dest.write_str("table-caption"),
440             _ => match (outside, inside) {
441                 #[cfg(feature = "gecko")]
442                 (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
443                 (DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"),
444                 #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
445                 (DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"),
446                 #[cfg(feature = "gecko")]
447                 (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
448                 (_, inside) => {
449                     if self.is_list_item() {
450                         if outside != DisplayOutside::Block {
451                             outside.to_css(dest)?;
452                             dest.write_str(" ")?;
453                         }
454                         if inside != DisplayInside::Flow {
455                             inside.to_css(dest)?;
456                             dest.write_str(" ")?;
457                         }
458                         dest.write_str("list-item")
459                     } else {
460                         inside.to_css(dest)
461                     }
462                 },
463             },
464         }
465     }
468 /// <display-inside> = flow | flow-root | table | flex | grid | ruby
469 /// https://drafts.csswg.org/css-display/#typedef-display-inside
470 fn parse_display_inside<'i, 't>(
471     input: &mut Parser<'i, 't>,
472 ) -> Result<DisplayInside, ParseError<'i>> {
473     Ok(try_match_ident_ignore_ascii_case! { input,
474         "flow" => DisplayInside::Flow,
475         "flex" if flexbox_enabled() => DisplayInside::Flex,
476         #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
477         "flow-root" => DisplayInside::FlowRoot,
478         #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
479         "table" => DisplayInside::Table,
480         #[cfg(feature = "gecko")]
481         "grid" => DisplayInside::Grid,
482         #[cfg(feature = "gecko")]
483         "ruby" => DisplayInside::Ruby,
484     })
487 /// <display-outside> = block | inline | run-in
488 /// https://drafts.csswg.org/css-display/#typedef-display-outside
489 fn parse_display_outside<'i, 't>(
490     input: &mut Parser<'i, 't>,
491 ) -> Result<DisplayOutside, ParseError<'i>> {
492     Ok(try_match_ident_ignore_ascii_case! { input,
493         "block" => DisplayOutside::Block,
494         "inline" => DisplayOutside::Inline,
495         // FIXME(bug 2056): not supported in layout yet:
496         //"run-in" => DisplayOutside::RunIn,
497     })
500 /// (flow | flow-root)?
501 fn parse_display_inside_for_list_item<'i, 't>(
502     input: &mut Parser<'i, 't>,
503 ) -> Result<DisplayInside, ParseError<'i>> {
504     Ok(try_match_ident_ignore_ascii_case! { input,
505         "flow" => DisplayInside::Flow,
506         #[cfg(feature = "gecko")]
507         "flow-root" => DisplayInside::FlowRoot,
508     })
510 /// Test a <display-inside> Result for same values as above.
511 fn is_valid_inside_for_list_item<'i>(inside: &Result<DisplayInside, ParseError<'i>>) -> bool {
512     match inside {
513         Ok(DisplayInside::Flow) => true,
514         #[cfg(feature = "gecko")]
515         Ok(DisplayInside::FlowRoot) => true,
516         _ => false,
517     }
520 /// Parse `list-item`.
521 fn parse_list_item<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
522     Ok(try_match_ident_ignore_ascii_case! { input,
523         "list-item" => (),
524     })
527 impl Parse for Display {
528     #[allow(unused)] // `context` isn't used for servo-2020 for now
529     fn parse<'i, 't>(
530         context: &ParserContext,
531         input: &mut Parser<'i, 't>,
532     ) -> Result<Display, ParseError<'i>> {
533         // Parse all combinations of <display-inside/outside>? and `list-item`? first.
534         let mut got_list_item = input.try_parse(parse_list_item).is_ok();
535         let mut inside = if got_list_item {
536             input.try_parse(parse_display_inside_for_list_item)
537         } else {
538             input.try_parse(parse_display_inside)
539         };
540         // <display-listitem> = <display-outside>? && [ flow | flow-root ]? && list-item
541         // https://drafts.csswg.org/css-display/#typedef-display-listitem
542         if !got_list_item && is_valid_inside_for_list_item(&inside) {
543             got_list_item = input.try_parse(parse_list_item).is_ok();
544         }
545         let outside = input.try_parse(parse_display_outside);
546         if outside.is_ok() {
547             if !got_list_item && (inside.is_err() || is_valid_inside_for_list_item(&inside)) {
548                 got_list_item = input.try_parse(parse_list_item).is_ok();
549             }
550             if inside.is_err() {
551                 inside = if got_list_item {
552                     input.try_parse(parse_display_inside_for_list_item)
553                 } else {
554                     input.try_parse(parse_display_inside)
555                 };
556                 if !got_list_item && is_valid_inside_for_list_item(&inside) {
557                     got_list_item = input.try_parse(parse_list_item).is_ok();
558                 }
559             }
560         }
561         if got_list_item || inside.is_ok() || outside.is_ok() {
562             let inside = inside.unwrap_or(DisplayInside::Flow);
563             let outside = outside.unwrap_or(match inside {
564                 // "If <display-outside> is omitted, the element’s outside display type
565                 // defaults to block â€” except for ruby, which defaults to inline."
566                 // https://drafts.csswg.org/css-display/#inside-model
567                 #[cfg(feature = "gecko")]
568                 DisplayInside::Ruby => DisplayOutside::Inline,
569                 _ => DisplayOutside::Block,
570             });
571             return Ok(Display::from3(outside, inside, got_list_item));
572         }
574         // Now parse the single-keyword `display` values.
575         Ok(try_match_ident_ignore_ascii_case! { input,
576             "none" => Display::None,
577             #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
578             "contents" => Display::Contents,
579             "inline-block" => Display::InlineBlock,
580             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
581             "inline-table" => Display::InlineTable,
582             "-webkit-flex" if flexbox_enabled() => Display::Flex,
583             "inline-flex" | "-webkit-inline-flex" if flexbox_enabled() => Display::InlineFlex,
584             #[cfg(feature = "gecko")]
585             "inline-grid" => Display::InlineGrid,
586             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
587             "table-caption" => Display::TableCaption,
588             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
589             "table-row-group" => Display::TableRowGroup,
590             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
591             "table-header-group" => Display::TableHeaderGroup,
592             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
593             "table-footer-group" => Display::TableFooterGroup,
594             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
595             "table-column" => Display::TableColumn,
596             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
597             "table-column-group" => Display::TableColumnGroup,
598             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
599             "table-row" => Display::TableRow,
600             #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
601             "table-cell" => Display::TableCell,
602             #[cfg(feature = "gecko")]
603             "ruby-base" => Display::RubyBase,
604             #[cfg(feature = "gecko")]
605             "ruby-base-container" => Display::RubyBaseContainer,
606             #[cfg(feature = "gecko")]
607             "ruby-text" => Display::RubyText,
608             #[cfg(feature = "gecko")]
609             "ruby-text-container" => Display::RubyTextContainer,
610             #[cfg(feature = "gecko")]
611             "-webkit-box" => Display::WebkitBox,
612             #[cfg(feature = "gecko")]
613             "-webkit-inline-box" => Display::WebkitInlineBox,
614             #[cfg(feature = "gecko")]
615             "-moz-box" if moz_box_display_values_enabled(context) => Display::MozBox,
616             #[cfg(feature = "gecko")]
617             "-moz-inline-box" if moz_box_display_values_enabled(context) => Display::MozInlineBox,
618             #[cfg(feature = "gecko")]
619             "-moz-grid" if moz_display_values_enabled(context) => Display::MozGrid,
620             #[cfg(feature = "gecko")]
621             "-moz-grid-group" if moz_display_values_enabled(context) => Display::MozGridGroup,
622             #[cfg(feature = "gecko")]
623             "-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine,
624             #[cfg(feature = "gecko")]
625             "-moz-stack" if moz_display_values_enabled(context) => Display::MozStack,
626             #[cfg(feature = "gecko")]
627             "-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
628             #[cfg(feature = "gecko")]
629             "-moz-popup" if moz_display_values_enabled(context) => Display::MozPopup,
630         })
631     }
634 impl SpecifiedValueInfo for Display {
635     fn collect_completion_keywords(f: KeywordsCollectFn) {
636         f(&[
637             "block",
638             "contents",
639             "flex",
640             "flow-root",
641             "flow-root list-item",
642             "grid",
643             "inline",
644             "inline-block",
645             "inline-flex",
646             "inline-grid",
647             "inline-table",
648             "inline list-item",
649             "inline flow-root list-item",
650             "list-item",
651             "none",
652             "block ruby",
653             "ruby",
654             "ruby-base",
655             "ruby-base-container",
656             "ruby-text",
657             "ruby-text-container",
658             "table",
659             "table-caption",
660             "table-cell",
661             "table-column",
662             "table-column-group",
663             "table-footer-group",
664             "table-header-group",
665             "table-row",
666             "table-row-group",
667             "-webkit-box",
668             "-webkit-inline-box",
669         ]);
670     }
673 /// A specified value for the `vertical-align` property.
674 pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;
676 impl Parse for VerticalAlign {
677     fn parse<'i, 't>(
678         context: &ParserContext,
679         input: &mut Parser<'i, 't>,
680     ) -> Result<Self, ParseError<'i>> {
681         if let Ok(lp) =
682             input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))
683         {
684             return Ok(GenericVerticalAlign::Length(lp));
685         }
687         Ok(GenericVerticalAlign::Keyword(VerticalAlignKeyword::parse(
688             input,
689         )?))
690     }
693 /// https://drafts.csswg.org/css-animations/#animation-iteration-count
694 pub type AnimationIterationCount = GenericAnimationIterationCount<Number>;
696 impl Parse for AnimationIterationCount {
697     fn parse<'i, 't>(
698         context: &ParserContext,
699         input: &mut ::cssparser::Parser<'i, 't>,
700     ) -> Result<Self, ParseError<'i>> {
701         if input
702             .try_parse(|input| input.expect_ident_matching("infinite"))
703             .is_ok()
704         {
705             return Ok(GenericAnimationIterationCount::Infinite);
706         }
708         let number = Number::parse_non_negative(context, input)?;
709         Ok(GenericAnimationIterationCount::Number(number))
710     }
713 impl AnimationIterationCount {
714     /// Returns the value `1.0`.
715     #[inline]
716     pub fn one() -> Self {
717         GenericAnimationIterationCount::Number(Number::new(1.0))
718     }
721 /// A value for the `animation-name` property.
722 #[derive(
723     Clone,
724     Debug,
725     Eq,
726     Hash,
727     MallocSizeOf,
728     PartialEq,
729     SpecifiedValueInfo,
730     ToComputedValue,
731     ToResolvedValue,
732     ToShmem,
734 #[value_info(other_values = "none")]
735 pub struct AnimationName(pub Option<KeyframesName>);
737 impl AnimationName {
738     /// Get the name of the animation as an `Atom`.
739     pub fn as_atom(&self) -> Option<&Atom> {
740         self.0.as_ref().map(|n| n.as_atom())
741     }
743     /// Returns the `none` value.
744     pub fn none() -> Self {
745         AnimationName(None)
746     }
749 impl ToCss for AnimationName {
750     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
751     where
752         W: Write,
753     {
754         match self.0 {
755             Some(ref name) => name.to_css(dest),
756             None => dest.write_str("none"),
757         }
758     }
761 impl Parse for AnimationName {
762     fn parse<'i, 't>(
763         context: &ParserContext,
764         input: &mut Parser<'i, 't>,
765     ) -> Result<Self, ParseError<'i>> {
766         if let Ok(name) = input.try_parse(|input| KeyframesName::parse(context, input)) {
767             return Ok(AnimationName(Some(name)));
768         }
770         input.expect_ident_matching("none")?;
771         Ok(AnimationName(None))
772     }
775 /// https://drafts.csswg.org/css-scroll-snap-1/#snap-axis
776 #[allow(missing_docs)]
777 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
778 #[derive(
779     Clone,
780     Copy,
781     Debug,
782     Eq,
783     MallocSizeOf,
784     Parse,
785     PartialEq,
786     SpecifiedValueInfo,
787     ToComputedValue,
788     ToCss,
789     ToResolvedValue,
790     ToShmem,
792 #[repr(u8)]
793 pub enum ScrollSnapAxis {
794     X,
795     Y,
796     Block,
797     Inline,
798     Both,
801 /// https://drafts.csswg.org/css-scroll-snap-1/#snap-strictness
802 #[allow(missing_docs)]
803 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
804 #[derive(
805     Clone,
806     Copy,
807     Debug,
808     Eq,
809     MallocSizeOf,
810     Parse,
811     PartialEq,
812     SpecifiedValueInfo,
813     ToComputedValue,
814     ToCss,
815     ToResolvedValue,
816     ToShmem,
818 #[repr(u8)]
819 pub enum ScrollSnapStrictness {
820     #[css(skip)]
821     None, // Used to represent scroll-snap-type: none.  It's not parsed.
822     Mandatory,
823     Proximity,
826 /// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type
827 #[allow(missing_docs)]
828 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
829 #[derive(
830     Clone,
831     Copy,
832     Debug,
833     Eq,
834     MallocSizeOf,
835     PartialEq,
836     SpecifiedValueInfo,
837     ToComputedValue,
838     ToResolvedValue,
839     ToShmem,
841 #[repr(C)]
842 pub struct ScrollSnapType {
843     axis: ScrollSnapAxis,
844     strictness: ScrollSnapStrictness,
847 impl ScrollSnapType {
848     /// Returns `none`.
849     #[inline]
850     pub fn none() -> Self {
851         Self {
852             axis: ScrollSnapAxis::Both,
853             strictness: ScrollSnapStrictness::None,
854         }
855     }
858 impl Parse for ScrollSnapType {
859     /// none | [ x | y | block | inline | both ] [ mandatory | proximity ]?
860     fn parse<'i, 't>(
861         _context: &ParserContext,
862         input: &mut Parser<'i, 't>,
863     ) -> Result<Self, ParseError<'i>> {
864         if input
865             .try_parse(|input| input.expect_ident_matching("none"))
866             .is_ok()
867         {
868             return Ok(ScrollSnapType::none());
869         }
871         let axis = ScrollSnapAxis::parse(input)?;
872         let strictness = input
873             .try_parse(ScrollSnapStrictness::parse)
874             .unwrap_or(ScrollSnapStrictness::Proximity);
875         Ok(Self { axis, strictness })
876     }
879 impl ToCss for ScrollSnapType {
880     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
881     where
882         W: Write,
883     {
884         if self.strictness == ScrollSnapStrictness::None {
885             return dest.write_str("none");
886         }
887         self.axis.to_css(dest)?;
888         if self.strictness != ScrollSnapStrictness::Proximity {
889             dest.write_str(" ")?;
890             self.strictness.to_css(dest)?;
891         }
892         Ok(())
893     }
896 /// Specified value of scroll-snap-align keyword value.
897 #[allow(missing_docs)]
898 #[derive(
899     Clone,
900     Copy,
901     Debug,
902     Eq,
903     FromPrimitive,
904     Hash,
905     MallocSizeOf,
906     Parse,
907     PartialEq,
908     SpecifiedValueInfo,
909     ToComputedValue,
910     ToCss,
911     ToResolvedValue,
912     ToShmem,
914 #[repr(u8)]
915 pub enum ScrollSnapAlignKeyword {
916     None,
917     Start,
918     End,
919     Center,
922 /// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align
923 #[allow(missing_docs)]
924 #[derive(
925     Clone,
926     Copy,
927     Debug,
928     Eq,
929     MallocSizeOf,
930     PartialEq,
931     SpecifiedValueInfo,
932     ToComputedValue,
933     ToResolvedValue,
934     ToShmem,
936 #[repr(C)]
937 pub struct ScrollSnapAlign {
938     block: ScrollSnapAlignKeyword,
939     inline: ScrollSnapAlignKeyword,
942 impl ScrollSnapAlign {
943     /// Returns `none`.
944     #[inline]
945     pub fn none() -> Self {
946         ScrollSnapAlign {
947             block: ScrollSnapAlignKeyword::None,
948             inline: ScrollSnapAlignKeyword::None,
949         }
950     }
953 impl Parse for ScrollSnapAlign {
954     /// [ none | start | end | center ]{1,2}
955     fn parse<'i, 't>(
956         _context: &ParserContext,
957         input: &mut Parser<'i, 't>,
958     ) -> Result<ScrollSnapAlign, ParseError<'i>> {
959         let block = ScrollSnapAlignKeyword::parse(input)?;
960         let inline = input
961             .try_parse(ScrollSnapAlignKeyword::parse)
962             .unwrap_or(block);
963         Ok(ScrollSnapAlign { block, inline })
964     }
967 impl ToCss for ScrollSnapAlign {
968     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
969     where
970         W: Write,
971     {
972         self.block.to_css(dest)?;
973         if self.block != self.inline {
974             dest.write_str(" ")?;
975             self.inline.to_css(dest)?;
976         }
977         Ok(())
978     }
981 #[allow(missing_docs)]
982 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
983 #[derive(
984     Clone,
985     Copy,
986     Debug,
987     Eq,
988     MallocSizeOf,
989     Parse,
990     PartialEq,
991     SpecifiedValueInfo,
992     ToComputedValue,
993     ToCss,
994     ToResolvedValue,
995     ToShmem,
997 #[repr(u8)]
998 pub enum OverscrollBehavior {
999     Auto,
1000     Contain,
1001     None,
1004 #[allow(missing_docs)]
1005 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1006 #[derive(
1007     Clone,
1008     Copy,
1009     Debug,
1010     Eq,
1011     MallocSizeOf,
1012     Parse,
1013     PartialEq,
1014     SpecifiedValueInfo,
1015     ToComputedValue,
1016     ToCss,
1017     ToResolvedValue,
1018     ToShmem,
1020 #[repr(u8)]
1021 pub enum OverflowAnchor {
1022     Auto,
1023     None,
1026 #[allow(missing_docs)]
1027 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1028 #[derive(
1029     Clone,
1030     Copy,
1031     Debug,
1032     Eq,
1033     MallocSizeOf,
1034     Parse,
1035     PartialEq,
1036     SpecifiedValueInfo,
1037     ToComputedValue,
1038     ToCss,
1039     ToResolvedValue,
1040     ToShmem,
1042 #[repr(u8)]
1043 pub enum OverflowClipBox {
1044     PaddingBox,
1045     ContentBox,
1048 #[derive(
1049     Clone,
1050     Debug,
1051     Default,
1052     MallocSizeOf,
1053     PartialEq,
1054     SpecifiedValueInfo,
1055     ToComputedValue,
1056     ToCss,
1057     ToResolvedValue,
1058     ToShmem,
1060 #[css(comma)]
1061 #[repr(C)]
1062 /// Provides a rendering hint to the user agent, stating what kinds of changes
1063 /// the author expects to perform on the element.
1065 /// `auto` is represented by an empty `features` list.
1067 /// <https://drafts.csswg.org/css-will-change/#will-change>
1068 pub struct WillChange {
1069     /// The features that are supposed to change.
1070     ///
1071     /// TODO(emilio): Consider using ArcSlice since we just clone them from the
1072     /// specified value? That'd save an allocation, which could be worth it.
1073     #[css(iterable, if_empty = "auto")]
1074     features: crate::OwnedSlice<CustomIdent>,
1075     /// A bitfield with the kind of change that the value will create, based
1076     /// on the above field.
1077     #[css(skip)]
1078     bits: WillChangeBits,
1081 impl WillChange {
1082     #[inline]
1083     /// Get default value of `will-change` as `auto`
1084     pub fn auto() -> Self {
1085         Self::default()
1086     }
1089 bitflags! {
1090     /// The change bits that we care about.
1091     #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
1092     #[repr(C)]
1093     pub struct WillChangeBits: u8 {
1094         /// Whether the stacking context will change.
1095         const STACKING_CONTEXT = 1 << 0;
1096         /// Whether `transform` will change.
1097         const TRANSFORM = 1 << 1;
1098         /// Whether `scroll-position` will change.
1099         const SCROLL = 1 << 2;
1100         /// Whether `opacity` will change.
1101         const OPACITY = 1 << 3;
1102         /// Fixed pos containing block.
1103         const FIXPOS_CB = 1 << 4;
1104         /// Abs pos containing block.
1105         const ABSPOS_CB = 1 << 5;
1106     }
1109 fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
1110     let mut flags = match longhand {
1111         LonghandId::Opacity => WillChangeBits::OPACITY,
1112         LonghandId::Transform => WillChangeBits::TRANSFORM,
1113         #[cfg(feature = "gecko")]
1114         LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale | LonghandId::OffsetPath => {
1115             WillChangeBits::TRANSFORM
1116         },
1117         _ => WillChangeBits::empty(),
1118     };
1120     let property_flags = longhand.flags();
1121     if property_flags.contains(PropertyFlags::CREATES_STACKING_CONTEXT) {
1122         flags |= WillChangeBits::STACKING_CONTEXT;
1123     }
1124     if property_flags.contains(PropertyFlags::FIXPOS_CB) {
1125         flags |= WillChangeBits::FIXPOS_CB;
1126     }
1127     if property_flags.contains(PropertyFlags::ABSPOS_CB) {
1128         flags |= WillChangeBits::ABSPOS_CB;
1129     }
1130     flags
1133 fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
1134     let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
1135         Ok(id) => id,
1136         Err(..) => return WillChangeBits::empty(),
1137     };
1139     match id.as_shorthand() {
1140         Ok(shorthand) => shorthand
1141             .longhands()
1142             .fold(WillChangeBits::empty(), |flags, p| {
1143                 flags | change_bits_for_longhand(p)
1144             }),
1145         Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
1146         Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
1147     }
1150 impl Parse for WillChange {
1151     /// auto | <animateable-feature>#
1152     fn parse<'i, 't>(
1153         context: &ParserContext,
1154         input: &mut Parser<'i, 't>,
1155     ) -> Result<Self, ParseError<'i>> {
1156         if input
1157             .try_parse(|input| input.expect_ident_matching("auto"))
1158             .is_ok()
1159         {
1160             return Ok(Self::default());
1161         }
1163         let mut bits = WillChangeBits::empty();
1164         let custom_idents = input.parse_comma_separated(|i| {
1165             let location = i.current_source_location();
1166             let parser_ident = i.expect_ident()?;
1167             let ident = CustomIdent::from_ident(
1168                 location,
1169                 parser_ident,
1170                 &["will-change", "none", "all", "auto"],
1171             )?;
1173             if ident.0 == atom!("scroll-position") {
1174                 bits |= WillChangeBits::SCROLL;
1175             } else {
1176                 bits |= change_bits_for_maybe_property(&parser_ident, context);
1177             }
1178             Ok(ident)
1179         })?;
1181         Ok(Self {
1182             features: custom_idents.into(),
1183             bits,
1184         })
1185     }
1188 bitflags! {
1189     /// Values for the `touch-action` property.
1190     #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
1191     /// These constants match Gecko's `NS_STYLE_TOUCH_ACTION_*` constants.
1192     #[value_info(other_values = "auto,none,manipulation,pan-x,pan-y")]
1193     #[repr(C)]
1194     pub struct TouchAction: u8 {
1195         /// `none` variant
1196         const NONE = 1 << 0;
1197         /// `auto` variant
1198         const AUTO = 1 << 1;
1199         /// `pan-x` variant
1200         const PAN_X = 1 << 2;
1201         /// `pan-y` variant
1202         const PAN_Y = 1 << 3;
1203         /// `manipulation` variant
1204         const MANIPULATION = 1 << 4;
1205     }
1208 impl TouchAction {
1209     #[inline]
1210     /// Get default `touch-action` as `auto`
1211     pub fn auto() -> TouchAction {
1212         TouchAction::AUTO
1213     }
1216 impl ToCss for TouchAction {
1217     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1218     where
1219         W: Write,
1220     {
1221         match *self {
1222             TouchAction::NONE => dest.write_str("none"),
1223             TouchAction::AUTO => dest.write_str("auto"),
1224             TouchAction::MANIPULATION => dest.write_str("manipulation"),
1225             _ if self.contains(TouchAction::PAN_X | TouchAction::PAN_Y) => {
1226                 dest.write_str("pan-x pan-y")
1227             },
1228             _ if self.contains(TouchAction::PAN_X) => dest.write_str("pan-x"),
1229             _ if self.contains(TouchAction::PAN_Y) => dest.write_str("pan-y"),
1230             _ => panic!("invalid touch-action value"),
1231         }
1232     }
1235 impl Parse for TouchAction {
1236     fn parse<'i, 't>(
1237         _context: &ParserContext,
1238         input: &mut Parser<'i, 't>,
1239     ) -> Result<TouchAction, ParseError<'i>> {
1240         try_match_ident_ignore_ascii_case! { input,
1241             "auto" => Ok(TouchAction::AUTO),
1242             "none" => Ok(TouchAction::NONE),
1243             "manipulation" => Ok(TouchAction::MANIPULATION),
1244             "pan-x" => {
1245                 if input.try_parse(|i| i.expect_ident_matching("pan-y")).is_ok() {
1246                     Ok(TouchAction::PAN_X | TouchAction::PAN_Y)
1247                 } else {
1248                     Ok(TouchAction::PAN_X)
1249                 }
1250             },
1251             "pan-y" => {
1252                 if input.try_parse(|i| i.expect_ident_matching("pan-x")).is_ok() {
1253                     Ok(TouchAction::PAN_X | TouchAction::PAN_Y)
1254                 } else {
1255                     Ok(TouchAction::PAN_Y)
1256                 }
1257             },
1258         }
1259     }
1262 bitflags! {
1263     #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
1264     #[value_info(other_values = "none,strict,content,size,layout,paint")]
1265     #[repr(C)]
1266     /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
1267     pub struct Contain: u8 {
1268         /// `none` variant, just for convenience.
1269         const NONE = 0;
1270         /// 'size' variant, turns on size containment
1271         const SIZE = 1 << 0;
1272         /// `layout` variant, turns on layout containment
1273         const LAYOUT = 1 << 1;
1274         /// `paint` variant, turns on paint containment
1275         const PAINT = 1 << 2;
1276         /// `strict` variant, turns on all types of containment
1277         const STRICT = 1 << 3;
1278         /// 'content' variant, turns on layout and paint containment
1279         const CONTENT = 1 << 4;
1280         /// variant with all the bits that contain: strict turns on
1281         const STRICT_BITS = Contain::LAYOUT.bits | Contain::PAINT.bits | Contain::SIZE.bits;
1282         /// variant with all the bits that contain: content turns on
1283         const CONTENT_BITS = Contain::LAYOUT.bits | Contain::PAINT.bits;
1284     }
1287 impl ToCss for Contain {
1288     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1289     where
1290         W: Write,
1291     {
1292         if self.is_empty() {
1293             return dest.write_str("none");
1294         }
1295         if self.contains(Contain::STRICT) {
1296             return dest.write_str("strict");
1297         }
1298         if self.contains(Contain::CONTENT) {
1299             return dest.write_str("content");
1300         }
1302         let mut has_any = false;
1303         macro_rules! maybe_write_value {
1304             ($ident:path => $str:expr) => {
1305                 if self.contains($ident) {
1306                     if has_any {
1307                         dest.write_str(" ")?;
1308                     }
1309                     has_any = true;
1310                     dest.write_str($str)?;
1311                 }
1312             };
1313         }
1314         maybe_write_value!(Contain::SIZE => "size");
1315         maybe_write_value!(Contain::LAYOUT => "layout");
1316         maybe_write_value!(Contain::PAINT => "paint");
1318         debug_assert!(has_any);
1319         Ok(())
1320     }
1323 impl Parse for Contain {
1324     /// none | strict | content | [ size || layout || paint ]
1325     fn parse<'i, 't>(
1326         _context: &ParserContext,
1327         input: &mut Parser<'i, 't>,
1328     ) -> Result<Contain, ParseError<'i>> {
1329         let mut result = Contain::empty();
1330         while let Ok(name) = input.try_parse(|i| i.expect_ident_cloned()) {
1331             let flag = match_ignore_ascii_case! { &name,
1332                 "size" => Some(Contain::SIZE),
1333                 "layout" => Some(Contain::LAYOUT),
1334                 "paint" => Some(Contain::PAINT),
1335                 "strict" if result.is_empty() => return Ok(Contain::STRICT | Contain::STRICT_BITS),
1336                 "content" if result.is_empty() => return Ok(Contain::CONTENT | Contain::CONTENT_BITS),
1337                 "none" if result.is_empty() => return Ok(result),
1338                 _ => None
1339             };
1341             let flag = match flag {
1342                 Some(flag) if !result.contains(flag) => flag,
1343                 _ => {
1344                     return Err(
1345                         input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name))
1346                     );
1347                 },
1348             };
1349             result.insert(flag);
1350         }
1352         if !result.is_empty() {
1353             Ok(result)
1354         } else {
1355             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1356         }
1357     }
1360 /// A specified value for the `perspective` property.
1361 pub type Perspective = GenericPerspective<NonNegativeLength>;
1363 /// A given transition property, that is either `All`, a longhand or shorthand
1364 /// property, or an unsupported or custom property.
1365 #[derive(
1366     Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
1368 pub enum TransitionProperty {
1369     /// A shorthand.
1370     Shorthand(ShorthandId),
1371     /// A longhand transitionable property.
1372     Longhand(LonghandId),
1373     /// A custom property.
1374     Custom(CustomPropertyName),
1375     /// Unrecognized property which could be any non-transitionable, custom property, or
1376     /// unknown property.
1377     Unsupported(CustomIdent),
1380 impl ToCss for TransitionProperty {
1381     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1382     where
1383         W: Write,
1384     {
1385         use crate::values::serialize_atom_name;
1386         match *self {
1387             TransitionProperty::Shorthand(ref s) => s.to_css(dest),
1388             TransitionProperty::Longhand(ref l) => l.to_css(dest),
1389             TransitionProperty::Custom(ref name) => {
1390                 dest.write_str("--")?;
1391                 serialize_atom_name(name, dest)
1392             },
1393             TransitionProperty::Unsupported(ref i) => i.to_css(dest),
1394         }
1395     }
1398 impl Parse for TransitionProperty {
1399     fn parse<'i, 't>(
1400         context: &ParserContext,
1401         input: &mut Parser<'i, 't>,
1402     ) -> Result<Self, ParseError<'i>> {
1403         let location = input.current_source_location();
1404         let ident = input.expect_ident()?;
1406         let id = match PropertyId::parse_ignoring_rule_type(&ident, context) {
1407             Ok(id) => id,
1408             Err(..) => {
1409                 return Ok(TransitionProperty::Unsupported(CustomIdent::from_ident(
1410                     location,
1411                     ident,
1412                     &["none"],
1413                 )?));
1414             },
1415         };
1417         Ok(match id.as_shorthand() {
1418             Ok(s) => TransitionProperty::Shorthand(s),
1419             Err(longhand_or_custom) => match longhand_or_custom {
1420                 PropertyDeclarationId::Longhand(id) => TransitionProperty::Longhand(id),
1421                 PropertyDeclarationId::Custom(custom) => TransitionProperty::Custom(custom.clone()),
1422             },
1423         })
1424     }
1427 impl SpecifiedValueInfo for TransitionProperty {
1428     fn collect_completion_keywords(f: KeywordsCollectFn) {
1429         // `transition-property` can actually accept all properties and
1430         // arbitrary identifiers, but `all` is a special one we'd like
1431         // to list.
1432         f(&["all"]);
1433     }
1436 impl TransitionProperty {
1437     /// Returns `all`.
1438     #[inline]
1439     pub fn all() -> Self {
1440         TransitionProperty::Shorthand(ShorthandId::All)
1441     }
1443     /// Convert TransitionProperty to nsCSSPropertyID.
1444     #[cfg(feature = "gecko")]
1445     pub fn to_nscsspropertyid(
1446         &self,
1447     ) -> Result<crate::gecko_bindings::structs::nsCSSPropertyID, ()> {
1448         Ok(match *self {
1449             TransitionProperty::Shorthand(ShorthandId::All) => {
1450                 crate::gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties
1451             },
1452             TransitionProperty::Shorthand(ref id) => id.to_nscsspropertyid(),
1453             TransitionProperty::Longhand(ref id) => id.to_nscsspropertyid(),
1454             TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => return Err(()),
1455         })
1456     }
1459 #[allow(missing_docs)]
1460 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1461 #[derive(
1462     Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1464 /// https://drafts.csswg.org/css-box/#propdef-float
1465 pub enum Float {
1466     Left,
1467     Right,
1468     None,
1469     // https://drafts.csswg.org/css-logical-props/#float-clear
1470     InlineStart,
1471     InlineEnd,
1474 #[allow(missing_docs)]
1475 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1476 #[derive(
1477     Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1479 /// https://drafts.csswg.org/css-box/#propdef-clear
1480 pub enum Clear {
1481     None,
1482     Left,
1483     Right,
1484     Both,
1485     // https://drafts.csswg.org/css-logical-props/#float-clear
1486     InlineStart,
1487     InlineEnd,
1490 /// https://drafts.csswg.org/css-ui/#propdef-resize
1491 #[allow(missing_docs)]
1492 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1493 #[derive(
1494     Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1496 pub enum Resize {
1497     None,
1498     Both,
1499     Horizontal,
1500     Vertical,
1501     // https://drafts.csswg.org/css-logical-1/#resize
1502     Inline,
1503     Block,
1506 /// The value for the `appearance` property.
1508 /// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
1509 #[allow(missing_docs)]
1510 #[derive(
1511     Clone,
1512     Copy,
1513     Debug,
1514     Eq,
1515     Hash,
1516     MallocSizeOf,
1517     Parse,
1518     PartialEq,
1519     SpecifiedValueInfo,
1520     ToCss,
1521     ToComputedValue,
1522     ToResolvedValue,
1523     ToShmem,
1525 #[repr(u8)]
1526 pub enum Appearance {
1527     /// No appearance at all.
1528     None,
1529     /// Default appearance for the element.
1530     ///
1531     /// This value doesn't make sense for -moz-default-appearance, but we don't bother to guard
1532     /// against parsing it.
1533     Auto,
1534     /// A typical dialog button.
1535     Button,
1536     /// Various arrows that go in buttons
1537     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1538     ButtonArrowDown,
1539     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1540     ButtonArrowNext,
1541     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1542     ButtonArrowPrevious,
1543     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1544     ButtonArrowUp,
1545     /// The focus outline box inside of a button.
1546     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1547     ButtonFocus,
1548     /// A dual toolbar button (e.g., a Back button with a dropdown)
1549     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1550     Dualbutton,
1551     /// A groupbox.
1552     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1553     Groupbox,
1554     /// List boxes.
1555     Listbox,
1556     /// Menu Bar background
1557     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1558     Menubar,
1559     /// <menu> and <menuitem> appearances
1560     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1561     Menuitem,
1562     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1563     Checkmenuitem,
1564     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1565     Radiomenuitem,
1566     /// For text on non-iconic menuitems only
1567     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1568     Menuitemtext,
1569     /// A dropdown list.
1570     Menulist,
1571     /// The dropdown button(s) that open up a dropdown list.
1572     MenulistButton,
1573     /// The text part of a dropdown list, to left of button.
1574     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1575     MenulistText,
1576     /// Menu Popup background.
1577     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1578     Menupopup,
1579     /// menu checkbox/radio appearances
1580     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1581     Menucheckbox,
1582     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1583     Menuradio,
1584     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1585     Menuseparator,
1586     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1587     Menuarrow,
1588     /// An image in the menu gutter, like in bookmarks or history.
1589     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1590     Menuimage,
1591     /// A horizontal meter bar.
1592     #[parse(aliases = "meterbar")]
1593     Meter,
1594     /// The meter bar's meter indicator.
1595     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1596     Meterchunk,
1597     /// The "arrowed" part of the dropdown button that open up a dropdown list.
1598     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1599     MozMenulistArrowButton,
1600     /// For HTML's <input type=number>
1601     NumberInput,
1602     /// A horizontal progress bar.
1603     #[parse(aliases = "progressbar")]
1604     ProgressBar,
1605     /// The progress bar's progress indicator
1606     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1607     Progresschunk,
1608     /// A checkbox element.
1609     Checkbox,
1610     /// A radio element within a radio group.
1611     Radio,
1612     /// A generic container that always repaints on state changes. This is a
1613     /// hack to make XUL checkboxes and radio buttons work.
1614     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1615     CheckboxContainer,
1616     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1617     RadioContainer,
1618     /// The label part of a checkbox or radio button, used for painting a focus
1619     /// outline.
1620     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1621     CheckboxLabel,
1622     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1623     RadioLabel,
1624     /// nsRangeFrame and its subparts
1625     Range,
1626     RangeThumb, // FIXME: This should not be exposed to content.
1627     /// The resizer background area in a status bar for the resizer widget in
1628     /// the corner of a window.
1629     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1630     Resizerpanel,
1631     /// The resizer itself.
1632     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1633     Resizer,
1634     /// A scrollbar.
1635     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1636     Scrollbar,
1637     /// A small scrollbar.
1638     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1639     ScrollbarSmall,
1640     /// The scrollbar slider
1641     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1642     ScrollbarHorizontal,
1643     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1644     ScrollbarVertical,
1645     /// A scrollbar button (up/down/left/right).
1646     /// Keep these in order (some code casts these values to `int` in order to
1647     /// compare them against each other).
1648     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1649     ScrollbarbuttonUp,
1650     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1651     ScrollbarbuttonDown,
1652     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1653     ScrollbarbuttonLeft,
1654     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1655     ScrollbarbuttonRight,
1656     /// The scrollbar thumb.
1657     ScrollbarthumbHorizontal,
1658     ScrollbarthumbVertical,
1659     /// The scrollbar track.
1660     ScrollbartrackHorizontal,
1661     ScrollbartrackVertical,
1662     /// The scroll corner
1663     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1664     Scrollcorner,
1665     /// A searchfield.
1666     Searchfield,
1667     /// A separator.  Can be horizontal or vertical.
1668     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1669     Separator,
1670     /// A spin control (up/down control for time/date pickers).
1671     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1672     Spinner,
1673     /// The up button of a spin control.
1674     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1675     SpinnerUpbutton,
1676     /// The down button of a spin control.
1677     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1678     SpinnerDownbutton,
1679     /// The textfield of a spin control
1680     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1681     SpinnerTextfield,
1682     /// A splitter.  Can be horizontal or vertical.
1683     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1684     Splitter,
1685     /// A status bar in a main application window.
1686     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1687     Statusbar,
1688     /// A single pane of a status bar.
1689     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1690     Statusbarpanel,
1691     /// A single tab in a tab widget.
1692     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1693     Tab,
1694     /// A single pane (inside the tabpanels container).
1695     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1696     Tabpanel,
1697     /// The tab panels container.
1698     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1699     Tabpanels,
1700     /// The tabs scroll arrows (left/right).
1701     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1702     TabScrollArrowBack,
1703     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1704     TabScrollArrowForward,
1705     /// A multi-line text field, e.g. HTML <textarea>.
1706     #[parse(aliases = "textfield-multiline")]
1707     Textarea,
1708     /// A single-line text field, e.g. HTML <input type=text>.
1709     Textfield,
1710     /// A toolbar in an application window.
1711     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1712     Toolbar,
1713     /// A single toolbar button (with no associated dropdown).
1714     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1715     Toolbarbutton,
1716     /// The dropdown portion of a toolbar button
1717     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1718     ToolbarbuttonDropdown,
1719     /// The gripper for a toolbar.
1720     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1721     Toolbargripper,
1722     /// The toolbox that contains the toolbars.
1723     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1724     Toolbox,
1725     /// A tooltip.
1726     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1727     Tooltip,
1728     /// A listbox or tree widget header
1729     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1730     Treeheader,
1731     /// An individual header cell
1732     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1733     Treeheadercell,
1734     /// The sort arrow for a header.
1735     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1736     Treeheadersortarrow,
1737     /// A tree item.
1738     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1739     Treeitem,
1740     /// A tree widget branch line
1741     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1742     Treeline,
1743     /// A tree widget twisty.
1744     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1745     Treetwisty,
1746     /// Open tree widget twisty.
1747     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1748     Treetwistyopen,
1749     /// A tree widget.
1750     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1751     Treeview,
1752     /// Window and dialog backgrounds.
1753     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1754     Window,
1755     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1756     Dialog,
1758     /// Vista Rebars.
1759     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1760     MozWinCommunicationsToolbox,
1761     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1762     MozWinMediaToolbox,
1763     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1764     MozWinBrowsertabbarToolbox,
1765     /// Vista glass.
1766     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1767     MozWinGlass,
1768     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1769     MozWinBorderlessGlass,
1770     /// -moz-apperance style used in setting proper glass margins.
1771     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1772     MozWinExcludeGlass,
1774     /// Titlebar elements on the Mac.
1775     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1776     MozMacFullscreenButton,
1777     /// Mac help button.
1778     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1779     MozMacHelpButton,
1781     /// Windows themed window frame elements.
1782     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1783     MozWindowButtonBox,
1784     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1785     MozWindowButtonBoxMaximized,
1786     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1787     MozWindowButtonClose,
1788     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1789     MozWindowButtonMaximize,
1790     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1791     MozWindowButtonMinimize,
1792     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1793     MozWindowButtonRestore,
1794     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1795     MozWindowFrameBottom,
1796     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1797     MozWindowFrameLeft,
1798     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1799     MozWindowFrameRight,
1800     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1801     MozWindowTitlebar,
1802     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1803     MozWindowTitlebarMaximized,
1805     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1806     MozGtkInfoBar,
1807     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1808     MozMacActiveSourceListSelection,
1809     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1810     MozMacDisclosureButtonClosed,
1811     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1812     MozMacDisclosureButtonOpen,
1813     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1814     MozMacSourceList,
1815     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1816     MozMacSourceListSelection,
1817     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1818     MozMacVibrancyDark,
1819     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1820     MozMacVibrancyLight,
1821     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1822     MozMacVibrantTitlebarDark,
1823     #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
1824     MozMacVibrantTitlebarLight,
1826     /// A non-disappearing scrollbar.
1827     #[css(skip)]
1828     ScrollbarNonDisappearing,
1830     /// A themed focus outline (for outline:auto).
1831     ///
1832     /// This isn't exposed to CSS at all, just here for convenience.
1833     #[css(skip)]
1834     FocusOutline,
1836     /// A dummy variant that should be last to let the GTK widget do hackery.
1837     #[css(skip)]
1838     Count,
1841 /// A kind of break between two boxes.
1843 /// https://drafts.csswg.org/css-break/#break-between
1844 #[allow(missing_docs)]
1845 #[derive(
1846     Clone,
1847     Copy,
1848     Debug,
1849     Eq,
1850     Hash,
1851     MallocSizeOf,
1852     Parse,
1853     PartialEq,
1854     SpecifiedValueInfo,
1855     ToCss,
1856     ToComputedValue,
1857     ToResolvedValue,
1858     ToShmem,
1860 #[repr(u8)]
1861 pub enum BreakBetween {
1862     Always,
1863     Auto,
1864     Page,
1865     Avoid,
1866     Left,
1867     Right,
1870 impl BreakBetween {
1871     /// Parse a legacy break-between value for `page-break-*`.
1872     ///
1873     /// See https://drafts.csswg.org/css-break/#page-break-properties.
1874     #[inline]
1875     pub fn parse_legacy<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
1876         let location = input.current_source_location();
1877         let ident = input.expect_ident()?;
1878         let break_value = match BreakBetween::from_ident(ident) {
1879             Ok(v) => v,
1880             Err(()) => {
1881                 return Err(location
1882                     .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
1883             },
1884         };
1885         match break_value {
1886             BreakBetween::Always => Ok(BreakBetween::Page),
1887             BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1888                 Ok(break_value)
1889             },
1890             BreakBetween::Page => {
1891                 Err(location
1892                     .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
1893             },
1894         }
1895     }
1897     /// Serialize a legacy break-between value for `page-break-*`.
1898     ///
1899     /// See https://drafts.csswg.org/css-break/#page-break-properties.
1900     pub fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1901     where
1902         W: Write,
1903     {
1904         match *self {
1905             BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1906                 self.to_css(dest)
1907             },
1908             BreakBetween::Page => dest.write_str("always"),
1909             BreakBetween::Always => Ok(()),
1910         }
1911     }
1914 /// A kind of break within a box.
1916 /// https://drafts.csswg.org/css-break/#break-within
1917 #[allow(missing_docs)]
1918 #[derive(
1919     Clone,
1920     Copy,
1921     Debug,
1922     Eq,
1923     Hash,
1924     MallocSizeOf,
1925     Parse,
1926     PartialEq,
1927     SpecifiedValueInfo,
1928     ToCss,
1929     ToComputedValue,
1930     ToResolvedValue,
1931     ToShmem,
1933 #[repr(u8)]
1934 pub enum BreakWithin {
1935     Auto,
1936     Avoid,
1939 /// The value for the `overflow-x` / `overflow-y` properties.
1940 #[allow(missing_docs)]
1941 #[derive(
1942     Clone,
1943     Copy,
1944     Debug,
1945     Eq,
1946     Hash,
1947     MallocSizeOf,
1948     Parse,
1949     PartialEq,
1950     SpecifiedValueInfo,
1951     ToCss,
1952     ToComputedValue,
1953     ToResolvedValue,
1954     ToShmem,
1956 #[repr(u8)]
1957 pub enum Overflow {
1958     Visible,
1959     Hidden,
1960     Scroll,
1961     Auto,
1962     #[cfg(feature = "gecko")]
1963     MozHiddenUnscrollable,