diff options
author | bors-servo <release+servo@mozilla.com> | 2014-02-19 05:25:58 -0500 |
---|---|---|
committer | bors-servo <release+servo@mozilla.com> | 2014-02-19 05:25:58 -0500 |
commit | dedecec0e38a3130084fc3fd9e3e0905e262bb13 (patch) | |
tree | ad070df96a1a874cf7df22d9c8ac34124366d9e7 | |
parent | 20bbf6a8596e53c7a27f2489874b81436afb4e6c (diff) | |
parent | 7b56e75adb0002a3bd001aa61b55249c938fc60e (diff) | |
download | servo-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_.rs | 38 | ||||
-rw-r--r-- | src/components/script/dom/element.rs | 4 | ||||
-rw-r--r-- | src/components/script/html/hubbub_html_parser.rs | 7 | ||||
-rw-r--r-- | src/components/style/media_queries.rs | 7 | ||||
-rw-r--r-- | src/components/style/properties.rs.mako | 170 | ||||
-rw-r--r-- | src/components/style/stylesheets.rs | 12 | ||||
-rw-r--r-- | src/test/html/background.html | 20 | ||||
-rw-r--r-- | src/test/ref/background_a.html | 9 | ||||
-rw-r--r-- | src/test/ref/background_b.html | 9 | ||||
-rw-r--r-- | src/test/ref/basic.list | 1 | ||||
-rw-r--r-- | src/test/ref/rust-0.png | bin | 0 -> 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 Binary files differnew file mode 100644 index 00000000000..20d93badf5e --- /dev/null +++ b/src/test/ref/rust-0.png |