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::computed_value_flags::ComputedValueFlags; 9 use crate::values::animated::ToAnimatedValue; 10 use crate::values::computed::NonNegativeNumber; 11 use crate::values::generics::length as generics; 12 use crate::values::generics::length::{ 13 GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize, 14 }; 15 use crate::values::generics::NonNegative; 16 use crate::values::specified::length::{AbsoluteLength, FontBaseSize}; 17 use crate::values::{specified, CSSFloat}; 18 use crate::Zero; 19 use app_units::Au; 20 use std::fmt::{self, Write}; 21 use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub}; 22 use style_traits::{CSSPixel, CssWriter, ToCss}; 23 24 pub use super::image::Image; 25 pub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage}; 26 pub use crate::values::specified::url::UrlOrNone; 27 pub use crate::values::specified::{Angle, BorderStyle, Time}; 28 29 impl ToComputedValue for specified::NoCalcLength { 30 type ComputedValue = Length; 31 32 #[inline] 33 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 34 match *self { 35 specified::NoCalcLength::Absolute(length) => length.to_computed_value(context), 36 specified::NoCalcLength::FontRelative(length) => { 37 length.to_computed_value(context, FontBaseSize::CurrentStyle) 38 }, 39 specified::NoCalcLength::ViewportPercentage(length) => { 40 context.builder.add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS); 41 length.to_computed_value(context.viewport_size_for_viewport_unit_resolution()) 42 }, 43 specified::NoCalcLength::ServoCharacterWidth(length) => { 44 length.to_computed_value(context.style().get_font().clone_font_size().size()) 45 }, 46 } 47 } 48 49 #[inline] 50 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 51 specified::NoCalcLength::Absolute(AbsoluteLength::Px(computed.px())) 52 } 53 } 54 55 impl ToComputedValue for specified::Length { 56 type ComputedValue = Length; 57 58 #[inline] 59 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 60 match *self { 61 specified::Length::NoCalc(l) => l.to_computed_value(context), 62 specified::Length::Calc(ref calc) => { 63 calc.to_computed_value(context).to_length().unwrap() 64 }, 65 } 66 } 67 68 #[inline] 69 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 70 specified::Length::NoCalc(specified::NoCalcLength::from_computed_value(computed)) 71 } 72 } 73 74 /// Some boilerplate to share between negative and non-negative 75 /// length-percentage or auto. 76 macro_rules! computed_length_percentage_or_auto { 77 ($inner:ty) => { 78 /// Returns the used value. 79 #[inline] 80 pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> { 81 match *self { 82 generics::GenericLengthPercentageOrAuto::Auto => None, 83 generics::GenericLengthPercentageOrAuto::LengthPercentage(ref lp) => { 84 Some(lp.to_used_value(percentage_basis)) 85 }, 86 } 87 } 88 89 /// Returns true if the computed value is absolute 0 or 0%. 90 #[inline] 91 pub fn is_definitely_zero(&self) -> bool { 92 use crate::values::generics::length::LengthPercentageOrAuto::*; 93 match *self { 94 LengthPercentage(ref l) => l.is_definitely_zero(), 95 Auto => false, 96 } 97 } 98 }; 99 } 100 101 /// A computed type for `<length-percentage> | auto`. 102 pub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>; 103 104 impl LengthPercentageOrAuto { 105 /// Clamps the value to a non-negative value. 106 pub fn clamp_to_non_negative(self) -> Self { 107 use crate::values::generics::length::LengthPercentageOrAuto::*; 108 match self { 109 LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()), 110 Auto => Auto, 111 } 112 } 113 114 /// Convert to have a borrow inside the enum 115 pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> { 116 use crate::values::generics::length::LengthPercentageOrAuto::*; 117 match *self { 118 LengthPercentage(ref lp) => LengthPercentage(lp), 119 Auto => Auto, 120 } 121 } 122 123 computed_length_percentage_or_auto!(LengthPercentage); 124 } 125 126 impl generics::GenericLengthPercentageOrAuto<&LengthPercentage> { 127 /// Resolves the percentage. 128 #[inline] 129 pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto { 130 use crate::values::generics::length::LengthPercentageOrAuto::*; 131 match self { 132 LengthPercentage(length_percentage) => { 133 LengthPercentage(length_percentage.percentage_relative_to(basis)) 134 }, 135 Auto => Auto, 136 } 137 } 138 139 /// Maybe resolves the percentage. 140 #[inline] 141 pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto { 142 use crate::values::generics::length::LengthPercentageOrAuto::*; 143 match self { 144 LengthPercentage(length_percentage) => length_percentage 145 .maybe_percentage_relative_to(basis) 146 .map_or(Auto, LengthPercentage), 147 Auto => Auto, 148 } 149 } 150 } 151 152 /// A wrapper of LengthPercentageOrAuto, whose value must be >= 0. 153 pub type NonNegativeLengthPercentageOrAuto = 154 generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>; 155 156 impl NonNegativeLengthPercentageOrAuto { 157 computed_length_percentage_or_auto!(NonNegativeLengthPercentage); 158 } 159 160 #[cfg(feature = "servo")] 161 impl MaxSize { 162 /// Convert the computed value into used value. 163 #[inline] 164 pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> { 165 match *self { 166 GenericMaxSize::None => None, 167 GenericMaxSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), 168 } 169 } 170 } 171 172 impl Size { 173 /// Convert the computed value into used value. 174 #[inline] 175 #[cfg(feature = "servo")] 176 pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> { 177 match *self { 178 GenericSize::Auto => None, 179 GenericSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), 180 } 181 } 182 183 /// Returns true if the computed value is absolute 0 or 0%. 184 #[inline] 185 pub fn is_definitely_zero(&self) -> bool { 186 match *self { 187 GenericSize::Auto => false, 188 GenericSize::LengthPercentage(ref lp) => lp.is_definitely_zero(), 189 #[cfg(feature = "gecko")] 190 GenericSize::MinContent | 191 GenericSize::MaxContent | 192 GenericSize::MozFitContent | 193 GenericSize::MozAvailable | 194 GenericSize::FitContentFunction(_) => false 195 } 196 } 197 } 198 199 /// The computed `<length>` value. 200 #[derive( 201 Animate, 202 Clone, 203 ComputeSquaredDistance, 204 Copy, 205 Deserialize, 206 MallocSizeOf, 207 PartialEq, 208 PartialOrd, 209 Serialize, 210 ToAnimatedValue, 211 ToAnimatedZero, 212 ToComputedValue, 213 ToResolvedValue, 214 ToShmem, 215 )] 216 #[repr(C)] 217 pub struct CSSPixelLength(CSSFloat); 218 219 impl fmt::Debug for CSSPixelLength { 220 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 221 self.0.fmt(f)?; 222 f.write_str(" px") 223 } 224 } 225 226 impl CSSPixelLength { 227 /// Return a new CSSPixelLength. 228 #[inline] 229 pub fn new(px: CSSFloat) -> Self { 230 CSSPixelLength(px) 231 } 232 233 /// Returns a normalized (NaN turned to zero) version of this length. 234 #[inline] 235 pub fn normalized(self) -> Self { 236 Self::new(crate::values::normalize(self.0)) 237 } 238 239 /// Scale the length by a given amount. 240 #[inline] 241 pub fn scale_by(self, scale: CSSFloat) -> Self { 242 CSSPixelLength(self.0 * scale) 243 } 244 245 /// Return the containing pixel value. 246 #[inline] 247 pub fn px(self) -> CSSFloat { 248 self.0 249 } 250 251 /// Return the length with app_unit i32 type. 252 #[inline] 253 pub fn to_i32_au(self) -> i32 { 254 Au::from(self).0 255 } 256 257 /// Return the absolute value of this length. 258 #[inline] 259 pub fn abs(self) -> Self { 260 CSSPixelLength::new(self.0.abs()) 261 } 262 263 /// Return the clamped value of this length. 264 #[inline] 265 pub fn clamp_to_non_negative(self) -> Self { 266 CSSPixelLength::new(self.0.max(0.)) 267 } 268 269 /// Returns the minimum between `self` and `other`. 270 #[inline] 271 pub fn min(self, other: Self) -> Self { 272 CSSPixelLength::new(self.0.min(other.0)) 273 } 274 275 /// Returns the maximum between `self` and `other`. 276 #[inline] 277 pub fn max(self, other: Self) -> Self { 278 CSSPixelLength::new(self.0.max(other.0)) 279 } 280 281 /// Sets `self` to the maximum between `self` and `other`. 282 #[inline] 283 pub fn max_assign(&mut self, other: Self) { 284 *self = self.max(other); 285 } 286 287 /// Clamp the value to a lower bound and an optional upper bound. 288 /// 289 /// Can be used for example with `min-width` and `max-width`. 290 #[inline] 291 pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self { 292 self.clamp_below_max(max_size).max(min_size) 293 } 294 295 /// Clamp the value to an optional upper bound. 296 /// 297 /// Can be used for example with `max-width`. 298 #[inline] 299 pub fn clamp_below_max(self, max_size: Option<Self>) -> Self { 300 match max_size { 301 None => self, 302 Some(max_size) => self.min(max_size), 303 } 304 } 305 } 306 307 impl num_traits::Zero for CSSPixelLength { 308 fn zero() -> Self { 309 CSSPixelLength::new(0.) 310 } 311 312 fn is_zero(&self) -> bool { 313 self.px() == 0. 314 } 315 } 316 317 impl ToCss for CSSPixelLength { 318 #[inline] 319 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 320 where 321 W: Write, 322 { 323 self.0.to_css(dest)?; 324 dest.write_str("px") 325 } 326 } 327 328 impl std::iter::Sum for CSSPixelLength { 329 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self { 330 iter.fold(Length::zero(), Add::add) 331 } 332 } 333 334 impl Add for CSSPixelLength { 335 type Output = Self; 336 337 #[inline] 338 fn add(self, other: Self) -> Self { 339 Self::new(self.px() + other.px()) 340 } 341 } 342 343 impl AddAssign for CSSPixelLength { 344 #[inline] 345 fn add_assign(&mut self, other: Self) { 346 self.0 += other.0; 347 } 348 } 349 350 impl Div for CSSPixelLength { 351 type Output = CSSFloat; 352 353 #[inline] 354 fn div(self, other: Self) -> CSSFloat { 355 self.px() / other.px() 356 } 357 } 358 359 impl Div<CSSFloat> for CSSPixelLength { 360 type Output = Self; 361 362 #[inline] 363 fn div(self, other: CSSFloat) -> Self { 364 Self::new(self.px() / other) 365 } 366 } 367 368 impl MulAssign<CSSFloat> for CSSPixelLength { 369 #[inline] 370 fn mul_assign(&mut self, other: CSSFloat) { 371 self.0 *= other; 372 } 373 } 374 375 impl Mul<CSSFloat> for CSSPixelLength { 376 type Output = Self; 377 378 #[inline] 379 fn mul(self, other: CSSFloat) -> Self { 380 Self::new(self.px() * other) 381 } 382 } 383 384 impl Neg for CSSPixelLength { 385 type Output = Self; 386 387 #[inline] 388 fn neg(self) -> Self { 389 CSSPixelLength::new(-self.0) 390 } 391 } 392 393 impl Sub for CSSPixelLength { 394 type Output = Self; 395 396 #[inline] 397 fn sub(self, other: Self) -> Self { 398 Self::new(self.px() - other.px()) 399 } 400 } 401 402 impl From<CSSPixelLength> for Au { 403 #[inline] 404 fn from(len: CSSPixelLength) -> Self { 405 Au::from_f32_px(len.0) 406 } 407 } 408 409 impl From<Au> for CSSPixelLength { 410 #[inline] 411 fn from(len: Au) -> Self { 412 CSSPixelLength::new(len.to_f32_px()) 413 } 414 } 415 416 impl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> { 417 #[inline] 418 fn from(length: CSSPixelLength) -> Self { 419 Self::new(length.0) 420 } 421 } 422 423 /// An alias of computed `<length>` value. 424 pub type Length = CSSPixelLength; 425 426 /// Either a computed `<length>` or the `auto` keyword. 427 pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>; 428 429 /// Either a non-negative `<length>` or the `auto` keyword. 430 pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>; 431 432 /// Either a computed `<length>` or a `<number>` value. 433 pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>; 434 435 /// A wrapper of Length, whose value must be >= 0. 436 pub type NonNegativeLength = NonNegative<Length>; 437 438 impl ToAnimatedValue for NonNegativeLength { 439 type AnimatedValue = Length; 440 441 #[inline] 442 fn to_animated_value(self) -> Self::AnimatedValue { 443 self.0 444 } 445 446 #[inline] 447 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 448 NonNegativeLength::new(animated.px().max(0.)) 449 } 450 } 451 452 impl NonNegativeLength { 453 /// Create a NonNegativeLength. 454 #[inline] 455 pub fn new(px: CSSFloat) -> Self { 456 NonNegative(Length::new(px.max(0.))) 457 } 458 459 /// Return the pixel value of |NonNegativeLength|. 460 #[inline] 461 pub fn px(&self) -> CSSFloat { 462 self.0.px() 463 } 464 465 #[inline] 466 /// Ensures it is non negative 467 pub fn clamp(self) -> Self { 468 if (self.0).0 < 0. { 469 Self::zero() 470 } else { 471 self 472 } 473 } 474 } 475 476 impl From<Length> for NonNegativeLength { 477 #[inline] 478 fn from(len: Length) -> Self { 479 NonNegative(len) 480 } 481 } 482 483 impl From<Au> for NonNegativeLength { 484 #[inline] 485 fn from(au: Au) -> Self { 486 NonNegative(au.into()) 487 } 488 } 489 490 impl From<NonNegativeLength> for Au { 491 #[inline] 492 fn from(non_negative_len: NonNegativeLength) -> Self { 493 Au::from(non_negative_len.0) 494 } 495 } 496 497 /// Either a computed NonNegativeLengthPercentage or the `normal` keyword. 498 pub type NonNegativeLengthPercentageOrNormal = 499 GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>; 500 501 /// Either a non-negative `<length>` or a `<number>`. 502 pub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>; 503 504 /// A computed value for `min-width`, `min-height`, `width` or `height` property. 505 pub type Size = GenericSize<NonNegativeLengthPercentage>; 506 507 /// A computed value for `max-width` or `min-height` property. 508 pub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>; 509