1pub use crate::logical_geometry::WritingModeProperty;
8use crate::parser::{Parse, ParserContext};
9use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};
10use crate::values::generics::box_::{
11 GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign,
12 VerticalAlignKeyword,
13};
14use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
15use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumberOrPercentage};
16use crate::values::CustomIdent;
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#[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 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#[allow(missing_docs)]
122#[allow(non_upper_case_globals)]
123impl Display {
124 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 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 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 #[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 #[inline]
231 const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {
232 Self((outside as u16) << Self::OUTSIDE_SHIFT | inside as u16)
233 }
234
235 #[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 #[inline]
247 pub fn inside(&self) -> DisplayInside {
248 DisplayInside::from_u16(self.0 & Self::INSIDE_MASK).unwrap()
249 }
250
251 #[inline]
253 pub fn outside(&self) -> DisplayOutside {
254 DisplayOutside::from_u16((self.0 & Self::OUTSIDE_MASK) >> Self::OUTSIDE_SHIFT).unwrap()
255 }
256
257 #[inline]
259 pub const fn to_u16(&self) -> u16 {
260 self.0
261 }
262
263 #[inline]
265 pub fn is_inline_flow(&self) -> bool {
266 self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow
267 }
268
269 #[inline]
271 pub const fn is_list_item(&self) -> bool {
272 (self.0 & Self::LIST_ITEM_MASK) != 0
273 }
274
275 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 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
298impl Display {
300 #[inline]
302 pub fn inline() -> Self {
303 Display::Inline
304 }
305
306 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 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 pub fn equivalent_block_display(&self, is_root_element: bool) -> Self {
336 if is_root_element && (self.is_contents() || self.is_list_item()) {
338 return Display::Block;
339 }
340
341 match self.outside() {
342 DisplayOutside::Inline => {
343 let inside = match self.inside() {
344 DisplayInside::FlowRoot => DisplayInside::Flow,
347 inside => inside,
348 };
349 Display::from3(DisplayOutside::Block, inside, self.is_list_item())
350 },
351 DisplayOutside::Block | DisplayOutside::None => *self,
352 _ => Display::Block,
353 }
354 }
355
356 #[cfg(feature = "gecko")]
359 pub fn inlinify(&self) -> Self {
360 match self.outside() {
361 DisplayOutside::Block => {
362 let inside = match self.inside() {
363 DisplayInside::Flow => DisplayInside::FlowRoot,
366 inside => inside,
367 };
368 Display::from3(DisplayOutside::Inline, inside, self.is_list_item())
369 },
370 _ => *self,
371 }
372 }
373
374 #[inline]
376 pub fn is_contents(&self) -> bool {
377 match *self {
378 Display::Contents => true,
379 _ => false,
380 }
381 }
382
383 #[inline]
385 pub fn is_none(&self) -> bool {
386 *self == Display::None
387 }
388}
389
390enum DisplayKeyword {
391 Full(Display),
392 Inside(DisplayInside),
393 Outside(DisplayOutside),
394 ListItem,
395}
396
397impl DisplayKeyword {
398 fn parse<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
399 use self::DisplayKeyword::*;
400 Ok(try_match_ident_ignore_ascii_case! { input,
401 "none" => Full(Display::None),
402 "contents" => Full(Display::Contents),
403 "inline-block" => Full(Display::InlineBlock),
404 "inline-table" => Full(Display::InlineTable),
405 "-webkit-flex" => Full(Display::Flex),
406 "inline-flex" | "-webkit-inline-flex" => Full(Display::InlineFlex),
407 "inline-grid" if grid_enabled() => Full(Display::InlineGrid),
408 "table-caption" => Full(Display::TableCaption),
409 "table-row-group" => Full(Display::TableRowGroup),
410 "table-header-group" => Full(Display::TableHeaderGroup),
411 "table-footer-group" => Full(Display::TableFooterGroup),
412 "table-column" => Full(Display::TableColumn),
413 "table-column-group" => Full(Display::TableColumnGroup),
414 "table-row" => Full(Display::TableRow),
415 "table-cell" => Full(Display::TableCell),
416 #[cfg(feature = "gecko")]
417 "ruby-base" => Full(Display::RubyBase),
418 #[cfg(feature = "gecko")]
419 "ruby-base-container" => Full(Display::RubyBaseContainer),
420 #[cfg(feature = "gecko")]
421 "ruby-text" => Full(Display::RubyText),
422 #[cfg(feature = "gecko")]
423 "ruby-text-container" => Full(Display::RubyTextContainer),
424 #[cfg(feature = "gecko")]
425 "-webkit-box" => Full(Display::WebkitBox),
426 #[cfg(feature = "gecko")]
427 "-webkit-inline-box" => Full(Display::WebkitInlineBox),
428
429 "block" => Outside(DisplayOutside::Block),
432 "inline" => Outside(DisplayOutside::Inline),
433
434 "list-item" => ListItem,
435
436 "flow" => Inside(DisplayInside::Flow),
439 "flex" => Inside(DisplayInside::Flex),
440 "flow-root" => Inside(DisplayInside::FlowRoot),
441 "table" => Inside(DisplayInside::Table),
442 "grid" if grid_enabled() => Inside(DisplayInside::Grid),
443 #[cfg(feature = "gecko")]
444 "ruby" => Inside(DisplayInside::Ruby),
445 })
446 }
447}
448
449impl ToCss for Display {
450 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
451 where
452 W: fmt::Write,
453 {
454 let outside = self.outside();
455 let inside = self.inside();
456 match *self {
457 Display::Block | Display::Inline => outside.to_css(dest),
458 Display::InlineBlock => dest.write_str("inline-block"),
459 #[cfg(feature = "gecko")]
460 Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
461 Display::TableCaption => dest.write_str("table-caption"),
462 _ => match (outside, inside) {
463 (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
464 (DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"),
465 (DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"),
466 #[cfg(feature = "gecko")]
467 (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
468 (_, inside) => {
469 if self.is_list_item() {
470 if outside != DisplayOutside::Block {
471 outside.to_css(dest)?;
472 dest.write_char(' ')?;
473 }
474 if inside != DisplayInside::Flow {
475 inside.to_css(dest)?;
476 dest.write_char(' ')?;
477 }
478 dest.write_str("list-item")
479 } else {
480 inside.to_css(dest)
481 }
482 },
483 },
484 }
485 }
486}
487
488impl Parse for Display {
489 fn parse<'i, 't>(
490 _: &ParserContext,
491 input: &mut Parser<'i, 't>,
492 ) -> Result<Display, ParseError<'i>> {
493 let mut got_list_item = false;
494 let mut inside = None;
495 let mut outside = None;
496 match DisplayKeyword::parse(input)? {
497 DisplayKeyword::Full(d) => return Ok(d),
498 DisplayKeyword::Outside(o) => {
499 outside = Some(o);
500 },
501 DisplayKeyword::Inside(i) => {
502 inside = Some(i);
503 },
504 DisplayKeyword::ListItem => {
505 got_list_item = true;
506 },
507 };
508
509 while let Ok(kw) = input.try_parse(DisplayKeyword::parse) {
510 match kw {
511 DisplayKeyword::ListItem if !got_list_item => {
512 got_list_item = true;
513 },
514 DisplayKeyword::Outside(o) if outside.is_none() => {
515 outside = Some(o);
516 },
517 DisplayKeyword::Inside(i) if inside.is_none() => {
518 inside = Some(i);
519 },
520 _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
521 }
522 }
523
524 let inside = inside.unwrap_or(DisplayInside::Flow);
525 let outside = outside.unwrap_or_else(|| inside.default_display_outside());
526 if got_list_item && !inside.is_valid_for_list_item() {
527 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
528 }
529
530 return Ok(Display::from3(outside, inside, got_list_item));
531 }
532}
533
534impl SpecifiedValueInfo for Display {
535 fn collect_completion_keywords(f: KeywordsCollectFn) {
536 f(&[
537 "block",
538 "contents",
539 "flex",
540 "flow-root",
541 "flow-root list-item",
542 "grid",
543 "inline",
544 "inline-block",
545 "inline-flex",
546 "inline-grid",
547 "inline-table",
548 "inline list-item",
549 "inline flow-root list-item",
550 "list-item",
551 "none",
552 "block ruby",
553 "ruby",
554 "ruby-base",
555 "ruby-base-container",
556 "ruby-text",
557 "ruby-text-container",
558 "table",
559 "table-caption",
560 "table-cell",
561 "table-column",
562 "table-column-group",
563 "table-footer-group",
564 "table-header-group",
565 "table-row",
566 "table-row-group",
567 "-webkit-box",
568 "-webkit-inline-box",
569 ]);
570 }
571}
572
573pub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;
575
576pub type LineClamp = GenericLineClamp<Integer>;
578
579pub type VerticalAlign = GenericVerticalAlign<LengthPercentage>;
581
582impl Parse for VerticalAlign {
583 fn parse<'i, 't>(
584 context: &ParserContext,
585 input: &mut Parser<'i, 't>,
586 ) -> Result<Self, ParseError<'i>> {
587 if let Ok(lp) =
588 input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))
589 {
590 return Ok(GenericVerticalAlign::Length(lp));
591 }
592
593 Ok(GenericVerticalAlign::Keyword(VerticalAlignKeyword::parse(
594 input,
595 )?))
596 }
597}
598
599#[derive(
602 Clone,
603 Copy,
604 Debug,
605 Eq,
606 Hash,
607 MallocSizeOf,
608 Parse,
609 PartialEq,
610 SpecifiedValueInfo,
611 ToCss,
612 ToShmem,
613 ToComputedValue,
614 ToResolvedValue,
615)]
616#[repr(u8)]
617pub enum BaselineSource {
618 Auto,
620 First,
622 Last,
624}
625
626#[allow(missing_docs)]
628#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
629#[derive(
630 Clone,
631 Copy,
632 Debug,
633 Eq,
634 MallocSizeOf,
635 Parse,
636 PartialEq,
637 SpecifiedValueInfo,
638 ToComputedValue,
639 ToCss,
640 ToResolvedValue,
641 ToShmem,
642)]
643#[repr(u8)]
644pub enum ScrollSnapAxis {
645 X,
646 Y,
647 Block,
648 Inline,
649 Both,
650}
651
652#[allow(missing_docs)]
654#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
655#[derive(
656 Clone,
657 Copy,
658 Debug,
659 Eq,
660 MallocSizeOf,
661 Parse,
662 PartialEq,
663 SpecifiedValueInfo,
664 ToComputedValue,
665 ToCss,
666 ToResolvedValue,
667 ToShmem,
668)]
669#[repr(u8)]
670pub enum ScrollSnapStrictness {
671 #[css(skip)]
672 None, Mandatory,
674 Proximity,
675}
676
677#[allow(missing_docs)]
679#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
680#[derive(
681 Clone,
682 Copy,
683 Debug,
684 Eq,
685 MallocSizeOf,
686 PartialEq,
687 SpecifiedValueInfo,
688 ToComputedValue,
689 ToResolvedValue,
690 ToShmem,
691)]
692#[repr(C)]
693pub struct ScrollSnapType {
694 axis: ScrollSnapAxis,
695 strictness: ScrollSnapStrictness,
696}
697
698impl ScrollSnapType {
699 #[inline]
701 pub fn none() -> Self {
702 Self {
703 axis: ScrollSnapAxis::Both,
704 strictness: ScrollSnapStrictness::None,
705 }
706 }
707}
708
709impl Parse for ScrollSnapType {
710 fn parse<'i, 't>(
712 _context: &ParserContext,
713 input: &mut Parser<'i, 't>,
714 ) -> Result<Self, ParseError<'i>> {
715 if input
716 .try_parse(|input| input.expect_ident_matching("none"))
717 .is_ok()
718 {
719 return Ok(ScrollSnapType::none());
720 }
721
722 let axis = ScrollSnapAxis::parse(input)?;
723 let strictness = input
724 .try_parse(ScrollSnapStrictness::parse)
725 .unwrap_or(ScrollSnapStrictness::Proximity);
726 Ok(Self { axis, strictness })
727 }
728}
729
730impl ToCss for ScrollSnapType {
731 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
732 where
733 W: Write,
734 {
735 if self.strictness == ScrollSnapStrictness::None {
736 return dest.write_str("none");
737 }
738 self.axis.to_css(dest)?;
739 if self.strictness != ScrollSnapStrictness::Proximity {
740 dest.write_char(' ')?;
741 self.strictness.to_css(dest)?;
742 }
743 Ok(())
744 }
745}
746
747#[allow(missing_docs)]
749#[derive(
750 Clone,
751 Copy,
752 Debug,
753 Eq,
754 FromPrimitive,
755 Hash,
756 MallocSizeOf,
757 Parse,
758 PartialEq,
759 SpecifiedValueInfo,
760 ToComputedValue,
761 ToCss,
762 ToResolvedValue,
763 ToShmem,
764)]
765#[repr(u8)]
766pub enum ScrollSnapAlignKeyword {
767 None,
768 Start,
769 End,
770 Center,
771}
772
773#[allow(missing_docs)]
775#[derive(
776 Clone,
777 Copy,
778 Debug,
779 Eq,
780 MallocSizeOf,
781 PartialEq,
782 SpecifiedValueInfo,
783 ToComputedValue,
784 ToResolvedValue,
785 ToShmem,
786)]
787#[repr(C)]
788pub struct ScrollSnapAlign {
789 block: ScrollSnapAlignKeyword,
790 inline: ScrollSnapAlignKeyword,
791}
792
793impl ScrollSnapAlign {
794 #[inline]
796 pub fn none() -> Self {
797 ScrollSnapAlign {
798 block: ScrollSnapAlignKeyword::None,
799 inline: ScrollSnapAlignKeyword::None,
800 }
801 }
802}
803
804impl Parse for ScrollSnapAlign {
805 fn parse<'i, 't>(
807 _context: &ParserContext,
808 input: &mut Parser<'i, 't>,
809 ) -> Result<ScrollSnapAlign, ParseError<'i>> {
810 let block = ScrollSnapAlignKeyword::parse(input)?;
811 let inline = input
812 .try_parse(ScrollSnapAlignKeyword::parse)
813 .unwrap_or(block);
814 Ok(ScrollSnapAlign { block, inline })
815 }
816}
817
818impl ToCss for ScrollSnapAlign {
819 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
820 where
821 W: Write,
822 {
823 self.block.to_css(dest)?;
824 if self.block != self.inline {
825 dest.write_char(' ')?;
826 self.inline.to_css(dest)?;
827 }
828 Ok(())
829 }
830}
831
832#[allow(missing_docs)]
833#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
834#[derive(
835 Clone,
836 Copy,
837 Debug,
838 Eq,
839 MallocSizeOf,
840 Parse,
841 PartialEq,
842 SpecifiedValueInfo,
843 ToComputedValue,
844 ToCss,
845 ToResolvedValue,
846 ToShmem,
847)]
848#[repr(u8)]
849pub enum ScrollSnapStop {
850 Normal,
851 Always,
852}
853
854#[allow(missing_docs)]
855#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
856#[derive(
857 Clone,
858 Copy,
859 Debug,
860 Eq,
861 MallocSizeOf,
862 Parse,
863 PartialEq,
864 SpecifiedValueInfo,
865 ToComputedValue,
866 ToCss,
867 ToResolvedValue,
868 ToShmem,
869)]
870#[repr(u8)]
871pub enum OverscrollBehavior {
872 Auto,
873 Contain,
874 None,
875}
876
877#[allow(missing_docs)]
878#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
879#[derive(
880 Clone,
881 Copy,
882 Debug,
883 Eq,
884 MallocSizeOf,
885 Parse,
886 PartialEq,
887 SpecifiedValueInfo,
888 ToComputedValue,
889 ToCss,
890 ToResolvedValue,
891 ToShmem,
892)]
893#[repr(u8)]
894pub enum OverflowAnchor {
895 Auto,
896 None,
897}
898
899#[allow(missing_docs)]
900#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
901#[derive(
902 Clone,
903 Copy,
904 Debug,
905 Eq,
906 MallocSizeOf,
907 Parse,
908 PartialEq,
909 SpecifiedValueInfo,
910 ToComputedValue,
911 ToCss,
912 ToResolvedValue,
913 ToShmem,
914)]
915#[repr(u8)]
916pub enum OverflowClipBox {
917 PaddingBox,
918 ContentBox,
919}
920
921#[derive(
922 Clone,
923 Debug,
924 Default,
925 MallocSizeOf,
926 PartialEq,
927 SpecifiedValueInfo,
928 ToComputedValue,
929 ToCss,
930 ToResolvedValue,
931 ToShmem,
932)]
933#[css(comma)]
934#[repr(C)]
935pub struct WillChange {
942 #[css(iterable, if_empty = "auto")]
947 features: crate::OwnedSlice<CustomIdent>,
948 #[css(skip)]
951 pub bits: WillChangeBits,
952}
953
954impl WillChange {
955 #[inline]
956 pub fn auto() -> Self {
958 Self::default()
959 }
960}
961
962#[derive(
964 Clone,
965 Copy,
966 Debug,
967 Default,
968 Eq,
969 MallocSizeOf,
970 PartialEq,
971 SpecifiedValueInfo,
972 ToComputedValue,
973 ToResolvedValue,
974 ToShmem,
975)]
976#[repr(C)]
977pub struct WillChangeBits(u16);
978bitflags! {
979 impl WillChangeBits: u16 {
980 const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;
983 const TRANSFORM = 1 << 1;
985 const SCROLL = 1 << 2;
987 const CONTAIN = 1 << 3;
989 const OPACITY = 1 << 4;
991 const PERSPECTIVE = 1 << 5;
993 const Z_INDEX = 1 << 6;
995 const FIXPOS_CB_NON_SVG = 1 << 7;
998 const POSITION = 1 << 8;
1000 const VIEW_TRANSITION_NAME = 1 << 9;
1002 const BACKDROP_ROOT = 1 << 10;
1005 }
1006}
1007
1008fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
1009 match longhand {
1010 LonghandId::Opacity => WillChangeBits::OPACITY | WillChangeBits::BACKDROP_ROOT,
1011 LonghandId::Contain => WillChangeBits::CONTAIN,
1012 LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
1013 LonghandId::Position => {
1014 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
1015 },
1016 LonghandId::ZIndex => WillChangeBits::Z_INDEX,
1017 LonghandId::Transform
1018 | LonghandId::TransformStyle
1019 | LonghandId::Translate
1020 | LonghandId::Rotate
1021 | LonghandId::Scale
1022 | LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
1023 LonghandId::Filter | LonghandId::BackdropFilter => {
1024 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
1025 | WillChangeBits::BACKDROP_ROOT
1026 | WillChangeBits::FIXPOS_CB_NON_SVG
1027 },
1028 LonghandId::ViewTransitionName => {
1029 WillChangeBits::VIEW_TRANSITION_NAME | WillChangeBits::BACKDROP_ROOT
1030 },
1031 LonghandId::MixBlendMode => {
1032 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
1033 },
1034 LonghandId::Isolation | LonghandId::MaskImage => {
1035 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL
1036 },
1037 LonghandId::ClipPath => {
1038 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT
1039 },
1040 _ => WillChangeBits::empty(),
1041 }
1042}
1043
1044fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
1045 let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
1046 Ok(id) => id,
1047 Err(..) => return WillChangeBits::empty(),
1048 };
1049
1050 match id.as_shorthand() {
1051 Ok(shorthand) => shorthand
1052 .longhands()
1053 .fold(WillChangeBits::empty(), |flags, p| {
1054 flags | change_bits_for_longhand(p)
1055 }),
1056 Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
1057 Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
1058 }
1059}
1060
1061impl Parse for WillChange {
1062 fn parse<'i, 't>(
1064 context: &ParserContext,
1065 input: &mut Parser<'i, 't>,
1066 ) -> Result<Self, ParseError<'i>> {
1067 if input
1068 .try_parse(|input| input.expect_ident_matching("auto"))
1069 .is_ok()
1070 {
1071 return Ok(Self::default());
1072 }
1073
1074 let mut bits = WillChangeBits::empty();
1075 let custom_idents = input.parse_comma_separated(|i| {
1076 let location = i.current_source_location();
1077 let parser_ident = i.expect_ident()?;
1078 let ident = CustomIdent::from_ident(
1079 location,
1080 parser_ident,
1081 &["will-change", "none", "all", "auto"],
1082 )?;
1083
1084 if context.in_ua_sheet() && ident.0 == atom!("-moz-fixed-pos-containing-block") {
1085 bits |= WillChangeBits::FIXPOS_CB_NON_SVG;
1086 } else if ident.0 == atom!("scroll-position") {
1087 bits |= WillChangeBits::SCROLL;
1088 } else {
1089 bits |= change_bits_for_maybe_property(&parser_ident, context);
1090 }
1091 Ok(ident)
1092 })?;
1093
1094 Ok(Self {
1095 features: custom_idents.into(),
1096 bits,
1097 })
1098 }
1099}
1100
1101#[derive(
1103 Clone,
1104 Copy,
1105 Debug,
1106 Eq,
1107 MallocSizeOf,
1108 Parse,
1109 PartialEq,
1110 SpecifiedValueInfo,
1111 ToComputedValue,
1112 ToCss,
1113 ToResolvedValue,
1114 ToShmem,
1115)]
1116#[css(bitflags(single = "none,auto,manipulation", mixed = "pan-x,pan-y,pinch-zoom"))]
1117#[repr(C)]
1118pub struct TouchAction(u8);
1119bitflags! {
1120 impl TouchAction: u8 {
1121 const NONE = 1 << 0;
1123 const AUTO = 1 << 1;
1125 const PAN_X = 1 << 2;
1127 const PAN_Y = 1 << 3;
1129 const MANIPULATION = 1 << 4;
1131 const PINCH_ZOOM = 1 << 5;
1133 }
1134}
1135
1136impl TouchAction {
1137 #[inline]
1138 pub fn auto() -> TouchAction {
1140 TouchAction::AUTO
1141 }
1142}
1143
1144#[derive(
1145 Clone,
1146 Copy,
1147 Debug,
1148 Eq,
1149 MallocSizeOf,
1150 Parse,
1151 PartialEq,
1152 SpecifiedValueInfo,
1153 ToComputedValue,
1154 ToCss,
1155 ToResolvedValue,
1156 ToShmem,
1157)]
1158#[css(bitflags(
1159 single = "none,strict,content",
1160 mixed = "size,layout,style,paint,inline-size",
1161 overlapping_bits
1162))]
1163#[repr(C)]
1164pub struct Contain(u8);
1166bitflags! {
1167 impl Contain: u8 {
1168 const NONE = 0;
1170 const INLINE_SIZE = 1 << 0;
1172 const BLOCK_SIZE = 1 << 1;
1174 const LAYOUT = 1 << 2;
1176 const STYLE = 1 << 3;
1178 const PAINT = 1 << 4;
1180 const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits() | Contain::BLOCK_SIZE.bits();
1182 const CONTENT = 1 << 6 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits();
1184 const STRICT = 1 << 7 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits() | Contain::SIZE.bits();
1186 }
1187}
1188
1189impl Parse for ContainIntrinsicSize {
1190 fn parse<'i, 't>(
1192 context: &ParserContext,
1193 input: &mut Parser<'i, 't>,
1194 ) -> Result<Self, ParseError<'i>> {
1195 if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
1196 return Ok(Self::Length(l));
1197 }
1198
1199 if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
1200 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1201 return Ok(Self::AutoNone);
1202 }
1203
1204 let l = NonNegativeLength::parse(context, input)?;
1205 return Ok(Self::AutoLength(l));
1206 }
1207
1208 input.expect_ident_matching("none")?;
1209 Ok(Self::None)
1210 }
1211}
1212
1213impl Parse for LineClamp {
1214 fn parse<'i, 't>(
1216 context: &ParserContext,
1217 input: &mut Parser<'i, 't>,
1218 ) -> Result<Self, ParseError<'i>> {
1219 if let Ok(i) =
1220 input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))
1221 {
1222 return Ok(Self(i.0));
1223 }
1224 input.expect_ident_matching("none")?;
1225 Ok(Self::none())
1226 }
1227}
1228
1229#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1231#[derive(
1232 Clone,
1233 Copy,
1234 Debug,
1235 Eq,
1236 FromPrimitive,
1237 MallocSizeOf,
1238 Parse,
1239 PartialEq,
1240 SpecifiedValueInfo,
1241 ToAnimatedValue,
1242 ToComputedValue,
1243 ToCss,
1244 ToResolvedValue,
1245 ToShmem,
1246)]
1247#[repr(u8)]
1248pub enum ContentVisibility {
1249 Auto,
1253 Hidden,
1255 Visible,
1257}
1258
1259#[derive(
1260 Clone,
1261 Copy,
1262 Debug,
1263 PartialEq,
1264 Eq,
1265 MallocSizeOf,
1266 SpecifiedValueInfo,
1267 ToComputedValue,
1268 ToCss,
1269 Parse,
1270 ToResolvedValue,
1271 ToShmem,
1272)]
1273#[css(bitflags(
1274 single = "normal",
1275 mixed = "size,inline-size,scroll-state",
1276 validate_mixed = "Self::validate_mixed_flags",
1277))]
1278#[repr(C)]
1279pub struct ContainerType(u8);
1286bitflags! {
1287 impl ContainerType: u8 {
1288 const NORMAL = 0;
1290 const INLINE_SIZE = 1 << 0;
1292 const SIZE = 1 << 1;
1294 const SCROLL_STATE = 1 << 2;
1296 }
1297}
1298
1299impl ContainerType {
1300 fn validate_mixed_flags(&self) -> bool {
1301 if self.contains(Self::SIZE | Self::INLINE_SIZE) {
1303 return false;
1304 }
1305 if self.contains(Self::SCROLL_STATE)
1306 && !static_prefs::pref!("layout.css.scroll-state.enabled")
1307 {
1308 return false;
1309 }
1310 true
1311 }
1312
1313 pub fn is_normal(self) -> bool {
1315 self == Self::NORMAL
1316 }
1317
1318 pub fn is_size_container_type(self) -> bool {
1320 self.intersects(Self::SIZE | Self::INLINE_SIZE)
1321 }
1322}
1323
1324#[repr(transparent)]
1326#[derive(
1327 Clone,
1328 Debug,
1329 MallocSizeOf,
1330 PartialEq,
1331 SpecifiedValueInfo,
1332 ToComputedValue,
1333 ToCss,
1334 ToResolvedValue,
1335 ToShmem,
1336)]
1337pub struct ContainerName(#[css(iterable, if_empty = "none")] pub crate::OwnedSlice<CustomIdent>);
1338
1339impl ContainerName {
1340 pub fn none() -> Self {
1342 Self(Default::default())
1343 }
1344
1345 pub fn is_none(&self) -> bool {
1347 self.0.is_empty()
1348 }
1349
1350 fn parse_internal<'i>(
1351 input: &mut Parser<'i, '_>,
1352 for_query: bool,
1353 ) -> Result<Self, ParseError<'i>> {
1354 let mut idents = vec![];
1355 let location = input.current_source_location();
1356 let first = input.expect_ident()?;
1357 if !for_query && first.eq_ignore_ascii_case("none") {
1358 return Ok(Self::none());
1359 }
1360 const DISALLOWED_CONTAINER_NAMES: &'static [&'static str] = &["none", "not", "or", "and"];
1361 idents.push(CustomIdent::from_ident(
1362 location,
1363 first,
1364 DISALLOWED_CONTAINER_NAMES,
1365 )?);
1366 if !for_query {
1367 while let Ok(name) =
1368 input.try_parse(|input| CustomIdent::parse(input, DISALLOWED_CONTAINER_NAMES))
1369 {
1370 idents.push(name);
1371 }
1372 }
1373 Ok(ContainerName(idents.into()))
1374 }
1375
1376 pub fn parse_for_query<'i, 't>(
1380 _: &ParserContext,
1381 input: &mut Parser<'i, 't>,
1382 ) -> Result<Self, ParseError<'i>> {
1383 Self::parse_internal(input, true)
1384 }
1385}
1386
1387impl Parse for ContainerName {
1388 fn parse<'i, 't>(
1389 _: &ParserContext,
1390 input: &mut Parser<'i, 't>,
1391 ) -> Result<Self, ParseError<'i>> {
1392 Self::parse_internal(input, false)
1393 }
1394}
1395
1396pub type Perspective = GenericPerspective<NonNegativeLength>;
1398
1399#[allow(missing_docs)]
1401#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1402#[derive(
1403 Clone,
1404 Copy,
1405 Debug,
1406 Eq,
1407 FromPrimitive,
1408 Hash,
1409 MallocSizeOf,
1410 Parse,
1411 PartialEq,
1412 SpecifiedValueInfo,
1413 ToComputedValue,
1414 ToCss,
1415 ToResolvedValue,
1416 ToShmem,
1417)]
1418#[repr(u8)]
1419pub enum Float {
1420 Left,
1421 Right,
1422 None,
1423 InlineStart,
1425 InlineEnd,
1426}
1427
1428impl Float {
1429 pub fn is_floating(self) -> bool {
1431 self != Self::None
1432 }
1433}
1434
1435#[allow(missing_docs)]
1437#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1438#[derive(
1439 Clone,
1440 Copy,
1441 Debug,
1442 Eq,
1443 FromPrimitive,
1444 Hash,
1445 MallocSizeOf,
1446 Parse,
1447 PartialEq,
1448 SpecifiedValueInfo,
1449 ToComputedValue,
1450 ToCss,
1451 ToResolvedValue,
1452 ToShmem,
1453)]
1454#[repr(u8)]
1455pub enum Clear {
1456 None,
1457 Left,
1458 Right,
1459 Both,
1460 InlineStart,
1462 InlineEnd,
1463}
1464
1465#[allow(missing_docs)]
1467#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1468#[derive(
1469 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1470)]
1471pub enum Resize {
1472 None,
1473 Both,
1474 Horizontal,
1475 Vertical,
1476 Inline,
1478 Block,
1479}
1480
1481#[allow(missing_docs)]
1485#[derive(
1486 Clone,
1487 Copy,
1488 Debug,
1489 Eq,
1490 Hash,
1491 MallocSizeOf,
1492 Parse,
1493 PartialEq,
1494 SpecifiedValueInfo,
1495 ToCss,
1496 ToComputedValue,
1497 ToResolvedValue,
1498 ToShmem,
1499)]
1500#[repr(u8)]
1501pub enum Appearance {
1502 None,
1504 Auto,
1509 Searchfield,
1511 Textarea,
1513 Checkbox,
1515 Radio,
1517 Menulist,
1519 Listbox,
1521 Meter,
1523 ProgressBar,
1525 Button,
1527 Textfield,
1529 MenulistButton,
1531 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1533 Menupopup,
1534 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1536 MozMenulistArrowButton,
1537 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1539 NumberInput,
1540 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1542 PasswordInput,
1543 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1545 Range,
1546 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1548 ScrollbarHorizontal,
1549 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1550 ScrollbarVertical,
1551 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1555 ScrollbarbuttonUp,
1556 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1557 ScrollbarbuttonDown,
1558 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1559 ScrollbarbuttonLeft,
1560 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1561 ScrollbarbuttonRight,
1562 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1564 ScrollbarthumbHorizontal,
1565 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1566 ScrollbarthumbVertical,
1567 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1569 Scrollcorner,
1570 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1572 SpinnerUpbutton,
1573 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1575 SpinnerDownbutton,
1576 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1578 Toolbarbutton,
1579 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1581 Tooltip,
1582
1583 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1585 MozSidebar,
1586
1587 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1589 MozMacHelpButton,
1590
1591 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1595 MozMacWindow,
1596
1597 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1599 MozWindowButtonBox,
1600 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1601 MozWindowButtonClose,
1602 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1603 MozWindowButtonMaximize,
1604 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1605 MozWindowButtonMinimize,
1606 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1607 MozWindowButtonRestore,
1608 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1609 MozWindowTitlebar,
1610 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1611 MozWindowTitlebarMaximized,
1612 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1613 MozWindowDecorations,
1614
1615 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1616 MozMacDisclosureButtonClosed,
1617 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1618 MozMacDisclosureButtonOpen,
1619
1620 #[css(skip)]
1624 FocusOutline,
1625
1626 #[css(skip)]
1628 Count,
1629}
1630
1631#[allow(missing_docs)]
1635#[derive(
1636 Clone,
1637 Copy,
1638 Debug,
1639 Eq,
1640 Hash,
1641 MallocSizeOf,
1642 Parse,
1643 PartialEq,
1644 SpecifiedValueInfo,
1645 ToCss,
1646 ToComputedValue,
1647 ToResolvedValue,
1648 ToShmem,
1649)]
1650#[repr(u8)]
1651pub enum BreakBetween {
1652 Always,
1653 Auto,
1654 Page,
1655 Avoid,
1656 Left,
1657 Right,
1658}
1659
1660impl BreakBetween {
1661 #[cfg_attr(feature = "servo", allow(unused))]
1665 #[inline]
1666 pub(crate) fn parse_legacy<'i>(
1667 _: &ParserContext,
1668 input: &mut Parser<'i, '_>,
1669 ) -> Result<Self, ParseError<'i>> {
1670 let break_value = BreakBetween::parse(input)?;
1671 match break_value {
1672 BreakBetween::Always => Ok(BreakBetween::Page),
1673 BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1674 Ok(break_value)
1675 },
1676 BreakBetween::Page => {
1677 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1678 },
1679 }
1680 }
1681
1682 #[cfg_attr(feature = "servo", allow(unused))]
1686 pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1687 where
1688 W: Write,
1689 {
1690 match *self {
1691 BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1692 self.to_css(dest)
1693 },
1694 BreakBetween::Page => dest.write_str("always"),
1695 BreakBetween::Always => Ok(()),
1696 }
1697 }
1698}
1699
1700#[allow(missing_docs)]
1704#[derive(
1705 Clone,
1706 Copy,
1707 Debug,
1708 Eq,
1709 Hash,
1710 MallocSizeOf,
1711 Parse,
1712 PartialEq,
1713 SpecifiedValueInfo,
1714 ToCss,
1715 ToComputedValue,
1716 ToResolvedValue,
1717 ToShmem,
1718)]
1719#[repr(u8)]
1720pub enum BreakWithin {
1721 Auto,
1722 Avoid,
1723 AvoidPage,
1724 AvoidColumn,
1725}
1726
1727impl BreakWithin {
1728 #[cfg_attr(feature = "servo", allow(unused))]
1732 #[inline]
1733 pub(crate) fn parse_legacy<'i>(
1734 _: &ParserContext,
1735 input: &mut Parser<'i, '_>,
1736 ) -> Result<Self, ParseError<'i>> {
1737 let break_value = BreakWithin::parse(input)?;
1738 match break_value {
1739 BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
1740 BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
1741 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1742 },
1743 }
1744 }
1745
1746 #[cfg_attr(feature = "servo", allow(unused))]
1750 pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1751 where
1752 W: Write,
1753 {
1754 match *self {
1755 BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
1756 BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
1757 }
1758 }
1759}
1760
1761#[allow(missing_docs)]
1763#[derive(
1764 Clone,
1765 Copy,
1766 Debug,
1767 Eq,
1768 Hash,
1769 MallocSizeOf,
1770 PartialEq,
1771 SpecifiedValueInfo,
1772 ToCss,
1773 ToComputedValue,
1774 ToResolvedValue,
1775 ToShmem,
1776)]
1777#[repr(u8)]
1778pub enum Overflow {
1779 Visible,
1780 Hidden,
1781 Scroll,
1782 Auto,
1783 Clip,
1784}
1785
1786impl Parse for Overflow {
1789 fn parse<'i, 't>(
1790 _: &ParserContext,
1791 input: &mut Parser<'i, 't>,
1792 ) -> Result<Self, ParseError<'i>> {
1793 Ok(try_match_ident_ignore_ascii_case! { input,
1794 "visible" => Self::Visible,
1795 "hidden" => Self::Hidden,
1796 "scroll" => Self::Scroll,
1797 "auto" | "overlay" => Self::Auto,
1798 "clip" => Self::Clip,
1799 #[cfg(feature = "gecko")]
1800 "-moz-hidden-unscrollable" if static_prefs::pref!("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
1801 Overflow::Clip
1802 },
1803 })
1804 }
1805}
1806
1807impl Overflow {
1808 #[inline]
1810 pub fn is_scrollable(&self) -> bool {
1811 matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)
1812 }
1813 #[inline]
1816 pub fn to_scrollable(&self) -> Self {
1817 match *self {
1818 Self::Hidden | Self::Scroll | Self::Auto => *self,
1819 Self::Visible => Self::Auto,
1820 Self::Clip => Self::Hidden,
1821 }
1822 }
1823}
1824
1825#[derive(
1826 Clone,
1827 Copy,
1828 Debug,
1829 Eq,
1830 MallocSizeOf,
1831 Parse,
1832 PartialEq,
1833 SpecifiedValueInfo,
1834 ToComputedValue,
1835 ToCss,
1836 ToResolvedValue,
1837 ToShmem,
1838)]
1839#[repr(C)]
1840#[css(bitflags(
1841 single = "auto",
1842 mixed = "stable,both-edges",
1843 validate_mixed = "Self::has_stable"
1844))]
1845pub struct ScrollbarGutter(u8);
1848bitflags! {
1849 impl ScrollbarGutter: u8 {
1850 const AUTO = 0;
1852 const STABLE = 1 << 0;
1854 const BOTH_EDGES = 1 << 1;
1856 }
1857}
1858
1859impl ScrollbarGutter {
1860 #[inline]
1861 fn has_stable(&self) -> bool {
1862 self.intersects(Self::STABLE)
1863 }
1864}
1865
1866#[derive(
1868 Clone, Copy, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem,
1869)]
1870#[allow(missing_docs)]
1871pub enum Zoom {
1872 Normal,
1873 #[parse(condition = "ParserContext::in_ua_sheet")]
1876 Document,
1877 Value(NonNegativeNumberOrPercentage),
1878}
1879
1880impl Zoom {
1881 #[inline]
1883 pub fn new_number(n: f32) -> Self {
1884 Self::Value(NonNegativeNumberOrPercentage::new_number(n))
1885 }
1886}
1887
1888pub use crate::values::generics::box_::PositionProperty;