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 //! Computed values. 6 7 use self::transform::DirectionVector; 8 use super::animated::ToAnimatedValue; 9 use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; 10 use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks; 11 use super::generics::grid::{GenericGridLine, GenericTrackBreadth}; 12 use super::generics::grid::{GenericTrackSize, TrackList as GenericTrackList}; 13 use super::generics::transform::IsParallelTo; 14 use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne}; 15 use super::specified; 16 use super::{CSSFloat, CSSInteger}; 17 use crate::context::QuirksMode; 18 use crate::font_metrics::{get_metrics_provider_for_product, FontMetricsProvider}; 19 use crate::media_queries::Device; 20 #[cfg(feature = "gecko")] 21 use crate::properties; 22 use crate::properties::{ComputedValues, LonghandId, StyleBuilder}; 23 use crate::rule_cache::RuleCacheConditions; 24 use crate::{ArcSlice, Atom, One}; 25 use euclid::{default, Point2D, Rect, Size2D}; 26 use servo_arc::Arc; 27 use std::cell::RefCell; 28 use std::cmp; 29 use std::f32; 30 use std::ops::{Add, Sub}; 31 32 #[cfg(feature = "gecko")] 33 pub use self::align::{ 34 AlignContent, AlignItems, AlignTracks, JustifyContent, JustifyItems, JustifyTracks, 35 SelfAlignment, 36 }; 37 #[cfg(feature = "gecko")] 38 pub use self::align::{AlignSelf, JustifySelf}; 39 pub use self::angle::Angle; 40 pub use self::background::{BackgroundRepeat, BackgroundSize}; 41 pub use self::basic_shape::FillRule; 42 pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing}; 43 pub use self::border::{BorderImageRepeat, BorderImageSideWidth}; 44 pub use self::border::{BorderImageSlice, BorderImageWidth}; 45 pub use self::box_::{AnimationIterationCount, AnimationName, AnimationTimeline, Contain}; 46 pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, Float}; 47 pub use self::box_::{Display, Overflow, OverflowAnchor, TransitionProperty}; 48 pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter}; 49 pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType}; 50 pub use self::box_::{TouchAction, VerticalAlign, WillChange}; 51 pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme, PrintColorAdjust}; 52 pub use self::column::ColumnCount; 53 pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet}; 54 pub use self::easing::TimingFunction; 55 pub use self::effects::{BoxShadow, Filter, SimpleShadow}; 56 pub use self::flex::FlexBasis; 57 pub use self::font::{FontFamily, FontLanguageOverride, FontStyle}; 58 pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric}; 59 pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis}; 60 pub use self::font::{FontVariantAlternates, FontWeight}; 61 pub use self::font::{FontVariantEastAsian, FontVariationSettings}; 62 pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; 63 pub use self::image::{Gradient, Image, ImageRendering, LineDirection, MozImageRect}; 64 pub use self::length::{CSSPixelLength, NonNegativeLength}; 65 pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber}; 66 pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size}; 67 pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; 68 #[cfg(feature = "gecko")] 69 pub use self::list::ListStyleType; 70 pub use self::list::Quotes; 71 pub use self::motion::{OffsetPath, OffsetRotate}; 72 pub use self::outline::OutlineStyle; 73 pub use self::page::{Orientation, PageName, PageSize, PaperSize}; 74 pub use self::percentage::{NonNegativePercentage, Percentage}; 75 pub use self::position::AspectRatio; 76 pub use self::position::{ 77 GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex, 78 }; 79 pub use self::ratio::Ratio; 80 pub use self::rect::NonNegativeLengthOrNumberRect; 81 pub use self::resolution::Resolution; 82 pub use self::svg::{DProperty, MozContextProperties}; 83 pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; 84 pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; 85 pub use self::text::HyphenateCharacter; 86 pub use self::text::TextUnderlinePosition; 87 pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight}; 88 pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing}; 89 pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle}; 90 pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify}; 91 pub use self::time::Time; 92 pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; 93 pub use self::transform::{TransformOrigin, TransformStyle, Translate}; 94 #[cfg(feature = "gecko")] 95 pub use self::ui::CursorImage; 96 pub use self::ui::{Cursor, MozForceBrokenImageIcon, UserSelect}; 97 pub use super::specified::TextTransform; 98 pub use super::specified::{BorderStyle, TextDecorationLine}; 99 pub use super::{Auto, Either, None_}; 100 pub use app_units::Au; 101 102 #[cfg(feature = "gecko")] 103 pub mod align; 104 pub mod angle; 105 pub mod background; 106 pub mod basic_shape; 107 pub mod border; 108 #[path = "box.rs"] 109 pub mod box_; 110 pub mod color; 111 pub mod column; 112 pub mod counters; 113 pub mod easing; 114 pub mod effects; 115 pub mod flex; 116 pub mod font; 117 pub mod image; 118 pub mod length; 119 pub mod length_percentage; 120 pub mod list; 121 pub mod motion; 122 pub mod outline; 123 pub mod page; 124 pub mod percentage; 125 pub mod position; 126 pub mod ratio; 127 pub mod rect; 128 pub mod resolution; 129 pub mod svg; 130 pub mod table; 131 pub mod text; 132 pub mod time; 133 pub mod transform; 134 pub mod ui; 135 pub mod url; 136 137 /// A `Context` is all the data a specified value could ever need to compute 138 /// itself and be transformed to a computed value. 139 pub struct Context<'a> { 140 /// Values accessed through this need to be in the properties "computed 141 /// early": color, text-decoration, font-size, display, position, float, 142 /// border-*-style, outline-style, font-family, writing-mode... 143 pub builder: StyleBuilder<'a>, 144 145 /// A cached computed system font value, for use by gecko. 146 /// 147 /// See properties/longhands/font.mako.rs 148 #[cfg(feature = "gecko")] 149 pub cached_system_font: Option<properties::longhands::system_font::ComputedSystemFont>, 150 151 /// A dummy option for servo so initializing a computed::Context isn't 152 /// painful. 153 /// 154 /// TODO(emilio): Make constructors for Context, and drop this. 155 #[cfg(feature = "servo")] 156 pub cached_system_font: Option<()>, 157 158 /// A font metrics provider, used to access font metrics to implement 159 /// font-relative units. 160 pub font_metrics_provider: &'a dyn FontMetricsProvider, 161 162 /// Whether or not we are computing the media list in a media query 163 pub in_media_query: bool, 164 165 /// The quirks mode of this context. 166 pub quirks_mode: QuirksMode, 167 168 /// Whether this computation is being done for a SMIL animation. 169 /// 170 /// This is used to allow certain properties to generate out-of-range 171 /// values, which SMIL allows. 172 pub for_smil_animation: bool, 173 174 /// The property we are computing a value for, if it is a non-inherited 175 /// property. None if we are computed a value for an inherited property 176 /// or not computing for a property at all (e.g. in a media query 177 /// evaluation). 178 pub for_non_inherited_property: Option<LonghandId>, 179 180 /// The conditions to cache a rule node on the rule cache. 181 /// 182 /// FIXME(emilio): Drop the refcell. 183 pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>, 184 } 185 186 impl<'a> Context<'a> { 187 /// Creates a suitable context for media query evaluation, in which 188 /// font-relative units compute against the system_font, and executes `f` 189 /// with it. for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R where F: FnOnce(&Context) -> R,190 pub fn for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R 191 where 192 F: FnOnce(&Context) -> R, 193 { 194 let mut conditions = RuleCacheConditions::default(); 195 let provider = get_metrics_provider_for_product(); 196 197 let context = Context { 198 builder: StyleBuilder::for_inheritance(device, None, None), 199 font_metrics_provider: &provider, 200 cached_system_font: None, 201 in_media_query: true, 202 quirks_mode, 203 for_smil_animation: false, 204 for_non_inherited_property: None, 205 rule_cache_conditions: RefCell::new(&mut conditions), 206 }; 207 208 f(&context) 209 } 210 211 /// The current device. device(&self) -> &Device212 pub fn device(&self) -> &Device { 213 self.builder.device 214 } 215 216 /// The current viewport size, used to resolve viewport units. viewport_size_for_viewport_unit_resolution(&self) -> default::Size2D<Au>217 pub fn viewport_size_for_viewport_unit_resolution(&self) -> default::Size2D<Au> { 218 self.builder 219 .device 220 .au_viewport_size_for_viewport_unit_resolution() 221 } 222 223 /// The default computed style we're getting our reset style from. default_style(&self) -> &ComputedValues224 pub fn default_style(&self) -> &ComputedValues { 225 self.builder.default_style() 226 } 227 228 /// The current style. style(&self) -> &StyleBuilder229 pub fn style(&self) -> &StyleBuilder { 230 &self.builder 231 } 232 233 /// Apply text-zoom if enabled. 234 #[cfg(feature = "gecko")] maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength235 pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength { 236 // We disable zoom for <svg:text> by unsetting the 237 // -x-text-zoom property, which leads to a false value 238 // in mAllowZoomAndMinSize 239 if self.style().get_font().gecko.mAllowZoomAndMinSize { 240 self.device().zoom_text(size) 241 } else { 242 size 243 } 244 } 245 246 /// (Servo doesn't do text-zoom) 247 #[cfg(feature = "servo")] maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength248 pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength { 249 size 250 } 251 } 252 253 /// An iterator over a slice of computed values 254 #[derive(Clone)] 255 pub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> { 256 cx: &'cx Context<'cx_a>, 257 values: &'a [S], 258 } 259 260 impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_a, S> { 261 /// Construct an iterator from a slice of specified values and a context new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self262 pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self { 263 ComputedVecIter { 264 cx: cx, 265 values: values, 266 } 267 } 268 } 269 270 impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ExactSizeIterator 271 for ComputedVecIter<'a, 'cx, 'cx_a, S> 272 { len(&self) -> usize273 fn len(&self) -> usize { 274 self.values.len() 275 } 276 } 277 278 impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter<'a, 'cx, 'cx_a, S> { 279 type Item = S::ComputedValue; next(&mut self) -> Option<Self::Item>280 fn next(&mut self) -> Option<Self::Item> { 281 if let Some((next, rest)) = self.values.split_first() { 282 let ret = next.to_computed_value(self.cx); 283 self.values = rest; 284 Some(ret) 285 } else { 286 None 287 } 288 } 289 size_hint(&self) -> (usize, Option<usize>)290 fn size_hint(&self) -> (usize, Option<usize>) { 291 (self.values.len(), Some(self.values.len())) 292 } 293 } 294 295 /// A trait to represent the conversion between computed and specified values. 296 /// 297 /// This trait is derivable with `#[derive(ToComputedValue)]`. The derived 298 /// implementation just calls `ToComputedValue::to_computed_value` on each field 299 /// of the passed value. The deriving code assumes that if the type isn't 300 /// generic, then the trait can be implemented as simple `Clone::clone` calls, 301 /// this means that a manual implementation with `ComputedValue = Self` is bogus 302 /// if it returns anything else than a clone. 303 pub trait ToComputedValue { 304 /// The computed value type we're going to be converted to. 305 type ComputedValue; 306 307 /// Convert a specified value to a computed value, using itself and the data 308 /// inside the `Context`. to_computed_value(&self, context: &Context) -> Self::ComputedValue309 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue; 310 311 /// Convert a computed value to specified value form. 312 /// 313 /// This will be used for recascading during animation. 314 /// Such from_computed_valued values should recompute to the same value. from_computed_value(computed: &Self::ComputedValue) -> Self315 fn from_computed_value(computed: &Self::ComputedValue) -> Self; 316 } 317 318 impl<A, B> ToComputedValue for (A, B) 319 where 320 A: ToComputedValue, 321 B: ToComputedValue, 322 { 323 type ComputedValue = ( 324 <A as ToComputedValue>::ComputedValue, 325 <B as ToComputedValue>::ComputedValue, 326 ); 327 328 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue329 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 330 ( 331 self.0.to_computed_value(context), 332 self.1.to_computed_value(context), 333 ) 334 } 335 336 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self337 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 338 ( 339 A::from_computed_value(&computed.0), 340 B::from_computed_value(&computed.1), 341 ) 342 } 343 } 344 345 impl<T> ToComputedValue for Option<T> 346 where 347 T: ToComputedValue, 348 { 349 type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>; 350 351 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue352 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 353 self.as_ref().map(|item| item.to_computed_value(context)) 354 } 355 356 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self357 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 358 computed.as_ref().map(T::from_computed_value) 359 } 360 } 361 362 impl<T> ToComputedValue for default::Size2D<T> 363 where 364 T: ToComputedValue, 365 { 366 type ComputedValue = default::Size2D<<T as ToComputedValue>::ComputedValue>; 367 368 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue369 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 370 Size2D::new( 371 self.width.to_computed_value(context), 372 self.height.to_computed_value(context), 373 ) 374 } 375 376 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self377 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 378 Size2D::new( 379 T::from_computed_value(&computed.width), 380 T::from_computed_value(&computed.height), 381 ) 382 } 383 } 384 385 impl<T> ToComputedValue for Vec<T> 386 where 387 T: ToComputedValue, 388 { 389 type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>; 390 391 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue392 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 393 self.iter() 394 .map(|item| item.to_computed_value(context)) 395 .collect() 396 } 397 398 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self399 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 400 computed.iter().map(T::from_computed_value).collect() 401 } 402 } 403 404 impl<T> ToComputedValue for Box<T> 405 where 406 T: ToComputedValue, 407 { 408 type ComputedValue = Box<<T as ToComputedValue>::ComputedValue>; 409 410 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue411 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 412 Box::new(T::to_computed_value(self, context)) 413 } 414 415 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self416 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 417 Box::new(T::from_computed_value(computed)) 418 } 419 } 420 421 impl<T> ToComputedValue for Box<[T]> 422 where 423 T: ToComputedValue, 424 { 425 type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>; 426 427 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue428 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 429 self.iter() 430 .map(|item| item.to_computed_value(context)) 431 .collect::<Vec<_>>() 432 .into_boxed_slice() 433 } 434 435 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self436 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 437 computed 438 .iter() 439 .map(T::from_computed_value) 440 .collect::<Vec<_>>() 441 .into_boxed_slice() 442 } 443 } 444 445 impl<T> ToComputedValue for crate::OwnedSlice<T> 446 where 447 T: ToComputedValue, 448 { 449 type ComputedValue = crate::OwnedSlice<<T as ToComputedValue>::ComputedValue>; 450 451 #[inline] to_computed_value(&self, context: &Context) -> Self::ComputedValue452 fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { 453 self.iter() 454 .map(|item| item.to_computed_value(context)) 455 .collect() 456 } 457 458 #[inline] from_computed_value(computed: &Self::ComputedValue) -> Self459 fn from_computed_value(computed: &Self::ComputedValue) -> Self { 460 computed.iter().map(T::from_computed_value).collect() 461 } 462 } 463 464 // NOTE(emilio): This is implementable more generically, but it's unlikely 465 // what you want there, as it forces you to have an extra allocation. 466 // 467 // We could do that if needed, ideally with specialization for the case where 468 // ComputedValue = T. But we don't need it for now. 469 impl<T> ToComputedValue for Arc<T> 470 where 471 T: ToComputedValue<ComputedValue = T>, 472 { 473 type ComputedValue = Self; 474 475 #[inline] to_computed_value(&self, _: &Context) -> Self476 fn to_computed_value(&self, _: &Context) -> Self { 477 self.clone() 478 } 479 480 #[inline] from_computed_value(computed: &Self) -> Self481 fn from_computed_value(computed: &Self) -> Self { 482 computed.clone() 483 } 484 } 485 486 // Same caveat as above applies. 487 impl<T> ToComputedValue for ArcSlice<T> 488 where 489 T: ToComputedValue<ComputedValue = T>, 490 { 491 type ComputedValue = Self; 492 493 #[inline] to_computed_value(&self, _: &Context) -> Self494 fn to_computed_value(&self, _: &Context) -> Self { 495 self.clone() 496 } 497 498 #[inline] from_computed_value(computed: &Self) -> Self499 fn from_computed_value(computed: &Self) -> Self { 500 computed.clone() 501 } 502 } 503 504 trivial_to_computed_value!(()); 505 trivial_to_computed_value!(bool); 506 trivial_to_computed_value!(f32); 507 trivial_to_computed_value!(i32); 508 trivial_to_computed_value!(u8); 509 trivial_to_computed_value!(u16); 510 trivial_to_computed_value!(u32); 511 trivial_to_computed_value!(usize); 512 trivial_to_computed_value!(Atom); 513 trivial_to_computed_value!(crate::values::AtomIdent); 514 #[cfg(feature = "servo")] 515 trivial_to_computed_value!(crate::Namespace); 516 #[cfg(feature = "servo")] 517 trivial_to_computed_value!(crate::Prefix); 518 trivial_to_computed_value!(String); 519 trivial_to_computed_value!(Box<str>); 520 trivial_to_computed_value!(crate::OwnedStr); 521 trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType); 522 523 #[allow(missing_docs)] 524 #[derive( 525 Animate, 526 Clone, 527 ComputeSquaredDistance, 528 Copy, 529 Debug, 530 MallocSizeOf, 531 PartialEq, 532 ToAnimatedZero, 533 ToCss, 534 ToResolvedValue, 535 )] 536 #[repr(C, u8)] 537 pub enum AngleOrPercentage { 538 Percentage(Percentage), 539 Angle(Angle), 540 } 541 542 impl ToComputedValue for specified::AngleOrPercentage { 543 type ComputedValue = AngleOrPercentage; 544 545 #[inline] to_computed_value(&self, context: &Context) -> AngleOrPercentage546 fn to_computed_value(&self, context: &Context) -> AngleOrPercentage { 547 match *self { 548 specified::AngleOrPercentage::Percentage(percentage) => { 549 AngleOrPercentage::Percentage(percentage.to_computed_value(context)) 550 }, 551 specified::AngleOrPercentage::Angle(angle) => { 552 AngleOrPercentage::Angle(angle.to_computed_value(context)) 553 }, 554 } 555 } 556 #[inline] from_computed_value(computed: &AngleOrPercentage) -> Self557 fn from_computed_value(computed: &AngleOrPercentage) -> Self { 558 match *computed { 559 AngleOrPercentage::Percentage(percentage) => specified::AngleOrPercentage::Percentage( 560 ToComputedValue::from_computed_value(&percentage), 561 ), 562 AngleOrPercentage::Angle(angle) => { 563 specified::AngleOrPercentage::Angle(ToComputedValue::from_computed_value(&angle)) 564 }, 565 } 566 } 567 } 568 569 /// A `<number>` value. 570 pub type Number = CSSFloat; 571 572 impl IsParallelTo for (Number, Number, Number) { is_parallel_to(&self, vector: &DirectionVector) -> bool573 fn is_parallel_to(&self, vector: &DirectionVector) -> bool { 574 use euclid::approxeq::ApproxEq; 575 // If a and b is parallel, the angle between them is 0deg, so 576 // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0. 577 let self_vector = DirectionVector::new(self.0, self.1, self.2); 578 self_vector 579 .cross(*vector) 580 .square_length() 581 .approx_eq(&0.0f32) 582 } 583 } 584 585 /// A wrapper of Number, but the value >= 0. 586 pub type NonNegativeNumber = NonNegative<CSSFloat>; 587 588 impl ToAnimatedValue for NonNegativeNumber { 589 type AnimatedValue = CSSFloat; 590 591 #[inline] to_animated_value(self) -> Self::AnimatedValue592 fn to_animated_value(self) -> Self::AnimatedValue { 593 self.0 594 } 595 596 #[inline] from_animated_value(animated: Self::AnimatedValue) -> Self597 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 598 animated.max(0.).into() 599 } 600 } 601 602 impl From<CSSFloat> for NonNegativeNumber { 603 #[inline] from(number: CSSFloat) -> NonNegativeNumber604 fn from(number: CSSFloat) -> NonNegativeNumber { 605 NonNegative::<CSSFloat>(number) 606 } 607 } 608 609 impl From<NonNegativeNumber> for CSSFloat { 610 #[inline] from(number: NonNegativeNumber) -> CSSFloat611 fn from(number: NonNegativeNumber) -> CSSFloat { 612 number.0 613 } 614 } 615 616 impl One for NonNegativeNumber { 617 #[inline] one() -> Self618 fn one() -> Self { 619 NonNegative(1.0) 620 } 621 622 #[inline] is_one(&self) -> bool623 fn is_one(&self) -> bool { 624 self.0 == 1.0 625 } 626 } 627 628 /// A wrapper of Number, but the value between 0 and 1 629 pub type ZeroToOneNumber = ZeroToOne<CSSFloat>; 630 631 impl ToAnimatedValue for ZeroToOneNumber { 632 type AnimatedValue = CSSFloat; 633 634 #[inline] to_animated_value(self) -> Self::AnimatedValue635 fn to_animated_value(self) -> Self::AnimatedValue { 636 self.0 637 } 638 639 #[inline] from_animated_value(animated: Self::AnimatedValue) -> Self640 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 641 Self(animated.max(0.).min(1.)) 642 } 643 } 644 645 impl From<CSSFloat> for ZeroToOneNumber { 646 #[inline] from(number: CSSFloat) -> Self647 fn from(number: CSSFloat) -> Self { 648 Self(number) 649 } 650 } 651 652 /// A wrapper of Number, but the value >= 1. 653 pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>; 654 655 impl ToAnimatedValue for GreaterThanOrEqualToOneNumber { 656 type AnimatedValue = CSSFloat; 657 658 #[inline] to_animated_value(self) -> Self::AnimatedValue659 fn to_animated_value(self) -> Self::AnimatedValue { 660 self.0 661 } 662 663 #[inline] from_animated_value(animated: Self::AnimatedValue) -> Self664 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 665 animated.max(1.).into() 666 } 667 } 668 669 impl From<CSSFloat> for GreaterThanOrEqualToOneNumber { 670 #[inline] from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber671 fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber { 672 GreaterThanOrEqualToOne::<CSSFloat>(number) 673 } 674 } 675 676 impl From<GreaterThanOrEqualToOneNumber> for CSSFloat { 677 #[inline] from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat678 fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat { 679 number.0 680 } 681 } 682 683 #[allow(missing_docs)] 684 #[derive( 685 Animate, 686 Clone, 687 ComputeSquaredDistance, 688 Copy, 689 Debug, 690 MallocSizeOf, 691 PartialEq, 692 ToAnimatedZero, 693 ToCss, 694 ToResolvedValue, 695 )] 696 #[repr(C, u8)] 697 pub enum NumberOrPercentage { 698 Percentage(Percentage), 699 Number(Number), 700 } 701 702 impl NumberOrPercentage { clamp_to_non_negative(self) -> Self703 fn clamp_to_non_negative(self) -> Self { 704 match self { 705 NumberOrPercentage::Percentage(p) => { 706 NumberOrPercentage::Percentage(p.clamp_to_non_negative()) 707 }, 708 NumberOrPercentage::Number(n) => NumberOrPercentage::Number(n.max(0.)), 709 } 710 } 711 } 712 713 impl ToComputedValue for specified::NumberOrPercentage { 714 type ComputedValue = NumberOrPercentage; 715 716 #[inline] to_computed_value(&self, context: &Context) -> NumberOrPercentage717 fn to_computed_value(&self, context: &Context) -> NumberOrPercentage { 718 match *self { 719 specified::NumberOrPercentage::Percentage(percentage) => { 720 NumberOrPercentage::Percentage(percentage.to_computed_value(context)) 721 }, 722 specified::NumberOrPercentage::Number(number) => { 723 NumberOrPercentage::Number(number.to_computed_value(context)) 724 }, 725 } 726 } 727 #[inline] from_computed_value(computed: &NumberOrPercentage) -> Self728 fn from_computed_value(computed: &NumberOrPercentage) -> Self { 729 match *computed { 730 NumberOrPercentage::Percentage(percentage) => { 731 specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value( 732 &percentage, 733 )) 734 }, 735 NumberOrPercentage::Number(number) => { 736 specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number)) 737 }, 738 } 739 } 740 } 741 742 /// A non-negative <number-percentage>. 743 pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>; 744 745 impl NonNegativeNumberOrPercentage { 746 /// Returns the `100%` value. 747 #[inline] hundred_percent() -> Self748 pub fn hundred_percent() -> Self { 749 NonNegative(NumberOrPercentage::Percentage(Percentage::hundred())) 750 } 751 } 752 753 impl ToAnimatedValue for NonNegativeNumberOrPercentage { 754 type AnimatedValue = NumberOrPercentage; 755 756 #[inline] to_animated_value(self) -> Self::AnimatedValue757 fn to_animated_value(self) -> Self::AnimatedValue { 758 self.0 759 } 760 761 #[inline] from_animated_value(animated: Self::AnimatedValue) -> Self762 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 763 NonNegative(animated.clamp_to_non_negative()) 764 } 765 } 766 767 /// A type used for opacity. 768 pub type Opacity = CSSFloat; 769 770 /// A `<integer>` value. 771 pub type Integer = CSSInteger; 772 773 /// A wrapper of Integer, but only accept a value >= 1. 774 pub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>; 775 776 impl ToAnimatedValue for PositiveInteger { 777 type AnimatedValue = CSSInteger; 778 779 #[inline] to_animated_value(self) -> Self::AnimatedValue780 fn to_animated_value(self) -> Self::AnimatedValue { 781 self.0 782 } 783 784 #[inline] from_animated_value(animated: Self::AnimatedValue) -> Self785 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 786 cmp::max(animated, 1).into() 787 } 788 } 789 790 impl From<CSSInteger> for PositiveInteger { 791 #[inline] from(int: CSSInteger) -> PositiveInteger792 fn from(int: CSSInteger) -> PositiveInteger { 793 GreaterThanOrEqualToOne::<CSSInteger>(int) 794 } 795 } 796 797 /// A computed positive `<integer>` value or `none`. 798 pub type PositiveIntegerOrNone = Either<PositiveInteger, None_>; 799 800 /// rect(...) | auto 801 pub type ClipRect = generics::GenericClipRect<LengthOrAuto>; 802 803 /// rect(...) | auto 804 pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>; 805 806 /// The computed value of a grid `<track-breadth>` 807 pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>; 808 809 /// The computed value of a grid `<track-size>` 810 pub type TrackSize = GenericTrackSize<LengthPercentage>; 811 812 /// The computed value of a grid `<track-size>+` 813 pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>; 814 815 /// The computed value of a grid `<track-list>` 816 /// (could also be `<auto-track-list>` or `<explicit-track-list>`) 817 pub type TrackList = GenericTrackList<LengthPercentage, Integer>; 818 819 /// The computed value of a `<grid-line>`. 820 pub type GridLine = GenericGridLine<Integer>; 821 822 /// `<grid-template-rows> | <grid-template-columns>` 823 pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>; 824 825 impl ClipRect { 826 /// Given a border box, resolves the clip rect against the border box 827 /// in the same space the border box is in for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>( &self, border_box: Rect<T, U>, ) -> Rect<T, U>828 pub fn for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>( 829 &self, 830 border_box: Rect<T, U>, 831 ) -> Rect<T, U> { 832 fn extract_clip_component<T: From<Length>>(p: &LengthOrAuto, or: T) -> T { 833 match *p { 834 LengthOrAuto::Auto => or, 835 LengthOrAuto::LengthPercentage(ref length) => T::from(*length), 836 } 837 } 838 839 let clip_origin = Point2D::new( 840 From::from(self.left.auto_is(|| Length::new(0.))), 841 From::from(self.top.auto_is(|| Length::new(0.))), 842 ); 843 let right = extract_clip_component(&self.right, border_box.size.width); 844 let bottom = extract_clip_component(&self.bottom, border_box.size.height); 845 let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y); 846 847 Rect::new(clip_origin, clip_size).translate(border_box.origin.to_vector()) 848 } 849 } 850