diff options
Diffstat (limited to '')
-rw-r--r-- | servo/components/style/values/computed/mod.rs | 996 |
1 files changed, 996 insertions, 0 deletions
diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs new file mode 100644 index 0000000000..dac4c4f246 --- /dev/null +++ b/servo/components/style/values/computed/mod.rs @@ -0,0 +1,996 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! Computed values. + +use self::transform::DirectionVector; +use super::animated::ToAnimatedValue; +use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; +use super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks; +use super::generics::grid::{GenericGridLine, GenericTrackBreadth}; +use super::generics::grid::{GenericTrackSize, TrackList as GenericTrackList}; +use super::generics::transform::IsParallelTo; +use super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne}; +use super::specified; +use super::{CSSFloat, CSSInteger}; +use crate::computed_value_flags::ComputedValueFlags; +use crate::context::QuirksMode; +use crate::font_metrics::{FontMetrics, FontMetricsOrientation}; +use crate::media_queries::Device; +#[cfg(feature = "gecko")] +use crate::properties; +use crate::properties::{ComputedValues, LonghandId, StyleBuilder}; +use crate::rule_cache::RuleCacheConditions; +use crate::stylesheets::container_rule::{ + ContainerInfo, ContainerSizeQuery, ContainerSizeQueryResult, +}; +use crate::values::specified::length::FontBaseSize; +use crate::{ArcSlice, Atom, One}; +use euclid::{default, Point2D, Rect, Size2D}; +use servo_arc::Arc; +use std::cell::RefCell; +use std::cmp; +use std::f32; +use std::ops::{Add, Sub}; + +#[cfg(feature = "gecko")] +pub use self::align::{ + AlignContent, AlignItems, AlignTracks, JustifyContent, JustifyItems, JustifyTracks, + SelfAlignment, +}; +#[cfg(feature = "gecko")] +pub use self::align::{AlignSelf, JustifySelf}; +pub use self::angle::Angle; +pub use self::background::{BackgroundRepeat, BackgroundSize}; +pub use self::basic_shape::FillRule; +pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing}; +pub use self::border::{BorderImageRepeat, BorderImageSideWidth}; +pub use self::border::{BorderImageSlice, BorderImageWidth}; +pub use self::box_::{ + AnimationIterationCount, AnimationName, AnimationTimeline, Contain, ContainerName, + ContainerType, +}; +pub use self::box_::{ + Appearance, BreakBetween, BreakWithin, Clear, ContainIntrinsicSize, ContentVisibility, Float, +}; +pub use self::box_::{Display, LineClamp, Overflow, OverflowAnchor, TransitionProperty}; +pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter}; +pub use self::box_::{ScrollAxis, ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop}; +pub use self::box_::{ScrollSnapStrictness, ScrollSnapType, ScrollTimelineName}; +pub use self::box_::{TouchAction, VerticalAlign, WillChange}; +pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme, PrintColorAdjust}; +pub use self::column::ColumnCount; +pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet}; +pub use self::easing::TimingFunction; +pub use self::effects::{BoxShadow, Filter, SimpleShadow}; +pub use self::flex::FlexBasis; +pub use self::font::{FontFamily, FontLanguageOverride, FontStyle, FontPalette}; +pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric}; +pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis}; +pub use self::font::{FontVariantAlternates, FontWeight}; +pub use self::font::{FontVariantEastAsian, FontVariationSettings}; +pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; +pub use self::image::{Gradient, Image, ImageRendering, LineDirection, MozImageRect}; +pub use self::length::{CSSPixelLength, NonNegativeLength}; +pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber}; +pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size}; +pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; +#[cfg(feature = "gecko")] +pub use self::list::ListStyleType; +pub use self::list::Quotes; +pub use self::motion::{OffsetPath, OffsetRotate}; +pub use self::outline::OutlineStyle; +pub use self::page::{PageName, PageSize, PageSizeOrientation, PaperSize}; +pub use self::percentage::{NonNegativePercentage, Percentage}; +pub use self::position::AspectRatio; +pub use self::position::{ + GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex, +}; +pub use self::ratio::Ratio; +pub use self::rect::NonNegativeLengthOrNumberRect; +pub use self::resolution::Resolution; +pub use self::svg::{DProperty, MozContextProperties}; +pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; +pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; +pub use self::text::HyphenateCharacter; +pub use self::text::TextUnderlinePosition; +pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight}; +pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing}; +pub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle}; +pub use self::text::{TextDecorationLength, TextDecorationSkipInk, TextJustify}; +pub use self::time::Time; +pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; +pub use self::transform::{TransformOrigin, TransformStyle, Translate}; +#[cfg(feature = "gecko")] +pub use self::ui::CursorImage; +pub use self::ui::{BoolInteger, Cursor, UserSelect}; +pub use super::specified::TextTransform; +pub use super::specified::ViewportVariant; +pub use super::specified::{BorderStyle, TextDecorationLine}; +pub use app_units::Au; + +#[cfg(feature = "gecko")] +pub mod align; +pub mod angle; +pub mod background; +pub mod basic_shape; +pub mod border; +#[path = "box.rs"] +pub mod box_; +pub mod color; +pub mod column; +pub mod counters; +pub mod easing; +pub mod effects; +pub mod flex; +pub mod font; +pub mod image; +pub mod length; +pub mod length_percentage; +pub mod list; +pub mod motion; +pub mod outline; +pub mod page; +pub mod percentage; +pub mod position; +pub mod ratio; +pub mod rect; +pub mod resolution; +pub mod svg; +pub mod table; +pub mod text; +pub mod time; +pub mod transform; +pub mod ui; +pub mod url; + +/// A `Context` is all the data a specified value could ever need to compute +/// itself and be transformed to a computed value. +pub struct Context<'a> { + /// Values accessed through this need to be in the properties "computed + /// early": color, text-decoration, font-size, display, position, float, + /// border-*-style, outline-style, font-family, writing-mode... + pub builder: StyleBuilder<'a>, + + /// A cached computed system font value, for use by gecko. + /// + /// See properties/longhands/font.mako.rs + #[cfg(feature = "gecko")] + pub cached_system_font: Option<properties::longhands::system_font::ComputedSystemFont>, + + /// A dummy option for servo so initializing a computed::Context isn't + /// painful. + /// + /// TODO(emilio): Make constructors for Context, and drop this. + #[cfg(feature = "servo")] + pub cached_system_font: Option<()>, + + /// Whether or not we are computing the media list in a media query. + pub in_media_query: bool, + + /// Whether or not we are computing the container query condition. + pub in_container_query: bool, + + /// The quirks mode of this context. + pub quirks_mode: QuirksMode, + + /// Whether this computation is being done for a SMIL animation. + /// + /// This is used to allow certain properties to generate out-of-range + /// values, which SMIL allows. + pub for_smil_animation: bool, + + /// Returns the container information to evaluate a given container query. + pub container_info: Option<ContainerInfo>, + + /// The property we are computing a value for, if it is a non-inherited + /// property. None if we are computed a value for an inherited property + /// or not computing for a property at all (e.g. in a media query + /// evaluation). + pub for_non_inherited_property: Option<LonghandId>, + + /// The conditions to cache a rule node on the rule cache. + /// + /// FIXME(emilio): Drop the refcell. + pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>, + + /// Container size query for this context. + container_size_query: RefCell<ContainerSizeQuery<'a>>, +} + +impl<'a> Context<'a> { + /// Lazily evaluate the container size query, returning the result. + pub fn get_container_size_query(&self) -> ContainerSizeQueryResult { + let mut resolved = self.container_size_query.borrow_mut(); + resolved.get().clone() + } + + /// Creates a suitable context for media query evaluation, in which + /// font-relative units compute against the system_font, and executes `f` + /// with it. + pub fn for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R + where + F: FnOnce(&Context) -> R, + { + let mut conditions = RuleCacheConditions::default(); + let context = Context { + builder: StyleBuilder::for_inheritance(device, None, None), + cached_system_font: None, + in_media_query: true, + in_container_query: false, + quirks_mode, + for_smil_animation: false, + container_info: None, + for_non_inherited_property: None, + rule_cache_conditions: RefCell::new(&mut conditions), + container_size_query: RefCell::new(ContainerSizeQuery::none()), + }; + f(&context) + } + + /// Creates a suitable context for container query evaluation for the style + /// specified. + pub fn for_container_query_evaluation<F, R>( + device: &Device, + container_info_and_style: Option<(ContainerInfo, Arc<ComputedValues>)>, + container_size_query: ContainerSizeQuery, + f: F, + ) -> R + where + F: FnOnce(&Context) -> R, + { + let mut conditions = RuleCacheConditions::default(); + + let (container_info, style) = match container_info_and_style { + Some((ci, s)) => (Some(ci), Some(s)), + None => (None, None), + }; + + let style = style.as_ref().map(|s| &**s); + let quirks_mode = device.quirks_mode(); + let context = Context { + builder: StyleBuilder::for_inheritance(device, style, None), + cached_system_font: None, + in_media_query: false, + in_container_query: true, + quirks_mode, + for_smil_animation: false, + container_info, + for_non_inherited_property: None, + rule_cache_conditions: RefCell::new(&mut conditions), + container_size_query: RefCell::new(container_size_query), + }; + + f(&context) + } + + /// Creates a context suitable for more general cases. + pub fn new( + builder: StyleBuilder<'a>, + quirks_mode: QuirksMode, + rule_cache_conditions: &'a mut RuleCacheConditions, + container_size_query: ContainerSizeQuery<'a>, + ) -> Self { + Self { + builder, + cached_system_font: None, + in_media_query: false, + in_container_query: false, + quirks_mode, + container_info: None, + for_smil_animation: false, + for_non_inherited_property: None, + rule_cache_conditions: RefCell::new(rule_cache_conditions), + container_size_query: RefCell::new(container_size_query), + } + } + + /// Creates a context suitable for computing animations. + pub fn new_for_animation( + builder: StyleBuilder<'a>, + for_smil_animation: bool, + quirks_mode: QuirksMode, + rule_cache_conditions: &'a mut RuleCacheConditions, + container_size_query: ContainerSizeQuery<'a>, + ) -> Self { + Self { + builder, + cached_system_font: None, + in_media_query: false, + in_container_query: false, + quirks_mode, + container_info: None, + for_smil_animation, + for_non_inherited_property: None, + rule_cache_conditions: RefCell::new(rule_cache_conditions), + container_size_query: RefCell::new(container_size_query), + } + } + + /// The current device. + pub fn device(&self) -> &Device { + self.builder.device + } + + /// Queries font metrics. + pub fn query_font_metrics( + &self, + base_size: FontBaseSize, + orientation: FontMetricsOrientation, + retrieve_math_scales: bool, + ) -> FontMetrics { + if self.for_non_inherited_property.is_some() { + self.rule_cache_conditions.borrow_mut().set_uncacheable(); + } + self.builder.add_flags(match base_size { + FontBaseSize::CurrentStyle => ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS, + FontBaseSize::InheritedStyle => ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS, + }); + let size = base_size.resolve(self).used_size(); + let style = self.style(); + + let (wm, font) = match base_size { + FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()), + // This is only used for font-size computation. + FontBaseSize::InheritedStyle => { + (*style.inherited_writing_mode(), style.get_parent_font()) + }, + }; + + let vertical = match orientation { + FontMetricsOrientation::MatchContextPreferHorizontal => { + wm.is_vertical() && wm.is_upright() + }, + FontMetricsOrientation::MatchContextPreferVertical => { + wm.is_vertical() && !wm.is_sideways() + }, + FontMetricsOrientation::Horizontal => false, + }; + self.device().query_font_metrics( + vertical, + font, + size, + self.in_media_or_container_query(), + retrieve_math_scales, + ) + } + + /// The current viewport size, used to resolve viewport units. + pub fn viewport_size_for_viewport_unit_resolution( + &self, + variant: ViewportVariant, + ) -> default::Size2D<Au> { + self.builder + .add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS); + self.builder + .device + .au_viewport_size_for_viewport_unit_resolution(variant) + } + + /// Whether we're in a media or container query. + pub fn in_media_or_container_query(&self) -> bool { + self.in_media_query || self.in_container_query + } + + /// The default computed style we're getting our reset style from. + pub fn default_style(&self) -> &ComputedValues { + self.builder.default_style() + } + + /// The current style. + pub fn style(&self) -> &StyleBuilder { + &self.builder + } + + /// Apply text-zoom if enabled. + #[cfg(feature = "gecko")] + pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength { + // We disable zoom for <svg:text> by unsetting the + // -x-text-zoom property, which leads to a false value + // in mAllowZoomAndMinSize + if self.style().get_font().gecko.mAllowZoomAndMinSize { + self.device().zoom_text(size) + } else { + size + } + } + + /// (Servo doesn't do text-zoom) + #[cfg(feature = "servo")] + pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength { + size + } +} + +/// An iterator over a slice of computed values +#[derive(Clone)] +pub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> { + cx: &'cx Context<'cx_a>, + values: &'a [S], +} + +impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_a, S> { + /// Construct an iterator from a slice of specified values and a context + pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self { + ComputedVecIter { cx, values } + } +} + +impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ExactSizeIterator + for ComputedVecIter<'a, 'cx, 'cx_a, S> +{ + fn len(&self) -> usize { + self.values.len() + } +} + +impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter<'a, 'cx, 'cx_a, S> { + type Item = S::ComputedValue; + fn next(&mut self) -> Option<Self::Item> { + if let Some((next, rest)) = self.values.split_first() { + let ret = next.to_computed_value(self.cx); + self.values = rest; + Some(ret) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.values.len(), Some(self.values.len())) + } +} + +/// A trait to represent the conversion between computed and specified values. +/// +/// This trait is derivable with `#[derive(ToComputedValue)]`. The derived +/// implementation just calls `ToComputedValue::to_computed_value` on each field +/// of the passed value. The deriving code assumes that if the type isn't +/// generic, then the trait can be implemented as simple `Clone::clone` calls, +/// this means that a manual implementation with `ComputedValue = Self` is bogus +/// if it returns anything else than a clone. +pub trait ToComputedValue { + /// The computed value type we're going to be converted to. + type ComputedValue; + + /// Convert a specified value to a computed value, using itself and the data + /// inside the `Context`. + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue; + + /// Convert a computed value to specified value form. + /// + /// This will be used for recascading during animation. + /// Such from_computed_valued values should recompute to the same value. + fn from_computed_value(computed: &Self::ComputedValue) -> Self; +} + +impl<A, B> ToComputedValue for (A, B) +where + A: ToComputedValue, + B: ToComputedValue, +{ + type ComputedValue = ( + <A as ToComputedValue>::ComputedValue, + <B as ToComputedValue>::ComputedValue, + ); + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + ( + self.0.to_computed_value(context), + self.1.to_computed_value(context), + ) + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + ( + A::from_computed_value(&computed.0), + B::from_computed_value(&computed.1), + ) + } +} + +impl<T> ToComputedValue for Option<T> +where + T: ToComputedValue, +{ + type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + self.as_ref().map(|item| item.to_computed_value(context)) + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + computed.as_ref().map(T::from_computed_value) + } +} + +impl<T> ToComputedValue for default::Size2D<T> +where + T: ToComputedValue, +{ + type ComputedValue = default::Size2D<<T as ToComputedValue>::ComputedValue>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + Size2D::new( + self.width.to_computed_value(context), + self.height.to_computed_value(context), + ) + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + Size2D::new( + T::from_computed_value(&computed.width), + T::from_computed_value(&computed.height), + ) + } +} + +impl<T> ToComputedValue for Vec<T> +where + T: ToComputedValue, +{ + type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + self.iter() + .map(|item| item.to_computed_value(context)) + .collect() + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + computed.iter().map(T::from_computed_value).collect() + } +} + +impl<T> ToComputedValue for Box<T> +where + T: ToComputedValue, +{ + type ComputedValue = Box<<T as ToComputedValue>::ComputedValue>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + Box::new(T::to_computed_value(self, context)) + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + Box::new(T::from_computed_value(computed)) + } +} + +impl<T> ToComputedValue for Box<[T]> +where + T: ToComputedValue, +{ + type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + self.iter() + .map(|item| item.to_computed_value(context)) + .collect::<Vec<_>>() + .into_boxed_slice() + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + computed + .iter() + .map(T::from_computed_value) + .collect::<Vec<_>>() + .into_boxed_slice() + } +} + +impl<T> ToComputedValue for crate::OwnedSlice<T> +where + T: ToComputedValue, +{ + type ComputedValue = crate::OwnedSlice<<T as ToComputedValue>::ComputedValue>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + self.iter() + .map(|item| item.to_computed_value(context)) + .collect() + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + computed.iter().map(T::from_computed_value).collect() + } +} + +// NOTE(emilio): This is implementable more generically, but it's unlikely +// what you want there, as it forces you to have an extra allocation. +// +// We could do that if needed, ideally with specialization for the case where +// ComputedValue = T. But we don't need it for now. +impl<T> ToComputedValue for Arc<T> +where + T: ToComputedValue<ComputedValue = T>, +{ + type ComputedValue = Self; + + #[inline] + fn to_computed_value(&self, _: &Context) -> Self { + self.clone() + } + + #[inline] + fn from_computed_value(computed: &Self) -> Self { + computed.clone() + } +} + +// Same caveat as above applies. +impl<T> ToComputedValue for ArcSlice<T> +where + T: ToComputedValue<ComputedValue = T>, +{ + type ComputedValue = Self; + + #[inline] + fn to_computed_value(&self, _: &Context) -> Self { + self.clone() + } + + #[inline] + fn from_computed_value(computed: &Self) -> Self { + computed.clone() + } +} + +trivial_to_computed_value!(()); +trivial_to_computed_value!(bool); +trivial_to_computed_value!(f32); +trivial_to_computed_value!(i32); +trivial_to_computed_value!(u8); +trivial_to_computed_value!(u16); +trivial_to_computed_value!(u32); +trivial_to_computed_value!(usize); +trivial_to_computed_value!(Atom); +trivial_to_computed_value!(crate::values::AtomIdent); +#[cfg(feature = "servo")] +trivial_to_computed_value!(crate::Namespace); +#[cfg(feature = "servo")] +trivial_to_computed_value!(crate::Prefix); +trivial_to_computed_value!(String); +trivial_to_computed_value!(Box<str>); +trivial_to_computed_value!(crate::OwnedStr); +trivial_to_computed_value!(style_traits::values::specified::AllowedNumericType); + +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + ToAnimatedZero, + ToCss, + ToResolvedValue, +)] +#[repr(C, u8)] +pub enum AngleOrPercentage { + Percentage(Percentage), + Angle(Angle), +} + +impl ToComputedValue for specified::AngleOrPercentage { + type ComputedValue = AngleOrPercentage; + + #[inline] + fn to_computed_value(&self, context: &Context) -> AngleOrPercentage { + match *self { + specified::AngleOrPercentage::Percentage(percentage) => { + AngleOrPercentage::Percentage(percentage.to_computed_value(context)) + }, + specified::AngleOrPercentage::Angle(angle) => { + AngleOrPercentage::Angle(angle.to_computed_value(context)) + }, + } + } + #[inline] + fn from_computed_value(computed: &AngleOrPercentage) -> Self { + match *computed { + AngleOrPercentage::Percentage(percentage) => specified::AngleOrPercentage::Percentage( + ToComputedValue::from_computed_value(&percentage), + ), + AngleOrPercentage::Angle(angle) => { + specified::AngleOrPercentage::Angle(ToComputedValue::from_computed_value(&angle)) + }, + } + } +} + +/// A `<number>` value. +pub type Number = CSSFloat; + +impl IsParallelTo for (Number, Number, Number) { + fn is_parallel_to(&self, vector: &DirectionVector) -> bool { + use euclid::approxeq::ApproxEq; + // If a and b is parallel, the angle between them is 0deg, so + // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0. + let self_vector = DirectionVector::new(self.0, self.1, self.2); + self_vector + .cross(*vector) + .square_length() + .approx_eq(&0.0f32) + } +} + +/// A wrapper of Number, but the value >= 0. +pub type NonNegativeNumber = NonNegative<CSSFloat>; + +impl ToAnimatedValue for NonNegativeNumber { + type AnimatedValue = CSSFloat; + + #[inline] + fn to_animated_value(self) -> Self::AnimatedValue { + self.0 + } + + #[inline] + fn from_animated_value(animated: Self::AnimatedValue) -> Self { + animated.max(0.).into() + } +} + +impl From<CSSFloat> for NonNegativeNumber { + #[inline] + fn from(number: CSSFloat) -> NonNegativeNumber { + NonNegative::<CSSFloat>(number) + } +} + +impl From<NonNegativeNumber> for CSSFloat { + #[inline] + fn from(number: NonNegativeNumber) -> CSSFloat { + number.0 + } +} + +impl One for NonNegativeNumber { + #[inline] + fn one() -> Self { + NonNegative(1.0) + } + + #[inline] + fn is_one(&self) -> bool { + self.0 == 1.0 + } +} + +/// A wrapper of Number, but the value between 0 and 1 +pub type ZeroToOneNumber = ZeroToOne<CSSFloat>; + +impl ToAnimatedValue for ZeroToOneNumber { + type AnimatedValue = CSSFloat; + + #[inline] + fn to_animated_value(self) -> Self::AnimatedValue { + self.0 + } + + #[inline] + fn from_animated_value(animated: Self::AnimatedValue) -> Self { + Self(animated.max(0.).min(1.)) + } +} + +impl From<CSSFloat> for ZeroToOneNumber { + #[inline] + fn from(number: CSSFloat) -> Self { + Self(number) + } +} + +/// A wrapper of Number, but the value >= 1. +pub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>; + +impl ToAnimatedValue for GreaterThanOrEqualToOneNumber { + type AnimatedValue = CSSFloat; + + #[inline] + fn to_animated_value(self) -> Self::AnimatedValue { + self.0 + } + + #[inline] + fn from_animated_value(animated: Self::AnimatedValue) -> Self { + animated.max(1.).into() + } +} + +impl From<CSSFloat> for GreaterThanOrEqualToOneNumber { + #[inline] + fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber { + GreaterThanOrEqualToOne::<CSSFloat>(number) + } +} + +impl From<GreaterThanOrEqualToOneNumber> for CSSFloat { + #[inline] + fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat { + number.0 + } +} + +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + ToAnimatedZero, + ToCss, + ToResolvedValue, +)] +#[repr(C, u8)] +pub enum NumberOrPercentage { + Percentage(Percentage), + Number(Number), +} + +impl NumberOrPercentage { + fn clamp_to_non_negative(self) -> Self { + match self { + NumberOrPercentage::Percentage(p) => { + NumberOrPercentage::Percentage(p.clamp_to_non_negative()) + }, + NumberOrPercentage::Number(n) => NumberOrPercentage::Number(n.max(0.)), + } + } +} + +impl ToComputedValue for specified::NumberOrPercentage { + type ComputedValue = NumberOrPercentage; + + #[inline] + fn to_computed_value(&self, context: &Context) -> NumberOrPercentage { + match *self { + specified::NumberOrPercentage::Percentage(percentage) => { + NumberOrPercentage::Percentage(percentage.to_computed_value(context)) + }, + specified::NumberOrPercentage::Number(number) => { + NumberOrPercentage::Number(number.to_computed_value(context)) + }, + } + } + #[inline] + fn from_computed_value(computed: &NumberOrPercentage) -> Self { + match *computed { + NumberOrPercentage::Percentage(percentage) => { + specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value( + &percentage, + )) + }, + NumberOrPercentage::Number(number) => { + specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number)) + }, + } + } +} + +/// A non-negative <number-percentage>. +pub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>; + +impl NonNegativeNumberOrPercentage { + /// Returns the `100%` value. + #[inline] + pub fn hundred_percent() -> Self { + NonNegative(NumberOrPercentage::Percentage(Percentage::hundred())) + } +} + +impl ToAnimatedValue for NonNegativeNumberOrPercentage { + type AnimatedValue = NumberOrPercentage; + + #[inline] + fn to_animated_value(self) -> Self::AnimatedValue { + self.0 + } + + #[inline] + fn from_animated_value(animated: Self::AnimatedValue) -> Self { + NonNegative(animated.clamp_to_non_negative()) + } +} + +/// A type used for opacity. +pub type Opacity = CSSFloat; + +/// A `<integer>` value. +pub type Integer = CSSInteger; + +/// A wrapper of Integer, but only accept a value >= 1. +pub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>; + +impl ToAnimatedValue for PositiveInteger { + type AnimatedValue = CSSInteger; + + #[inline] + fn to_animated_value(self) -> Self::AnimatedValue { + self.0 + } + + #[inline] + fn from_animated_value(animated: Self::AnimatedValue) -> Self { + cmp::max(animated, 1).into() + } +} + +impl From<CSSInteger> for PositiveInteger { + #[inline] + fn from(int: CSSInteger) -> PositiveInteger { + GreaterThanOrEqualToOne::<CSSInteger>(int) + } +} + +/// rect(...) | auto +pub type ClipRect = generics::GenericClipRect<LengthOrAuto>; + +/// rect(...) | auto +pub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>; + +/// The computed value of a grid `<track-breadth>` +pub type TrackBreadth = GenericTrackBreadth<LengthPercentage>; + +/// The computed value of a grid `<track-size>` +pub type TrackSize = GenericTrackSize<LengthPercentage>; + +/// The computed value of a grid `<track-size>+` +pub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>; + +/// The computed value of a grid `<track-list>` +/// (could also be `<auto-track-list>` or `<explicit-track-list>`) +pub type TrackList = GenericTrackList<LengthPercentage, Integer>; + +/// The computed value of a `<grid-line>`. +pub type GridLine = GenericGridLine<Integer>; + +/// `<grid-template-rows> | <grid-template-columns>` +pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>; + +impl ClipRect { + /// Given a border box, resolves the clip rect against the border box + /// in the same space the border box is in + pub fn for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>( + &self, + border_box: Rect<T, U>, + ) -> Rect<T, U> { + fn extract_clip_component<T: From<Length>>(p: &LengthOrAuto, or: T) -> T { + match *p { + LengthOrAuto::Auto => or, + LengthOrAuto::LengthPercentage(ref length) => T::from(*length), + } + } + + let clip_origin = Point2D::new( + From::from(self.left.auto_is(|| Length::new(0.))), + From::from(self.top.auto_is(|| Length::new(0.))), + ); + let right = extract_clip_component(&self.right, border_box.size.width); + let bottom = extract_clip_component(&self.bottom, border_box.size.height); + let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y); + + Rect::new(clip_origin, clip_size).translate(border_box.origin.to_vector()) + } +} |