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