length.rs (18200B)
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 //! Generic types for CSS values related to length. 6 7 use crate::derives::*; 8 use crate::logical_geometry::PhysicalSide; 9 use crate::parser::{Parse, ParserContext}; 10 use crate::values::computed::position::TryTacticAdjustment; 11 use crate::values::generics::box_::PositionProperty; 12 use crate::values::generics::Optional; 13 use crate::values::DashedIdent; 14 use crate::Zero; 15 use cssparser::Parser; 16 use std::fmt::Write; 17 use style_traits::ParseError; 18 use style_traits::StyleParseErrorKind; 19 use style_traits::ToCss; 20 use style_traits::{CssWriter, SpecifiedValueInfo}; 21 22 /// A `<length-percentage> | auto` value. 23 #[allow(missing_docs)] 24 #[derive( 25 Animate, 26 Clone, 27 ComputeSquaredDistance, 28 Copy, 29 Debug, 30 MallocSizeOf, 31 PartialEq, 32 SpecifiedValueInfo, 33 ToAnimatedValue, 34 ToAnimatedZero, 35 ToComputedValue, 36 ToCss, 37 ToResolvedValue, 38 ToShmem, 39 ToTyped, 40 )] 41 #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] 42 #[repr(C, u8)] 43 #[typed_value(derive_fields)] 44 pub enum GenericLengthPercentageOrAuto<LengthPercent> { 45 LengthPercentage(LengthPercent), 46 Auto, 47 } 48 49 pub use self::GenericLengthPercentageOrAuto as LengthPercentageOrAuto; 50 51 impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage> { 52 /// `auto` value. 53 #[inline] 54 pub fn auto() -> Self { 55 LengthPercentageOrAuto::Auto 56 } 57 58 /// Whether this is the `auto` value. 59 #[inline] 60 pub fn is_auto(&self) -> bool { 61 matches!(*self, LengthPercentageOrAuto::Auto) 62 } 63 64 /// A helper function to parse this with quirks or not and so forth. 65 pub fn parse_with<'i, 't>( 66 context: &ParserContext, 67 input: &mut Parser<'i, 't>, 68 parser: impl FnOnce( 69 &ParserContext, 70 &mut Parser<'i, 't>, 71 ) -> Result<LengthPercentage, ParseError<'i>>, 72 ) -> Result<Self, ParseError<'i>> { 73 if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() { 74 return Ok(LengthPercentageOrAuto::Auto); 75 } 76 77 Ok(LengthPercentageOrAuto::LengthPercentage(parser( 78 context, input, 79 )?)) 80 } 81 } 82 83 impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage> 84 where 85 LengthPercentage: Clone, 86 { 87 /// Resolves `auto` values by calling `f`. 88 #[inline] 89 pub fn auto_is(&self, f: impl FnOnce() -> LengthPercentage) -> LengthPercentage { 90 match self { 91 LengthPercentageOrAuto::LengthPercentage(length) => length.clone(), 92 LengthPercentageOrAuto::Auto => f(), 93 } 94 } 95 96 /// Returns the non-`auto` value, if any. 97 #[inline] 98 pub fn non_auto(&self) -> Option<LengthPercentage> { 99 match self { 100 LengthPercentageOrAuto::LengthPercentage(length) => Some(length.clone()), 101 LengthPercentageOrAuto::Auto => None, 102 } 103 } 104 105 /// Maps the length of this value. 106 pub fn map<T>(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto<T> { 107 match self { 108 LengthPercentageOrAuto::LengthPercentage(l) => { 109 LengthPercentageOrAuto::LengthPercentage(f(l.clone())) 110 }, 111 LengthPercentageOrAuto::Auto => LengthPercentageOrAuto::Auto, 112 } 113 } 114 } 115 116 impl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> { 117 fn zero() -> Self { 118 LengthPercentageOrAuto::LengthPercentage(Zero::zero()) 119 } 120 121 fn is_zero(&self) -> bool { 122 match *self { 123 LengthPercentageOrAuto::LengthPercentage(ref l) => l.is_zero(), 124 LengthPercentageOrAuto::Auto => false, 125 } 126 } 127 } 128 129 impl<LengthPercentage: Parse> Parse for LengthPercentageOrAuto<LengthPercentage> { 130 fn parse<'i, 't>( 131 context: &ParserContext, 132 input: &mut Parser<'i, 't>, 133 ) -> Result<Self, ParseError<'i>> { 134 Self::parse_with(context, input, LengthPercentage::parse) 135 } 136 } 137 138 /// A generic value for the `width`, `height`, `min-width`, or `min-height` property. 139 /// 140 /// Unlike `max-width` or `max-height` properties, a Size can be `auto`, 141 /// and cannot be `none`. 142 /// 143 /// Note that it only accepts non-negative values. 144 #[allow(missing_docs)] 145 #[derive( 146 Animate, 147 ComputeSquaredDistance, 148 Clone, 149 Debug, 150 MallocSizeOf, 151 PartialEq, 152 ToAnimatedValue, 153 ToAnimatedZero, 154 ToComputedValue, 155 ToCss, 156 ToResolvedValue, 157 ToShmem, 158 ToTyped, 159 )] 160 #[repr(C, u8)] 161 pub enum GenericSize<LengthPercent> { 162 LengthPercentage(LengthPercent), 163 Auto, 164 #[animation(error)] 165 MaxContent, 166 #[animation(error)] 167 MinContent, 168 #[animation(error)] 169 FitContent, 170 #[cfg(feature = "gecko")] 171 #[animation(error)] 172 MozAvailable, 173 #[animation(error)] 174 WebkitFillAvailable, 175 #[animation(error)] 176 Stretch, 177 #[animation(error)] 178 #[css(function = "fit-content")] 179 FitContentFunction(LengthPercent), 180 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>), 181 AnchorContainingCalcFunction(LengthPercent), 182 } 183 184 impl<LengthPercent> SpecifiedValueInfo for GenericSize<LengthPercent> 185 where 186 LengthPercent: SpecifiedValueInfo, 187 { 188 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) { 189 LengthPercent::collect_completion_keywords(f); 190 f(&["auto", "fit-content", "max-content", "min-content"]); 191 if cfg!(feature = "gecko") { 192 f(&["-moz-available"]); 193 } 194 if static_prefs::pref!("layout.css.stretch-size-keyword.enabled") { 195 f(&["stretch"]); 196 } 197 if static_prefs::pref!("layout.css.webkit-fill-available.enabled") { 198 f(&["-webkit-fill-available"]); 199 } 200 if static_prefs::pref!("layout.css.anchor-positioning.enabled") { 201 f(&["anchor-size"]); 202 } 203 } 204 } 205 206 pub use self::GenericSize as Size; 207 208 impl<LengthPercentage> Size<LengthPercentage> { 209 /// `auto` value. 210 #[inline] 211 pub fn auto() -> Self { 212 Size::Auto 213 } 214 215 /// Returns whether we're the auto value. 216 #[inline] 217 pub fn is_auto(&self) -> bool { 218 matches!(*self, Size::Auto) 219 } 220 } 221 222 /// A generic value for the `max-width` or `max-height` property. 223 #[allow(missing_docs)] 224 #[derive( 225 Animate, 226 Clone, 227 ComputeSquaredDistance, 228 Debug, 229 MallocSizeOf, 230 PartialEq, 231 ToAnimatedValue, 232 ToAnimatedZero, 233 ToComputedValue, 234 ToCss, 235 ToResolvedValue, 236 ToShmem, 237 ToTyped, 238 )] 239 #[repr(C, u8)] 240 pub enum GenericMaxSize<LengthPercent> { 241 LengthPercentage(LengthPercent), 242 None, 243 #[animation(error)] 244 MaxContent, 245 #[animation(error)] 246 MinContent, 247 #[animation(error)] 248 FitContent, 249 #[cfg(feature = "gecko")] 250 #[animation(error)] 251 MozAvailable, 252 #[animation(error)] 253 WebkitFillAvailable, 254 #[animation(error)] 255 Stretch, 256 #[animation(error)] 257 #[css(function = "fit-content")] 258 FitContentFunction(LengthPercent), 259 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>), 260 AnchorContainingCalcFunction(LengthPercent), 261 } 262 263 impl<LP> SpecifiedValueInfo for GenericMaxSize<LP> 264 where 265 LP: SpecifiedValueInfo, 266 { 267 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) { 268 LP::collect_completion_keywords(f); 269 f(&["none", "fit-content", "max-content", "min-content"]); 270 if cfg!(feature = "gecko") { 271 f(&["-moz-available"]); 272 } 273 if static_prefs::pref!("layout.css.stretch-size-keyword.enabled") { 274 f(&["stretch"]); 275 } 276 if static_prefs::pref!("layout.css.webkit-fill-available.enabled") { 277 f(&["-webkit-fill-available"]); 278 } 279 if static_prefs::pref!("layout.css.anchor-positioning.enabled") { 280 f(&["anchor-size"]); 281 } 282 } 283 } 284 285 pub use self::GenericMaxSize as MaxSize; 286 287 impl<LengthPercentage> MaxSize<LengthPercentage> { 288 /// `none` value. 289 #[inline] 290 pub fn none() -> Self { 291 MaxSize::None 292 } 293 } 294 295 /// A generic `<length>` | `<number>` value for the `tab-size` property. 296 #[derive( 297 Animate, 298 Clone, 299 ComputeSquaredDistance, 300 Copy, 301 Debug, 302 MallocSizeOf, 303 Parse, 304 PartialEq, 305 SpecifiedValueInfo, 306 ToAnimatedValue, 307 ToAnimatedZero, 308 ToComputedValue, 309 ToCss, 310 ToResolvedValue, 311 ToShmem, 312 ToTyped, 313 )] 314 #[repr(C, u8)] 315 pub enum GenericLengthOrNumber<L, N> { 316 /// A number. 317 /// 318 /// NOTE: Numbers need to be before lengths, in order to parse them 319 /// first, since `0` should be a number, not the `0px` length. 320 Number(N), 321 /// A length. 322 Length(L), 323 } 324 325 pub use self::GenericLengthOrNumber as LengthOrNumber; 326 327 impl<L, N: Zero> Zero for LengthOrNumber<L, N> { 328 fn zero() -> Self { 329 LengthOrNumber::Number(Zero::zero()) 330 } 331 332 fn is_zero(&self) -> bool { 333 match *self { 334 LengthOrNumber::Number(ref n) => n.is_zero(), 335 LengthOrNumber::Length(..) => false, 336 } 337 } 338 } 339 340 /// A generic `<length-percentage>` | normal` value. 341 #[derive( 342 Animate, 343 Clone, 344 ComputeSquaredDistance, 345 Copy, 346 Debug, 347 MallocSizeOf, 348 Parse, 349 PartialEq, 350 SpecifiedValueInfo, 351 ToAnimatedValue, 352 ToAnimatedZero, 353 ToComputedValue, 354 ToCss, 355 ToResolvedValue, 356 ToShmem, 357 ToTyped, 358 )] 359 #[repr(C, u8)] 360 #[allow(missing_docs)] 361 pub enum GenericLengthPercentageOrNormal<LengthPercent> { 362 LengthPercentage(LengthPercent), 363 Normal, 364 } 365 366 pub use self::GenericLengthPercentageOrNormal as LengthPercentageOrNormal; 367 368 impl<LengthPercent> LengthPercentageOrNormal<LengthPercent> { 369 /// Returns the normal value. 370 #[inline] 371 pub fn normal() -> Self { 372 LengthPercentageOrNormal::Normal 373 } 374 } 375 376 /// Anchor size function used by sizing, margin and inset properties. 377 /// This resolves to the size of the anchor at computed time. 378 /// 379 /// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor-size 380 #[derive( 381 Animate, 382 Clone, 383 ComputeSquaredDistance, 384 Debug, 385 MallocSizeOf, 386 PartialEq, 387 SpecifiedValueInfo, 388 ToShmem, 389 ToAnimatedValue, 390 ToAnimatedZero, 391 ToComputedValue, 392 ToResolvedValue, 393 Serialize, 394 Deserialize, 395 )] 396 #[repr(C)] 397 pub struct GenericAnchorSizeFunction<Fallback> { 398 /// Anchor name of the element to anchor to. 399 /// If omitted (i.e. empty), selects the implicit anchor element. 400 #[animation(constant)] 401 pub target_element: DashedIdent, 402 /// Size of the positioned element, expressed in that of the anchor element. 403 /// If omitted, defaults to the axis of the property the function is used in. 404 pub size: AnchorSizeKeyword, 405 /// Value to use in case the anchor function is invalid. 406 pub fallback: Optional<Fallback>, 407 } 408 409 impl<Fallback: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSizeFunction<Fallback> { 410 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) { 411 self.size.try_tactic_adjustment(old_side, new_side); 412 if let Some(fallback) = self.fallback.as_mut() { 413 fallback.try_tactic_adjustment(old_side, new_side); 414 } 415 } 416 } 417 418 impl<Fallback> ToCss for GenericAnchorSizeFunction<Fallback> 419 where 420 Fallback: ToCss, 421 { 422 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result 423 where 424 W: Write, 425 { 426 dest.write_str("anchor-size(")?; 427 let mut previous_entry_printed = false; 428 if !self.target_element.is_empty() { 429 previous_entry_printed = true; 430 self.target_element.to_css(dest)?; 431 } 432 if self.size != AnchorSizeKeyword::None { 433 if previous_entry_printed { 434 dest.write_str(" ")?; 435 } 436 previous_entry_printed = true; 437 self.size.to_css(dest)?; 438 } 439 if let Some(f) = self.fallback.as_ref() { 440 if previous_entry_printed { 441 dest.write_str(", ")?; 442 } 443 f.to_css(dest)?; 444 } 445 dest.write_str(")") 446 } 447 } 448 449 impl<Fallback> Parse for GenericAnchorSizeFunction<Fallback> 450 where 451 Fallback: Parse, 452 { 453 fn parse<'i, 't>( 454 context: &ParserContext, 455 input: &mut Parser<'i, 't>, 456 ) -> Result<Self, ParseError<'i>> { 457 if !static_prefs::pref!("layout.css.anchor-positioning.enabled") { 458 return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 459 } 460 input.expect_function_matching("anchor-size")?; 461 Self::parse_inner(context, input, |i| Fallback::parse(context, i)) 462 } 463 } 464 impl<Fallback> GenericAnchorSizeFunction<Fallback> { 465 /// Is the anchor-size use valid for given property? 466 pub fn valid_for(&self, position_property: PositionProperty) -> bool { 467 position_property.is_absolutely_positioned() 468 } 469 } 470 471 /// Result of resolving an anchor function. 472 pub enum AnchorResolutionResult<'a, LengthPercentage> { 473 /// Function resolved to a valid anchor. 474 Resolved(LengthPercentage), 475 /// Referenced anchor is invalid, but fallback is used. 476 Fallback(&'a LengthPercentage), 477 /// Referenced anchor is invalid. 478 Invalid, 479 } 480 481 impl<'a, LengthPercentage> AnchorResolutionResult<'a, LengthPercentage> { 482 /// Return result for an invalid anchor function, depending on if it has any fallback. 483 pub fn new_anchor_invalid(fallback: Option<&'a LengthPercentage>) -> Self { 484 if let Some(fb) = fallback { 485 return Self::Fallback(fb); 486 } 487 Self::Invalid 488 } 489 } 490 491 impl<LengthPercentage> GenericAnchorSizeFunction<LengthPercentage> { 492 /// Parse the inner part of `anchor-size()`, after the parser has consumed "anchor-size(". 493 pub fn parse_inner<'i, 't, F>( 494 context: &ParserContext, 495 input: &mut Parser<'i, 't>, 496 f: F, 497 ) -> Result<Self, ParseError<'i>> 498 where 499 F: FnOnce(&mut Parser<'i, '_>) -> Result<LengthPercentage, ParseError<'i>>, 500 { 501 input.parse_nested_block(|i| { 502 let mut target_element = i 503 .try_parse(|i| DashedIdent::parse(context, i)) 504 .unwrap_or(DashedIdent::empty()); 505 let size = i 506 .try_parse(AnchorSizeKeyword::parse) 507 .unwrap_or(AnchorSizeKeyword::None); 508 if target_element.is_empty() { 509 target_element = i 510 .try_parse(|i| DashedIdent::parse(context, i)) 511 .unwrap_or(DashedIdent::empty()); 512 } 513 let previous_parsed = !target_element.is_empty() || size != AnchorSizeKeyword::None; 514 let fallback = i 515 .try_parse(|i| { 516 if previous_parsed { 517 i.expect_comma()?; 518 } 519 f(i) 520 }) 521 .ok(); 522 Ok(GenericAnchorSizeFunction { 523 target_element, 524 size: size.into(), 525 fallback: fallback.into(), 526 }) 527 }) 528 } 529 } 530 531 /// Keyword values for the anchor size function. 532 #[derive( 533 Animate, 534 Clone, 535 ComputeSquaredDistance, 536 Copy, 537 Debug, 538 MallocSizeOf, 539 PartialEq, 540 Parse, 541 SpecifiedValueInfo, 542 ToCss, 543 ToShmem, 544 ToAnimatedValue, 545 ToAnimatedZero, 546 ToComputedValue, 547 ToResolvedValue, 548 Serialize, 549 Deserialize, 550 )] 551 #[repr(u8)] 552 pub enum AnchorSizeKeyword { 553 /// Magic value for nothing. 554 #[css(skip)] 555 None, 556 /// Width of the anchor element. 557 Width, 558 /// Height of the anchor element. 559 Height, 560 /// Block size of the anchor element. 561 Block, 562 /// Inline size of the anchor element. 563 Inline, 564 /// Same as `Block`, resolved against the positioned element's writing mode. 565 SelfBlock, 566 /// Same as `Inline`, resolved against the positioned element's writing mode. 567 SelfInline, 568 } 569 570 impl TryTacticAdjustment for AnchorSizeKeyword { 571 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) { 572 if old_side.parallel_to(new_side) { 573 return; 574 } 575 *self = match *self { 576 Self::None => Self::None, 577 Self::Width => Self::Height, 578 Self::Height => Self::Width, 579 Self::Block => Self::Inline, 580 Self::Inline => Self::Block, 581 Self::SelfBlock => Self::SelfInline, 582 Self::SelfInline => Self::SelfBlock, 583 } 584 } 585 } 586 587 /// Specified type for `margin` properties, which allows 588 /// the use of the `anchor-size()` function. 589 #[derive( 590 Animate, 591 Clone, 592 ComputeSquaredDistance, 593 Debug, 594 MallocSizeOf, 595 PartialEq, 596 ToCss, 597 ToShmem, 598 ToAnimatedValue, 599 ToAnimatedZero, 600 ToComputedValue, 601 ToResolvedValue, 602 ToTyped, 603 )] 604 #[repr(C)] 605 pub enum GenericMargin<LP> { 606 /// A `<length-percentage>` value. 607 LengthPercentage(LP), 608 /// An `auto` value. 609 Auto, 610 /// Margin size defined by the anchor element. 611 /// 612 /// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor-size 613 AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>), 614 /// A `<length-percentage>` value, guaranteed to contain `calc()`, 615 /// which then is guaranteed to contain `anchor()` or `anchor-size()`. 616 AnchorContainingCalcFunction(LP), 617 } 618 619 #[cfg(feature = "servo")] 620 impl<LP> GenericMargin<LP> { 621 /// Return true if it is 'auto'. 622 #[inline] 623 pub fn is_auto(&self) -> bool { 624 matches!(self, Self::Auto) 625 } 626 } 627 628 impl<LP> SpecifiedValueInfo for GenericMargin<LP> 629 where 630 LP: SpecifiedValueInfo, 631 { 632 fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) { 633 LP::collect_completion_keywords(f); 634 f(&["auto"]); 635 if static_prefs::pref!("layout.css.anchor-positioning.enabled") { 636 f(&["anchor-size"]); 637 } 638 } 639 } 640 641 impl<LP> Zero for GenericMargin<LP> 642 where 643 LP: Zero, 644 { 645 fn is_zero(&self) -> bool { 646 match self { 647 Self::LengthPercentage(l) => l.is_zero(), 648 Self::Auto | Self::AnchorSizeFunction(_) | Self::AnchorContainingCalcFunction(_) => { 649 false 650 }, 651 } 652 } 653 654 fn zero() -> Self { 655 Self::LengthPercentage(LP::zero()) 656 } 657 }