servo: Merge #3296 - Support CSS2 background-position keyword and percent values. r=SimonSapin (from mbrubeck:background-position)

Source-Repo: https://github.com/servo/servo
Source-Revision: 8e2ac7a5c54326f47cbb23394142beee0c17cff2
This commit is contained in:
Matt Brubeck 2014-09-16 10:35:00 -07:00
Родитель 2a3015f090
Коммит 88a04bafa4
3 изменённых файлов: 155 добавлений и 50 удалений

Просмотреть файл

@ -728,12 +728,15 @@ impl Fragment {
};
debug!("(building display list) building background image");
let image_width = Au::from_px(image.width as int);
let image_height = Au::from_px(image.height as int);
// Adjust bounds for `background-position` and `background-attachment`.
let mut bounds = *absolute_bounds;
let horizontal_position = model::specified(background.background_position.horizontal,
bounds.size.width);
bounds.size.width - image_width);
let vertical_position = model::specified(background.background_position.vertical,
bounds.size.height);
bounds.size.height - image_height);
// TODO: These are some situations below where it is possible
// to determine that clipping is not necessary - this is an
@ -743,9 +746,6 @@ impl Fragment {
children: DisplayList::new(),
});
let image_width = Au::from_px(image.width as int);
let image_height = Au::from_px(image.height as int);
match background.background_attachment {
background_attachment::scroll => {
bounds.origin.x = bounds.origin.x + horizontal_position;
@ -759,25 +759,21 @@ impl Fragment {
}
background_repeat::repeat_x => {
bounds.size.height = Au::min(bounds.size.height - vertical_position, image_height);
if horizontal_position > Au(0) {
bounds.origin.x = bounds.origin.x - image_width;
if horizontal_position != Au(0) {
bounds.size.width = bounds.size.width + image_width;
}
}
background_repeat::repeat_y => {
bounds.size.width = Au::min(bounds.size.width - horizontal_position, image_width);
if vertical_position > Au(0) {
bounds.origin.y = bounds.origin.y - image_height;
if vertical_position != Au(0) {
bounds.size.height = bounds.size.height + image_height;
}
}
background_repeat::repeat => {
if horizontal_position > Au(0) {
bounds.origin.x = bounds.origin.x - image_width;
if horizontal_position != Au(0) {
bounds.size.width = bounds.size.width + image_width;
}
if vertical_position > Au(0) {
bounds.origin.y = bounds.origin.y - image_height;
if vertical_position != Au(0) {
bounds.size.height = bounds.size.height + image_height;
}
}

Просмотреть файл

@ -160,6 +160,47 @@ pub mod specified {
LengthOrPercentageOrNone::parse_internal(input, /* negative_ok = */ false)
}
}
// http://dev.w3.org/csswg/css2/colors.html#propdef-background-position
#[deriving(Clone)]
pub enum PositionComponent {
Pos_Length(Length),
Pos_Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
Pos_Center,
Pos_Left,
Pos_Right,
Pos_Top,
Pos_Bottom,
}
impl PositionComponent {
pub fn parse(input: &ComponentValue) -> Result<PositionComponent, ()> {
match input {
&Dimension(ref value, ref unit) =>
Length::parse_dimension(value.value, unit.as_slice()).map(Pos_Length),
&ast::Percentage(ref value) => Ok(Pos_Percentage(value.value / 100.)),
&Number(ref value) if value.value == 0. => Ok(Pos_Length(Au_(Au(0)))),
&Ident(ref value) => {
if value.as_slice().eq_ignore_ascii_case("center") { Ok(Pos_Center) }
else if value.as_slice().eq_ignore_ascii_case("left") { Ok(Pos_Left) }
else if value.as_slice().eq_ignore_ascii_case("right") { Ok(Pos_Right) }
else if value.as_slice().eq_ignore_ascii_case("top") { Ok(Pos_Top) }
else if value.as_slice().eq_ignore_ascii_case("bottom") { Ok(Pos_Bottom) }
else { Err(()) }
}
_ => Err(())
}
}
#[inline]
pub fn to_length_or_percentage(self) -> LengthOrPercentage {
match self {
Pos_Length(x) => LP_Length(x),
Pos_Percentage(x) => LP_Percentage(x),
Pos_Center => LP_Percentage(0.5),
Pos_Left | Pos_Top => LP_Percentage(0.0),
Pos_Right | Pos_Bottom => LP_Percentage(1.0),
}
}
}
}
pub mod computed {

Просмотреть файл

@ -627,6 +627,46 @@ pub mod longhands {
pub vertical: specified::LengthOrPercentage,
}
impl SpecifiedValue {
fn new(first: specified::PositionComponent, second: specified::PositionComponent)
-> Result<SpecifiedValue,()> {
let (horiz, vert) = match (category(first), category(second)) {
// Don't allow two vertical keywords or two horizontal keywords.
(HorizontalKeyword, HorizontalKeyword) |
(VerticalKeyword, VerticalKeyword) => return Err(()),
// Swap if both are keywords and vertical precedes horizontal.
(VerticalKeyword, HorizontalKeyword) |
(VerticalKeyword, OtherKeyword) |
(OtherKeyword, 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::Pos_Left | specified::Pos_Right => HorizontalKeyword,
specified::Pos_Top | specified::Pos_Bottom => VerticalKeyword,
specified::Pos_Center => OtherKeyword,
specified::Pos_Length(_) |
specified::Pos_Percentage(_) => LengthOrPercentage,
}
}
#[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
@ -644,29 +684,32 @@ pub mod longhands {
}
}
// FIXME(#1997, pcwalton): Support complete CSS2 syntax.
pub fn parse_horizontal_and_vertical(horiz: &ComponentValue, vert: &ComponentValue)
-> Result<SpecifiedValue, ()> {
let horiz = try!(specified::LengthOrPercentage::parse_non_negative(horiz));
let vert = try!(specified::LengthOrPercentage::parse_non_negative(vert));
pub fn parse_one(first: &ComponentValue) -> Result<SpecifiedValue, ()> {
let first = try!(specified::PositionComponent::parse(first));
// If only one value is provided, use `center` for the second.
SpecifiedValue::new(first, specified::Pos_Center)
}
Ok(SpecifiedValue {
horizontal: horiz,
vertical: vert,
})
pub fn parse_two(first: &ComponentValue, second: &ComponentValue)
-> Result<SpecifiedValue, ()> {
let first = try!(specified::PositionComponent::parse(first));
let second = try!(specified::PositionComponent::parse(second));
SpecifiedValue::new(first, second)
}
pub fn parse(input: &[ComponentValue], _: &Url) -> Result<SpecifiedValue, ()> {
let mut input_iter = input.skip_whitespace();
let horizontal = input_iter.next();
let vertical = input_iter.next();
let first = input_iter.next();
let second = input_iter.next();
if input_iter.next().is_some() {
return Err(())
}
match (horizontal, vertical) {
(Some(horizontal), Some(vertical)) => {
parse_horizontal_and_vertical(horizontal, vertical)
match (first, second) {
(Some(first), Some(second)) => {
parse_two(first, second)
}
(Some(first), None) => {
parse_one(first)
}
_ => Err(())
}
@ -1097,10 +1140,40 @@ pub mod shorthands {
let (mut color, mut image, mut position, mut repeat, mut attachment) =
(None, None, None, None, None);
let mut last_component_value = None;
let mut unused_component_value = None;
let mut any = false;
for component_value in input.skip_whitespace() {
// Try `background-position` first because it might not use the value.
if position.is_none() {
match mem::replace(&mut unused_component_value, None) {
Some(saved_component_value) => {
// First try parsing a pair of values, then a single value.
match background_position::parse_two(saved_component_value,
component_value) {
Ok(v) => {
position = Some(v);
any = true;
continue
},
Err(()) => {
match background_position::parse_one(saved_component_value) {
Ok(v) => {
position = Some(v);
any = true;
// We haven't used the current `component_value`;
// keep attempting to parse it below.
},
// If we get here, parsing failed.
Err(()) => return Err(())
}
}
}
}
None => () // Wait until we have a pair of potential values.
}
}
if color.is_none() {
match background_color::from_component_value(component_value, base_url) {
Ok(v) => {
@ -1146,32 +1219,27 @@ pub mod shorthands {
}
}
match mem::replace(&mut last_component_value, None) {
Some(saved_component_value) => {
if position.is_none() {
match background_position::parse_horizontal_and_vertical(
saved_component_value,
component_value) {
Ok(v) => {
position = Some(v);
any = true;
continue
},
Err(()) => (),
}
}
// Save the component value. It may the first of a background-position pair.
unused_component_value = Some(component_value);
}
// If we get here, parsing failed.
return Err(())
}
None => {
// Save the component value.
last_component_value = Some(component_value)
if position.is_none() {
// Check for a lone trailing background-position value.
match mem::replace(&mut unused_component_value, None) {
Some(saved_component_value) => {
match background_position::parse_one(saved_component_value) {
Ok(v) => {
position = Some(v);
any = true;
},
Err(()) => return Err(())
}
}
None => ()
}
}
if any && last_component_value.is_none() {
if any && unused_component_value.is_none() {
Ok(Longhands {
background_color: color,
background_image: image,