diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 58ff6d23e44..229f826b4e3 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -520,9 +520,9 @@ impl FragmentDisplayListBuilding for Fragment { }; // Use `background-position` to get the offset. - let horizontal_position = model::specified(background.background_position.horizontal, + let horizontal_position = model::specified(background.background_position.0.horizontal, bounds.size.width - image_size.width); - let vertical_position = model::specified(background.background_position.vertical, + let vertical_position = model::specified(background.background_position.0.vertical, bounds.size.height - image_size.height); let abs_x = border.left + virtual_origin_x + horizontal_position + origin_x; diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 24893da9f7b..109a83f0bee 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -1019,17 +1019,18 @@ fn static_assert() { } pub fn clone_background_position(&self) -> longhands::background_position::computed_value::T { + use values::computed::position::Position; let position = &self.gecko.mImage.mLayers.mFirstElement.mPosition; - longhands::background_position::computed_value::T { + longhands::background_position::computed_value::T(Position { horizontal: position.mXPosition.into(), vertical: position.mYPosition.into(), - } + }) } pub fn set_background_position(&mut self, v: longhands::background_position::computed_value::T) { let position = &mut self.gecko.mImage.mLayers.mFirstElement.mPosition; - position.mXPosition = v.horizontal.into(); - position.mYPosition = v.vertical.into(); + position.mXPosition = v.0.horizontal.into(); + position.mYPosition = v.0.vertical.into(); self.gecko.mImage.mPositionXCount = 1; self.gecko.mImage.mPositionYCount = 1; } diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 10bac440f30..c9a3ab8cb64 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -30,6 +30,7 @@ use super::ComputedValues; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use values::computed::{BorderRadiusSize, LengthOrNone}; use values::computed::{CalcLengthOrPercentage, LengthOrPercentage}; +use values::computed::position::Position; // NB: This needs to be here because it needs all the longhands generated // beforehand. @@ -469,16 +470,23 @@ impl Interpolate for ClipRect { } /// https://drafts.csswg.org/css-transitions/#animtype-simple-list -impl Interpolate for BackgroundPosition { +impl Interpolate for Position { #[inline] fn interpolate(&self, other: &Self, time: f64) -> Result { - Ok(BackgroundPosition { + Ok(Position { horizontal: try!(self.horizontal.interpolate(&other.horizontal, time)), vertical: try!(self.vertical.interpolate(&other.vertical, time)), }) } } +impl Interpolate for BackgroundPosition { + #[inline] + fn interpolate(&self, other: &Self, time: f64) -> Result { + Ok(BackgroundPosition(try!(self.0.interpolate(&other.0, time)))) + } +} + impl Interpolate for BackgroundSize { fn interpolate(&self, other: &Self, time: f64) -> Result { use properties::longhands::background_size::computed_value::ExplicitSize; diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index 501a98fd7fa..1a0f2aaeffe 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -80,91 +80,35 @@ ${helpers.predefined_type("background-color", "CSSColor", use std::fmt; use values::LocalToCss; use values::HasViewportPercentage; + use values::specified::position::Position; pub mod computed_value { - use values::computed::LengthOrPercentage; + use values::computed::position::Position; #[derive(PartialEq, Copy, Clone, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct T { - pub horizontal: LengthOrPercentage, - pub vertical: LengthOrPercentage, - } + pub struct T(pub Position); } impl HasViewportPercentage for SpecifiedValue { fn has_viewport_percentage(&self) -> bool { - return self.horizontal.has_viewport_percentage() || self.vertical.has_viewport_percentage(); + self.0.has_viewport_percentage() } } #[derive(Debug, Clone, PartialEq, Copy)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct SpecifiedValue { - pub horizontal: specified::LengthOrPercentage, - pub vertical: specified::LengthOrPercentage, - } + pub struct SpecifiedValue(pub Position); impl ToCss for SpecifiedValue { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(self.horizontal.to_css(dest)); - try!(dest.write_str(" ")); - try!(self.vertical.to_css(dest)); - Ok(()) + self.0.to_css(dest) } } impl ToCss for computed_value::T { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - try!(self.horizontal.to_css(dest)); - try!(dest.write_str(" ")); - try!(self.vertical.to_css(dest)); - Ok(()) - } - } - - impl SpecifiedValue { - fn new(first: specified::PositionComponent, second: specified::PositionComponent) - -> Result { - let (horiz, vert) = match (category(first), category(second)) { - // Don't allow two vertical keywords or two horizontal keywords. - (PositionCategory::HorizontalKeyword, PositionCategory::HorizontalKeyword) | - (PositionCategory::VerticalKeyword, PositionCategory::VerticalKeyword) => return Err(()), - - // Swap if both are keywords and vertical precedes horizontal. - (PositionCategory::VerticalKeyword, PositionCategory::HorizontalKeyword) | - (PositionCategory::VerticalKeyword, PositionCategory::OtherKeyword) | - (PositionCategory::OtherKeyword, PositionCategory::HorizontalKeyword) => (second, first), - - // By default, horizontal is first. - _ => (first, second), - }; - Ok(SpecifiedValue { - horizontal: horiz.to_length_or_percentage(), - vertical: vert.to_length_or_percentage(), - }) - } - } - - // Collapse `Position` into a few categories to simplify the above `match` expression. - enum PositionCategory { - HorizontalKeyword, - VerticalKeyword, - OtherKeyword, - LengthOrPercentage, - } - fn category(p: specified::PositionComponent) -> PositionCategory { - match p { - specified::PositionComponent::Left | - specified::PositionComponent::Right => - PositionCategory::HorizontalKeyword, - specified::PositionComponent::Top | - specified::PositionComponent::Bottom => - PositionCategory::VerticalKeyword, - specified::PositionComponent::Center => - PositionCategory::OtherKeyword, - specified::PositionComponent::LengthOrPercentage(_) => - PositionCategory::LengthOrPercentage, + self.0.to_css(dest) } } @@ -173,27 +117,22 @@ ${helpers.predefined_type("background-color", "CSSColor", #[inline] fn to_computed_value(&self, context: &Context) -> computed_value::T { - computed_value::T { - horizontal: self.horizontal.to_computed_value(context), - vertical: self.vertical.to_computed_value(context), - } + computed_value::T(self.0.to_computed_value(context)) } } #[inline] pub fn get_initial_value() -> computed_value::T { - computed_value::T { + use values::computed::position::Position; + computed_value::T(Position { horizontal: computed::LengthOrPercentage::Percentage(0.0), vertical: computed::LengthOrPercentage::Percentage(0.0), - } + }) } pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result { - let first = try!(specified::PositionComponent::parse(input)); - let second = input.try(specified::PositionComponent::parse) - .unwrap_or(specified::PositionComponent::Center); - SpecifiedValue::new(first, second) + Ok(SpecifiedValue(try!(Position::parse(input)))) } diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 129d480332d..49b3f2e7285 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -14,6 +14,7 @@ pub use cssparser::Color as CSSColor; pub use super::specified::{Angle, BorderStyle, Time, UrlExtraData}; pub mod basic_shape; +pub mod position; pub struct Context<'a> { pub is_root_element: bool, diff --git a/components/style/values/computed/position.rs b/components/style/values/computed/position.rs new file mode 100644 index 00000000000..265ed9769ac --- /dev/null +++ b/components/style/values/computed/position.rs @@ -0,0 +1,28 @@ +/* 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/. */ + +//! CSS handling for the computed value of +//! [`position`][position]s +//! +//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position + +use cssparser::{Parser, ToCss, Token}; +use values::computed::{Length, LengthOrPercentage}; +use std::fmt; + +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct Position { + pub horizontal: LengthOrPercentage, + pub vertical: LengthOrPercentage, +} + +impl ToCss for Position { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + try!(self.horizontal.to_css(dest)); + try!(dest.write_str(" ")); + try!(self.vertical.to_css(dest)); + Ok(()) + } +} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 20b2f2778ab..3bede8228bc 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -19,6 +19,7 @@ use super::{CSSFloat, FONT_MEDIUM_PX, HasViewportPercentage, LocalToCss, NoViewp use url::Url; pub mod basic_shape; +pub mod position; impl NoViewportPercentage for i32 {} // For PropertyDeclaration::Order @@ -1124,59 +1125,6 @@ impl ToCss for BorderRadiusSize { } } -// http://dev.w3.org/csswg/css2/colors.html#propdef-background-position -#[derive(Clone, PartialEq, Copy)] -pub enum PositionComponent { - LengthOrPercentage(LengthOrPercentage), - Center, - Left, - Right, - Top, - Bottom, -} - -impl HasViewportPercentage for PositionComponent { - fn has_viewport_percentage(&self) -> bool { - match *self { - PositionComponent::LengthOrPercentage(length) => length.has_viewport_percentage(), - _ => false - } - } -} - -impl PositionComponent { - pub fn parse(input: &mut Parser) -> Result { - input.try(LengthOrPercentage::parse) - .map(PositionComponent::LengthOrPercentage) - .or_else(|()| { - match try!(input.next()) { - Token::Ident(value) => { - match_ignore_ascii_case! { value, - "center" => Ok(PositionComponent::Center), - "left" => Ok(PositionComponent::Left), - "right" => Ok(PositionComponent::Right), - "top" => Ok(PositionComponent::Top), - "bottom" => Ok(PositionComponent::Bottom), - _ => Err(()) - } - }, - _ => Err(()) - } - }) - } - #[inline] - pub fn to_length_or_percentage(self) -> LengthOrPercentage { - match self { - PositionComponent::LengthOrPercentage(value) => value, - PositionComponent::Center => LengthOrPercentage::Percentage(Percentage(0.5)), - PositionComponent::Left | - PositionComponent::Top => LengthOrPercentage::Percentage(Percentage(0.0)), - PositionComponent::Right | - PositionComponent::Bottom => LengthOrPercentage::Percentage(Percentage(1.0)), - } - } -} - #[derive(Clone, PartialEq, PartialOrd, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] /// An angle, normalized to radians. diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs new file mode 100644 index 00000000000..add352491c6 --- /dev/null +++ b/components/style/values/specified/position.rs @@ -0,0 +1,156 @@ +/* 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/. */ + +//! CSS handling for the specified value of +//! [`position`][position]s +//! +//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position + +use std::fmt; +use app_units::Au; +use cssparser::{Parser, ToCss, Token}; +use values::HasViewportPercentage; +use values::specified::{LengthOrPercentage, Percentage}; +use values::computed::{Context, ToComputedValue}; + +use values::computed::position as computed_position; + +#[derive(Debug, Clone, PartialEq, Copy)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct Position { + pub horizontal: LengthOrPercentage, + pub vertical: LengthOrPercentage, +} + +impl ToCss for Position { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + try!(self.horizontal.to_css(dest)); + try!(dest.write_str(" ")); + try!(self.vertical.to_css(dest)); + Ok(()) + } +} + +impl HasViewportPercentage for Position { + fn has_viewport_percentage(&self) -> bool { + self.horizontal.has_viewport_percentage() || self.vertical.has_viewport_percentage() + } +} +// http://dev.w3.org/csswg/css2/colors.html#propdef-background-position +#[derive(Clone, PartialEq, Copy)] +pub enum PositionComponent { + LengthOrPercentage(LengthOrPercentage), + Center, + Left, + Right, + Top, + Bottom, +} + +impl Position { + fn new(first: PositionComponent, second: PositionComponent) + -> Result { + let (horiz, vert) = match (category(first), category(second)) { + // Don't allow two vertical keywords or two horizontal keywords. + (PositionCategory::HorizontalKeyword, PositionCategory::HorizontalKeyword) | + (PositionCategory::VerticalKeyword, PositionCategory::VerticalKeyword) => return Err(()), + + // Swap if both are keywords and vertical precedes horizontal. + (PositionCategory::VerticalKeyword, PositionCategory::HorizontalKeyword) | + (PositionCategory::VerticalKeyword, PositionCategory::OtherKeyword) | + (PositionCategory::OtherKeyword, PositionCategory::HorizontalKeyword) => (second, first), + + // By default, horizontal is first. + _ => (first, second), + }; + Ok(Position { + horizontal: horiz.to_length_or_percentage(), + vertical: vert.to_length_or_percentage(), + }) + } + + pub fn parse(input: &mut Parser) -> Result { + let first = try!(PositionComponent::parse(input)); + let second = input.try(PositionComponent::parse) + .unwrap_or(PositionComponent::Center); + Position::new(first, second) + } +} + +// Collapse `Position` into a few categories to simplify the above `match` expression. +enum PositionCategory { + HorizontalKeyword, + VerticalKeyword, + OtherKeyword, + LengthOrPercentage, +} + +fn category(p: PositionComponent) -> PositionCategory { + match p { + PositionComponent::Left | + PositionComponent::Right => + PositionCategory::HorizontalKeyword, + PositionComponent::Top | + PositionComponent::Bottom => + PositionCategory::VerticalKeyword, + PositionComponent::Center => + PositionCategory::OtherKeyword, + PositionComponent::LengthOrPercentage(_) => + PositionCategory::LengthOrPercentage, + } +} + +impl ToComputedValue for Position { + type ComputedValue = computed_position::Position; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_position::Position { + computed_position::Position { + horizontal: self.horizontal.to_computed_value(context), + vertical: self.vertical.to_computed_value(context), + } + } +} + +impl HasViewportPercentage for PositionComponent { + fn has_viewport_percentage(&self) -> bool { + match *self { + PositionComponent::LengthOrPercentage(length) => length.has_viewport_percentage(), + _ => false + } + } +} + +impl PositionComponent { + pub fn parse(input: &mut Parser) -> Result { + input.try(LengthOrPercentage::parse) + .map(PositionComponent::LengthOrPercentage) + .or_else(|()| { + match try!(input.next()) { + Token::Ident(value) => { + match_ignore_ascii_case! { value, + "center" => Ok(PositionComponent::Center), + "left" => Ok(PositionComponent::Left), + "right" => Ok(PositionComponent::Right), + "top" => Ok(PositionComponent::Top), + "bottom" => Ok(PositionComponent::Bottom), + _ => Err(()) + } + }, + _ => Err(()) + } + }) + } + #[inline] + pub fn to_length_or_percentage(self) -> LengthOrPercentage { + match self { + PositionComponent::LengthOrPercentage(value) => value, + PositionComponent::Center => LengthOrPercentage::Percentage(Percentage(0.5)), + PositionComponent::Left | + PositionComponent::Top => LengthOrPercentage::Percentage(Percentage(0.0)), + PositionComponent::Right | + PositionComponent::Bottom => LengthOrPercentage::Percentage(Percentage(1.0)), + } + } +} \ No newline at end of file