Implement 'background-origin' property in CSS3 Background

This property determines the background positioning area, that is the position of
the origin of an image specified using the 'background-image' CSS property.

'background-origin' is ignored when background-attachment is fixed.

Spec: http://dev.w3.org/csswg/css-backgrounds-3/#background-origin

Fixes #6045.
This commit is contained in:
Jinwoo Song 2015-05-13 18:18:40 +09:00
parent b05c3fc0c0
commit b1b22c0c53
7 changed files with 119 additions and 8 deletions

View file

@ -43,7 +43,7 @@ use std::sync::Arc;
use std::sync::mpsc::channel;
use style::computed_values::filter::Filter;
use style::computed_values::transform::ComputedMatrix;
use style::computed_values::{background_attachment, background_repeat, background_size};
use style::computed_values::{background_attachment, background_origin, background_repeat, background_size};
use style::computed_values::{border_style, image_rendering, overflow_x, position, visibility};
use style::properties::ComputedValues;
use style::properties::style_structs::Border;
@ -419,12 +419,32 @@ impl FragmentDisplayListBuilding for Fragment {
// TODO: Check the bounds to see if a clip item is actually required.
let clip = clip.clone().intersect_rect(&bounds);
// Background image should be positioned on the padding box basis.
let border = style.logical_border_width().to_physical(style.writing_mode);
// Use 'background-origin' to get the origin value.
let (mut origin_x, mut origin_y) = match background.background_origin {
background_origin::T::padding_box => {
(Au(0), Au(0))
}
background_origin::T::border_box => {
(-border.left, -border.top)
}
background_origin::T::content_box => {
let border_padding = (self.border_padding).to_physical(self.style.writing_mode);
(border_padding.left - border.left, border_padding.top - border.top)
}
};
// Use `background-attachment` to get the initial virtual origin
let (virtual_origin_x, virtual_origin_y) = match background.background_attachment {
background_attachment::T::scroll => {
(absolute_bounds.origin.x, absolute_bounds.origin.y)
}
background_attachment::T::fixed => {
// If the background-attachment value for this image is fixed, then 'background-origin' has no effect.
origin_x = Au(0);
origin_y = Au(0);
(Au(0), Au(0))
}
};
@ -435,11 +455,8 @@ impl FragmentDisplayListBuilding for Fragment {
let vertical_position = model::specified(background.background_position.vertical,
bounds.size.height - image_size.height);
// Background image should be positioned on the padding box basis.
let border = style.logical_border_width().to_physical(style.writing_mode);
let abs_x = border.left + virtual_origin_x + horizontal_position;
let abs_y = border.top + virtual_origin_y + vertical_position;
let abs_x = border.left + virtual_origin_x + horizontal_position + origin_x;
let abs_y = border.top + virtual_origin_y + vertical_position + origin_y;
// Adjust origin and size based on background-repeat
match background.background_repeat {

View file

@ -39,6 +39,7 @@ partial interface CSSStyleDeclaration {
[TreatNullAs=EmptyString] attribute DOMString backgroundImage;
[TreatNullAs=EmptyString] attribute DOMString backgroundAttachment;
[TreatNullAs=EmptyString] attribute DOMString backgroundSize;
[TreatNullAs=EmptyString] attribute DOMString backgroundOrigin;
[TreatNullAs=EmptyString] attribute DOMString border;
[TreatNullAs=EmptyString] attribute DOMString borderColor;

View file

@ -1295,6 +1295,8 @@ pub mod longhands {
${single_keyword("background-attachment", "scroll fixed")}
${single_keyword("background-origin", "padding-box border-box content-box")}
<%self:longhand name="background-size">
use cssparser::{ToCss, Token};
use std::ascii::AsciiExt;
@ -4244,9 +4246,10 @@ pub mod shorthands {
// TODO: other background-* properties
<%self:shorthand name="background"
sub_properties="background-color background-position background-repeat background-attachment background-image background-size">
sub_properties="background-color background-position background-repeat background-attachment
background-image background-size background-origin">
use properties::longhands::{background_color, background_position, background_repeat};
use properties::longhands::{background_attachment, background_image, background_size};
use properties::longhands::{background_attachment, background_image, background_size, background_origin};
let mut color = None;
let mut image = None;
@ -4255,6 +4258,7 @@ pub mod shorthands {
let mut size = None;
let mut attachment = None;
let mut any = false;
let mut origin = None;
loop {
if position.is_none() {
@ -4299,6 +4303,13 @@ pub mod shorthands {
continue
}
}
if origin.is_none() {
if let Ok(value) = input.try(|input| background_origin::parse(context, input)) {
origin = Some(value);
any = true;
continue
}
}
break
}
@ -4310,6 +4321,7 @@ pub mod shorthands {
background_repeat: repeat,
background_attachment: attachment,
background_size: size,
background_origin: origin,
})
} else {
Err(())

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html>
<head>
<style>
#foo1 {
background: url(400x400_green.png);
background-origin: padding-box;
background-repeat: no-repeat;
width: 400px;
height: 400px;
padding: 20px;
border: 20px dotted red;
}
#foo2 {
background: url(400x400_green.png);
background-origin: border-box;
background-repeat: no-repeat;
width: 400px;
height: 400px;
padding: 20px;
border: 20px dotted red;
}
#foo3 {
background: url(400x400_green.png);
background-origin: content-box;
background-repeat: no-repeat;
width: 400px;
height: 400px;
padding: 20px;
border: 20px dotted red;
}
</style>
</head>
<body>
<div id=foo1></div>
<div id=foo2></div>
<div id=foo3></div>
</body>
</html>

View file

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<style>
#foo1 {
background: url(400x400_green.png);
background-repeat: no-repeat;
width: 400px;
height: 400px;
padding: 20px;
border: 20px dotted red;
}
#foo2 {
background: url(400x400_green.png);
background-position: -20px -20px;
background-repeat: no-repeat;
width: 400px;
height: 400px;
padding: 20px;
border: 20px dotted red;
}
#foo3 {
background: url(400x400_green.png);
background-position: 20px 20px;
background-repeat: no-repeat;
width: 400px;
height: 400px;
padding: 20px;
border: 20px dotted red;
}
</style>
</head>
<body>
<div id=foo1></div>
<div id=foo2></div>
<div id=foo3></div>
</body>
</html>

View file

@ -25,6 +25,7 @@ flaky_cpu == append_style_a.html append_style_b.html
== background_external_stylesheet.html background_ref.html
== background_image_position_a.html background_image_position_ref.html
== background_none_a.html background_none_b.html
== background_origin_a.html background_origin_ref.html
== background_position_a.html background_position_b.html
== background_position_keyword.html background_position_b.html
== background_position_percent.html background_position_b.html

View file

@ -116,6 +116,7 @@ fn test_parse_stylesheet() {
],
declarations: PropertyDeclarationBlock {
normal: Arc::new(vec![
PropertyDeclaration::BackgroundOrigin(DeclaredValue::Initial),
PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),