length.rs (18135B)
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>` computed values, and related ones. 6 7 use super::{Context, Number, ToComputedValue}; 8 use crate::derives::*; 9 use crate::logical_geometry::PhysicalSide; 10 use crate::values::animated::{Context as AnimatedContext, ToAnimatedValue}; 11 use crate::values::computed::position::TryTacticAdjustment; 12 use crate::values::computed::{NonNegativeNumber, Percentage, Zoom}; 13 use crate::values::generics::length::{ 14 GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize, 15 }; 16 use crate::values::generics::NonNegative; 17 use crate::values::generics::{length as generics, ClampToNonNegative}; 18 use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue}; 19 use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase}; 20 use crate::values::{specified, CSSFloat}; 21 use crate::Zero; 22 use app_units::Au; 23 use std::fmt::{self, Write}; 24 use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; 25 use style_traits::{CSSPixel, CssString, CssWriter, NumericValue, ToCss, ToTyped, TypedValue}; 26 27 pub use super::image::Image; 28 pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage}; 29 pub use crate::values::specified::url::UrlOrNone; 30 pub use crate::values::specified::{Angle, BorderStyle, Time}; 31 32 impl ToComputedValue for specified::NoCalcLength { 33 type ComputedValue = Length; 34 35 #[inline] 36 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 37 self.to_computed_value_with_base_size( 38 context, 39 FontBaseSize::CurrentStyle, 40 LineHeightBase::CurrentStyle, 41 ) 42 } 43 44 #[inline] 45 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 46 Self::Absolute(AbsoluteLength::Px(computed.px())) 47 } 48 } 49 50 impl specified::NoCalcLength { 51 /// Computes a length with a given font-relative base size. 52 pub fn to_computed_value_with_base_size( 53 &self, 54 context: &Context, 55 base_size: FontBaseSize, 56 line_height_base: LineHeightBase, 57 ) -> Length { 58 match *self { 59 Self::Absolute(length) => length.to_computed_value(context), 60 Self::FontRelative(length) => { 61 length.to_computed_value(context, base_size, line_height_base) 62 }, 63 Self::ViewportPercentage(length) => length.to_computed_value(context), 64 Self::ContainerRelative(length) => length.to_computed_value(context), 65 Self::ServoCharacterWidth(length) => length 66 .to_computed_value(context.style().get_font().clone_font_size().computed_size()), 67 } 68 } 69 } 70 71 impl ToComputedValue for specified::Length { 72 type ComputedValue = Length; 73 74 #[inline] 75 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 76 match *self { 77 Self::NoCalc(l) => l.to_computed_value(context), 78 Self::Calc(ref calc) => { 79 let result = calc.to_computed_value(context); 80 debug_assert!( 81 result.to_length().is_some(), 82 "{:?} didn't resolve to a length: {:?}", 83 calc, 84 result, 85 ); 86 result.to_length().unwrap_or_else(Length::zero) 87 }, 88 } 89 } 90 91 #[inline] 92 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 93 Self::NoCalc(specified::NoCalcLength::from_computed_value(computed)) 94 } 95 } 96 97 /// Some boilerplate to share between negative and non-negative 98 /// length-percentage or auto. 99 macro_rules! computed_length_percentage_or_auto { 100 ($inner:ty) => { 101 /// Returns the used value. 102 #[inline] 103 pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> { 104 match *self { 105 Self::Auto => None, 106 Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), 107 } 108 } 109 }; 110 } 111 112 /// A computed type for `<length-percentage> | auto`. 113 pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>; 114 115 impl LengthPercentageOrAuto { 116 /// Clamps the value to a non-negative value. 117 pub fn clamp_to_non_negative(self) -> Self { 118 use crate::values::generics::length::LengthPercentageOrAuto::*; 119 match self { 120 LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()), 121 Auto => Auto, 122 } 123 } 124 125 /// Convert to have a borrow inside the enum 126 pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> { 127 use crate::values::generics::length::LengthPercentageOrAuto::*; 128 match *self { 129 LengthPercentage(ref lp) => LengthPercentage(lp), 130 Auto => Auto, 131 } 132 } 133 134 computed_length_percentage_or_auto!(LengthPercentage); 135 } 136 137 impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> { 138 /// Resolves the percentage. 139 #[inline] 140 pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto { 141 use crate::values::generics::length::LengthPercentageOrAuto::*; 142 match self { 143 LengthPercentage(length_percentage) => { 144 LengthPercentage(length_percentage.percentage_relative_to(basis)) 145 }, 146 Auto => Auto, 147 } 148 } 149 150 /// Maybe resolves the percentage. 151 #[inline] 152 pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto { 153 use crate::values::generics::length::LengthPercentageOrAuto::*; 154 match self { 155 LengthPercentage(length_percentage) => length_percentage 156 .maybe_percentage_relative_to(basis) 157 .map_or(Auto, LengthPercentage), 158 Auto => Auto, 159 } 160 } 161 } 162 163 /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0. 164 pub type NonNegativeLengthPercentageOrAuto = 165 generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>; 166 167 impl NonNegativeLengthPercentageOrAuto { 168 computed_length_percentage_or_auto!(NonNegativeLengthPercentage); 169 } 170 171 /// The computed `<length>` value. 172 #[derive( 173 Animate, 174 Clone, 175 ComputeSquaredDistance, 176 Copy, 177 Deserialize, 178 MallocSizeOf, 179 PartialEq, 180 PartialOrd, 181 Serialize, 182 ToAnimatedZero, 183 ToComputedValue, 184 ToShmem, 185 )] 186 #[repr(C)] 187 pub struct CSSPixelLength(CSSFloat); 188 189 impl ToResolvedValue for CSSPixelLength { 190 type ResolvedValue = Self; 191 192 fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue { 193 Self(context.style.effective_zoom.unzoom(self.0)) 194 } 195 196 #[inline] 197 fn from_resolved_value(value: Self::ResolvedValue) -> Self { 198 value 199 } 200 } 201 202 impl ToAnimatedValue for CSSPixelLength { 203 type AnimatedValue = Self; 204 205 fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue { 206 Self(context.style.effective_zoom.unzoom(self.0)) 207 } 208 209 #[inline] 210 fn from_animated_value(value: Self::AnimatedValue) -> Self { 211 value 212 } 213 } 214 215 impl fmt::Debug for CSSPixelLength { 216 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 217 self.0.fmt(f)?; 218 f.write_str(" px") 219 } 220 } 221 222 impl CSSPixelLength { 223 /// Return a new CSSPixelLength. 224 #[inline] 225 pub fn new(px: CSSFloat) -> Self { 226 CSSPixelLength(px) 227 } 228 229 /// Returns a normalized (NaN turned to zero) version of this length. 230 #[inline] 231 pub fn normalized(self) -> Self { 232 Self::new(crate::values::normalize(self.0)) 233 } 234 235 /// Returns a finite (normalized and clamped to float min and max) version of this length. 236 #[inline] 237 pub fn finite(self) -> Self { 238 Self::new(crate::values::normalize(self.0).min(f32::MAX).max(f32::MIN)) 239 } 240 241 /// Scale the length by a given amount. 242 #[inline] 243 pub fn scale_by(self, scale: CSSFloat) -> Self { 244 CSSPixelLength(self.0 * scale) 245 } 246 247 /// Return the containing pixel value. 248 #[inline] 249 pub fn px(self) -> CSSFloat { 250 self.0 251 } 252 253 /// Zooms a particular length. 254 #[inline] 255 pub fn zoom(self, zoom: Zoom) -> Self { 256 Self::new(zoom.zoom(self.px())) 257 } 258 259 /// Return the length with app_unit i32 type. 260 #[inline] 261 pub fn to_i32_au(self) -> i32 { 262 Au::from(self).0 263 } 264 265 /// Return the absolute value of this length. 266 #[inline] 267 pub fn abs(self) -> Self { 268 CSSPixelLength::new(self.0.abs()) 269 } 270 271 /// Return the clamped value of this length. 272 #[inline] 273 pub fn clamp_to_non_negative(self) -> Self { 274 CSSPixelLength::new(self.0.max(0.)) 275 } 276 277 /// Returns the minimum between `self` and `other`. 278 #[inline] 279 pub fn min(self, other: Self) -> Self { 280 CSSPixelLength::new(self.0.min(other.0)) 281 } 282 283 /// Returns the maximum between `self` and `other`. 284 #[inline] 285 pub fn max(self, other: Self) -> Self { 286 CSSPixelLength::new(self.0.max(other.0)) 287 } 288 289 /// Sets `self` to the maximum between `self` and `other`. 290 #[inline] 291 pub fn max_assign(&mut self, other: Self) { 292 *self = self.max(other); 293 } 294 295 /// Clamp the value to a lower bound and an optional upper bound. 296 /// 297 /// Can be used for example with `min-width` and `max-width`. 298 #[inline] 299 pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self { 300 self.clamp_below_max(max_size).max(min_size) 301 } 302 303 /// Clamp the value to an optional upper bound. 304 /// 305 /// Can be used for example with `max-width`. 306 #[inline] 307 pub fn clamp_below_max(self, max_size: Option<Self>) -> Self { 308 match max_size { 309 None => self, 310 Some(max_size) => self.min(max_size), 311 } 312 } 313 } 314 315 impl num_traits::Zero for CSSPixelLength { 316 fn zero() -> Self { 317 CSSPixelLength::new(0.) 318 } 319 320 fn is_zero(&self) -> bool { 321 self.px() == 0. 322 } 323 } 324 325 impl ToCss for CSSPixelLength { 326 #[inline] 327 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 328 where 329 W: Write, 330 { 331 self.0.to_css(dest)?; 332 dest.write_str("px") 333 } 334 } 335 336 impl ToTyped for CSSPixelLength { 337 fn to_typed(&self) -> Option<TypedValue> { 338 Some(TypedValue::Numeric(NumericValue::Unit { 339 value: self.0 as f32, 340 unit: CssString::from("px"), 341 })) 342 } 343 } 344 345 impl std::iter::Sum for CSSPixelLength { 346 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { 347 iter.fold(Length::zero(), Add::add) 348 } 349 } 350 351 impl Add for CSSPixelLength { 352 type Output = Self; 353 354 #[inline] 355 fn add(self, other: Self) -> Self { 356 Self::new(self.px() + other.px()) 357 } 358 } 359 360 impl AddAssign for CSSPixelLength { 361 #[inline] 362 fn add_assign(&mut self, other: Self) { 363 self.0 += other.0; 364 } 365 } 366 367 impl Div for CSSPixelLength { 368 type Output = CSSFloat; 369 370 #[inline] 371 fn div(self, other: Self) -> CSSFloat { 372 self.px() / other.px() 373 } 374 } 375 376 impl Div<CSSFloat> for CSSPixelLength { 377 type Output = Self; 378 379 #[inline] 380 fn div(self, other: CSSFloat) -> Self { 381 Self::new(self.px() / other) 382 } 383 } 384 385 impl MulAssign<CSSFloat> for CSSPixelLength { 386 #[inline] 387 fn mul_assign(&mut self, other: CSSFloat) { 388 self.0 *= other; 389 } 390 } 391 392 impl Mul<CSSFloat> for CSSPixelLength { 393 type Output = Self; 394 395 #[inline] 396 fn mul(self, other: CSSFloat) -> Self { 397 Self::new(self.px() * other) 398 } 399 } 400 401 impl Neg for CSSPixelLength { 402 type Output = Self; 403 404 #[inline] 405 fn neg(self) -> Self { 406 CSSPixelLength::new(-self.0) 407 } 408 } 409 410 impl Sub for CSSPixelLength { 411 type Output = Self; 412 413 #[inline] 414 fn sub(self, other: Self) -> Self { 415 Self::new(self.px() - other.px()) 416 } 417 } 418 419 impl SubAssign for CSSPixelLength { 420 #[inline] 421 fn sub_assign(&mut self, other: Self) { 422 self.0 -= other.0; 423 } 424 } 425 426 impl From<CSSPixelLength> for Au { 427 #[inline] 428 fn from(len: CSSPixelLength) -> Self { 429 Au::from_f32_px(len.0) 430 } 431 } 432 433 impl From<Au> for CSSPixelLength { 434 #[inline] 435 fn from(len: Au) -> Self { 436 CSSPixelLength::new(len.to_f32_px()) 437 } 438 } 439 440 impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> { 441 #[inline] 442 fn from(length: CSSPixelLength) -> Self { 443 Self::new(length.0) 444 } 445 } 446 447 /// An alias of computed `<length>` value. 448 pub type Length = CSSPixelLength; 449 450 /// Either a computed `<length>` or the `auto` keyword. 451 pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>; 452 453 /// Either a non-negative `<length>` or the `auto` keyword. 454 pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>; 455 456 /// Either a computed `<length>` or a `<number>` value. 457 pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>; 458 459 /// A wrapper of Length, whose value must be >= 0. 460 pub type NonNegativeLength = NonNegative<Length>; 461 462 impl ClampToNonNegative for Length { 463 fn clamp_to_non_negative(self) -> Self { 464 Self::new(self.px().max(0.)) 465 } 466 } 467 468 impl NonNegativeLength { 469 /// Create a NonNegativeLength. 470 #[inline] 471 pub fn new(px: CSSFloat) -> Self { 472 NonNegative(Length::new(px.max(0.))) 473 } 474 475 /// Return the pixel value of |NonNegativeLength|. 476 #[inline] 477 pub fn px(&self) -> CSSFloat { 478 self.0.px() 479 } 480 481 #[inline] 482 /// Ensures it is non negative 483 pub fn clamp(self) -> Self { 484 if (self.0).0 < 0. { 485 Self::zero() 486 } else { 487 self 488 } 489 } 490 } 491 492 impl From<Length> for NonNegativeLength { 493 #[inline] 494 fn from(len: Length) -> Self { 495 NonNegative(len) 496 } 497 } 498 499 impl From<Au> for NonNegativeLength { 500 #[inline] 501 fn from(au: Au) -> Self { 502 NonNegative(au.into()) 503 } 504 } 505 506 impl From<NonNegativeLength> for Au { 507 #[inline] 508 fn from(non_negative_len: NonNegativeLength) -> Self { 509 Au::from(non_negative_len.0) 510 } 511 } 512 513 /// Either a computed NonNegativeLengthPercentage or the `normal` keyword. 514 pub type NonNegativeLengthPercentageOrNormal = 515 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>; 516 517 /// Either a non-negative `<length>` or a `<number>`. 518 pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>; 519 520 /// A computed value for `min-width`, `min-height`, `width` or `height` property. 521 pub type Size = GenericSize<NonNegativeLengthPercentage>; 522 523 /// A computed value for `max-width` or `max-height` property. 524 pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>; 525 526 #[cfg(feature = "gecko")] 527 use crate::{ 528 gecko_bindings::structs::AnchorPosResolutionParams, logical_geometry::PhysicalAxis, 529 values::generics::length::AnchorSizeKeyword, values::DashedIdent, 530 }; 531 532 /// Resolve the anchor function with the given resolver. Returns `Err()` if no anchor is found. 533 /// `prop_axis`, axis of the property (e.g. `margin-left` -> Horizontal axis), is used if the 534 /// anchor size keyword is not specified. 535 #[cfg(feature = "gecko")] 536 pub fn resolve_anchor_size( 537 anchor_name: &DashedIdent, 538 prop_axis: PhysicalAxis, 539 anchor_size_keyword: AnchorSizeKeyword, 540 params: &AnchorPosResolutionParams, 541 ) -> Result<Length, ()> { 542 use crate::gecko_bindings::structs::Gecko_GetAnchorPosSize; 543 544 let mut offset = Length::zero(); 545 let valid = unsafe { 546 Gecko_GetAnchorPosSize( 547 params, 548 anchor_name.0.as_ptr(), 549 prop_axis as u8, 550 anchor_size_keyword as u8, 551 &mut offset, 552 ) 553 }; 554 555 if !valid { 556 return Err(()); 557 } 558 559 Ok(offset) 560 } 561 562 /// A computed type for `margin` properties. 563 pub type Margin = generics::GenericMargin<LengthPercentage>; 564 565 impl TryTacticAdjustment for MaxSize { 566 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) { 567 debug_assert!( 568 old_side.orthogonal_to(new_side), 569 "Sizes should only change axes" 570 ); 571 match self { 572 Self::FitContentFunction(lp) 573 | Self::LengthPercentage(lp) 574 | Self::AnchorContainingCalcFunction(lp) => { 575 lp.try_tactic_adjustment(old_side, new_side); 576 }, 577 Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side), 578 Self::None 579 | Self::MaxContent 580 | Self::MinContent 581 | Self::FitContent 582 | Self::MozAvailable 583 | Self::WebkitFillAvailable 584 | Self::Stretch => {}, 585 } 586 } 587 } 588 589 impl TryTacticAdjustment for Size { 590 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) { 591 debug_assert!( 592 old_side.orthogonal_to(new_side), 593 "Sizes should only change axes" 594 ); 595 match self { 596 Self::FitContentFunction(lp) 597 | Self::LengthPercentage(lp) 598 | Self::AnchorContainingCalcFunction(lp) => { 599 lp.try_tactic_adjustment(old_side, new_side); 600 }, 601 Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side), 602 Self::Auto 603 | Self::MaxContent 604 | Self::MinContent 605 | Self::FitContent 606 | Self::MozAvailable 607 | Self::WebkitFillAvailable 608 | Self::Stretch => {}, 609 } 610 } 611 } 612 613 impl TryTacticAdjustment for Percentage { 614 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) { 615 if old_side.parallel_to(new_side) { 616 self.0 = 1.0 - self.0; 617 } 618 } 619 } 620 621 impl TryTacticAdjustment for Margin { 622 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) { 623 match self { 624 Self::Auto => {}, 625 Self::LengthPercentage(lp) | Self::AnchorContainingCalcFunction(lp) => { 626 lp.try_tactic_adjustment(old_side, new_side) 627 }, 628 Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side), 629 } 630 } 631 }