aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <release+servo@mozilla.com>2014-02-19 05:25:58 -0500
committerbors-servo <release+servo@mozilla.com>2014-02-19 05:25:58 -0500
commitdedecec0e38a3130084fc3fd9e3e0905e262bb13 (patch)
treead070df96a1a874cf7df22d9c8ac34124366d9e7
parent20bbf6a8596e53c7a27f2489874b81436afb4e6c (diff)
parent7b56e75adb0002a3bd001aa61b55249c938fc60e (diff)
downloadservo-dedecec0e38a3130084fc3fd9e3e0905e262bb13.tar.gz
servo-dedecec0e38a3130084fc3fd9e3e0905e262bb13.zip
auto merge of #1631 : recrack/servo/background-image, r=SimonSapin
for #777 #800 - support(http://www.w3.org/TR/CSS21/colors.html#background) - background: url(foo.png) - background: url(data:image/png;base64...) - background-image: url(foo.png) - background-image: url(data:image/png;base64...) - not support(http://www.w3.org/TR/css3-background/)
-rw-r--r--src/components/main/layout/box_.rs38
-rw-r--r--src/components/script/dom/element.rs4
-rw-r--r--src/components/script/html/hubbub_html_parser.rs7
-rw-r--r--src/components/style/media_queries.rs7
-rw-r--r--src/components/style/properties.rs.mako170
-rw-r--r--src/components/style/stylesheets.rs12
-rw-r--r--src/test/html/background.html20
-rw-r--r--src/test/ref/background_a.html9
-rw-r--r--src/test/ref/background_b.html9
-rw-r--r--src/test/ref/basic.list1
-rw-r--r--src/test/ref/rust-0.pngbin0 -> 4399 bytes
11 files changed, 249 insertions, 28 deletions
diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs
index bcbb0ccebd1..d50d76ccb0a 100644
--- a/src/components/main/layout/box_.rs
+++ b/src/components/main/layout/box_.rs
@@ -22,6 +22,7 @@ use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::range::*;
use servo_util::namespace;
+
use std::cast;
use std::cell::RefCell;
use std::cmp::ApproxEq;
@@ -937,6 +938,7 @@ impl Box {
/// necessary.
pub fn paint_background_if_applicable<E:ExtraDisplayListData>(
&self,
+ builder: &DisplayListBuilder,
index: uint,
lists: &RefCell<DisplayListCollection<E>>,
absolute_bounds: &Rect<Au>) {
@@ -959,6 +961,39 @@ impl Box {
lists.lists[index].append_item(SolidColorDisplayItemClass(solid_color_display_item))
});
}
+
+ // The background image is painted on top of the background color.
+ // Implements background image, per spec:
+ // http://www.w3.org/TR/CSS21/colors.html#background
+ match style.Background.get().background_image {
+ Some(ref image_url) => {
+ let mut holder = ImageHolder::new(image_url.clone(), builder.ctx.image_cache.clone());
+ match holder.get_image() {
+ Some(image) => {
+ debug!("(building display list) building background image");
+
+ // Place the image into the display list.
+ lists.with_mut(|lists| {
+ let image_display_item = ~ImageDisplayItem {
+ base: BaseDisplayItem {
+ bounds: *absolute_bounds,
+ extra: ExtraDisplayListData::new(self),
+ },
+ image: image.clone(),
+ };
+ lists.lists[index].append_item(ImageDisplayItemClass(image_display_item));
+ });
+ }
+ None => {
+ // No image data at all? Do nothing.
+ //
+ // TODO: Add some kind of placeholder background image.
+ debug!("(building display list) no background image :(");
+ }
+ }
+ }
+ None => {}
+ }
}
/// Adds the display items necessary to paint the borders of this box to a display list if
@@ -1052,7 +1087,7 @@ impl Box {
self.paint_inline_background_border_if_applicable(index, lists, &absolute_box_bounds, &offset);
// Add the background to the list, if applicable.
- self.paint_background_if_applicable(index, lists, &absolute_box_bounds);
+ self.paint_background_if_applicable(builder, index, lists, &absolute_box_bounds);
match self.specific {
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
@@ -1259,7 +1294,6 @@ impl Box {
//
// TODO: Outlines.
self.paint_borders_if_applicable(index, lists, &absolute_box_bounds);
-
}
/// Returns the *minimum width* and *preferred width* of this box as defined by CSS 2.1.
diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs
index 9ffb17c3254..2eb0553f2f6 100644
--- a/src/components/script/dom/element.rs
+++ b/src/components/script/dom/element.rs
@@ -221,7 +221,9 @@ impl Element {
match local_name.as_slice() {
"style" => {
- self.style_attribute = Some(style::parse_style_attribute(value))
+ let doc = self.node.owner_doc();
+ let base_url = doc.document().url.clone();
+ self.style_attribute = Some(style::parse_style_attribute(value, &base_url))
}
"id" if abstract_self.is_in_doc() => {
// XXX: this dual declaration are workaround to avoid the compile error:
diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs
index 4ba46343817..9b66e4ecb1a 100644
--- a/src/components/script/html/hubbub_html_parser.rs
+++ b/src/components/script/html/hubbub_html_parser.rs
@@ -26,7 +26,6 @@ use servo_util::url::parse_url;
use std::cast;
use std::cell::RefCell;
use std::comm::{Port, SharedChan};
-use std::from_str::FromStr;
use std::str;
use style::Stylesheet;
@@ -277,7 +276,8 @@ pub fn parse_html(cx: *JSContext,
debug!("Fetched page; metadata is {:?}", load_response.metadata);
- let url2 = load_response.metadata.final_url.clone();
+ let base_url = load_response.metadata.final_url.clone();
+ let url2 = base_url.clone();
let url3 = url2.clone();
// Store the final URL before we start parsing, so that DOM routines
@@ -485,7 +485,6 @@ pub fn parse_html(cx: *JSContext,
// We've reached the end of a <style> so we can submit all the text to the parser.
unsafe {
let style: AbstractNode = NodeWrapping::from_hubbub_node(style);
- let url = FromStr::from_str("http://example.com/"); // FIXME
let mut data = ~[];
debug!("iterating over children {:?}", style.first_child());
for child in style.children() {
@@ -496,7 +495,7 @@ pub fn parse_html(cx: *JSContext,
}
debug!("style data = {:?}", data);
- let provenance = InlineProvenance(url.unwrap(), data.concat());
+ let provenance = InlineProvenance(base_url.clone(), data.concat());
css_chan3.send(CSSTaskNewFile(provenance));
}
},
diff --git a/src/components/style/media_queries.rs b/src/components/style/media_queries.rs
index 06866736305..a7ed89e8230 100644
--- a/src/components/style/media_queries.rs
+++ b/src/components/style/media_queries.rs
@@ -9,6 +9,7 @@ use cssparser::ast::*;
use errors::{ErrorLoggerIterator, log_css_error};
use stylesheets::{CSSRule, CSSMediaRule, parse_style_rule, parse_nested_at_rule};
use namespaces::NamespaceMap;
+use extra::url::Url;
pub struct MediaRule {
@@ -48,7 +49,7 @@ pub struct Device {
pub fn parse_media_rule(rule: AtRule, parent_rules: &mut ~[CSSRule],
- namespaces: &NamespaceMap) {
+ namespaces: &NamespaceMap, base_url: &Url) {
let media_queries = parse_media_query_list(rule.prelude);
let block = match rule.block {
Some(block) => block,
@@ -60,9 +61,9 @@ pub fn parse_media_rule(rule: AtRule, parent_rules: &mut ~[CSSRule],
let mut rules = ~[];
for rule in ErrorLoggerIterator(parse_rule_list(block.move_iter())) {
match rule {
- QualifiedRule(rule) => parse_style_rule(rule, &mut rules, namespaces),
+ QualifiedRule(rule) => parse_style_rule(rule, &mut rules, namespaces, base_url),
AtRule(rule) => parse_nested_at_rule(
- rule.name.to_ascii_lower(), rule, &mut rules, namespaces),
+ rule.name.to_ascii_lower(), rule, &mut rules, namespaces, base_url),
}
}
parent_rules.push(CSSMediaRule(MediaRule {
diff --git a/src/components/style/properties.rs.mako b/src/components/style/properties.rs.mako
index be73277dd3d..bdd51a702a8 100644
--- a/src/components/style/properties.rs.mako
+++ b/src/components/style/properties.rs.mako
@@ -5,8 +5,11 @@
// This file is a Mako template: http://www.makotemplates.org/
use std::ascii::StrAsciiExt;
+pub use servo_util::url::parse_url;
pub use extra::arc::Arc;
+pub use extra::url::Url;
use servo_util::cowarc::CowArc;
+
pub use cssparser::*;
pub use cssparser::ast::*;
@@ -48,7 +51,9 @@ STYLE_STRUCTS = []
THIS_STYLE_STRUCT = None
LONGHANDS = []
LONGHANDS_BY_NAME = {}
+LONGHANDS_WITH_URL = []
SHORTHANDS = []
+SHORTHANDS_WITH_URL = []
def new_style_struct(name, is_inherited):
global THIS_STYLE_STRUCT
@@ -101,6 +106,32 @@ pub mod longhands {
}
</%def>
+ <%def name="url_longhand(name, no_super=False)">
+ <%
+ property = Longhand(name)
+ THIS_STYLE_STRUCT.longhands.append(property)
+ LONGHANDS_WITH_URL.append(property)
+ LONGHANDS_BY_NAME[name] = property
+ %>
+ pub mod ${property.ident} {
+ % if not no_super:
+ use super::*;
+ % endif
+ pub use self::computed_value::*;
+ ${caller.body()}
+
+ pub fn parse_declared(input: &[ComponentValue], base_url: &Url)
+ -> Option<DeclaredValue<SpecifiedValue>> {
+ match CSSWideKeyword::parse(input) {
+ Some(Some(keyword)) => Some(CSSWideKeyword(keyword)),
+ Some(None) => Some(CSSWideKeyword(${
+ "Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"})),
+ None => parse_specified(input, base_url),
+ }
+ }
+ }
+ </%def>
+
<%def name="longhand(name, no_super=False)">
<%self:raw_longhand name="${name}">
${caller.body()}
@@ -470,10 +501,44 @@ pub mod longhands {
// CSS 2.1, Section 14 - Colors and Backgrounds
${new_style_struct("Background", is_inherited=False)}
-
${predefined_type("background-color", "CSSColor",
"RGBA(RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */")}
+ <%self:url_longhand name="background-image">
+ // The computed value is the same as the specified value.
+ pub use to_computed_value = super::computed_as_specified;
+ pub mod computed_value {
+ pub use extra::url::Url;
+ pub type T = Option<Url>;
+ }
+ pub type SpecifiedValue = computed_value::T;
+ #[inline] pub fn get_initial_value() -> SpecifiedValue {
+ None
+ }
+ pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<SpecifiedValue> {
+ from_iter(input.skip_whitespace(), base_url)
+ }
+ pub fn from_iter<'a>(mut iter: SkipWhitespaceIterator<'a>, base_url: &Url) -> Option<SpecifiedValue> {
+ match iter.next() {
+ Some(v) => from_component_value_with_url(v, base_url),
+ _ => None
+ }
+ }
+ pub fn from_component_value_with_url(component_value: &ComponentValue, base_url: &Url) -> Option<SpecifiedValue> {
+ match component_value {
+ &ast::URL(ref url) => {
+ let image_url = parse_url(url.as_slice(), Some(base_url.clone()));
+ Some(Some(image_url))
+ },
+ _ => None,
+ }
+ }
+ pub fn parse_specified(input: &[ComponentValue], base_url: &Url)
+ -> Option<DeclaredValue<SpecifiedValue>> {
+ parse(input, base_url).map(super::SpecifiedValue)
+ }
+ </%self:url_longhand>
+
${new_style_struct("Color", is_inherited=True)}
@@ -782,6 +847,24 @@ pub mod shorthands {
}
</%def>
+ <%def name="url_shorthand(name, sub_properties)">
+ <%
+ shorthand = Shorthand(name, sub_properties.split())
+ SHORTHANDS_WITH_URL.append(shorthand)
+ %>
+ pub mod ${shorthand.ident} {
+ use super::*;
+
+ struct Longhands {
+ % for sub_property in shorthand.sub_properties:
+ ${sub_property.ident}: Option<${sub_property.ident}::SpecifiedValue>,
+ % endfor
+ }
+ pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<Longhands> {
+ ${caller.body()}
+ }
+ }
+ </%def>
<%def name="four_sides_shorthand(name, sub_property_pattern, parser_function)">
<%self:shorthand name="${name}" sub_properties="${
' '.join(sub_property_pattern % side
@@ -809,13 +892,31 @@ pub mod shorthands {
</%self:shorthand>
</%def>
-
// TODO: other background-* properties
- <%self:shorthand name="background" sub_properties="background-color">
- one_component_value(input).and_then(specified::CSSColor::parse).map(|color| {
- Longhands { background_color: Some(color) }
- })
- </%self:shorthand>
+ <%self:url_shorthand name="background" sub_properties="background-color background-image">
+ let mut color = None;
+ let mut image = None;
+ let mut any = false;
+
+ for component_value in input.skip_whitespace() {
+ if color.is_none() {
+ match background_color::from_component_value(component_value) {
+ Some(v) => { color = Some(v); any = true; continue },
+ None => ()
+ }
+ }
+
+ if image.is_none() {
+ match background_image::from_component_value_with_url(component_value, base_url) {
+ Some(v) => { image = Some(v); any = true; continue },
+ None => (),
+ }
+ }
+ return None;
+ }
+ if any { Some(Longhands { background_color: color, background_image: image }) }
+ else { None }
+ </%self:url_shorthand>
${four_sides_shorthand("margin", "margin-%s", "margin_top::from_component_value")}
${four_sides_shorthand("padding", "padding-%s", "padding_top::from_component_value")}
@@ -973,12 +1074,12 @@ pub struct PropertyDeclarationBlock {
}
-pub fn parse_style_attribute(input: &str) -> PropertyDeclarationBlock {
- parse_property_declaration_list(tokenize(input))
+pub fn parse_style_attribute(input: &str, base_url: &Url) -> PropertyDeclarationBlock {
+ parse_property_declaration_list(tokenize(input), base_url)
}
-pub fn parse_property_declaration_list<I: Iterator<Node>>(input: I) -> PropertyDeclarationBlock {
+pub fn parse_property_declaration_list<I: Iterator<Node>>(input: I, base_url: &Url) -> PropertyDeclarationBlock {
let mut important = ~[];
let mut normal = ~[];
for item in ErrorLoggerIterator(parse_declaration_list(input)) {
@@ -988,7 +1089,7 @@ pub fn parse_property_declaration_list<I: Iterator<Node>>(input: I) -> PropertyD
Declaration(Declaration{ location: l, name: n, value: v, important: i}) => {
// TODO: only keep the last valid declaration for a given name.
let list = if i { &mut important } else { &mut normal };
- match PropertyDeclaration::parse(n, v, list) {
+ match PropertyDeclaration::parse(n, v, list, base_url) {
UnknownProperty => log_css_error(l, format!(
"Unsupported property: {}:{}", n, v.iter().to_css())),
InvalidValue => log_css_error(l, format!(
@@ -1036,6 +1137,9 @@ pub enum PropertyDeclaration {
% for property in LONGHANDS:
${property.ident}_declaration(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
% endfor
+ % for property in LONGHANDS_WITH_URL:
+ ${property.ident}_declaration(DeclaredValue<longhands::${property.ident}::SpecifiedValue>),
+ % endfor
}
@@ -1045,9 +1149,11 @@ enum PropertyDeclarationParseResult {
ValidDeclaration,
}
+
impl PropertyDeclaration {
pub fn parse(name: &str, value: &[ComponentValue],
- result_list: &mut ~[PropertyDeclaration]) -> PropertyDeclarationParseResult {
+ result_list: &mut ~[PropertyDeclaration],
+ base_url: &Url) -> PropertyDeclarationParseResult {
// FIXME: local variable to work around Rust #10683
let name_lower = name.to_ascii_lower();
match name_lower.as_slice() {
@@ -1059,6 +1165,14 @@ impl PropertyDeclaration {
}
)),
% endfor
+ % for property in LONGHANDS_WITH_URL:
+ "${property.name}" => result_list.push(${property.ident}_declaration(
+ match longhands::${property.ident}::parse_declared(value, base_url) {
+ Some(value) => value,
+ None => return InvalidValue,
+ }
+ )),
+ % endfor
% for shorthand in SHORTHANDS:
"${shorthand.name}" => match CSSWideKeyword::parse(value) {
Some(Some(keyword)) => {
@@ -1091,6 +1205,38 @@ impl PropertyDeclaration {
}
},
% endfor
+ % for shorthand in SHORTHANDS_WITH_URL:
+ "${shorthand.name}" => match CSSWideKeyword::parse(value) {
+ Some(Some(keyword)) => {
+ % for sub_property in shorthand.sub_properties:
+ result_list.push(${sub_property.ident}_declaration(
+ CSSWideKeyword(keyword)
+ ));
+ % endfor
+ },
+ Some(None) => {
+ % for sub_property in shorthand.sub_properties:
+ result_list.push(${sub_property.ident}_declaration(
+ CSSWideKeyword(${
+ "Inherit" if sub_property.style_struct.inherited else "Initial"})
+ ));
+ % endfor
+ },
+ None => match shorthands::${shorthand.ident}::parse(value, base_url) {
+ Some(result) => {
+ % for sub_property in shorthand.sub_properties:
+ result_list.push(${sub_property.ident}_declaration(
+ match result.${sub_property.ident} {
+ Some(value) => SpecifiedValue(value),
+ None => CSSWideKeyword(Initial),
+ }
+ ));
+ % endfor
+ },
+ None => return InvalidValue,
+ }
+ },
+ % endfor
_ => return UnknownProperty,
}
ValidDeclaration
diff --git a/src/components/style/stylesheets.rs b/src/components/style/stylesheets.rs
index 0b5b1406457..6c66b707022 100644
--- a/src/components/style/stylesheets.rs
+++ b/src/components/style/stylesheets.rs
@@ -75,7 +75,7 @@ impl Stylesheet {
match rule {
QualifiedRule(rule) => {
next_state = STATE_BODY;
- parse_style_rule(rule, &mut rules, &namespaces)
+ parse_style_rule(rule, &mut rules, &namespaces, &base_url)
},
AtRule(rule) => {
let lower_name = rule.name.to_ascii_lower();
@@ -112,7 +112,7 @@ impl Stylesheet {
},
_ => {
next_state = STATE_BODY;
- parse_nested_at_rule(lower_name, rule, &mut rules, &namespaces)
+ parse_nested_at_rule(lower_name, rule, &mut rules, &namespaces, &base_url)
},
}
},
@@ -125,14 +125,14 @@ impl Stylesheet {
pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut ~[CSSRule],
- namespaces: &NamespaceMap) {
+ namespaces: &NamespaceMap, base_url: &Url) {
let QualifiedRule{location: location, prelude: prelude, block: block} = rule;
// FIXME: avoid doing this for valid selectors
let serialized = prelude.iter().to_css();
match selectors::parse_selector_list(prelude, namespaces) {
Some(selectors) => parent_rules.push(CSSStyleRule(StyleRule{
selectors: selectors,
- declarations: properties::parse_property_declaration_list(block.move_iter())
+ declarations: properties::parse_property_declaration_list(block.move_iter(), base_url)
})),
None => log_css_error(location, format!(
"Invalid/unsupported selector: {}", serialized)),
@@ -142,9 +142,9 @@ pub fn parse_style_rule(rule: QualifiedRule, parent_rules: &mut ~[CSSRule],
// lower_name is passed explicitly to avoid computing it twice.
pub fn parse_nested_at_rule(lower_name: &str, rule: AtRule,
- parent_rules: &mut ~[CSSRule], namespaces: &NamespaceMap) {
+ parent_rules: &mut ~[CSSRule], namespaces: &NamespaceMap, base_url: &Url) {
match lower_name {
- "media" => parse_media_rule(rule, parent_rules, namespaces),
+ "media" => parse_media_rule(rule, parent_rules, namespaces, base_url),
_ => log_css_error(rule.location, format!("Unsupported at-rule: @{:s}", lower_name))
}
}
diff --git a/src/test/html/background.html b/src/test/html/background.html
new file mode 100644
index 00000000000..c3bb0df99ab
--- /dev/null
+++ b/src/test/html/background.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title></title>
+ </head>
+ <style>
+ .test { background: url(rust-0.png) gray; }
+ </style>
+ <body>
+ <div class="test" style="width:200px; height:200px; color:red;">
+ background: url(rust-0.png) gray; width:200px; height:200px;
+ </div>
+ <div class="test" style="background-image: url(rust-45.png); width:200px; height:200px; color:red;">
+ background-image: url(rust-45.png); width:200px; height:200px;
+ </div>
+ <div style="background: url(rust-90.png) yellow; width:200px; height:200px; border: 5px solid #000; color:blue;">
+ background: url(rust-90.png) yellow; width:200px; height:200px; border: 5px solid #000;
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/background_a.html b/src/test/ref/background_a.html
new file mode 100644
index 00000000000..28bc8f6b21d
--- /dev/null
+++ b/src/test/ref/background_a.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+</head>
+<body>
+<div class="test" style="background: url(rust-0.png); width:200px; height:200px;"></div>
+</body>
+</html>
diff --git a/src/test/ref/background_b.html b/src/test/ref/background_b.html
new file mode 100644
index 00000000000..11faa177fb7
--- /dev/null
+++ b/src/test/ref/background_b.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title></title>
+</head>
+<body>
+<img class="test" src="rust-0.png" style="width:200px; height:200px;" />
+</body>
+</html>
diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list
index 290a00741ca..bfd262afecb 100644
--- a/src/test/ref/basic.list
+++ b/src/test/ref/basic.list
@@ -29,3 +29,4 @@
== position_relative_a.html position_relative_b.html
== attr_exists_selector.html attr_exists_selector_ref.html
== data_img_a.html data_img_b.html
+== background_a.html background_b.html
diff --git a/src/test/ref/rust-0.png b/src/test/ref/rust-0.png
new file mode 100644
index 00000000000..20d93badf5e
--- /dev/null
+++ b/src/test/ref/rust-0.png
Binary files differ