/* 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 http://mozilla.org/MPL/2.0/. */ //! Computed values. use Atom; use context::QuirksMode; use euclid::size::Size2D; use font_metrics::FontMetricsProvider; use media_queries::Device; #[cfg(feature = "gecko")] use properties; use properties::{ComputedValues, StyleBuilder}; use std::f32; use std::f32::consts::PI; use std::fmt; use style_traits::ToCss; use super::{CSSFloat, CSSInteger, RGBA}; use super::generics::BorderRadiusSize as GenericBorderRadiusSize; use super::specified; use super::specified::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize}; use super::specified::grid::TrackList as GenericTrackList; pub use app_units::Au; pub use cssparser::Color as CSSColor; pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect}; pub use super::{Auto, Either, None_}; #[cfg(feature = "gecko")] pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use super::specified::{BorderStyle, GridLine, Percentage, UrlOrNone}; pub use super::specified::url::SpecifiedUrl; pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; pub use self::length::{MaxLength, MozLength}; pub use self::position::Position; pub mod basic_shape; pub mod image; pub mod length; pub mod position; /// 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> { /// Whether the current element is the root element. pub is_root_element: bool, /// The Device holds the viewport and other external state. pub device: &'a Device, /// The style we're inheriting from. pub inherited_style: &'a ComputedValues, /// The style of the layout parent node. This will almost always be /// `inherited_style`, except when `display: contents` is at play, in which /// case it's the style of the last ancestor with a `display` value that /// isn't `contents`. pub layout_parent_style: &'a ComputedValues, /// 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 style: 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, /// 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<()>, /// A font metrics provider, used to access font metrics to implement /// font-relative units. pub font_metrics_provider: &'a FontMetricsProvider, /// Whether or not we are computing the media list in a media query pub in_media_query: bool, /// The quirks mode of this context. pub quirks_mode: QuirksMode, } impl<'a> Context<'a> { /// Whether the current element is the root element. pub fn is_root_element(&self) -> bool { self.is_root_element } /// The current viewport size. pub fn viewport_size(&self) -> Size2D { self.device.au_viewport_size() } /// The style we're inheriting from. pub fn inherited_style(&self) -> &ComputedValues { &self.inherited_style } /// The current style. Note that only "eager" properties should be accessed /// from here, see the comment in the member. pub fn style(&self) -> &StyleBuilder { &self.style } /// A mutable reference to the current style. pub fn mutate_style(&mut self) -> &mut StyleBuilder<'a> { &mut self.style } } /// 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: cx, values: 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 { if let Some((next, rest)) = self.values.split_first() { let ret = next.to_computed_value(self.cx); self.values = rest; Some(ret) } else { None } } } /// A trait to represent the conversion between computed and specified values. 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`. #[inline] fn to_computed_value(&self, context: &Context) -> Self::ComputedValue; #[inline] /// 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 ToComputedValue for (A, B) where A: ToComputedValue, B: ToComputedValue, { type ComputedValue = ( ::ComputedValue, ::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 ToComputedValue for Option where T: ToComputedValue { type ComputedValue = Option<::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 ToComputedValue for Size2D where T: ToComputedValue { type ComputedValue = Size2D<::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 ToComputedValue for Vec where T: ToComputedValue { type ComputedValue = Vec<::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() } } /// A marker trait to represent that the specified value is also the computed /// value. pub trait ComputedValueAsSpecified {} impl ToComputedValue for T where T: ComputedValueAsSpecified + Clone, { type ComputedValue = T; #[inline] fn to_computed_value(&self, _context: &Context) -> T { self.clone() } #[inline] fn from_computed_value(computed: &T) -> Self { computed.clone() } } impl ComputedValueAsSpecified for Atom {} impl ComputedValueAsSpecified for bool {} /// A computed `` value. #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd)] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] pub enum Angle { /// An angle with degree unit Degree(CSSFloat), /// An angle with gradian unit Gradian(CSSFloat), /// An angle with radian unit Radian(CSSFloat), /// An angle with turn unit Turn(CSSFloat), } impl Angle { /// Construct a computed `Angle` value from a radian amount. pub fn from_radians(radians: CSSFloat) -> Self { Angle::Radian(radians) } /// Return the amount of radians this angle represents. #[inline] pub fn radians(&self) -> CSSFloat { const RAD_PER_DEG: CSSFloat = PI / 180.0; const RAD_PER_GRAD: CSSFloat = PI / 200.0; const RAD_PER_TURN: CSSFloat = PI * 2.0; let radians = match *self { Angle::Degree(val) => val * RAD_PER_DEG, Angle::Gradian(val) => val * RAD_PER_GRAD, Angle::Turn(val) => val * RAD_PER_TURN, Angle::Radian(val) => val, }; radians.min(f32::MAX).max(f32::MIN) } /// Returns an angle that represents a rotation of zero radians. pub fn zero() -> Self { Angle::Radian(0.0) } } impl ToCss for Angle { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, { match *self { Angle::Degree(val) => write!(dest, "{}deg", val), Angle::Gradian(val) => write!(dest, "{}grad", val), Angle::Radian(val) => write!(dest, "{}rad", val), Angle::Turn(val) => write!(dest, "{}turn", val), } } } /// A computed `