length.rs (90734B)
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 //! [Length values][length]. 6 //! 7 //! [length]: https://drafts.csswg.org/css-values/#lengths 8 9 use super::{AllowQuirks, Number, Percentage, ToComputedValue}; 10 use crate::computed_value_flags::ComputedValueFlags; 11 use crate::derives::*; 12 use crate::font_metrics::{FontMetrics, FontMetricsOrientation}; 13 #[cfg(feature = "gecko")] 14 use crate::gecko_bindings::structs::GeckoFontMetrics; 15 use crate::parser::{Parse, ParserContext}; 16 use crate::values::computed::{self, CSSPixelLength, Context, FontSize}; 17 use crate::values::generics::length as generics; 18 use crate::values::generics::length::{ 19 GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal, 20 GenericMargin, GenericMaxSize, GenericSize, 21 }; 22 use crate::values::generics::NonNegative; 23 use crate::values::specified::calc::{self, AllowAnchorPositioningFunctions, CalcNode}; 24 use crate::values::specified::font::QueryFontMetricsFlags; 25 use crate::values::specified::NonNegativeNumber; 26 use crate::values::CSSFloat; 27 use crate::{Zero, ZeroNoPercent}; 28 use app_units::AU_PER_PX; 29 use cssparser::{match_ignore_ascii_case, Parser, Token}; 30 use debug_unreachable::debug_unreachable; 31 use std::cmp; 32 use std::fmt::{self, Write}; 33 use style_traits::values::specified::AllowedNumericType; 34 use style_traits::{ 35 CssString, CssWriter, NumericValue, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, 36 ToTyped, TypedValue, 37 }; 38 39 pub use super::image::Image; 40 pub use super::image::{EndingShape as GradientEndingShape, Gradient}; 41 pub use crate::values::specified::calc::CalcLengthPercentage; 42 43 /// Number of pixels per inch 44 pub const PX_PER_IN: CSSFloat = 96.; 45 /// Number of pixels per centimeter 46 pub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54; 47 /// Number of pixels per millimeter 48 pub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4; 49 /// Number of pixels per quarter 50 pub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.; 51 /// Number of pixels per point 52 pub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.; 53 /// Number of pixels per pica 54 pub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.; 55 56 /// A font relative length. Note that if any new value is 57 /// added here, `custom_properties::NonCustomReferences::from_unit` 58 /// must also be updated. Consult the comment in that function as to why. 59 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] 60 #[repr(u8)] 61 pub enum FontRelativeLength { 62 /// A "em" value: https://drafts.csswg.org/css-values/#em 63 #[css(dimension)] 64 Em(CSSFloat), 65 /// A "ex" value: https://drafts.csswg.org/css-values/#ex 66 #[css(dimension)] 67 Ex(CSSFloat), 68 /// A "rex" value: https://drafts.csswg.org/css-values/#rex 69 #[css(dimension)] 70 Rex(CSSFloat), 71 /// A "ch" value: https://drafts.csswg.org/css-values/#ch 72 #[css(dimension)] 73 Ch(CSSFloat), 74 /// A "rch" value: https://drafts.csswg.org/css-values/#rch 75 #[css(dimension)] 76 Rch(CSSFloat), 77 /// A "cap" value: https://drafts.csswg.org/css-values/#cap 78 #[css(dimension)] 79 Cap(CSSFloat), 80 /// A "rcap" value: https://drafts.csswg.org/css-values/#rcap 81 #[css(dimension)] 82 Rcap(CSSFloat), 83 /// An "ic" value: https://drafts.csswg.org/css-values/#ic 84 #[css(dimension)] 85 Ic(CSSFloat), 86 /// A "ric" value: https://drafts.csswg.org/css-values/#ric 87 #[css(dimension)] 88 Ric(CSSFloat), 89 /// A "rem" value: https://drafts.csswg.org/css-values/#rem 90 #[css(dimension)] 91 Rem(CSSFloat), 92 /// A "lh" value: https://drafts.csswg.org/css-values/#lh 93 #[css(dimension)] 94 Lh(CSSFloat), 95 /// A "rlh" value: https://drafts.csswg.org/css-values/#rlh 96 #[css(dimension)] 97 Rlh(CSSFloat), 98 } 99 100 /// A source to resolve font-relative units against 101 #[derive(Clone, Copy, Debug, PartialEq)] 102 pub enum FontBaseSize { 103 /// Use the font-size of the current element. 104 CurrentStyle, 105 /// Use the inherited font-size. 106 InheritedStyle, 107 } 108 109 /// A source to resolve font-relative line-height units against. 110 #[derive(Clone, Copy, Debug, PartialEq)] 111 pub enum LineHeightBase { 112 /// Use the line-height of the current element. 113 CurrentStyle, 114 /// Use the inherited line-height. 115 InheritedStyle, 116 } 117 118 impl FontBaseSize { 119 /// Calculate the actual size for a given context 120 pub fn resolve(&self, context: &Context) -> computed::FontSize { 121 let style = context.style(); 122 match *self { 123 Self::CurrentStyle => style.get_font().clone_font_size(), 124 Self::InheritedStyle => { 125 // If we're using the size from our inherited style, we still need to apply our 126 // own zoom. 127 let zoom = style.effective_zoom_for_inheritance; 128 style.get_parent_font().clone_font_size().zoom(zoom) 129 }, 130 } 131 } 132 } 133 134 impl FontRelativeLength { 135 /// Unit identifier for `em`. 136 pub const EM: &'static str = "em"; 137 /// Unit identifier for `ex`. 138 pub const EX: &'static str = "ex"; 139 /// Unit identifier for `rex`. 140 pub const REX: &'static str = "rex"; 141 /// Unit identifier for `ch`. 142 pub const CH: &'static str = "ch"; 143 /// Unit identifier for `rch`. 144 pub const RCH: &'static str = "rch"; 145 /// Unit identifier for `cap`. 146 pub const CAP: &'static str = "cap"; 147 /// Unit identifier for `rcap`. 148 pub const RCAP: &'static str = "rcap"; 149 /// Unit identifier for `ic`. 150 pub const IC: &'static str = "ic"; 151 /// Unit identifier for `ric`. 152 pub const RIC: &'static str = "ric"; 153 /// Unit identifier for `rem`. 154 pub const REM: &'static str = "rem"; 155 /// Unit identifier for `lh`. 156 pub const LH: &'static str = "lh"; 157 /// Unit identifier for `rlh`. 158 pub const RLH: &'static str = "rlh"; 159 160 /// Return the unitless, raw value. 161 fn unitless_value(&self) -> CSSFloat { 162 match *self { 163 Self::Em(v) 164 | Self::Ex(v) 165 | Self::Rex(v) 166 | Self::Ch(v) 167 | Self::Rch(v) 168 | Self::Cap(v) 169 | Self::Rcap(v) 170 | Self::Ic(v) 171 | Self::Ric(v) 172 | Self::Rem(v) 173 | Self::Lh(v) 174 | Self::Rlh(v) => v, 175 } 176 } 177 178 // Return the unit, as a string. 179 fn unit(&self) -> &'static str { 180 match *self { 181 Self::Em(_) => Self::EM, 182 Self::Ex(_) => Self::EX, 183 Self::Rex(_) => Self::REX, 184 Self::Ch(_) => Self::CH, 185 Self::Rch(_) => Self::RCH, 186 Self::Cap(_) => Self::CAP, 187 Self::Rcap(_) => Self::RCAP, 188 Self::Ic(_) => Self::IC, 189 Self::Ric(_) => Self::RIC, 190 Self::Rem(_) => Self::REM, 191 Self::Lh(_) => Self::LH, 192 Self::Rlh(_) => Self::RLH, 193 } 194 } 195 196 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()> 197 where 198 O: Fn(f32, f32) -> f32, 199 { 200 use self::FontRelativeLength::*; 201 202 if std::mem::discriminant(self) != std::mem::discriminant(other) { 203 return Err(()); 204 } 205 206 Ok(match (self, other) { 207 (&Em(one), &Em(other)) => Em(op(one, other)), 208 (&Ex(one), &Ex(other)) => Ex(op(one, other)), 209 (&Rex(one), &Rex(other)) => Rex(op(one, other)), 210 (&Ch(one), &Ch(other)) => Ch(op(one, other)), 211 (&Rch(one), &Rch(other)) => Rch(op(one, other)), 212 (&Cap(one), &Cap(other)) => Cap(op(one, other)), 213 (&Rcap(one), &Rcap(other)) => Rcap(op(one, other)), 214 (&Ic(one), &Ic(other)) => Ic(op(one, other)), 215 (&Ric(one), &Ric(other)) => Ric(op(one, other)), 216 (&Rem(one), &Rem(other)) => Rem(op(one, other)), 217 (&Lh(one), &Lh(other)) => Lh(op(one, other)), 218 (&Rlh(one), &Rlh(other)) => Rlh(op(one, other)), 219 // See https://github.com/rust-lang/rust/issues/68867. rustc isn't 220 // able to figure it own on its own so we help. 221 _ => unsafe { 222 match *self { 223 Em(..) | Rem(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..) 224 | Ic(..) | Ric(..) | Lh(..) | Rlh(..) => {}, 225 } 226 debug_unreachable!("Forgot to handle unit in try_op()") 227 }, 228 }) 229 } 230 231 fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self { 232 match self { 233 Self::Em(x) => Self::Em(op(*x)), 234 Self::Ex(x) => Self::Ex(op(*x)), 235 Self::Rex(x) => Self::Rex(op(*x)), 236 Self::Ch(x) => Self::Ch(op(*x)), 237 Self::Rch(x) => Self::Rch(op(*x)), 238 Self::Cap(x) => Self::Cap(op(*x)), 239 Self::Rcap(x) => Self::Rcap(op(*x)), 240 Self::Ic(x) => Self::Ic(op(*x)), 241 Self::Ric(x) => Self::Ric(op(*x)), 242 Self::Rem(x) => Self::Rem(op(*x)), 243 Self::Lh(x) => Self::Lh(op(*x)), 244 Self::Rlh(x) => Self::Rlh(op(*x)), 245 } 246 } 247 248 /// Computes the font-relative length. 249 pub fn to_computed_value( 250 &self, 251 context: &Context, 252 base_size: FontBaseSize, 253 line_height_base: LineHeightBase, 254 ) -> computed::Length { 255 let (reference_size, length) = 256 self.reference_font_size_and_length(context, base_size, line_height_base); 257 (reference_size * length).finite() 258 } 259 260 /// Computes the length, given a GeckoFontMetrics getter to resolve font-relative units. 261 #[cfg(feature = "gecko")] 262 pub fn to_computed_pixel_length_with_font_metrics( 263 &self, 264 get_font_metrics: impl Fn() -> GeckoFontMetrics, 265 ) -> Result<CSSFloat, ()> { 266 let metrics = get_font_metrics(); 267 Ok(match *self { 268 Self::Em(v) => v * metrics.mComputedEmSize.px(), 269 Self::Ex(v) => v * metrics.mXSize.px(), 270 Self::Ch(v) => v * metrics.mChSize.px(), 271 Self::Cap(v) => v * metrics.mCapHeight.px(), 272 Self::Ic(v) => v * metrics.mIcWidth.px(), 273 // `lh`, `rlh` are unsupported as we have no line-height context 274 // `rem`, `rex`, `rch`, `rcap`, and `ric` are unsupported as we have no root font context. 275 Self::Lh(_) 276 | Self::Rlh(_) 277 | Self::Rem(_) 278 | Self::Rex(_) 279 | Self::Rch(_) 280 | Self::Rcap(_) 281 | Self::Ric(_) => return Err(()), 282 }) 283 } 284 285 /// Return reference font size. 286 /// 287 /// We use the base_size flag to pass a different size for computing 288 /// font-size and unconstrained font-size. 289 /// 290 /// This returns a pair, the first one is the reference font size, and the 291 /// second one is the unpacked relative length. 292 fn reference_font_size_and_length( 293 &self, 294 context: &Context, 295 base_size: FontBaseSize, 296 line_height_base: LineHeightBase, 297 ) -> (computed::Length, CSSFloat) { 298 fn query_font_metrics( 299 context: &Context, 300 base_size: FontBaseSize, 301 orientation: FontMetricsOrientation, 302 flags: QueryFontMetricsFlags, 303 ) -> FontMetrics { 304 context.query_font_metrics(base_size, orientation, flags) 305 } 306 307 fn ex_size( 308 context: &Context, 309 base_size: FontBaseSize, 310 reference_font_size: &FontSize, 311 ) -> computed::Length { 312 // The x-height is an intrinsically horizontal metric. 313 let metrics = query_font_metrics( 314 context, 315 base_size, 316 FontMetricsOrientation::Horizontal, 317 QueryFontMetricsFlags::empty(), 318 ); 319 metrics.x_height_or_default(reference_font_size.used_size()) 320 } 321 322 fn ch_size( 323 context: &Context, 324 base_size: FontBaseSize, 325 reference_font_size: &FontSize, 326 ) -> computed::Length { 327 // https://drafts.csswg.org/css-values/#ch: 328 // 329 // Equal to the used advance measure of the “0” (ZERO, 330 // U+0030) glyph in the font used to render it. (The advance 331 // measure of a glyph is its advance width or height, 332 // whichever is in the inline axis of the element.) 333 // 334 let metrics = query_font_metrics( 335 context, 336 base_size, 337 FontMetricsOrientation::MatchContextPreferHorizontal, 338 QueryFontMetricsFlags::NEEDS_CH, 339 ); 340 metrics.zero_advance_measure_or_default( 341 reference_font_size.used_size(), 342 context.style().writing_mode.is_upright(), 343 ) 344 } 345 346 fn cap_size(context: &Context, base_size: FontBaseSize) -> computed::Length { 347 let metrics = query_font_metrics( 348 context, 349 base_size, 350 FontMetricsOrientation::Horizontal, 351 QueryFontMetricsFlags::empty(), 352 ); 353 metrics.cap_height_or_default() 354 } 355 356 fn ic_size( 357 context: &Context, 358 base_size: FontBaseSize, 359 reference_font_size: &FontSize, 360 ) -> computed::Length { 361 let metrics = query_font_metrics( 362 context, 363 base_size, 364 FontMetricsOrientation::MatchContextPreferVertical, 365 QueryFontMetricsFlags::NEEDS_IC, 366 ); 367 metrics.ic_width_or_default(reference_font_size.used_size()) 368 } 369 370 let reference_font_size = base_size.resolve(context); 371 match *self { 372 // Local font-relative units 373 Self::Em(length) => { 374 if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle { 375 context 376 .rule_cache_conditions 377 .borrow_mut() 378 .set_font_size_dependency(reference_font_size.computed_size); 379 } 380 381 (reference_font_size.computed_size(), length) 382 }, 383 Self::Lh(length) => { 384 // https://drafts.csswg.org/css-values-4/#lh 385 // 386 // When specified in media-query, the lh units refer to the 387 // initial values of font and line-height properties. 388 // 389 let reference_size = if context.in_media_query { 390 context 391 .device() 392 .calc_line_height( 393 &context.default_style().get_font(), 394 context.style().writing_mode, 395 None, 396 ) 397 .0 398 } else { 399 let line_height = context.builder.calc_line_height( 400 context.device(), 401 line_height_base, 402 context.style().writing_mode, 403 ); 404 if context.for_non_inherited_property 405 && line_height_base == LineHeightBase::CurrentStyle 406 { 407 context 408 .rule_cache_conditions 409 .borrow_mut() 410 .set_line_height_dependency(line_height) 411 } 412 line_height.0 413 }; 414 (reference_size, length) 415 }, 416 Self::Ex(length) => (ex_size(context, base_size, &reference_font_size), length), 417 Self::Ch(length) => (ch_size(context, base_size, &reference_font_size), length), 418 Self::Cap(length) => (cap_size(context, base_size), length), 419 Self::Ic(length) => (ic_size(context, base_size, &reference_font_size), length), 420 421 // Root font relative units 422 Self::Rex(length) => { 423 let reference_size = if context.builder.is_root_element || context.in_media_query { 424 ex_size(context, base_size, &reference_font_size) 425 } else { 426 context 427 .device() 428 .root_font_metrics_ex() 429 .zoom(context.builder.effective_zoom) 430 }; 431 (reference_size, length) 432 }, 433 Self::Rch(length) => { 434 let reference_size = if context.builder.is_root_element || context.in_media_query { 435 ch_size(context, base_size, &reference_font_size) 436 } else { 437 context 438 .device() 439 .root_font_metrics_ch() 440 .zoom(context.builder.effective_zoom) 441 }; 442 (reference_size, length) 443 }, 444 Self::Rcap(length) => { 445 let reference_size = if context.builder.is_root_element || context.in_media_query { 446 cap_size(context, base_size) 447 } else { 448 context 449 .device() 450 .root_font_metrics_cap() 451 .zoom(context.builder.effective_zoom) 452 }; 453 (reference_size, length) 454 }, 455 Self::Ric(length) => { 456 let reference_size = if context.builder.is_root_element || context.in_media_query { 457 ic_size(context, base_size, &reference_font_size) 458 } else { 459 context 460 .device() 461 .root_font_metrics_ic() 462 .zoom(context.builder.effective_zoom) 463 }; 464 (reference_size, length) 465 }, 466 Self::Rem(length) => { 467 // https://drafts.csswg.org/css-values/#rem: 468 // 469 // When specified on the font-size property of the root 470 // element, the rem units refer to the property's initial 471 // value. 472 // 473 let reference_size = if context.builder.is_root_element || context.in_media_query { 474 reference_font_size.computed_size() 475 } else { 476 context 477 .device() 478 .root_font_size() 479 .zoom(context.builder.effective_zoom) 480 }; 481 (reference_size, length) 482 }, 483 Self::Rlh(length) => { 484 // https://drafts.csswg.org/css-values-4/#rlh 485 // 486 // When specified on the root element, the rlh units refer 487 // to the initial values of font and line-height properties. 488 // 489 let reference_size = if context.builder.is_root_element { 490 context 491 .builder 492 .calc_line_height( 493 context.device(), 494 line_height_base, 495 context.style().writing_mode, 496 ) 497 .0 498 } else if context.in_media_query { 499 context 500 .device() 501 .calc_line_height( 502 &context.default_style().get_font(), 503 context.style().writing_mode, 504 None, 505 ) 506 .0 507 } else { 508 context.device().root_line_height() 509 }; 510 let reference_size = reference_size.zoom(context.builder.effective_zoom); 511 (reference_size, length) 512 }, 513 } 514 } 515 } 516 517 /// https://drafts.csswg.org/css-values/#viewport-variants 518 pub enum ViewportVariant { 519 /// https://drafts.csswg.org/css-values/#ua-default-viewport-size 520 UADefault, 521 /// https://drafts.csswg.org/css-values/#small-viewport-percentage-units 522 Small, 523 /// https://drafts.csswg.org/css-values/#large-viewport-percentage-units 524 Large, 525 /// https://drafts.csswg.org/css-values/#dynamic-viewport-percentage-units 526 Dynamic, 527 } 528 529 /// https://drafts.csswg.org/css-values/#viewport-relative-units 530 #[derive(PartialEq)] 531 enum ViewportUnit { 532 /// *vw units. 533 Vw, 534 /// *vh units. 535 Vh, 536 /// *vmin units. 537 Vmin, 538 /// *vmax units. 539 Vmax, 540 /// *vb units. 541 Vb, 542 /// *vi units. 543 Vi, 544 } 545 546 /// A viewport-relative length. 547 /// 548 /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths> 549 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] 550 #[repr(u8)] 551 pub enum ViewportPercentageLength { 552 /// <https://drafts.csswg.org/css-values/#valdef-length-vw> 553 #[css(dimension)] 554 Vw(CSSFloat), 555 /// <https://drafts.csswg.org/css-values/#valdef-length-svw> 556 #[css(dimension)] 557 Svw(CSSFloat), 558 /// <https://drafts.csswg.org/css-values/#valdef-length-lvw> 559 #[css(dimension)] 560 Lvw(CSSFloat), 561 /// <https://drafts.csswg.org/css-values/#valdef-length-dvw> 562 #[css(dimension)] 563 Dvw(CSSFloat), 564 /// <https://drafts.csswg.org/css-values/#valdef-length-vh> 565 #[css(dimension)] 566 Vh(CSSFloat), 567 /// <https://drafts.csswg.org/css-values/#valdef-length-svh> 568 #[css(dimension)] 569 Svh(CSSFloat), 570 /// <https://drafts.csswg.org/css-values/#valdef-length-lvh> 571 #[css(dimension)] 572 Lvh(CSSFloat), 573 /// <https://drafts.csswg.org/css-values/#valdef-length-dvh> 574 #[css(dimension)] 575 Dvh(CSSFloat), 576 /// <https://drafts.csswg.org/css-values/#valdef-length-vmin> 577 #[css(dimension)] 578 Vmin(CSSFloat), 579 /// <https://drafts.csswg.org/css-values/#valdef-length-svmin> 580 #[css(dimension)] 581 Svmin(CSSFloat), 582 /// <https://drafts.csswg.org/css-values/#valdef-length-lvmin> 583 #[css(dimension)] 584 Lvmin(CSSFloat), 585 /// <https://drafts.csswg.org/css-values/#valdef-length-dvmin> 586 #[css(dimension)] 587 Dvmin(CSSFloat), 588 /// <https://drafts.csswg.org/css-values/#valdef-length-vmax> 589 #[css(dimension)] 590 Vmax(CSSFloat), 591 /// <https://drafts.csswg.org/css-values/#valdef-length-svmax> 592 #[css(dimension)] 593 Svmax(CSSFloat), 594 /// <https://drafts.csswg.org/css-values/#valdef-length-lvmax> 595 #[css(dimension)] 596 Lvmax(CSSFloat), 597 /// <https://drafts.csswg.org/css-values/#valdef-length-dvmax> 598 #[css(dimension)] 599 Dvmax(CSSFloat), 600 /// <https://drafts.csswg.org/css-values/#valdef-length-vb> 601 #[css(dimension)] 602 Vb(CSSFloat), 603 /// <https://drafts.csswg.org/css-values/#valdef-length-svb> 604 #[css(dimension)] 605 Svb(CSSFloat), 606 /// <https://drafts.csswg.org/css-values/#valdef-length-lvb> 607 #[css(dimension)] 608 Lvb(CSSFloat), 609 /// <https://drafts.csswg.org/css-values/#valdef-length-dvb> 610 #[css(dimension)] 611 Dvb(CSSFloat), 612 /// <https://drafts.csswg.org/css-values/#valdef-length-vi> 613 #[css(dimension)] 614 Vi(CSSFloat), 615 /// <https://drafts.csswg.org/css-values/#valdef-length-svi> 616 #[css(dimension)] 617 Svi(CSSFloat), 618 /// <https://drafts.csswg.org/css-values/#valdef-length-lvi> 619 #[css(dimension)] 620 Lvi(CSSFloat), 621 /// <https://drafts.csswg.org/css-values/#valdef-length-dvi> 622 #[css(dimension)] 623 Dvi(CSSFloat), 624 } 625 626 impl ViewportPercentageLength { 627 /// Return the unitless, raw value. 628 fn unitless_value(&self) -> CSSFloat { 629 self.unpack().2 630 } 631 632 // Return the unit, as a string. 633 fn unit(&self) -> &'static str { 634 match *self { 635 Self::Vw(_) => "vw", 636 Self::Lvw(_) => "lvw", 637 Self::Svw(_) => "svw", 638 Self::Dvw(_) => "dvw", 639 Self::Vh(_) => "vh", 640 Self::Svh(_) => "svh", 641 Self::Lvh(_) => "lvh", 642 Self::Dvh(_) => "dvh", 643 Self::Vmin(_) => "vmin", 644 Self::Svmin(_) => "svmin", 645 Self::Lvmin(_) => "lvmin", 646 Self::Dvmin(_) => "dvmin", 647 Self::Vmax(_) => "vmax", 648 Self::Svmax(_) => "svmax", 649 Self::Lvmax(_) => "lvmax", 650 Self::Dvmax(_) => "dvmax", 651 Self::Vb(_) => "vb", 652 Self::Svb(_) => "svb", 653 Self::Lvb(_) => "lvb", 654 Self::Dvb(_) => "dvb", 655 Self::Vi(_) => "vi", 656 Self::Svi(_) => "svi", 657 Self::Lvi(_) => "lvi", 658 Self::Dvi(_) => "dvi", 659 } 660 } 661 662 fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) { 663 match *self { 664 Self::Vw(v) => (ViewportVariant::UADefault, ViewportUnit::Vw, v), 665 Self::Svw(v) => (ViewportVariant::Small, ViewportUnit::Vw, v), 666 Self::Lvw(v) => (ViewportVariant::Large, ViewportUnit::Vw, v), 667 Self::Dvw(v) => (ViewportVariant::Dynamic, ViewportUnit::Vw, v), 668 Self::Vh(v) => (ViewportVariant::UADefault, ViewportUnit::Vh, v), 669 Self::Svh(v) => (ViewportVariant::Small, ViewportUnit::Vh, v), 670 Self::Lvh(v) => (ViewportVariant::Large, ViewportUnit::Vh, v), 671 Self::Dvh(v) => (ViewportVariant::Dynamic, ViewportUnit::Vh, v), 672 Self::Vmin(v) => (ViewportVariant::UADefault, ViewportUnit::Vmin, v), 673 Self::Svmin(v) => (ViewportVariant::Small, ViewportUnit::Vmin, v), 674 Self::Lvmin(v) => (ViewportVariant::Large, ViewportUnit::Vmin, v), 675 Self::Dvmin(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmin, v), 676 Self::Vmax(v) => (ViewportVariant::UADefault, ViewportUnit::Vmax, v), 677 Self::Svmax(v) => (ViewportVariant::Small, ViewportUnit::Vmax, v), 678 Self::Lvmax(v) => (ViewportVariant::Large, ViewportUnit::Vmax, v), 679 Self::Dvmax(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmax, v), 680 Self::Vb(v) => (ViewportVariant::UADefault, ViewportUnit::Vb, v), 681 Self::Svb(v) => (ViewportVariant::Small, ViewportUnit::Vb, v), 682 Self::Lvb(v) => (ViewportVariant::Large, ViewportUnit::Vb, v), 683 Self::Dvb(v) => (ViewportVariant::Dynamic, ViewportUnit::Vb, v), 684 Self::Vi(v) => (ViewportVariant::UADefault, ViewportUnit::Vi, v), 685 Self::Svi(v) => (ViewportVariant::Small, ViewportUnit::Vi, v), 686 Self::Lvi(v) => (ViewportVariant::Large, ViewportUnit::Vi, v), 687 Self::Dvi(v) => (ViewportVariant::Dynamic, ViewportUnit::Vi, v), 688 } 689 } 690 691 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()> 692 where 693 O: Fn(f32, f32) -> f32, 694 { 695 use self::ViewportPercentageLength::*; 696 697 if std::mem::discriminant(self) != std::mem::discriminant(other) { 698 return Err(()); 699 } 700 701 Ok(match (self, other) { 702 (&Vw(one), &Vw(other)) => Vw(op(one, other)), 703 (&Svw(one), &Svw(other)) => Svw(op(one, other)), 704 (&Lvw(one), &Lvw(other)) => Lvw(op(one, other)), 705 (&Dvw(one), &Dvw(other)) => Dvw(op(one, other)), 706 (&Vh(one), &Vh(other)) => Vh(op(one, other)), 707 (&Svh(one), &Svh(other)) => Svh(op(one, other)), 708 (&Lvh(one), &Lvh(other)) => Lvh(op(one, other)), 709 (&Dvh(one), &Dvh(other)) => Dvh(op(one, other)), 710 (&Vmin(one), &Vmin(other)) => Vmin(op(one, other)), 711 (&Svmin(one), &Svmin(other)) => Svmin(op(one, other)), 712 (&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)), 713 (&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)), 714 (&Vmax(one), &Vmax(other)) => Vmax(op(one, other)), 715 (&Svmax(one), &Svmax(other)) => Svmax(op(one, other)), 716 (&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)), 717 (&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)), 718 (&Vb(one), &Vb(other)) => Vb(op(one, other)), 719 (&Svb(one), &Svb(other)) => Svb(op(one, other)), 720 (&Lvb(one), &Lvb(other)) => Lvb(op(one, other)), 721 (&Dvb(one), &Dvb(other)) => Dvb(op(one, other)), 722 (&Vi(one), &Vi(other)) => Vi(op(one, other)), 723 (&Svi(one), &Svi(other)) => Svi(op(one, other)), 724 (&Lvi(one), &Lvi(other)) => Lvi(op(one, other)), 725 (&Dvi(one), &Dvi(other)) => Dvi(op(one, other)), 726 // See https://github.com/rust-lang/rust/issues/68867. rustc isn't 727 // able to figure it own on its own so we help. 728 _ => unsafe { 729 match *self { 730 Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..) 731 | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..) 732 | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..) 733 | Svi(..) | Lvi(..) | Dvi(..) => {}, 734 } 735 debug_unreachable!("Forgot to handle unit in try_op()") 736 }, 737 }) 738 } 739 740 fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self { 741 match self { 742 Self::Vw(x) => Self::Vw(op(*x)), 743 Self::Svw(x) => Self::Svw(op(*x)), 744 Self::Lvw(x) => Self::Lvw(op(*x)), 745 Self::Dvw(x) => Self::Dvw(op(*x)), 746 Self::Vh(x) => Self::Vh(op(*x)), 747 Self::Svh(x) => Self::Svh(op(*x)), 748 Self::Lvh(x) => Self::Lvh(op(*x)), 749 Self::Dvh(x) => Self::Dvh(op(*x)), 750 Self::Vmin(x) => Self::Vmin(op(*x)), 751 Self::Svmin(x) => Self::Svmin(op(*x)), 752 Self::Lvmin(x) => Self::Lvmin(op(*x)), 753 Self::Dvmin(x) => Self::Dvmin(op(*x)), 754 Self::Vmax(x) => Self::Vmax(op(*x)), 755 Self::Svmax(x) => Self::Svmax(op(*x)), 756 Self::Lvmax(x) => Self::Lvmax(op(*x)), 757 Self::Dvmax(x) => Self::Dvmax(op(*x)), 758 Self::Vb(x) => Self::Vb(op(*x)), 759 Self::Svb(x) => Self::Svb(op(*x)), 760 Self::Lvb(x) => Self::Lvb(op(*x)), 761 Self::Dvb(x) => Self::Dvb(op(*x)), 762 Self::Vi(x) => Self::Vi(op(*x)), 763 Self::Svi(x) => Self::Svi(op(*x)), 764 Self::Lvi(x) => Self::Lvi(op(*x)), 765 Self::Dvi(x) => Self::Dvi(op(*x)), 766 } 767 } 768 769 /// Computes the given viewport-relative length for the given viewport size. 770 pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength { 771 let (variant, unit, factor) = self.unpack(); 772 let size = context.viewport_size_for_viewport_unit_resolution(variant); 773 let length: app_units::Au = match unit { 774 ViewportUnit::Vw => size.width, 775 ViewportUnit::Vh => size.height, 776 ViewportUnit::Vmin => cmp::min(size.width, size.height), 777 ViewportUnit::Vmax => cmp::max(size.width, size.height), 778 ViewportUnit::Vi | ViewportUnit::Vb => { 779 context 780 .rule_cache_conditions 781 .borrow_mut() 782 .set_writing_mode_dependency(context.builder.writing_mode); 783 if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() { 784 size.width 785 } else { 786 size.height 787 } 788 }, 789 }; 790 791 // NOTE: This is in app units! 792 let length = context.builder.effective_zoom.zoom(length.0 as f32); 793 794 // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform. 795 // See bug 989802. We truncate so that adding multiple viewport units that add up to 100 796 // does not overflow due to rounding differences. We convert appUnits to CSS px manually 797 // here to avoid premature clamping by going through the Au type. 798 let trunc_scaled = 799 ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32; 800 CSSPixelLength::new(crate::values::normalize(trunc_scaled)) 801 } 802 } 803 804 /// HTML5 "character width", as defined in HTML5 § 14.5.4. 805 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] 806 #[repr(C)] 807 pub struct CharacterWidth(pub i32); 808 809 impl CharacterWidth { 810 /// Computes the given character width. 811 pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length { 812 // This applies the *converting a character width to pixels* algorithm 813 // as specified in HTML5 § 14.5.4. 814 // 815 // TODO(pcwalton): Find these from the font. 816 let average_advance = reference_font_size * 0.5; 817 let max_advance = reference_font_size; 818 (average_advance * (self.0 as CSSFloat - 1.0) + max_advance).finite() 819 } 820 } 821 822 /// Represents an absolute length with its unit 823 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] 824 #[repr(u8)] 825 pub enum AbsoluteLength { 826 /// An absolute length in pixels (px) 827 #[css(dimension)] 828 Px(CSSFloat), 829 /// An absolute length in inches (in) 830 #[css(dimension)] 831 In(CSSFloat), 832 /// An absolute length in centimeters (cm) 833 #[css(dimension)] 834 Cm(CSSFloat), 835 /// An absolute length in millimeters (mm) 836 #[css(dimension)] 837 Mm(CSSFloat), 838 /// An absolute length in quarter-millimeters (q) 839 #[css(dimension)] 840 Q(CSSFloat), 841 /// An absolute length in points (pt) 842 #[css(dimension)] 843 Pt(CSSFloat), 844 /// An absolute length in pica (pc) 845 #[css(dimension)] 846 Pc(CSSFloat), 847 } 848 849 impl AbsoluteLength { 850 /// Return the unitless, raw value. 851 fn unitless_value(&self) -> CSSFloat { 852 match *self { 853 Self::Px(v) 854 | Self::In(v) 855 | Self::Cm(v) 856 | Self::Mm(v) 857 | Self::Q(v) 858 | Self::Pt(v) 859 | Self::Pc(v) => v, 860 } 861 } 862 863 // Return the unit, as a string. 864 fn unit(&self) -> &'static str { 865 match *self { 866 Self::Px(_) => "px", 867 Self::In(_) => "in", 868 Self::Cm(_) => "cm", 869 Self::Mm(_) => "mm", 870 Self::Q(_) => "q", 871 Self::Pt(_) => "pt", 872 Self::Pc(_) => "pc", 873 } 874 } 875 876 /// Convert this into a pixel value. 877 #[inline] 878 pub fn to_px(&self) -> CSSFloat { 879 match *self { 880 Self::Px(value) => value, 881 Self::In(value) => value * PX_PER_IN, 882 Self::Cm(value) => value * PX_PER_CM, 883 Self::Mm(value) => value * PX_PER_MM, 884 Self::Q(value) => value * PX_PER_Q, 885 Self::Pt(value) => value * PX_PER_PT, 886 Self::Pc(value) => value * PX_PER_PC, 887 } 888 } 889 890 fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()> 891 where 892 O: Fn(f32, f32) -> f32, 893 { 894 Ok(Self::Px(op(self.to_px(), other.to_px()))) 895 } 896 897 fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self { 898 Self::Px(op(self.to_px())) 899 } 900 } 901 902 impl ToComputedValue for AbsoluteLength { 903 type ComputedValue = CSSPixelLength; 904 905 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 906 CSSPixelLength::new(self.to_px()) 907 .zoom(context.builder.effective_zoom) 908 .finite() 909 } 910 911 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 912 Self::Px(computed.px()) 913 } 914 } 915 916 impl PartialOrd for AbsoluteLength { 917 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { 918 self.to_px().partial_cmp(&other.to_px()) 919 } 920 } 921 922 /// A container query length. 923 /// 924 /// <https://drafts.csswg.org/css-contain-3/#container-lengths> 925 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)] 926 #[repr(u8)] 927 pub enum ContainerRelativeLength { 928 /// 1% of query container's width 929 #[css(dimension)] 930 Cqw(CSSFloat), 931 /// 1% of query container's height 932 #[css(dimension)] 933 Cqh(CSSFloat), 934 /// 1% of query container's inline size 935 #[css(dimension)] 936 Cqi(CSSFloat), 937 /// 1% of query container's block size 938 #[css(dimension)] 939 Cqb(CSSFloat), 940 /// The smaller value of `cqi` or `cqb` 941 #[css(dimension)] 942 Cqmin(CSSFloat), 943 /// The larger value of `cqi` or `cqb` 944 #[css(dimension)] 945 Cqmax(CSSFloat), 946 } 947 948 impl ContainerRelativeLength { 949 fn unitless_value(&self) -> CSSFloat { 950 match *self { 951 Self::Cqw(v) 952 | Self::Cqh(v) 953 | Self::Cqi(v) 954 | Self::Cqb(v) 955 | Self::Cqmin(v) 956 | Self::Cqmax(v) => v, 957 } 958 } 959 960 // Return the unit, as a string. 961 fn unit(&self) -> &'static str { 962 match *self { 963 Self::Cqw(_) => "cqw", 964 Self::Cqh(_) => "cqh", 965 Self::Cqi(_) => "cqi", 966 Self::Cqb(_) => "cqb", 967 Self::Cqmin(_) => "cqmin", 968 Self::Cqmax(_) => "cqmax", 969 } 970 } 971 972 pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()> 973 where 974 O: Fn(f32, f32) -> f32, 975 { 976 use self::ContainerRelativeLength::*; 977 978 if std::mem::discriminant(self) != std::mem::discriminant(other) { 979 return Err(()); 980 } 981 982 Ok(match (self, other) { 983 (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)), 984 (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)), 985 (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)), 986 (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)), 987 (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)), 988 (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)), 989 990 // See https://github.com/rust-lang/rust/issues/68867, then 991 // https://github.com/rust-lang/rust/pull/95161. rustc isn't 992 // able to figure it own on its own so we help. 993 _ => unsafe { 994 match *self { 995 Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {}, 996 } 997 debug_unreachable!("Forgot to handle unit in try_op()") 998 }, 999 }) 1000 } 1001 1002 pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self { 1003 match self { 1004 Self::Cqw(x) => Self::Cqw(op(*x)), 1005 Self::Cqh(x) => Self::Cqh(op(*x)), 1006 Self::Cqi(x) => Self::Cqi(op(*x)), 1007 Self::Cqb(x) => Self::Cqb(op(*x)), 1008 Self::Cqmin(x) => Self::Cqmin(op(*x)), 1009 Self::Cqmax(x) => Self::Cqmax(op(*x)), 1010 } 1011 } 1012 1013 /// Computes the given container-relative length. 1014 pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength { 1015 if context.for_non_inherited_property { 1016 context.rule_cache_conditions.borrow_mut().set_uncacheable(); 1017 } 1018 context 1019 .builder 1020 .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS); 1021 1022 // TODO(emilio, bug 1894104): Need to handle zoom here, probably something like 1023 // container_zoom - effective_zoom or so. See 1024 // https://github.com/w3c/csswg-drafts/issues/10268 1025 let size = context.get_container_size_query(); 1026 let (factor, container_length) = match *self { 1027 Self::Cqw(v) => (v, size.get_container_width(context)), 1028 Self::Cqh(v) => (v, size.get_container_height(context)), 1029 Self::Cqi(v) => (v, size.get_container_inline_size(context)), 1030 Self::Cqb(v) => (v, size.get_container_block_size(context)), 1031 Self::Cqmin(v) => ( 1032 v, 1033 cmp::min( 1034 size.get_container_inline_size(context), 1035 size.get_container_block_size(context), 1036 ), 1037 ), 1038 Self::Cqmax(v) => ( 1039 v, 1040 cmp::max( 1041 size.get_container_inline_size(context), 1042 size.get_container_block_size(context), 1043 ), 1044 ), 1045 }; 1046 CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite() 1047 } 1048 } 1049 1050 /// A `<length>` without taking `calc` expressions into account 1051 /// 1052 /// <https://drafts.csswg.org/css-values/#lengths> 1053 #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)] 1054 #[repr(u8)] 1055 pub enum NoCalcLength { 1056 /// An absolute length 1057 /// 1058 /// <https://drafts.csswg.org/css-values/#absolute-length> 1059 Absolute(AbsoluteLength), 1060 1061 /// A font-relative length: 1062 /// 1063 /// <https://drafts.csswg.org/css-values/#font-relative-lengths> 1064 FontRelative(FontRelativeLength), 1065 1066 /// A viewport-relative length. 1067 /// 1068 /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths> 1069 ViewportPercentage(ViewportPercentageLength), 1070 1071 /// A container query length. 1072 /// 1073 /// <https://drafts.csswg.org/css-contain-3/#container-lengths> 1074 ContainerRelative(ContainerRelativeLength), 1075 /// HTML5 "character width", as defined in HTML5 § 14.5.4. 1076 /// 1077 /// This cannot be specified by the user directly and is only generated by 1078 /// `Stylist::synthesize_rules_for_legacy_attributes()`. 1079 ServoCharacterWidth(CharacterWidth), 1080 } 1081 1082 impl NoCalcLength { 1083 /// Return the unitless, raw value. 1084 pub fn unitless_value(&self) -> CSSFloat { 1085 match *self { 1086 Self::Absolute(v) => v.unitless_value(), 1087 Self::FontRelative(v) => v.unitless_value(), 1088 Self::ViewportPercentage(v) => v.unitless_value(), 1089 Self::ContainerRelative(v) => v.unitless_value(), 1090 Self::ServoCharacterWidth(c) => c.0 as f32, 1091 } 1092 } 1093 1094 // Return the unit, as a string. 1095 fn unit(&self) -> &'static str { 1096 match *self { 1097 Self::Absolute(v) => v.unit(), 1098 Self::FontRelative(v) => v.unit(), 1099 Self::ViewportPercentage(v) => v.unit(), 1100 Self::ContainerRelative(v) => v.unit(), 1101 Self::ServoCharacterWidth(_) => "", 1102 } 1103 } 1104 1105 /// Returns whether the value of this length without unit is less than zero. 1106 pub fn is_negative(&self) -> bool { 1107 self.unitless_value().is_sign_negative() 1108 } 1109 1110 /// Returns whether the value of this length without unit is equal to zero. 1111 pub fn is_zero(&self) -> bool { 1112 self.unitless_value() == 0.0 1113 } 1114 1115 /// Returns whether the value of this length without unit is infinite. 1116 pub fn is_infinite(&self) -> bool { 1117 self.unitless_value().is_infinite() 1118 } 1119 1120 /// Returns whether the value of this length without unit is NaN. 1121 pub fn is_nan(&self) -> bool { 1122 self.unitless_value().is_nan() 1123 } 1124 1125 /// Whether text-only zoom should be applied to this length. 1126 /// 1127 /// Generally, font-dependent/relative units don't get text-only-zoomed, 1128 /// because the font they're relative to should be zoomed already. 1129 pub fn should_zoom_text(&self) -> bool { 1130 match *self { 1131 Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true, 1132 Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false, 1133 } 1134 } 1135 1136 /// Parse a given absolute or relative dimension. 1137 pub fn parse_dimension( 1138 context: &ParserContext, 1139 value: CSSFloat, 1140 unit: &str, 1141 ) -> Result<Self, ()> { 1142 Ok(match_ignore_ascii_case! { unit, 1143 "px" => Self::Absolute(AbsoluteLength::Px(value)), 1144 "in" => Self::Absolute(AbsoluteLength::In(value)), 1145 "cm" => Self::Absolute(AbsoluteLength::Cm(value)), 1146 "mm" => Self::Absolute(AbsoluteLength::Mm(value)), 1147 "q" => Self::Absolute(AbsoluteLength::Q(value)), 1148 "pt" => Self::Absolute(AbsoluteLength::Pt(value)), 1149 "pc" => Self::Absolute(AbsoluteLength::Pc(value)), 1150 // font-relative 1151 "em" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Em(value)), 1152 "ex" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ex(value)), 1153 "rex" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rex(value)), 1154 "ch" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ch(value)), 1155 "rch" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rch(value)), 1156 "cap" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Cap(value)), 1157 "rcap" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rcap(value)), 1158 "ic" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ic(value)), 1159 "ric" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Ric(value)), 1160 "rem" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rem(value)), 1161 "lh" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Lh(value)), 1162 "rlh" if context.allows_computational_dependence() => Self::FontRelative(FontRelativeLength::Rlh(value)), 1163 // viewport percentages 1164 "vw" if !context.in_page_rule() => { 1165 Self::ViewportPercentage(ViewportPercentageLength::Vw(value)) 1166 }, 1167 "svw" if !context.in_page_rule() => { 1168 Self::ViewportPercentage(ViewportPercentageLength::Svw(value)) 1169 }, 1170 "lvw" if !context.in_page_rule() => { 1171 Self::ViewportPercentage(ViewportPercentageLength::Lvw(value)) 1172 }, 1173 "dvw" if !context.in_page_rule() => { 1174 Self::ViewportPercentage(ViewportPercentageLength::Dvw(value)) 1175 }, 1176 "vh" if !context.in_page_rule() => { 1177 Self::ViewportPercentage(ViewportPercentageLength::Vh(value)) 1178 }, 1179 "svh" if !context.in_page_rule() => { 1180 Self::ViewportPercentage(ViewportPercentageLength::Svh(value)) 1181 }, 1182 "lvh" if !context.in_page_rule() => { 1183 Self::ViewportPercentage(ViewportPercentageLength::Lvh(value)) 1184 }, 1185 "dvh" if !context.in_page_rule() => { 1186 Self::ViewportPercentage(ViewportPercentageLength::Dvh(value)) 1187 }, 1188 "vmin" if !context.in_page_rule() => { 1189 Self::ViewportPercentage(ViewportPercentageLength::Vmin(value)) 1190 }, 1191 "svmin" if !context.in_page_rule() => { 1192 Self::ViewportPercentage(ViewportPercentageLength::Svmin(value)) 1193 }, 1194 "lvmin" if !context.in_page_rule() => { 1195 Self::ViewportPercentage(ViewportPercentageLength::Lvmin(value)) 1196 }, 1197 "dvmin" if !context.in_page_rule() => { 1198 Self::ViewportPercentage(ViewportPercentageLength::Dvmin(value)) 1199 }, 1200 "vmax" if !context.in_page_rule() => { 1201 Self::ViewportPercentage(ViewportPercentageLength::Vmax(value)) 1202 }, 1203 "svmax" if !context.in_page_rule() => { 1204 Self::ViewportPercentage(ViewportPercentageLength::Svmax(value)) 1205 }, 1206 "lvmax" if !context.in_page_rule() => { 1207 Self::ViewportPercentage(ViewportPercentageLength::Lvmax(value)) 1208 }, 1209 "dvmax" if !context.in_page_rule() => { 1210 Self::ViewportPercentage(ViewportPercentageLength::Dvmax(value)) 1211 }, 1212 "vb" if !context.in_page_rule() => { 1213 Self::ViewportPercentage(ViewportPercentageLength::Vb(value)) 1214 }, 1215 "svb" if !context.in_page_rule() => { 1216 Self::ViewportPercentage(ViewportPercentageLength::Svb(value)) 1217 }, 1218 "lvb" if !context.in_page_rule() => { 1219 Self::ViewportPercentage(ViewportPercentageLength::Lvb(value)) 1220 }, 1221 "dvb" if !context.in_page_rule() => { 1222 Self::ViewportPercentage(ViewportPercentageLength::Dvb(value)) 1223 }, 1224 "vi" if !context.in_page_rule() => { 1225 Self::ViewportPercentage(ViewportPercentageLength::Vi(value)) 1226 }, 1227 "svi" if !context.in_page_rule() => { 1228 Self::ViewportPercentage(ViewportPercentageLength::Svi(value)) 1229 }, 1230 "lvi" if !context.in_page_rule() => { 1231 Self::ViewportPercentage(ViewportPercentageLength::Lvi(value)) 1232 }, 1233 "dvi" if !context.in_page_rule() => { 1234 Self::ViewportPercentage(ViewportPercentageLength::Dvi(value)) 1235 }, 1236 // Container query lengths. Inherit the limitation from viewport units since 1237 // we may fall back to them. 1238 "cqw" if !context.in_page_rule() && cfg!(feature = "gecko") => { 1239 Self::ContainerRelative(ContainerRelativeLength::Cqw(value)) 1240 }, 1241 "cqh" if !context.in_page_rule() && cfg!(feature = "gecko") => { 1242 Self::ContainerRelative(ContainerRelativeLength::Cqh(value)) 1243 }, 1244 "cqi" if !context.in_page_rule() && cfg!(feature = "gecko") => { 1245 Self::ContainerRelative(ContainerRelativeLength::Cqi(value)) 1246 }, 1247 "cqb" if !context.in_page_rule() && cfg!(feature = "gecko") => { 1248 Self::ContainerRelative(ContainerRelativeLength::Cqb(value)) 1249 }, 1250 "cqmin" if !context.in_page_rule() && cfg!(feature = "gecko") => { 1251 Self::ContainerRelative(ContainerRelativeLength::Cqmin(value)) 1252 }, 1253 "cqmax" if !context.in_page_rule() && cfg!(feature = "gecko") => { 1254 Self::ContainerRelative(ContainerRelativeLength::Cqmax(value)) 1255 }, 1256 _ => return Err(()), 1257 }) 1258 } 1259 1260 pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()> 1261 where 1262 O: Fn(f32, f32) -> f32, 1263 { 1264 use self::NoCalcLength::*; 1265 1266 if std::mem::discriminant(self) != std::mem::discriminant(other) { 1267 return Err(()); 1268 } 1269 1270 Ok(match (self, other) { 1271 (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?), 1272 (&FontRelative(ref one), &FontRelative(ref other)) => { 1273 FontRelative(one.try_op(other, op)?) 1274 }, 1275 (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => { 1276 ViewportPercentage(one.try_op(other, op)?) 1277 }, 1278 (&ContainerRelative(ref one), &ContainerRelative(ref other)) => { 1279 ContainerRelative(one.try_op(other, op)?) 1280 }, 1281 (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => { 1282 ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32)) 1283 }, 1284 // See https://github.com/rust-lang/rust/issues/68867. rustc isn't 1285 // able to figure it own on its own so we help. 1286 _ => unsafe { 1287 match *self { 1288 Absolute(..) 1289 | FontRelative(..) 1290 | ViewportPercentage(..) 1291 | ContainerRelative(..) 1292 | ServoCharacterWidth(..) => {}, 1293 } 1294 debug_unreachable!("Forgot to handle unit in try_op()") 1295 }, 1296 }) 1297 } 1298 1299 pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self { 1300 use self::NoCalcLength::*; 1301 1302 match self { 1303 Absolute(ref one) => Absolute(one.map(op)), 1304 FontRelative(ref one) => FontRelative(one.map(op)), 1305 ViewportPercentage(ref one) => ViewportPercentage(one.map(op)), 1306 ContainerRelative(ref one) => ContainerRelative(one.map(op)), 1307 ServoCharacterWidth(ref one) => { 1308 ServoCharacterWidth(CharacterWidth(op(one.0 as f32) as i32)) 1309 }, 1310 } 1311 } 1312 1313 /// Get a px value without context (so only absolute units can be handled). 1314 #[inline] 1315 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> { 1316 match *self { 1317 Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()), 1318 _ => Err(()), 1319 } 1320 } 1321 1322 /// Get a px value without a full style context; this can handle either 1323 /// absolute or (if a font metrics getter is provided) font-relative units. 1324 #[cfg(feature = "gecko")] 1325 #[inline] 1326 pub fn to_computed_pixel_length_with_font_metrics( 1327 &self, 1328 get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>, 1329 ) -> Result<CSSFloat, ()> { 1330 match *self { 1331 Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()), 1332 Self::FontRelative(fr) => { 1333 if let Some(getter) = get_font_metrics { 1334 fr.to_computed_pixel_length_with_font_metrics(getter) 1335 } else { 1336 Err(()) 1337 } 1338 }, 1339 _ => Err(()), 1340 } 1341 } 1342 1343 /// Get an absolute length from a px value. 1344 #[inline] 1345 pub fn from_px(px_value: CSSFloat) -> NoCalcLength { 1346 NoCalcLength::Absolute(AbsoluteLength::Px(px_value)) 1347 } 1348 } 1349 1350 impl ToCss for NoCalcLength { 1351 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 1352 where 1353 W: Write, 1354 { 1355 crate::values::serialize_specified_dimension( 1356 self.unitless_value(), 1357 self.unit(), 1358 false, 1359 dest, 1360 ) 1361 } 1362 } 1363 1364 impl ToTyped for NoCalcLength { 1365 fn to_typed(&self) -> Option<TypedValue> { 1366 let value = self.unitless_value(); 1367 let unit = CssString::from(self.unit()); 1368 Some(TypedValue::Numeric(NumericValue::Unit { value, unit })) 1369 } 1370 } 1371 1372 impl SpecifiedValueInfo for NoCalcLength {} 1373 1374 impl PartialOrd for NoCalcLength { 1375 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { 1376 use self::NoCalcLength::*; 1377 1378 if std::mem::discriminant(self) != std::mem::discriminant(other) { 1379 return None; 1380 } 1381 1382 match (self, other) { 1383 (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()), 1384 (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other), 1385 (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => { 1386 one.partial_cmp(other) 1387 }, 1388 (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other), 1389 (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => { 1390 one.0.partial_cmp(&other.0) 1391 }, 1392 // See https://github.com/rust-lang/rust/issues/68867. rustc isn't 1393 // able to figure it own on its own so we help. 1394 _ => unsafe { 1395 match *self { 1396 Absolute(..) 1397 | FontRelative(..) 1398 | ViewportPercentage(..) 1399 | ContainerRelative(..) 1400 | ServoCharacterWidth(..) => {}, 1401 } 1402 debug_unreachable!("Forgot an arm in partial_cmp?") 1403 }, 1404 } 1405 } 1406 } 1407 1408 impl Zero for NoCalcLength { 1409 fn zero() -> Self { 1410 NoCalcLength::Absolute(AbsoluteLength::Px(0.)) 1411 } 1412 1413 fn is_zero(&self) -> bool { 1414 NoCalcLength::is_zero(self) 1415 } 1416 } 1417 1418 /// An extension to `NoCalcLength` to parse `calc` expressions. 1419 /// This is commonly used for the `<length>` values. 1420 /// 1421 /// <https://drafts.csswg.org/css-values/#lengths> 1422 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)] 1423 #[typed_value(derive_fields)] 1424 pub enum Length { 1425 /// The internal length type that cannot parse `calc` 1426 NoCalc(NoCalcLength), 1427 /// A calc expression. 1428 /// 1429 /// <https://drafts.csswg.org/css-values/#calc-notation> 1430 Calc(Box<CalcLengthPercentage>), 1431 } 1432 1433 impl From<NoCalcLength> for Length { 1434 #[inline] 1435 fn from(len: NoCalcLength) -> Self { 1436 Length::NoCalc(len) 1437 } 1438 } 1439 1440 impl PartialOrd for FontRelativeLength { 1441 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { 1442 use self::FontRelativeLength::*; 1443 1444 if std::mem::discriminant(self) != std::mem::discriminant(other) { 1445 return None; 1446 } 1447 1448 match (self, other) { 1449 (&Em(ref one), &Em(ref other)) => one.partial_cmp(other), 1450 (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other), 1451 (&Rex(ref one), &Rex(ref other)) => one.partial_cmp(other), 1452 (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other), 1453 (&Rch(ref one), &Rch(ref other)) => one.partial_cmp(other), 1454 (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other), 1455 (&Rcap(ref one), &Rcap(ref other)) => one.partial_cmp(other), 1456 (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other), 1457 (&Ric(ref one), &Ric(ref other)) => one.partial_cmp(other), 1458 (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other), 1459 (&Lh(ref one), &Lh(ref other)) => one.partial_cmp(other), 1460 (&Rlh(ref one), &Rlh(ref other)) => one.partial_cmp(other), 1461 // See https://github.com/rust-lang/rust/issues/68867. rustc isn't 1462 // able to figure it own on its own so we help. 1463 _ => unsafe { 1464 match *self { 1465 Em(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..) | Ic(..) 1466 | Ric(..) | Rem(..) | Lh(..) | Rlh(..) => {}, 1467 } 1468 debug_unreachable!("Forgot an arm in partial_cmp?") 1469 }, 1470 } 1471 } 1472 } 1473 1474 impl PartialOrd for ContainerRelativeLength { 1475 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { 1476 use self::ContainerRelativeLength::*; 1477 1478 if std::mem::discriminant(self) != std::mem::discriminant(other) { 1479 return None; 1480 } 1481 1482 match (self, other) { 1483 (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other), 1484 (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other), 1485 (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other), 1486 (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other), 1487 (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other), 1488 (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other), 1489 1490 // See https://github.com/rust-lang/rust/issues/68867, then 1491 // https://github.com/rust-lang/rust/pull/95161. rustc isn't 1492 // able to figure it own on its own so we help. 1493 _ => unsafe { 1494 match *self { 1495 Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {}, 1496 } 1497 debug_unreachable!("Forgot to handle unit in partial_cmp()") 1498 }, 1499 } 1500 } 1501 } 1502 1503 impl PartialOrd for ViewportPercentageLength { 1504 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { 1505 use self::ViewportPercentageLength::*; 1506 1507 if std::mem::discriminant(self) != std::mem::discriminant(other) { 1508 return None; 1509 } 1510 1511 match (self, other) { 1512 (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other), 1513 (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other), 1514 (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other), 1515 (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other), 1516 (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other), 1517 (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other), 1518 (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other), 1519 (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other), 1520 (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other), 1521 (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other), 1522 (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other), 1523 (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other), 1524 (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other), 1525 (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other), 1526 (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other), 1527 (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other), 1528 (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other), 1529 (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other), 1530 (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other), 1531 (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other), 1532 (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other), 1533 (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other), 1534 (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other), 1535 (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other), 1536 // See https://github.com/rust-lang/rust/issues/68867. rustc isn't 1537 // able to figure it own on its own so we help. 1538 _ => unsafe { 1539 match *self { 1540 Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..) 1541 | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..) 1542 | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..) 1543 | Svi(..) | Lvi(..) | Dvi(..) => {}, 1544 } 1545 debug_unreachable!("Forgot an arm in partial_cmp?") 1546 }, 1547 } 1548 } 1549 } 1550 1551 impl Length { 1552 #[inline] 1553 fn parse_internal<'i, 't>( 1554 context: &ParserContext, 1555 input: &mut Parser<'i, 't>, 1556 num_context: AllowedNumericType, 1557 allow_quirks: AllowQuirks, 1558 ) -> Result<Self, ParseError<'i>> { 1559 let location = input.current_source_location(); 1560 let token = input.next()?; 1561 match *token { 1562 Token::Dimension { 1563 value, ref unit, .. 1564 } if num_context.is_ok(context.parsing_mode, value) => { 1565 NoCalcLength::parse_dimension(context, value, unit) 1566 .map(Length::NoCalc) 1567 .map_err(|()| location.new_unexpected_token_error(token.clone())) 1568 }, 1569 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { 1570 if value != 0. 1571 && !context.parsing_mode.allows_unitless_lengths() 1572 && !allow_quirks.allowed(context.quirks_mode) 1573 { 1574 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); 1575 } 1576 Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px( 1577 value, 1578 )))) 1579 }, 1580 Token::Function(ref name) => { 1581 let function = CalcNode::math_function(context, name, location)?; 1582 let calc = CalcNode::parse_length(context, input, num_context, function)?; 1583 Ok(Length::Calc(Box::new(calc))) 1584 }, 1585 ref token => return Err(location.new_unexpected_token_error(token.clone())), 1586 } 1587 } 1588 1589 /// Parse a non-negative length 1590 #[inline] 1591 pub fn parse_non_negative<'i, 't>( 1592 context: &ParserContext, 1593 input: &mut Parser<'i, 't>, 1594 ) -> Result<Self, ParseError<'i>> { 1595 Self::parse_non_negative_quirky(context, input, AllowQuirks::No) 1596 } 1597 1598 /// Parse a non-negative length, allowing quirks. 1599 #[inline] 1600 pub fn parse_non_negative_quirky<'i, 't>( 1601 context: &ParserContext, 1602 input: &mut Parser<'i, 't>, 1603 allow_quirks: AllowQuirks, 1604 ) -> Result<Self, ParseError<'i>> { 1605 Self::parse_internal( 1606 context, 1607 input, 1608 AllowedNumericType::NonNegative, 1609 allow_quirks, 1610 ) 1611 } 1612 1613 /// Get an absolute length from a px value. 1614 #[inline] 1615 pub fn from_px(px_value: CSSFloat) -> Length { 1616 Length::NoCalc(NoCalcLength::from_px(px_value)) 1617 } 1618 1619 /// Get a px value without context. 1620 pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> { 1621 match *self { 1622 Self::NoCalc(ref l) => l.to_computed_pixel_length_without_context(), 1623 Self::Calc(ref l) => l.to_computed_pixel_length_without_context(), 1624 } 1625 } 1626 1627 /// Get a px value, with an optional GeckoFontMetrics getter to resolve font-relative units. 1628 #[cfg(feature = "gecko")] 1629 pub fn to_computed_pixel_length_with_font_metrics( 1630 &self, 1631 get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>, 1632 ) -> Result<CSSFloat, ()> { 1633 match *self { 1634 Self::NoCalc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics), 1635 Self::Calc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics), 1636 } 1637 } 1638 } 1639 1640 impl Parse for Length { 1641 fn parse<'i, 't>( 1642 context: &ParserContext, 1643 input: &mut Parser<'i, 't>, 1644 ) -> Result<Self, ParseError<'i>> { 1645 Self::parse_quirky(context, input, AllowQuirks::No) 1646 } 1647 } 1648 1649 impl Zero for Length { 1650 fn zero() -> Self { 1651 Length::NoCalc(NoCalcLength::zero()) 1652 } 1653 1654 fn is_zero(&self) -> bool { 1655 // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as 1656 // non-zero here? 1657 match *self { 1658 Length::NoCalc(ref l) => l.is_zero(), 1659 Length::Calc(..) => false, 1660 } 1661 } 1662 } 1663 1664 impl Length { 1665 /// Parses a length, with quirks. 1666 pub fn parse_quirky<'i, 't>( 1667 context: &ParserContext, 1668 input: &mut Parser<'i, 't>, 1669 allow_quirks: AllowQuirks, 1670 ) -> Result<Self, ParseError<'i>> { 1671 Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks) 1672 } 1673 } 1674 1675 /// A wrapper of Length, whose value must be >= 0. 1676 pub type NonNegativeLength = NonNegative<Length>; 1677 1678 impl Parse for NonNegativeLength { 1679 #[inline] 1680 fn parse<'i, 't>( 1681 context: &ParserContext, 1682 input: &mut Parser<'i, 't>, 1683 ) -> Result<Self, ParseError<'i>> { 1684 Ok(NonNegative(Length::parse_non_negative(context, input)?)) 1685 } 1686 } 1687 1688 impl From<NoCalcLength> for NonNegativeLength { 1689 #[inline] 1690 fn from(len: NoCalcLength) -> Self { 1691 NonNegative(Length::NoCalc(len)) 1692 } 1693 } 1694 1695 impl From<Length> for NonNegativeLength { 1696 #[inline] 1697 fn from(len: Length) -> Self { 1698 NonNegative(len) 1699 } 1700 } 1701 1702 impl NonNegativeLength { 1703 /// Get an absolute length from a px value. 1704 #[inline] 1705 pub fn from_px(px_value: CSSFloat) -> Self { 1706 Length::from_px(px_value.max(0.)).into() 1707 } 1708 1709 /// Parses a non-negative length, optionally with quirks. 1710 #[inline] 1711 pub fn parse_quirky<'i, 't>( 1712 context: &ParserContext, 1713 input: &mut Parser<'i, 't>, 1714 allow_quirks: AllowQuirks, 1715 ) -> Result<Self, ParseError<'i>> { 1716 Ok(NonNegative(Length::parse_non_negative_quirky( 1717 context, 1718 input, 1719 allow_quirks, 1720 )?)) 1721 } 1722 } 1723 1724 /// A `<length-percentage>` value. This can be either a `<length>`, a 1725 /// `<percentage>`, or a combination of both via `calc()`. 1726 /// 1727 /// https://drafts.csswg.org/css-values-4/#typedef-length-percentage 1728 #[allow(missing_docs)] 1729 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)] 1730 pub enum LengthPercentage { 1731 Length(NoCalcLength), 1732 Percentage(computed::Percentage), 1733 Calc(Box<CalcLengthPercentage>), 1734 } 1735 1736 impl From<Length> for LengthPercentage { 1737 fn from(len: Length) -> LengthPercentage { 1738 match len { 1739 Length::NoCalc(l) => LengthPercentage::Length(l), 1740 Length::Calc(l) => LengthPercentage::Calc(l), 1741 } 1742 } 1743 } 1744 1745 impl From<NoCalcLength> for LengthPercentage { 1746 #[inline] 1747 fn from(len: NoCalcLength) -> Self { 1748 LengthPercentage::Length(len) 1749 } 1750 } 1751 1752 impl From<Percentage> for LengthPercentage { 1753 #[inline] 1754 fn from(pc: Percentage) -> Self { 1755 if let Some(clamping_mode) = pc.calc_clamping_mode() { 1756 LengthPercentage::Calc(Box::new(CalcLengthPercentage { 1757 clamping_mode, 1758 node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())), 1759 })) 1760 } else { 1761 LengthPercentage::Percentage(computed::Percentage(pc.get())) 1762 } 1763 } 1764 } 1765 1766 impl From<computed::Percentage> for LengthPercentage { 1767 #[inline] 1768 fn from(pc: computed::Percentage) -> Self { 1769 LengthPercentage::Percentage(pc) 1770 } 1771 } 1772 1773 impl Parse for LengthPercentage { 1774 #[inline] 1775 fn parse<'i, 't>( 1776 context: &ParserContext, 1777 input: &mut Parser<'i, 't>, 1778 ) -> Result<Self, ParseError<'i>> { 1779 Self::parse_quirky(context, input, AllowQuirks::No) 1780 } 1781 } 1782 1783 impl LengthPercentage { 1784 #[inline] 1785 /// Returns a `0%` value. 1786 pub fn zero_percent() -> LengthPercentage { 1787 LengthPercentage::Percentage(computed::Percentage::zero()) 1788 } 1789 1790 #[inline] 1791 /// Returns a `100%` value. 1792 pub fn hundred_percent() -> LengthPercentage { 1793 LengthPercentage::Percentage(computed::Percentage::hundred()) 1794 } 1795 1796 fn parse_internal<'i, 't>( 1797 context: &ParserContext, 1798 input: &mut Parser<'i, 't>, 1799 num_context: AllowedNumericType, 1800 allow_quirks: AllowQuirks, 1801 allow_anchor: AllowAnchorPositioningFunctions, 1802 ) -> Result<Self, ParseError<'i>> { 1803 let location = input.current_source_location(); 1804 let token = input.next()?; 1805 match *token { 1806 Token::Dimension { 1807 value, ref unit, .. 1808 } if num_context.is_ok(context.parsing_mode, value) => { 1809 return NoCalcLength::parse_dimension(context, value, unit) 1810 .map(LengthPercentage::Length) 1811 .map_err(|()| location.new_unexpected_token_error(token.clone())); 1812 }, 1813 Token::Percentage { unit_value, .. } 1814 if num_context.is_ok(context.parsing_mode, unit_value) => 1815 { 1816 return Ok(LengthPercentage::Percentage(computed::Percentage( 1817 unit_value, 1818 ))); 1819 }, 1820 Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => { 1821 if value != 0. 1822 && !context.parsing_mode.allows_unitless_lengths() 1823 && !allow_quirks.allowed(context.quirks_mode) 1824 { 1825 return Err(location.new_unexpected_token_error(token.clone())); 1826 } else { 1827 return Ok(LengthPercentage::Length(NoCalcLength::from_px(value))); 1828 } 1829 }, 1830 Token::Function(ref name) => { 1831 let function = CalcNode::math_function(context, name, location)?; 1832 let calc = CalcNode::parse_length_or_percentage( 1833 context, 1834 input, 1835 num_context, 1836 function, 1837 allow_anchor, 1838 )?; 1839 Ok(LengthPercentage::Calc(Box::new(calc))) 1840 }, 1841 _ => return Err(location.new_unexpected_token_error(token.clone())), 1842 } 1843 } 1844 1845 /// Parses allowing the unitless length quirk. 1846 /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk> 1847 #[inline] 1848 pub fn parse_quirky<'i, 't>( 1849 context: &ParserContext, 1850 input: &mut Parser<'i, 't>, 1851 allow_quirks: AllowQuirks, 1852 ) -> Result<Self, ParseError<'i>> { 1853 Self::parse_internal( 1854 context, 1855 input, 1856 AllowedNumericType::All, 1857 allow_quirks, 1858 AllowAnchorPositioningFunctions::No, 1859 ) 1860 } 1861 1862 /// Parses allowing the unitless length quirk, as well as allowing 1863 /// anchor-positioning related function, `anchor-size()`. 1864 #[inline] 1865 fn parse_quirky_with_anchor_size_function<'i, 't>( 1866 context: &ParserContext, 1867 input: &mut Parser<'i, 't>, 1868 allow_quirks: AllowQuirks, 1869 ) -> Result<Self, ParseError<'i>> { 1870 Self::parse_internal( 1871 context, 1872 input, 1873 AllowedNumericType::All, 1874 allow_quirks, 1875 AllowAnchorPositioningFunctions::AllowAnchorSize, 1876 ) 1877 } 1878 1879 /// Parses allowing the unitless length quirk, as well as allowing 1880 /// anchor-positioning related functions, `anchor()` and `anchor-size()`. 1881 #[inline] 1882 pub fn parse_quirky_with_anchor_functions<'i, 't>( 1883 context: &ParserContext, 1884 input: &mut Parser<'i, 't>, 1885 allow_quirks: AllowQuirks, 1886 ) -> Result<Self, ParseError<'i>> { 1887 Self::parse_internal( 1888 context, 1889 input, 1890 AllowedNumericType::All, 1891 allow_quirks, 1892 AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize, 1893 ) 1894 } 1895 1896 /// Parses non-negative length, allowing the unitless length quirk, 1897 /// as well as allowing `anchor-size()`. 1898 pub fn parse_non_negative_with_anchor_size<'i, 't>( 1899 context: &ParserContext, 1900 input: &mut Parser<'i, 't>, 1901 allow_quirks: AllowQuirks, 1902 ) -> Result<Self, ParseError<'i>> { 1903 Self::parse_internal( 1904 context, 1905 input, 1906 AllowedNumericType::NonNegative, 1907 allow_quirks, 1908 AllowAnchorPositioningFunctions::AllowAnchorSize, 1909 ) 1910 } 1911 1912 /// Parse a non-negative length. 1913 /// 1914 /// FIXME(emilio): This should be not public and we should use 1915 /// NonNegativeLengthPercentage instead. 1916 #[inline] 1917 pub fn parse_non_negative<'i, 't>( 1918 context: &ParserContext, 1919 input: &mut Parser<'i, 't>, 1920 ) -> Result<Self, ParseError<'i>> { 1921 Self::parse_non_negative_quirky(context, input, AllowQuirks::No) 1922 } 1923 1924 /// Parse a non-negative length, with quirks. 1925 #[inline] 1926 pub fn parse_non_negative_quirky<'i, 't>( 1927 context: &ParserContext, 1928 input: &mut Parser<'i, 't>, 1929 allow_quirks: AllowQuirks, 1930 ) -> Result<Self, ParseError<'i>> { 1931 Self::parse_internal( 1932 context, 1933 input, 1934 AllowedNumericType::NonNegative, 1935 allow_quirks, 1936 AllowAnchorPositioningFunctions::No, 1937 ) 1938 } 1939 1940 /// Returns self as specified::calc::CalcNode. 1941 /// Note that this expect the clamping_mode is AllowedNumericType::All for Calc. The caller 1942 /// should take care about it when using this function. 1943 fn to_calc_node(self) -> CalcNode { 1944 match self { 1945 LengthPercentage::Length(l) => CalcNode::Leaf(calc::Leaf::Length(l)), 1946 LengthPercentage::Percentage(p) => CalcNode::Leaf(calc::Leaf::Percentage(p.0)), 1947 LengthPercentage::Calc(p) => p.node, 1948 } 1949 } 1950 1951 /// Construct the value representing `calc(100% - self)`. 1952 pub fn hundred_percent_minus(self, clamping_mode: AllowedNumericType) -> Self { 1953 let mut sum = smallvec::SmallVec::<[CalcNode; 2]>::new(); 1954 sum.push(CalcNode::Leaf(calc::Leaf::Percentage(1.0))); 1955 1956 let mut node = self.to_calc_node(); 1957 node.negate(); 1958 sum.push(node); 1959 1960 let calc = CalcNode::Sum(sum.into_boxed_slice().into()); 1961 LengthPercentage::Calc(Box::new( 1962 calc.into_length_or_percentage(clamping_mode).unwrap(), 1963 )) 1964 } 1965 } 1966 1967 impl Zero for LengthPercentage { 1968 fn zero() -> Self { 1969 LengthPercentage::Length(NoCalcLength::zero()) 1970 } 1971 1972 fn is_zero(&self) -> bool { 1973 match *self { 1974 LengthPercentage::Length(l) => l.is_zero(), 1975 LengthPercentage::Percentage(p) => p.0 == 0.0, 1976 LengthPercentage::Calc(_) => false, 1977 } 1978 } 1979 } 1980 1981 impl ZeroNoPercent for LengthPercentage { 1982 fn is_zero_no_percent(&self) -> bool { 1983 match *self { 1984 LengthPercentage::Percentage(_) => false, 1985 _ => self.is_zero(), 1986 } 1987 } 1988 } 1989 1990 /// A specified type for `<length-percentage> | auto`. 1991 pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>; 1992 1993 impl LengthPercentageOrAuto { 1994 /// Returns a value representing `0%`. 1995 #[inline] 1996 pub fn zero_percent() -> Self { 1997 generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent()) 1998 } 1999 2000 /// Parses a length or a percentage, allowing the unitless length quirk. 2001 /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk> 2002 #[inline] 2003 pub fn parse_quirky<'i, 't>( 2004 context: &ParserContext, 2005 input: &mut Parser<'i, 't>, 2006 allow_quirks: AllowQuirks, 2007 ) -> Result<Self, ParseError<'i>> { 2008 Self::parse_with(context, input, |context, input| { 2009 LengthPercentage::parse_quirky(context, input, allow_quirks) 2010 }) 2011 } 2012 } 2013 2014 /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0. 2015 pub type NonNegativeLengthPercentageOrAuto = 2016 generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>; 2017 2018 impl NonNegativeLengthPercentageOrAuto { 2019 /// Returns a value representing `0%`. 2020 #[inline] 2021 pub fn zero_percent() -> Self { 2022 generics::LengthPercentageOrAuto::LengthPercentage( 2023 NonNegativeLengthPercentage::zero_percent(), 2024 ) 2025 } 2026 2027 /// Parses a non-negative length-percentage, allowing the unitless length 2028 /// quirk. 2029 #[inline] 2030 pub fn parse_quirky<'i, 't>( 2031 context: &ParserContext, 2032 input: &mut Parser<'i, 't>, 2033 allow_quirks: AllowQuirks, 2034 ) -> Result<Self, ParseError<'i>> { 2035 Self::parse_with(context, input, |context, input| { 2036 NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks) 2037 }) 2038 } 2039 } 2040 2041 /// A wrapper of LengthPercentage, whose value must be >= 0. 2042 pub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>; 2043 2044 /// Either a NonNegativeLengthPercentage or the `normal` keyword. 2045 pub type NonNegativeLengthPercentageOrNormal = 2046 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>; 2047 2048 impl From<NoCalcLength> for NonNegativeLengthPercentage { 2049 #[inline] 2050 fn from(len: NoCalcLength) -> Self { 2051 NonNegative(LengthPercentage::from(len)) 2052 } 2053 } 2054 2055 impl Parse for NonNegativeLengthPercentage { 2056 #[inline] 2057 fn parse<'i, 't>( 2058 context: &ParserContext, 2059 input: &mut Parser<'i, 't>, 2060 ) -> Result<Self, ParseError<'i>> { 2061 Self::parse_quirky(context, input, AllowQuirks::No) 2062 } 2063 } 2064 2065 impl NonNegativeLengthPercentage { 2066 #[inline] 2067 /// Returns a `0%` value. 2068 pub fn zero_percent() -> Self { 2069 NonNegative(LengthPercentage::zero_percent()) 2070 } 2071 2072 /// Parses a length or a percentage, allowing the unitless length quirk. 2073 /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk> 2074 #[inline] 2075 pub fn parse_quirky<'i, 't>( 2076 context: &ParserContext, 2077 input: &mut Parser<'i, 't>, 2078 allow_quirks: AllowQuirks, 2079 ) -> Result<Self, ParseError<'i>> { 2080 LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative) 2081 } 2082 2083 /// Parses a length or a percentage, allowing the unitless length quirk, 2084 /// as well as allowing `anchor-size()`. 2085 #[inline] 2086 pub fn parse_non_negative_with_anchor_size<'i, 't>( 2087 context: &ParserContext, 2088 input: &mut Parser<'i, 't>, 2089 allow_quirks: AllowQuirks, 2090 ) -> Result<Self, ParseError<'i>> { 2091 LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks) 2092 .map(NonNegative) 2093 } 2094 } 2095 2096 /// Either a `<length>` or the `auto` keyword. 2097 /// 2098 /// Note that we use LengthPercentage just for convenience, since it pretty much 2099 /// is everything we care about, but we could just add a similar LengthOrAuto 2100 /// instead if we think getting rid of this weirdness is worth it. 2101 pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>; 2102 2103 impl LengthOrAuto { 2104 /// Parses a length, allowing the unitless length quirk. 2105 /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk> 2106 #[inline] 2107 pub fn parse_quirky<'i, 't>( 2108 context: &ParserContext, 2109 input: &mut Parser<'i, 't>, 2110 allow_quirks: AllowQuirks, 2111 ) -> Result<Self, ParseError<'i>> { 2112 Self::parse_with(context, input, |context, input| { 2113 Length::parse_quirky(context, input, allow_quirks) 2114 }) 2115 } 2116 } 2117 2118 /// Either a non-negative `<length>` or the `auto` keyword. 2119 pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>; 2120 2121 /// Either a `<length>` or a `<number>`. 2122 pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>; 2123 2124 /// A specified value for `min-width`, `min-height`, `width` or `height` property. 2125 pub type Size = GenericSize<NonNegativeLengthPercentage>; 2126 2127 impl Parse for Size { 2128 fn parse<'i, 't>( 2129 context: &ParserContext, 2130 input: &mut Parser<'i, 't>, 2131 ) -> Result<Self, ParseError<'i>> { 2132 Size::parse_quirky(context, input, AllowQuirks::No) 2133 } 2134 } 2135 2136 macro_rules! parse_size_non_length { 2137 ($size:ident, $input:expr, $allow_webkit_fill_available:expr, 2138 $auto_or_none:expr => $auto_or_none_ident:ident) => {{ 2139 let size = $input.try_parse(|input| { 2140 Ok(try_match_ident_ignore_ascii_case! { input, 2141 "min-content" | "-moz-min-content" => $size::MinContent, 2142 "max-content" | "-moz-max-content" => $size::MaxContent, 2143 "fit-content" | "-moz-fit-content" => $size::FitContent, 2144 #[cfg(feature = "gecko")] 2145 "-moz-available" => $size::MozAvailable, 2146 "-webkit-fill-available" if $allow_webkit_fill_available => $size::WebkitFillAvailable, 2147 "stretch" if is_stretch_enabled() => $size::Stretch, 2148 $auto_or_none => $size::$auto_or_none_ident, 2149 }) 2150 }); 2151 if size.is_ok() { 2152 return size; 2153 } 2154 }}; 2155 } 2156 2157 fn is_webkit_fill_available_enabled_in_width_and_height() -> bool { 2158 static_prefs::pref!("layout.css.webkit-fill-available.enabled") 2159 } 2160 2161 fn is_webkit_fill_available_enabled_in_all_size_properties() -> bool { 2162 // For convenience at the callsites, we check both prefs here, 2163 // since both must be 'true' in order for the keyword to be 2164 // enabled in all size properties. 2165 static_prefs::pref!("layout.css.webkit-fill-available.enabled") 2166 && static_prefs::pref!("layout.css.webkit-fill-available.all-size-properties.enabled") 2167 } 2168 2169 fn is_stretch_enabled() -> bool { 2170 static_prefs::pref!("layout.css.stretch-size-keyword.enabled") 2171 } 2172 2173 fn is_fit_content_function_enabled() -> bool { 2174 static_prefs::pref!("layout.css.fit-content-function.enabled") 2175 } 2176 2177 macro_rules! parse_fit_content_function { 2178 ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => { 2179 if is_fit_content_function_enabled() { 2180 if let Ok(length) = $input.try_parse(|input| { 2181 input.expect_function_matching("fit-content")?; 2182 input.parse_nested_block(|i| { 2183 NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks) 2184 }) 2185 }) { 2186 return Ok($size::FitContentFunction(length)); 2187 } 2188 } 2189 }; 2190 } 2191 2192 #[derive(Clone, Copy, PartialEq, Eq)] 2193 enum ParseAnchorFunctions { 2194 Yes, 2195 No, 2196 } 2197 2198 impl Size { 2199 /// Parses, with quirks. 2200 pub fn parse_quirky<'i, 't>( 2201 context: &ParserContext, 2202 input: &mut Parser<'i, 't>, 2203 allow_quirks: AllowQuirks, 2204 ) -> Result<Self, ParseError<'i>> { 2205 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties(); 2206 Self::parse_quirky_internal( 2207 context, 2208 input, 2209 allow_quirks, 2210 allow_webkit_fill_available, 2211 ParseAnchorFunctions::Yes, 2212 ) 2213 } 2214 2215 /// Parses for flex-basis: <width> 2216 pub fn parse_size_for_flex_basis_width<'i, 't>( 2217 context: &ParserContext, 2218 input: &mut Parser<'i, 't>, 2219 ) -> Result<Self, ParseError<'i>> { 2220 Self::parse_quirky_internal( 2221 context, 2222 input, 2223 AllowQuirks::No, 2224 true, 2225 ParseAnchorFunctions::No, 2226 ) 2227 } 2228 2229 /// Parses, with quirks and configurable support for 2230 /// whether the '-webkit-fill-available' keyword is allowed. 2231 /// TODO(dholbert) Fold this function into callsites in bug 1989073 when 2232 /// removing 'layout.css.webkit-fill-available.all-size-properties.enabled'. 2233 fn parse_quirky_internal<'i, 't>( 2234 context: &ParserContext, 2235 input: &mut Parser<'i, 't>, 2236 allow_quirks: AllowQuirks, 2237 allow_webkit_fill_available: bool, 2238 allow_anchor_functions: ParseAnchorFunctions, 2239 ) -> Result<Self, ParseError<'i>> { 2240 parse_size_non_length!(Size, input, allow_webkit_fill_available, 2241 "auto" => Auto); 2242 parse_fit_content_function!(Size, input, context, allow_quirks); 2243 2244 let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes 2245 && static_prefs::pref!("layout.css.anchor-positioning.enabled"); 2246 match input 2247 .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks)) 2248 { 2249 Ok(length) => return Ok(GenericSize::LengthPercentage(length)), 2250 Err(e) if !allow_anchor => return Err(e.into()), 2251 Err(_) => (), 2252 }; 2253 if let Ok(length) = input.try_parse(|i| { 2254 NonNegativeLengthPercentage::parse_non_negative_with_anchor_size( 2255 context, 2256 i, 2257 allow_quirks, 2258 ) 2259 }) { 2260 return Ok(GenericSize::AnchorContainingCalcFunction(length)); 2261 } 2262 Ok(Self::AnchorSizeFunction(Box::new( 2263 GenericAnchorSizeFunction::parse(context, input)?, 2264 ))) 2265 } 2266 2267 /// Parse a size for width or height, where -webkit-fill-available 2268 /// support is only controlled by one pref (vs. other properties where 2269 /// there's an additional pref check): 2270 /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with 2271 /// 'layout.css.webkit-fill-available.all-size-properties.enabled'. 2272 pub fn parse_size_for_width_or_height_quirky<'i, 't>( 2273 context: &ParserContext, 2274 input: &mut Parser<'i, 't>, 2275 allow_quirks: AllowQuirks, 2276 ) -> Result<Self, ParseError<'i>> { 2277 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height(); 2278 Self::parse_quirky_internal( 2279 context, 2280 input, 2281 allow_quirks, 2282 allow_webkit_fill_available, 2283 ParseAnchorFunctions::Yes, 2284 ) 2285 } 2286 2287 /// Parse a size for width or height, where -webkit-fill-available 2288 /// support is only controlled by one pref (vs. other properties where 2289 /// there's an additional pref check): 2290 /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with 2291 /// 'layout.css.webkit-fill-available.all-size-properties.enabled'. 2292 pub fn parse_size_for_width_or_height<'i, 't>( 2293 context: &ParserContext, 2294 input: &mut Parser<'i, 't>, 2295 ) -> Result<Self, ParseError<'i>> { 2296 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height(); 2297 Self::parse_quirky_internal( 2298 context, 2299 input, 2300 AllowQuirks::No, 2301 allow_webkit_fill_available, 2302 ParseAnchorFunctions::Yes, 2303 ) 2304 } 2305 2306 /// Returns `0%`. 2307 #[inline] 2308 pub fn zero_percent() -> Self { 2309 GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent()) 2310 } 2311 } 2312 2313 /// A specified value for `max-width` or `max-height` property. 2314 pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>; 2315 2316 impl Parse for MaxSize { 2317 fn parse<'i, 't>( 2318 context: &ParserContext, 2319 input: &mut Parser<'i, 't>, 2320 ) -> Result<Self, ParseError<'i>> { 2321 MaxSize::parse_quirky(context, input, AllowQuirks::No) 2322 } 2323 } 2324 2325 impl MaxSize { 2326 /// Parses, with quirks. 2327 pub fn parse_quirky<'i, 't>( 2328 context: &ParserContext, 2329 input: &mut Parser<'i, 't>, 2330 allow_quirks: AllowQuirks, 2331 ) -> Result<Self, ParseError<'i>> { 2332 let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties(); 2333 parse_size_non_length!(MaxSize, input, allow_webkit_fill_available, 2334 "none" => None); 2335 parse_fit_content_function!(MaxSize, input, context, allow_quirks); 2336 2337 match input 2338 .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks)) 2339 { 2340 Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)), 2341 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => { 2342 return Err(e.into()) 2343 }, 2344 Err(_) => (), 2345 }; 2346 if let Ok(length) = input.try_parse(|i| { 2347 NonNegativeLengthPercentage::parse_non_negative_with_anchor_size( 2348 context, 2349 i, 2350 allow_quirks, 2351 ) 2352 }) { 2353 return Ok(GenericMaxSize::AnchorContainingCalcFunction(length)); 2354 } 2355 Ok(Self::AnchorSizeFunction(Box::new( 2356 GenericAnchorSizeFunction::parse(context, input)?, 2357 ))) 2358 } 2359 } 2360 2361 /// A specified non-negative `<length>` | `<number>`. 2362 pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>; 2363 2364 /// A specified value for `margin` properties. 2365 pub type Margin = GenericMargin<LengthPercentage>; 2366 2367 impl Margin { 2368 /// Parses a margin type, allowing the unitless length quirk. 2369 /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk> 2370 #[inline] 2371 pub fn parse_quirky<'i, 't>( 2372 context: &ParserContext, 2373 input: &mut Parser<'i, 't>, 2374 allow_quirks: AllowQuirks, 2375 ) -> Result<Self, ParseError<'i>> { 2376 if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks)) 2377 { 2378 return Ok(Self::LengthPercentage(l)); 2379 } 2380 match input.try_parse(|i| i.expect_ident_matching("auto")) { 2381 Ok(_) => return Ok(Self::Auto), 2382 Err(e) if !static_prefs::pref!("layout.css.anchor-positioning.enabled") => { 2383 return Err(e.into()) 2384 }, 2385 Err(_) => (), 2386 }; 2387 if let Ok(l) = input.try_parse(|i| { 2388 LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks) 2389 }) { 2390 return Ok(Self::AnchorContainingCalcFunction(l)); 2391 } 2392 let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?; 2393 Ok(Self::AnchorSizeFunction(Box::new(inner))) 2394 } 2395 } 2396 2397 impl Parse for Margin { 2398 fn parse<'i, 't>( 2399 context: &ParserContext, 2400 input: &mut Parser<'i, 't>, 2401 ) -> Result<Self, ParseError<'i>> { 2402 Self::parse_quirky(context, input, AllowQuirks::No) 2403 } 2404 }