1use crate::parser::{Parse, ParserContext};
8use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};
9use crate::values::generics::box_::{
10 GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign,
11 VerticalAlignKeyword,
12};
13use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
14use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumberOrPercentage};
15use crate::values::CustomIdent;
16pub use crate::logical_geometry::WritingModeProperty;
17use cssparser::Parser;
18use num_traits::FromPrimitive;
19use std::fmt::{self, Write};
20use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
21use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
22
23#[cfg(not(feature = "servo"))]
24fn grid_enabled() -> bool {
25 true
26}
27
28#[cfg(feature = "servo")]
29fn grid_enabled() -> bool {
30 style_config::get_bool("layout.grid.enabled")
31}
32
33#[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 => {
1011 WillChangeBits::OPACITY | WillChangeBits::BACKDROP_ROOT
1012 },
1013 LonghandId::Contain => WillChangeBits::CONTAIN,
1014 LonghandId::Perspective => WillChangeBits::PERSPECTIVE,
1015 LonghandId::Position => {
1016 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION
1017 },
1018 LonghandId::ZIndex => WillChangeBits::Z_INDEX,
1019 LonghandId::Transform |
1020 LonghandId::TransformStyle |
1021 LonghandId::Translate |
1022 LonghandId::Rotate |
1023 LonghandId::Scale |
1024 LonghandId::OffsetPath => WillChangeBits::TRANSFORM,
1025 LonghandId::Filter | LonghandId::BackdropFilter => {
1026 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL |
1027 WillChangeBits::BACKDROP_ROOT |
1028 WillChangeBits::FIXPOS_CB_NON_SVG
1029 },
1030 LonghandId::ViewTransitionName => {
1031 WillChangeBits::VIEW_TRANSITION_NAME |
1032 WillChangeBits::BACKDROP_ROOT
1033 },
1034 LonghandId::MixBlendMode => {
1035 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL |
1036 WillChangeBits::BACKDROP_ROOT
1037 },
1038 LonghandId::Isolation |
1039 LonghandId::MaskImage => WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL,
1040 LonghandId::ClipPath => {
1041 WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL |
1042 WillChangeBits::BACKDROP_ROOT
1043 },
1044 _ => WillChangeBits::empty(),
1045 }
1046}
1047
1048fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
1049 let id = match PropertyId::parse_ignoring_rule_type(ident, context) {
1050 Ok(id) => id,
1051 Err(..) => return WillChangeBits::empty(),
1052 };
1053
1054 match id.as_shorthand() {
1055 Ok(shorthand) => shorthand
1056 .longhands()
1057 .fold(WillChangeBits::empty(), |flags, p| {
1058 flags | change_bits_for_longhand(p)
1059 }),
1060 Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
1061 Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
1062 }
1063}
1064
1065impl Parse for WillChange {
1066 fn parse<'i, 't>(
1068 context: &ParserContext,
1069 input: &mut Parser<'i, 't>,
1070 ) -> Result<Self, ParseError<'i>> {
1071 if input
1072 .try_parse(|input| input.expect_ident_matching("auto"))
1073 .is_ok()
1074 {
1075 return Ok(Self::default());
1076 }
1077
1078 let mut bits = WillChangeBits::empty();
1079 let custom_idents = input.parse_comma_separated(|i| {
1080 let location = i.current_source_location();
1081 let parser_ident = i.expect_ident()?;
1082 let ident = CustomIdent::from_ident(
1083 location,
1084 parser_ident,
1085 &["will-change", "none", "all", "auto"],
1086 )?;
1087
1088 if context.in_ua_sheet() && ident.0 == atom!("-moz-fixed-pos-containing-block") {
1089 bits |= WillChangeBits::FIXPOS_CB_NON_SVG;
1090 } else if ident.0 == atom!("scroll-position") {
1091 bits |= WillChangeBits::SCROLL;
1092 } else {
1093 bits |= change_bits_for_maybe_property(&parser_ident, context);
1094 }
1095 Ok(ident)
1096 })?;
1097
1098 Ok(Self {
1099 features: custom_idents.into(),
1100 bits,
1101 })
1102 }
1103}
1104
1105#[derive(
1107 Clone,
1108 Copy,
1109 Debug,
1110 Eq,
1111 MallocSizeOf,
1112 Parse,
1113 PartialEq,
1114 SpecifiedValueInfo,
1115 ToComputedValue,
1116 ToCss,
1117 ToResolvedValue,
1118 ToShmem,
1119)]
1120#[css(bitflags(single = "none,auto,manipulation", mixed = "pan-x,pan-y,pinch-zoom"))]
1121#[repr(C)]
1122pub struct TouchAction(u8);
1123bitflags! {
1124 impl TouchAction: u8 {
1125 const NONE = 1 << 0;
1127 const AUTO = 1 << 1;
1129 const PAN_X = 1 << 2;
1131 const PAN_Y = 1 << 3;
1133 const MANIPULATION = 1 << 4;
1135 const PINCH_ZOOM = 1 << 5;
1137 }
1138}
1139
1140impl TouchAction {
1141 #[inline]
1142 pub fn auto() -> TouchAction {
1144 TouchAction::AUTO
1145 }
1146}
1147
1148#[derive(
1149 Clone,
1150 Copy,
1151 Debug,
1152 Eq,
1153 MallocSizeOf,
1154 Parse,
1155 PartialEq,
1156 SpecifiedValueInfo,
1157 ToComputedValue,
1158 ToCss,
1159 ToResolvedValue,
1160 ToShmem,
1161)]
1162#[css(bitflags(
1163 single = "none,strict,content",
1164 mixed = "size,layout,style,paint,inline-size",
1165 overlapping_bits
1166))]
1167#[repr(C)]
1168pub struct Contain(u8);
1170bitflags! {
1171 impl Contain: u8 {
1172 const NONE = 0;
1174 const INLINE_SIZE = 1 << 0;
1176 const BLOCK_SIZE = 1 << 1;
1178 const LAYOUT = 1 << 2;
1180 const STYLE = 1 << 3;
1182 const PAINT = 1 << 4;
1184 const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits() | Contain::BLOCK_SIZE.bits();
1186 const CONTENT = 1 << 6 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits();
1188 const STRICT = 1 << 7 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits() | Contain::SIZE.bits();
1190 }
1191}
1192
1193impl Parse for ContainIntrinsicSize {
1194 fn parse<'i, 't>(
1196 context: &ParserContext,
1197 input: &mut Parser<'i, 't>,
1198 ) -> Result<Self, ParseError<'i>> {
1199 if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {
1200 return Ok(Self::Length(l));
1201 }
1202
1203 if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
1204 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1205 return Ok(Self::AutoNone);
1206 }
1207
1208 let l = NonNegativeLength::parse(context, input)?;
1209 return Ok(Self::AutoLength(l));
1210 }
1211
1212 input.expect_ident_matching("none")?;
1213 Ok(Self::None)
1214 }
1215}
1216
1217impl Parse for LineClamp {
1218 fn parse<'i, 't>(
1220 context: &ParserContext,
1221 input: &mut Parser<'i, 't>,
1222 ) -> Result<Self, ParseError<'i>> {
1223 if let Ok(i) =
1224 input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))
1225 {
1226 return Ok(Self(i.0));
1227 }
1228 input.expect_ident_matching("none")?;
1229 Ok(Self::none())
1230 }
1231}
1232
1233#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1235#[derive(
1236 Clone,
1237 Copy,
1238 Debug,
1239 Eq,
1240 FromPrimitive,
1241 MallocSizeOf,
1242 Parse,
1243 PartialEq,
1244 SpecifiedValueInfo,
1245 ToAnimatedValue,
1246 ToComputedValue,
1247 ToCss,
1248 ToResolvedValue,
1249 ToShmem,
1250)]
1251#[repr(u8)]
1252pub enum ContentVisibility {
1253 Auto,
1257 Hidden,
1259 Visible,
1261}
1262
1263#[derive(
1264 Clone,
1265 Copy,
1266 Debug,
1267 PartialEq,
1268 Eq,
1269 MallocSizeOf,
1270 SpecifiedValueInfo,
1271 ToComputedValue,
1272 ToCss,
1273 Parse,
1274 ToResolvedValue,
1275 ToShmem,
1276)]
1277#[css(bitflags(
1278 single = "normal",
1279 mixed = "size,inline-size,scroll-state",
1280 validate_mixed = "Self::validate_mixed_flags",
1281))]
1282#[repr(C)]
1283pub struct ContainerType(u8);
1290bitflags! {
1291 impl ContainerType: u8 {
1292 const NORMAL = 0;
1294 const INLINE_SIZE = 1 << 0;
1296 const SIZE = 1 << 1;
1298 const SCROLL_STATE = 1 << 2;
1300 }
1301}
1302
1303impl ContainerType {
1304 fn validate_mixed_flags(&self) -> bool {
1305 if self.contains(Self::SIZE | Self::INLINE_SIZE) {
1307 return false;
1308 }
1309 if self.contains(Self::SCROLL_STATE) && !static_prefs::pref!("layout.css.scroll-state.enabled") {
1310 return false;
1311 }
1312 true
1313 }
1314
1315 pub fn is_normal(self) -> bool {
1317 self == Self::NORMAL
1318 }
1319
1320 pub fn is_size_container_type(self) -> bool {
1322 self.intersects(Self::SIZE | Self::INLINE_SIZE)
1323 }
1324}
1325
1326#[repr(transparent)]
1328#[derive(
1329 Clone,
1330 Debug,
1331 MallocSizeOf,
1332 PartialEq,
1333 SpecifiedValueInfo,
1334 ToComputedValue,
1335 ToCss,
1336 ToResolvedValue,
1337 ToShmem,
1338)]
1339pub struct ContainerName(#[css(iterable, if_empty = "none")] pub crate::OwnedSlice<CustomIdent>);
1340
1341impl ContainerName {
1342 pub fn none() -> Self {
1344 Self(Default::default())
1345 }
1346
1347 pub fn is_none(&self) -> bool {
1349 self.0.is_empty()
1350 }
1351
1352 fn parse_internal<'i>(
1353 input: &mut Parser<'i, '_>,
1354 for_query: bool,
1355 ) -> Result<Self, ParseError<'i>> {
1356 let mut idents = vec![];
1357 let location = input.current_source_location();
1358 let first = input.expect_ident()?;
1359 if !for_query && first.eq_ignore_ascii_case("none") {
1360 return Ok(Self::none());
1361 }
1362 const DISALLOWED_CONTAINER_NAMES: &'static [&'static str] = &["none", "not", "or", "and"];
1363 idents.push(CustomIdent::from_ident(
1364 location,
1365 first,
1366 DISALLOWED_CONTAINER_NAMES,
1367 )?);
1368 if !for_query {
1369 while let Ok(name) =
1370 input.try_parse(|input| CustomIdent::parse(input, DISALLOWED_CONTAINER_NAMES))
1371 {
1372 idents.push(name);
1373 }
1374 }
1375 Ok(ContainerName(idents.into()))
1376 }
1377
1378 pub fn parse_for_query<'i, 't>(
1382 _: &ParserContext,
1383 input: &mut Parser<'i, 't>,
1384 ) -> Result<Self, ParseError<'i>> {
1385 Self::parse_internal(input, true)
1386 }
1387}
1388
1389impl Parse for ContainerName {
1390 fn parse<'i, 't>(
1391 _: &ParserContext,
1392 input: &mut Parser<'i, 't>,
1393 ) -> Result<Self, ParseError<'i>> {
1394 Self::parse_internal(input, false)
1395 }
1396}
1397
1398pub type Perspective = GenericPerspective<NonNegativeLength>;
1400
1401#[allow(missing_docs)]
1403#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1404#[derive(
1405 Clone,
1406 Copy,
1407 Debug,
1408 Eq,
1409 FromPrimitive,
1410 Hash,
1411 MallocSizeOf,
1412 Parse,
1413 PartialEq,
1414 SpecifiedValueInfo,
1415 ToComputedValue,
1416 ToCss,
1417 ToResolvedValue,
1418 ToShmem,
1419)]
1420#[repr(u8)]
1421pub enum Float {
1422 Left,
1423 Right,
1424 None,
1425 InlineStart,
1427 InlineEnd,
1428}
1429
1430impl Float {
1431 pub fn is_floating(self) -> bool {
1433 self != Self::None
1434 }
1435}
1436
1437#[allow(missing_docs)]
1439#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1440#[derive(
1441 Clone,
1442 Copy,
1443 Debug,
1444 Eq,
1445 FromPrimitive,
1446 Hash,
1447 MallocSizeOf,
1448 Parse,
1449 PartialEq,
1450 SpecifiedValueInfo,
1451 ToComputedValue,
1452 ToCss,
1453 ToResolvedValue,
1454 ToShmem,
1455)]
1456#[repr(u8)]
1457pub enum Clear {
1458 None,
1459 Left,
1460 Right,
1461 Both,
1462 InlineStart,
1464 InlineEnd,
1465}
1466
1467#[allow(missing_docs)]
1469#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
1470#[derive(
1471 Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
1472)]
1473pub enum Resize {
1474 None,
1475 Both,
1476 Horizontal,
1477 Vertical,
1478 Inline,
1480 Block,
1481}
1482
1483#[allow(missing_docs)]
1487#[derive(
1488 Clone,
1489 Copy,
1490 Debug,
1491 Eq,
1492 Hash,
1493 MallocSizeOf,
1494 Parse,
1495 PartialEq,
1496 SpecifiedValueInfo,
1497 ToCss,
1498 ToComputedValue,
1499 ToResolvedValue,
1500 ToShmem,
1501)]
1502#[repr(u8)]
1503pub enum Appearance {
1504 None,
1506 Auto,
1511 Searchfield,
1513 Textarea,
1515 Checkbox,
1517 Radio,
1519 Menulist,
1521 Listbox,
1523 Meter,
1525 ProgressBar,
1527 Button,
1529 Textfield,
1531 MenulistButton,
1533 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1535 Menupopup,
1536 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1538 MozMenulistArrowButton,
1539 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1541 NumberInput,
1542 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1544 PasswordInput,
1545 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1547 Range,
1548 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1550 ScrollbarHorizontal,
1551 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1552 ScrollbarVertical,
1553 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1557 ScrollbarbuttonUp,
1558 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1559 ScrollbarbuttonDown,
1560 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1561 ScrollbarbuttonLeft,
1562 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1563 ScrollbarbuttonRight,
1564 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1566 ScrollbarthumbHorizontal,
1567 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1568 ScrollbarthumbVertical,
1569 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1571 Scrollcorner,
1572 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1574 SpinnerUpbutton,
1575 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1577 SpinnerDownbutton,
1578 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1580 Toolbarbutton,
1581 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1583 Tooltip,
1584
1585 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1587 MozSidebar,
1588
1589 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1591 MozMacHelpButton,
1592
1593 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1597 MozMacWindow,
1598
1599 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1601 MozWindowButtonBox,
1602 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1603 MozWindowButtonClose,
1604 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1605 MozWindowButtonMaximize,
1606 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1607 MozWindowButtonMinimize,
1608 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1609 MozWindowButtonRestore,
1610 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1611 MozWindowTitlebar,
1612 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1613 MozWindowTitlebarMaximized,
1614 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1615 MozWindowDecorations,
1616
1617 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1618 MozMacDisclosureButtonClosed,
1619 #[parse(condition = "ParserContext::chrome_rules_enabled")]
1620 MozMacDisclosureButtonOpen,
1621
1622 #[css(skip)]
1626 FocusOutline,
1627
1628 #[css(skip)]
1630 Count,
1631}
1632
1633#[allow(missing_docs)]
1637#[derive(
1638 Clone,
1639 Copy,
1640 Debug,
1641 Eq,
1642 Hash,
1643 MallocSizeOf,
1644 Parse,
1645 PartialEq,
1646 SpecifiedValueInfo,
1647 ToCss,
1648 ToComputedValue,
1649 ToResolvedValue,
1650 ToShmem,
1651)]
1652#[repr(u8)]
1653pub enum BreakBetween {
1654 Always,
1655 Auto,
1656 Page,
1657 Avoid,
1658 Left,
1659 Right,
1660}
1661
1662impl BreakBetween {
1663 #[cfg_attr(feature = "servo", allow(unused))]
1667 #[inline]
1668 pub(crate) fn parse_legacy<'i>(
1669 _: &ParserContext,
1670 input: &mut Parser<'i, '_>,
1671 ) -> Result<Self, ParseError<'i>> {
1672 let break_value = BreakBetween::parse(input)?;
1673 match break_value {
1674 BreakBetween::Always => Ok(BreakBetween::Page),
1675 BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1676 Ok(break_value)
1677 },
1678 BreakBetween::Page => {
1679 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1680 },
1681 }
1682 }
1683
1684 #[cfg_attr(feature = "servo", allow(unused))]
1688 pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1689 where
1690 W: Write,
1691 {
1692 match *self {
1693 BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
1694 self.to_css(dest)
1695 },
1696 BreakBetween::Page => dest.write_str("always"),
1697 BreakBetween::Always => Ok(()),
1698 }
1699 }
1700}
1701
1702#[allow(missing_docs)]
1706#[derive(
1707 Clone,
1708 Copy,
1709 Debug,
1710 Eq,
1711 Hash,
1712 MallocSizeOf,
1713 Parse,
1714 PartialEq,
1715 SpecifiedValueInfo,
1716 ToCss,
1717 ToComputedValue,
1718 ToResolvedValue,
1719 ToShmem,
1720)]
1721#[repr(u8)]
1722pub enum BreakWithin {
1723 Auto,
1724 Avoid,
1725 AvoidPage,
1726 AvoidColumn,
1727}
1728
1729impl BreakWithin {
1730 #[cfg_attr(feature = "servo", allow(unused))]
1734 #[inline]
1735 pub(crate) fn parse_legacy<'i>(
1736 _: &ParserContext,
1737 input: &mut Parser<'i, '_>,
1738 ) -> Result<Self, ParseError<'i>> {
1739 let break_value = BreakWithin::parse(input)?;
1740 match break_value {
1741 BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
1742 BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
1743 Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
1744 },
1745 }
1746 }
1747
1748 #[cfg_attr(feature = "servo", allow(unused))]
1752 pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
1753 where
1754 W: Write,
1755 {
1756 match *self {
1757 BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
1758 BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
1759 }
1760 }
1761}
1762
1763#[allow(missing_docs)]
1765#[derive(
1766 Clone,
1767 Copy,
1768 Debug,
1769 Eq,
1770 Hash,
1771 MallocSizeOf,
1772 PartialEq,
1773 SpecifiedValueInfo,
1774 ToCss,
1775 ToComputedValue,
1776 ToResolvedValue,
1777 ToShmem,
1778)]
1779#[repr(u8)]
1780pub enum Overflow {
1781 Visible,
1782 Hidden,
1783 Scroll,
1784 Auto,
1785 Clip,
1786}
1787
1788impl Parse for Overflow {
1791 fn parse<'i, 't>(
1792 _: &ParserContext,
1793 input: &mut Parser<'i, 't>,
1794 ) -> Result<Self, ParseError<'i>> {
1795 Ok(try_match_ident_ignore_ascii_case! { input,
1796 "visible" => Self::Visible,
1797 "hidden" => Self::Hidden,
1798 "scroll" => Self::Scroll,
1799 "auto" | "overlay" => Self::Auto,
1800 "clip" => Self::Clip,
1801 #[cfg(feature = "gecko")]
1802 "-moz-hidden-unscrollable" if static_prefs::pref!("layout.css.overflow-moz-hidden-unscrollable.enabled") => {
1803 Overflow::Clip
1804 },
1805 })
1806 }
1807}
1808
1809impl Overflow {
1810 #[inline]
1812 pub fn is_scrollable(&self) -> bool {
1813 matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)
1814 }
1815 #[inline]
1818 pub fn to_scrollable(&self) -> Self {
1819 match *self {
1820 Self::Hidden | Self::Scroll | Self::Auto => *self,
1821 Self::Visible => Self::Auto,
1822 Self::Clip => Self::Hidden,
1823 }
1824 }
1825}
1826
1827#[derive(
1828 Clone,
1829 Copy,
1830 Debug,
1831 Eq,
1832 MallocSizeOf,
1833 Parse,
1834 PartialEq,
1835 SpecifiedValueInfo,
1836 ToComputedValue,
1837 ToCss,
1838 ToResolvedValue,
1839 ToShmem,
1840)]
1841#[repr(C)]
1842#[css(bitflags(
1843 single = "auto",
1844 mixed = "stable,both-edges",
1845 validate_mixed = "Self::has_stable"
1846))]
1847pub struct ScrollbarGutter(u8);
1850bitflags! {
1851 impl ScrollbarGutter: u8 {
1852 const AUTO = 0;
1854 const STABLE = 1 << 0;
1856 const BOTH_EDGES = 1 << 1;
1858 }
1859}
1860
1861impl ScrollbarGutter {
1862 #[inline]
1863 fn has_stable(&self) -> bool {
1864 self.intersects(Self::STABLE)
1865 }
1866}
1867
1868#[derive(
1870 Clone, Copy, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem,
1871)]
1872#[allow(missing_docs)]
1873pub enum Zoom {
1874 Normal,
1875 #[parse(condition = "ParserContext::in_ua_sheet")]
1878 Document,
1879 Value(NonNegativeNumberOrPercentage),
1880}
1881
1882impl Zoom {
1883 #[inline]
1885 pub fn new_number(n: f32) -> Self {
1886 Self::Value(NonNegativeNumberOrPercentage::new_number(n))
1887 }
1888}
1889
1890pub use crate::values::generics::box_::PositionProperty;