diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 27ff1f7eb84..ce3df3f4c47 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -60,6 +60,10 @@ use table_cell::CollapsedBordersForCell; use url::Url; use util::opts; +fn get_cyclic(arr: &[T], index: usize) -> &T { + &arr[index % arr.len()] +} + pub struct DisplayListBuildState<'a> { pub layout_context: &'a LayoutContext<'a>, pub items: Vec, @@ -146,7 +150,7 @@ pub trait FragmentDisplayListBuilding { fn compute_background_image_size(&self, style: &ServoComputedValues, bounds: &Rect, - image: &WebRenderImageInfo) + image: &WebRenderImageInfo, index: usize) -> Size2D; /// Adds the display items necessary to paint the background image of this fragment to the @@ -157,7 +161,8 @@ pub trait FragmentDisplayListBuilding { display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion, - image_url: &Url); + image_url: &Url, + background_index: usize); /// Adds the display items necessary to paint the background linear gradient of this fragment /// to the appropriate section of the display list. @@ -344,27 +349,32 @@ impl FragmentDisplayListBuilding for Fragment { if !border_radii.is_square() { clip.intersect_with_rounded_rect(absolute_bounds, &border_radii) } + let background = style.get_background(); // FIXME: This causes a lot of background colors to be displayed when they are clearly not // needed. We could use display list optimization to clean this up, but it still seems // inefficient. What we really want is something like "nearest ancestor element that // doesn't have a fragment". - let background_color = style.resolve_color(style.get_background().background_color); + let background_color = style.resolve_color(background.background_color); // 'background-clip' determines the area within which the background is painted. // http://dev.w3.org/csswg/css-backgrounds-3/#the-background-clip let mut bounds = *absolute_bounds; - match style.get_background().background_clip { - background_clip::T::border_box => {} - background_clip::T::padding_box => { + // This is the clip for the color (which is the last element in the bg array) + let color_clip = get_cyclic(&background.background_clip.0, + background.background_image.0.len() - 1); + + match *color_clip { + background_clip::single_value::T::border_box => {} + background_clip::single_value::T::padding_box => { let border = style.logical_border_width().to_physical(style.writing_mode); bounds.origin.x = bounds.origin.x + border.left; bounds.origin.y = bounds.origin.y + border.top; bounds.size.width = bounds.size.width - border.horizontal(); bounds.size.height = bounds.size.height - border.vertical(); } - background_clip::T::content_box => { + background_clip::single_value::T::content_box => { let border_padding = self.border_padding.to_physical(style.writing_mode); bounds.origin.x = bounds.origin.x + border_padding.left; bounds.origin.y = bounds.origin.y + border_padding.top; @@ -388,23 +398,26 @@ impl FragmentDisplayListBuilding for Fragment { // Implements background image, per spec: // http://www.w3.org/TR/CSS21/colors.html#background let background = style.get_background(); - match background.background_image.0 { - None => {} - Some(computed::Image::LinearGradient(ref gradient)) => { - self.build_display_list_for_background_linear_gradient(state, - display_list_section, - &bounds, - &clip, - gradient, - style); - } - Some(computed::Image::Url(ref image_url, ref _extra_data)) => { - self.build_display_list_for_background_image(state, - style, - display_list_section, - &bounds, - &clip, - image_url); + for (i, background_image) in background.background_image.0.iter().enumerate().rev() { + match background_image.0 { + None => {} + Some(computed::Image::LinearGradient(ref gradient)) => { + self.build_display_list_for_background_linear_gradient(state, + display_list_section, + &bounds, + &clip, + gradient, + style); + } + Some(computed::Image::Url(ref image_url, ref _extra_data)) => { + self.build_display_list_for_background_image(state, + style, + display_list_section, + &bounds, + &clip, + image_url, + i); + } } } } @@ -412,7 +425,8 @@ impl FragmentDisplayListBuilding for Fragment { fn compute_background_image_size(&self, style: &ServoComputedValues, bounds: &Rect, - image: &WebRenderImageInfo) + image: &WebRenderImageInfo, + index: usize) -> Size2D { // If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is // wide. @@ -420,19 +434,22 @@ impl FragmentDisplayListBuilding for Fragment { let bounds_aspect_ratio = bounds.size.width.to_f64_px() / bounds.size.height.to_f64_px(); let intrinsic_size = Size2D::new(Au::from_px(image.width as i32), Au::from_px(image.height as i32)); - match (style.get_background().background_size.clone(), - image_aspect_ratio < bounds_aspect_ratio) { - (background_size::T::Contain, false) | (background_size::T::Cover, true) => { + let background_size = get_cyclic(&style.get_background().background_size.0, index).clone(); + match (background_size, image_aspect_ratio < bounds_aspect_ratio) { + (background_size::single_value::T::Contain, false) | + (background_size::single_value::T::Cover, true) => { Size2D::new(bounds.size.width, Au::from_f64_px(bounds.size.width.to_f64_px() / image_aspect_ratio)) } - (background_size::T::Contain, true) | (background_size::T::Cover, false) => { + (background_size::single_value::T::Contain, true) | + (background_size::single_value::T::Cover, false) => { Size2D::new(Au::from_f64_px(bounds.size.height.to_f64_px() * image_aspect_ratio), bounds.size.height) } - (background_size::T::Explicit(background_size::ExplicitSize { + (background_size::single_value::T::Explicit(background_size::single_value + ::ExplicitSize { width, height: LengthOrPercentageOrAuto::Auto, }), _) => { @@ -441,7 +458,8 @@ impl FragmentDisplayListBuilding for Fragment { Size2D::new(width, Au::from_f64_px(width.to_f64_px() / image_aspect_ratio)) } - (background_size::T::Explicit(background_size::ExplicitSize { + (background_size::single_value::T::Explicit(background_size::single_value + ::ExplicitSize { width: LengthOrPercentageOrAuto::Auto, height }), _) => { @@ -450,7 +468,8 @@ impl FragmentDisplayListBuilding for Fragment { Size2D::new(Au::from_f64_px(height.to_f64_px() * image_aspect_ratio), height) } - (background_size::T::Explicit(background_size::ExplicitSize { + (background_size::single_value::T::Explicit(background_size::single_value + ::ExplicitSize { width, height }), _) => { @@ -468,19 +487,22 @@ impl FragmentDisplayListBuilding for Fragment { display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion, - image_url: &Url) { + image_url: &Url, + index: usize) { let background = style.get_background(); let fetch_image_data_as_well = !opts::get().use_webrender; let webrender_image = state.layout_context.get_webrender_image_for_url(image_url, UsePlaceholder::No, fetch_image_data_as_well); + if let Some((webrender_image, image_data)) = webrender_image { debug!("(building display list) building background image"); // Use `background-size` to get the size. let mut bounds = *absolute_bounds; - let image_size = self.compute_background_image_size(style, &bounds, &webrender_image); + let image_size = self.compute_background_image_size(style, &bounds, + &webrender_image, index); // Clip. // @@ -492,25 +514,27 @@ impl FragmentDisplayListBuilding for Fragment { 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 => { + let origin = get_cyclic(&background.background_origin.0, index); + let (mut origin_x, mut origin_y) = match *origin { + background_origin::single_value::T::padding_box => { (Au(0), Au(0)) } - background_origin::T::border_box => { + background_origin::single_value::T::border_box => { (-border.left, -border.top) } - background_origin::T::content_box => { + background_origin::single_value::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 => { + let attachment = get_cyclic(&background.background_attachment.0, index); + let (virtual_origin_x, virtual_origin_y) = match *attachment { + background_attachment::single_value::T::scroll => { (absolute_bounds.origin.x, absolute_bounds.origin.y) } - background_attachment::T::fixed => { + background_attachment::single_value::T::fixed => { // If the ‘background-attachment’ value for this image is ‘fixed’, then // 'background-origin' has no effect. origin_x = Au(0); @@ -519,24 +543,25 @@ impl FragmentDisplayListBuilding for Fragment { } }; + let position = *get_cyclic(&background.background_position.0, index); // Use `background-position` to get the offset. - let horizontal_position = model::specified(background.background_position.horizontal, - bounds.size.width - image_size.width); - let vertical_position = model::specified(background.background_position.vertical, - bounds.size.height - image_size.height); + let horizontal_position = model::specified(position.horizontal, + bounds.size.width - image_size.width); + let vertical_position = model::specified(position.vertical, + bounds.size.height - image_size.height); 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 { - background_repeat::T::no_repeat => { + match *get_cyclic(&background.background_repeat.0, index) { + background_repeat::single_value::T::no_repeat => { bounds.origin.x = abs_x; bounds.origin.y = abs_y; bounds.size.width = image_size.width; bounds.size.height = image_size.height; } - background_repeat::T::repeat_x => { + background_repeat::single_value::T::repeat_x => { bounds.origin.y = abs_y; bounds.size.height = image_size.height; ImageFragmentInfo::tile_image(&mut bounds.origin.x, @@ -544,7 +569,7 @@ impl FragmentDisplayListBuilding for Fragment { abs_x, image_size.width.to_nearest_px() as u32); } - background_repeat::T::repeat_y => { + background_repeat::single_value::T::repeat_y => { bounds.origin.x = abs_x; bounds.size.width = image_size.width; ImageFragmentInfo::tile_image(&mut bounds.origin.y, @@ -552,31 +577,32 @@ impl FragmentDisplayListBuilding for Fragment { abs_y, image_size.height.to_nearest_px() as u32); } - background_repeat::T::repeat => { + background_repeat::single_value::T::repeat => { ImageFragmentInfo::tile_image(&mut bounds.origin.x, - &mut bounds.size.width, - abs_x, - image_size.width.to_nearest_px() as u32); + &mut bounds.size.width, + abs_x, + image_size.width.to_nearest_px() as u32); ImageFragmentInfo::tile_image(&mut bounds.origin.y, - &mut bounds.size.height, - abs_y, - image_size.height.to_nearest_px() as u32); + &mut bounds.size.height, + abs_y, + image_size.height.to_nearest_px() as u32); } }; // Create the image display item. let base = state.create_base_display_item(&bounds, - &clip, - self.node, - style.get_cursor(Cursor::Default), - display_list_section); + &clip, + self.node, + style.get_cursor(Cursor::Default), + display_list_section); state.add_display_item(DisplayItem::ImageClass(box ImageDisplayItem { - base: base, - webrender_image: webrender_image, - image_data: image_data.map(Arc::new), - stretch_size: Size2D::new(image_size.width, image_size.height), - image_rendering: style.get_inheritedbox().image_rendering.clone(), + base: base, + webrender_image: webrender_image, + image_data: image_data.map(Arc::new), + stretch_size: Size2D::new(image_size.width, image_size.height), + image_rendering: style.get_inheritedbox().image_rendering.clone(), })); + } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 9c5b80fb103..2d57ddab4d6 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -362,8 +362,11 @@ impl LayoutElementHelpers for LayoutJS { if let Some(url) = background { hints.push(from_declaration( PropertyDeclaration::BackgroundImage(DeclaredValue::Value( - background_image::SpecifiedValue(Some( - specified::Image::Url(url, specified::UrlExtraData { }))))))); + background_image::SpecifiedValue(vec![ + background_image::single_value::SpecifiedValue(Some( + specified::Image::Url(url, specified::UrlExtraData { }) + )) + ]))))); } let color = if let Some(this) = self.downcast::() { diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index cebdd269793..4751b0725ab 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -2236,6 +2236,7 @@ dependencies = [ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index 4f0d53cce20..daceb3178d1 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -34,6 +34,7 @@ heapsize_plugin = {version = "0.1.2", optional = true} lazy_static = "0.2" log = "0.3.5" matches = "0.1" +num-integer = "0.1.32" num-traits = "0.1.32" ordered-float = "0.2.2" quickersort = "2.0.0" diff --git a/components/style/lib.rs b/components/style/lib.rs index 86b3e258859..fe1957d7c4e 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -56,6 +56,7 @@ extern crate log; #[allow(unused_extern_crates)] #[macro_use] extern crate matches; +extern crate num_integer; extern crate num_traits; extern crate ordered_float; extern crate quickersort; diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 2c7e6f0ea1a..0676df08c57 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -937,6 +937,32 @@ fn static_assert() { +<%def name="simple_background_array_property(name, field_name)"> + pub fn copy_background_${name}_from(&mut self, other: &Self) { + unsafe { + Gecko_EnsureImageLayersLength(&mut self.gecko.mImage, other.gecko.mImage.mLayers.len()); + } + for (layer, other) in self.gecko.mImage.mLayers.iter_mut() + .zip(other.gecko.mImage.mLayers.iter()) + .take(other.gecko.mImage.${field_name}Count as usize) { + layer.${field_name} = other.${field_name}; + } + self.gecko.mImage.${field_name}Count = other.gecko.mImage.${field_name}Count; + } + + pub fn set_background_${name}(&mut self, v: longhands::background_${name}::computed_value::T) { + unsafe { + Gecko_EnsureImageLayersLength(&mut self.gecko.mImage, v.0.len()); + } + + self.gecko.mImage.${field_name}Count = v.0.len() as u32; + for (servo, geckolayer) in v.0.into_iter().zip(self.gecko.mImage.mLayers.iter_mut()) { + geckolayer.${field_name} = { + ${caller.body()} + }; + } + } + // TODO: Gecko accepts lists in most background-related properties. We just use // the first element (which is the common case), but at some point we want to // add support for parsing these lists in servo and pushing to nsTArray's. @@ -950,109 +976,105 @@ fn static_assert() { <% impl_color("background_color", "mBackgroundColor", need_clone=True) %> - pub fn copy_background_repeat_from(&mut self, other: &Self) { - self.gecko.mImage.mRepeatCount = cmp::min(1, other.gecko.mImage.mRepeatCount); - self.gecko.mImage.mLayers.mFirstElement.mRepeat = - other.gecko.mImage.mLayers.mFirstElement.mRepeat; - } - - pub fn set_background_repeat(&mut self, v: longhands::background_repeat::computed_value::T) { - use properties::longhands::background_repeat::computed_value::T; - use gecko_bindings::structs::{NS_STYLE_IMAGELAYER_REPEAT_REPEAT, NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT}; + <%self:simple_background_array_property name="repeat" field_name="mRepeat"> + use properties::longhands::background_repeat::single_value::computed_value::T; use gecko_bindings::structs::nsStyleImageLayers_Repeat; - let (repeat_x, repeat_y) = match v { - T::repeat_x => (NS_STYLE_IMAGELAYER_REPEAT_REPEAT, - NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT), - T::repeat_y => (NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT, - NS_STYLE_IMAGELAYER_REPEAT_REPEAT), - T::repeat => (NS_STYLE_IMAGELAYER_REPEAT_REPEAT, + use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_REPEAT; + use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT; + + let (repeat_x, repeat_y) = match servo { + T::repeat_x => (NS_STYLE_IMAGELAYER_REPEAT_REPEAT, + NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT), + T::repeat_y => (NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT, NS_STYLE_IMAGELAYER_REPEAT_REPEAT), - T::no_repeat => (NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT, - NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT), + T::repeat => (NS_STYLE_IMAGELAYER_REPEAT_REPEAT, + NS_STYLE_IMAGELAYER_REPEAT_REPEAT), + T::no_repeat => (NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT, + NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT), }; + nsStyleImageLayers_Repeat { + mXRepeat: repeat_x as u8, + mYRepeat: repeat_y as u8, + } + - self.gecko.mImage.mRepeatCount = 1; - self.gecko.mImage.mLayers.mFirstElement.mRepeat = nsStyleImageLayers_Repeat { - mXRepeat: repeat_x as u8, - mYRepeat: repeat_y as u8, - }; - } + <%self:simple_background_array_property name="clip" field_name="mClip"> + use properties::longhands::background_clip::single_value::computed_value::T; - pub fn copy_background_clip_from(&mut self, other: &Self) { - self.gecko.mImage.mClipCount = cmp::min(1, other.gecko.mImage.mClipCount); - self.gecko.mImage.mLayers.mFirstElement.mClip = - other.gecko.mImage.mLayers.mFirstElement.mClip; - } - - pub fn set_background_clip(&mut self, v: longhands::background_clip::computed_value::T) { - use properties::longhands::background_clip::computed_value::T; - self.gecko.mImage.mClipCount = 1; - - // TODO: Gecko supports background-clip: text, but just on -webkit- - // prefixed properties. - self.gecko.mImage.mLayers.mFirstElement.mClip = match v { + match servo { T::border_box => structs::NS_STYLE_IMAGELAYER_CLIP_BORDER as u8, T::padding_box => structs::NS_STYLE_IMAGELAYER_CLIP_PADDING as u8, T::content_box => structs::NS_STYLE_IMAGELAYER_CLIP_CONTENT as u8, - }; - } + } + - pub fn copy_background_origin_from(&mut self, other: &Self) { - self.gecko.mImage.mOriginCount = cmp::min(1, other.gecko.mImage.mOriginCount); - self.gecko.mImage.mLayers.mFirstElement.mOrigin = - other.gecko.mImage.mLayers.mFirstElement.mOrigin; - } + <%self:simple_background_array_property name="origin" field_name="mOrigin"> + use properties::longhands::background_origin::single_value::computed_value::T; - pub fn set_background_origin(&mut self, v: longhands::background_origin::computed_value::T) { - use properties::longhands::background_origin::computed_value::T; - - self.gecko.mImage.mOriginCount = 1; - self.gecko.mImage.mLayers.mFirstElement.mOrigin = match v { + match servo { T::border_box => structs::NS_STYLE_IMAGELAYER_ORIGIN_BORDER as u8, T::padding_box => structs::NS_STYLE_IMAGELAYER_ORIGIN_PADDING as u8, T::content_box => structs::NS_STYLE_IMAGELAYER_ORIGIN_CONTENT as u8, - }; - } + } + - pub fn copy_background_attachment_from(&mut self, other: &Self) { - self.gecko.mImage.mAttachmentCount = cmp::min(1, other.gecko.mImage.mAttachmentCount); - self.gecko.mImage.mLayers.mFirstElement.mAttachment = - other.gecko.mImage.mLayers.mFirstElement.mAttachment; - } + <%self:simple_background_array_property name="attachment" field_name="mAttachment"> + use properties::longhands::background_attachment::single_value::computed_value::T; - pub fn set_background_attachment(&mut self, v: longhands::background_attachment::computed_value::T) { - use properties::longhands::background_attachment::computed_value::T; - - self.gecko.mImage.mAttachmentCount = 1; - self.gecko.mImage.mLayers.mFirstElement.mAttachment = match v { + match servo { T::scroll => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL as u8, T::fixed => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED as u8, T::local => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL as u8, - }; - } + } + pub fn copy_background_position_from(&mut self, other: &Self) { self.gecko.mImage.mPositionXCount = cmp::min(1, other.gecko.mImage.mPositionXCount); self.gecko.mImage.mPositionYCount = cmp::min(1, other.gecko.mImage.mPositionYCount); self.gecko.mImage.mLayers.mFirstElement.mPosition = other.gecko.mImage.mLayers.mFirstElement.mPosition; + unsafe { + Gecko_EnsureImageLayersLength(&mut self.gecko.mImage, other.gecko.mImage.mLayers.len()); + } + for (layer, other) in self.gecko.mImage.mLayers.iter_mut() + .zip(other.gecko.mImage.mLayers.iter()) + .take(other.gecko.mImage.mPositionXCount as usize) { + layer.mPosition.mXPosition = other.mPosition.mXPosition; + } + for (layer, other) in self.gecko.mImage.mLayers.iter_mut() + .zip(other.gecko.mImage.mLayers.iter()) + .take(other.gecko.mImage.mPositionYCount as usize) { + layer.mPosition.mYPosition = other.mPosition.mYPosition; + } + self.gecko.mImage.mPositionXCount = other.gecko.mImage.mPositionXCount; + self.gecko.mImage.mPositionYCount = other.gecko.mImage.mPositionYCount; } 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; - Position { - horizontal: position.mXPosition.into(), - vertical: position.mYPosition.into(), - } + longhands::background_position::computed_value::T( + self.gecko.mImage.mLayers.iter() + .take(self.gecko.mImage.mPositionXCount as usize) + .take(self.gecko.mImage.mPositionYCount as usize) + .map(|position| Position { + horizontal: position.mPosition.mXPosition.into(), + vertical: position.mPosition.mYPosition.into(), + }) + .collect() + ) } 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(); - self.gecko.mImage.mPositionXCount = 1; - self.gecko.mImage.mPositionYCount = 1; + unsafe { + Gecko_EnsureImageLayersLength(&mut self.gecko.mImage, v.0.len()); + } + + self.gecko.mImage.mPositionXCount = v.0.len() as u32; + self.gecko.mImage.mPositionYCount = v.0.len() as u32; + for (servo, geckolayer) in v.0.into_iter().zip(self.gecko.mImage.mLayers.iter_mut()) { + geckolayer.mPosition.mXPosition = servo.horizontal.into(); + geckolayer.mPosition.mYPosition = servo.vertical.into(); + } } pub fn copy_background_image_from(&mut self, other: &Self) { @@ -1156,6 +1178,24 @@ fn static_assert() { } } + + pub fn fill_arrays(&mut self) { + use gecko_bindings::bindings::Gecko_FillAllBackgroundLists; + use std::cmp; + let mut max_len = 1; + % for member in "mRepeat mClip mOrigin mAttachment mPositionX mPositionY mImage".split(): + max_len = cmp::max(max_len, self.gecko.mImage.${member}Count); + % endfor + + // XXXManishearth Gecko does an optimization here where it only + // fills things in if any of the properties have been set + + unsafe { + // While we could do this manually, we'd need to also manually + // run all the copy constructors, so we just delegate to gecko + Gecko_FillAllBackgroundLists(&mut self.gecko.mImage, max_len); + } + } <%self:impl_trait style_struct_name="List" skip_longhands="list-style-type" skip_additionals="*"> diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index acb4504eea9..43b3d7bda17 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -69,10 +69,10 @@ ${caller.body()} } pub mod computed_value { - use super::single_value; + pub use super::single_value::computed_value as single_value; #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct T(pub Vec); + pub struct T(pub Vec); } impl ToCss for computed_value::T { @@ -285,8 +285,8 @@ } -<%def name="single_keyword(name, values, **kwargs)"> - <%call expr="single_keyword_computed(name, values, **kwargs)"> +<%def name="single_keyword(name, values, vector=False, **kwargs)"> + <%call expr="single_keyword_computed(name, values, vector, **kwargs)"> use values::computed::ComputedValueAsSpecified; use values::NoViewportPercentage; impl ComputedValueAsSpecified for SpecifiedValue {} @@ -294,16 +294,16 @@ -<%def name="single_keyword_computed(name, values, **kwargs)"> +<%def name="single_keyword_computed(name, values, vector=False, **kwargs)"> <% keyword_kwargs = {a: kwargs.pop(a, None) for a in [ 'gecko_constant_prefix', 'gecko_enum_prefix', 'extra_gecko_values', 'extra_servo_values', ]} %> - <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> + + <%def name="inner_body()"> pub use self::computed_value::T as SpecifiedValue; - ${caller.body()} pub mod computed_value { define_css_keyword_enum! { T: % for value in data.longhands_by_name[name].keyword.values_for(product): @@ -316,11 +316,26 @@ computed_value::T::${to_rust_ident(values.split()[0])} } #[inline] + pub fn get_initial_specified_value() -> SpecifiedValue { + get_initial_value() + } + #[inline] pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result { computed_value::T::parse(input) } - + + % if vector: + <%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> + ${inner_body()} + ${caller.body()} + + % else: + <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)"> + ${inner_body()} + ${caller.body()} + + % endif <%def name="keyword_list(name, values, **kwargs)"> @@ -464,6 +479,36 @@ } } + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + let mut all_inherit = true; + let mut all_initial = true; + let mut with_variables = false; + % for sub_property in shorthand.sub_properties: + match *self.${sub_property.ident} { + DeclaredValue::Initial => all_inherit = false, + DeclaredValue::Inherit => all_initial = false, + DeclaredValue::WithVariables {..} => with_variables = true, + DeclaredValue::Value(..) => { + all_initial = false; + all_inherit = false; + } + } + % endfor + + if with_variables { + // We don't serialize shorthands with variables + dest.write_str("") + } else if all_inherit { + dest.write_str("inherit") + } else if all_initial { + dest.write_str("initial") + } else { + self.to_css_declared(dest) + } + } + } + pub fn parse(context: &ParserContext, input: &mut Parser, declarations: &mut Vec) @@ -526,8 +571,8 @@ }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { super::serialize_four_sides( dest, self.${to_rust_ident(sub_property_pattern % 'top')}, diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index a45b68b6816..e3d0a1a3db5 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -155,6 +155,18 @@ pub trait Interpolate: Sized { fn interpolate(&self, other: &Self, time: f64) -> Result; } +/// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list +pub trait RepeatableListInterpolate: Interpolate {} + +impl Interpolate for Vec { + fn interpolate(&self, other: &Self, time: f64) -> Result { + use num_integer::lcm; + let len = lcm(self.len(), other.len()); + self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| { + me.interpolate(you, time) + }).collect() + } +} /// https://drafts.csswg.org/css-transitions/#animtype-number impl Interpolate for Au { #[inline] @@ -296,6 +308,14 @@ impl Interpolate for BorderSpacing { } } +impl Interpolate for BackgroundSize { + #[inline] + fn interpolate(&self, other: &Self, time: f64) -> Result { + self.0.interpolate(&other.0, time).map(BackgroundSize) + } +} + + /// https://drafts.csswg.org/css-transitions/#animtype-color impl Interpolate for RGBA { #[inline] @@ -496,18 +516,12 @@ impl Interpolate for Position { } } -impl Interpolate for BackgroundSize { +impl RepeatableListInterpolate for Position {} + +impl Interpolate for BackgroundPosition { + #[inline] fn interpolate(&self, other: &Self, time: f64) -> Result { - use properties::longhands::background_size::computed_value::ExplicitSize; - match (self, other) { - (&BackgroundSize::Explicit(ref me), &BackgroundSize::Explicit(ref other)) => { - Ok(BackgroundSize::Explicit(ExplicitSize { - width: try!(me.width.interpolate(&other.width, time)), - height: try!(me.height.interpolate(&other.height, time)), - })) - } - _ => Err(()), - } + Ok(BackgroundPosition(try!(self.0.interpolate(&other.0, time)))) } } diff --git a/components/style/properties/longhand/background.mako.rs b/components/style/properties/longhand/background.mako.rs index c36e6e63e74..9ad9daf4262 100644 --- a/components/style/properties/longhand/background.mako.rs +++ b/components/style/properties/longhand/background.mako.rs @@ -10,7 +10,7 @@ ${helpers.predefined_type("background-color", "CSSColor", "::cssparser::Color::RGBA(::cssparser::RGBA { red: 0., green: 0., blue: 0., alpha: 0. }) /* transparent */", animatable=True)} -<%helpers:vector_longhand gecko_only="True" name="background-image" animatable="False"> +<%helpers:vector_longhand name="background-image" animatable="False"> use cssparser::ToCss; use std::fmt; use values::specified::Image; @@ -54,6 +54,10 @@ ${helpers.predefined_type("background-color", "CSSColor", pub fn get_initial_value() -> computed_value::T { computed_value::T(None) } + #[inline] + pub fn get_initial_specified_value() -> SpecifiedValue { + SpecifiedValue(None) + } pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { if input.try(|input| input.expect_ident_matching("none")).is_ok() { Ok(SpecifiedValue(None)) @@ -75,7 +79,7 @@ ${helpers.predefined_type("background-color", "CSSColor", } -<%helpers:longhand name="background-position" animatable="True"> +<%helpers:vector_longhand name="background-position" animatable="True"> use cssparser::ToCss; use std::fmt; use values::LocalToCss; @@ -84,6 +88,7 @@ ${helpers.predefined_type("background-color", "CSSColor", pub mod computed_value { use values::computed::position::Position; + use properties::animated_properties::{Interpolate, RepeatableListInterpolate}; pub type T = Position; } @@ -98,30 +103,42 @@ ${helpers.predefined_type("background-color", "CSSColor", vertical: computed::LengthOrPercentage::Percentage(0.0), } } + #[inline] + pub fn get_initial_specified_value() -> SpecifiedValue { + use values::specified::Percentage; + Position { + horizontal: specified::LengthOrPercentage::Percentage(Percentage(0.0)), + vertical: specified::LengthOrPercentage::Percentage(Percentage(0.0)), + } + } pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result { Ok(try!(Position::parse(input))) } - + ${helpers.single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat", + vector=True, animatable=False)} ${helpers.single_keyword("background-attachment", "scroll fixed" + (" local" if product == "gecko" else ""), + vector=True, animatable=False)} ${helpers.single_keyword("background-clip", "border-box padding-box content-box", + vector=True, animatable=False)} ${helpers.single_keyword("background-origin", "padding-box border-box content-box", + vector=True, animatable=False)} -<%helpers:longhand name="background-size" animatable="True"> +<%helpers:vector_longhand name="background-size" animatable="True"> use cssparser::{ToCss, Token}; use std::ascii::AsciiExt; use std::fmt; @@ -129,6 +146,7 @@ ${helpers.single_keyword("background-origin", pub mod computed_value { use values::computed::LengthOrPercentageOrAuto; + use properties::animated_properties::{Interpolate, RepeatableListInterpolate}; #[derive(PartialEq, Clone, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -144,6 +162,23 @@ ${helpers.single_keyword("background-origin", Cover, Contain, } + + impl RepeatableListInterpolate for T {} + + impl Interpolate for T { + fn interpolate(&self, other: &Self, time: f64) -> Result { + use properties::longhands::background_size::single_value::computed_value::ExplicitSize; + match (self, other) { + (&T::Explicit(ref me), &T::Explicit(ref other)) => { + Ok(T::Explicit(ExplicitSize { + width: try!(me.width.interpolate(&other.width, time)), + height: try!(me.height.interpolate(&other.height, time)), + })) + } + _ => Err(()), + } + } + } } impl ToCss for computed_value::T { @@ -237,6 +272,13 @@ ${helpers.single_keyword("background-origin", height: computed::LengthOrPercentageOrAuto::Auto, }) } + #[inline] + pub fn get_initial_specified_value() -> SpecifiedValue { + SpecifiedValue::Explicit(SpecifiedExplicitSize { + width: specified::LengthOrPercentageOrAuto::Auto, + height: specified::LengthOrPercentageOrAuto::Auto, + }) + } pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { let width; @@ -274,4 +316,4 @@ ${helpers.single_keyword("background-origin", height: height, })) } - + diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index a61f69483ac..1c3c87501e3 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2034,6 +2034,10 @@ pub fn cascade(viewport_size: Size2D, } % endfor + % if product == "gecko": + style.mutate_background().fill_arrays(); + % endif + // The initial value of outline width may be changed at computed value time. if style.get_outline().clone_outline_style().none_or_hidden() && style.get_outline().outline_has_nonzero_width() { diff --git a/components/style/properties/shorthand/background.mako.rs b/components/style/properties/shorthand/background.mako.rs index 853e5554937..f507af9ec56 100644 --- a/components/style/properties/shorthand/background.mako.rs +++ b/components/style/properties/shorthand/background.mako.rs @@ -12,172 +12,204 @@ use properties::longhands::{background_image, background_size, background_origin, background_clip}; pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result { - let mut color = None; - let mut image = None; - let mut position = None; - let mut repeat = None; - let mut size = None; - let mut attachment = None; - let mut any = false; - let mut origin = None; - let mut clip = None; + let mut background_color = None; - loop { - if position.is_none() { - if let Ok(value) = input.try(|input| background_position::parse(context, input)) { - position = Some(value); - any = true; - - // Parse background size, if applicable. - size = input.try(|input| { - try!(input.expect_delim('/')); - background_size::parse(context, input) - }).ok(); - - continue - } - } - if color.is_none() { + % for name in "image position repeat size attachment origin clip".split(): + let mut background_${name} = background_${name}::SpecifiedValue(Vec::new()); + % endfor + try!(input.parse_comma_separated(|input| { + % for name in "image position repeat size attachment origin clip".split(): + let mut ${name} = None; + % endfor + loop { if let Ok(value) = input.try(|input| background_color::parse(context, input)) { - color = Some(value); - any = true; - continue + if background_color.is_none() { + background_color = Some(value); + continue + } else { + // color can only be the last element + return Err(()) + } } - } - if image.is_none() { - if let Ok(value) = input.try(|input| background_image::parse(context, input)) { - image = Some(value); - any = true; - continue + if position.is_none() { + if let Ok(value) = input.try(|input| background_position::single_value + ::parse(context, input)) { + position = Some(value); + + // Parse background size, if applicable. + size = input.try(|input| { + try!(input.expect_delim('/')); + background_size::single_value::parse(context, input) + }).ok(); + + continue + } } - } - if repeat.is_none() { - if let Ok(value) = input.try(|input| background_repeat::parse(context, input)) { - repeat = Some(value); - any = true; - continue - } - } - if attachment.is_none() { - if let Ok(value) = input.try(|input| background_attachment::parse(context, input)) { - attachment = Some(value); - any = true; - continue - } - } - if origin.is_none() { - if let Ok(value) = input.try(|input| background_origin::parse(context, input)) { - origin = Some(value); - any = true; - continue - } - } - if clip.is_none() { - if let Ok(value) = input.try(|input| background_clip::parse(context, input)) { - clip = Some(value); - any = true; - continue - } - } - break - } - - if any { - Ok(Longhands { - background_color: color, - background_image: image, - background_position: position, - background_repeat: repeat, - background_attachment: attachment, - background_size: size, - background_origin: origin, - background_clip: clip, - }) - } else { - Err(()) - } - } - - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self.background_color { - DeclaredValue::Value(ref color) => { - try!(color.to_css(dest)); - }, - _ => { - try!(write!(dest, "transparent")); - } - }; - - try!(write!(dest, " ")); - - match *self.background_image { - DeclaredValue::Value(ref image) => { - try!(image.to_css(dest)); - }, - _ => { - try!(write!(dest, "none")); - } - }; - - try!(write!(dest, " ")); - - try!(self.background_repeat.to_css(dest)); - - try!(write!(dest, " ")); - - match *self.background_attachment { - DeclaredValue::Value(ref attachment) => { - try!(attachment.to_css(dest)); - }, - _ => { - try!(write!(dest, "scroll")); - } - }; - - try!(write!(dest, " ")); - - try!(self.background_position.to_css(dest)); - - if let DeclaredValue::Value(ref size) = *self.background_size { - try!(write!(dest, " / ")); - try!(size.to_css(dest)); - } - - match (self.background_origin, self.background_clip) { - (&DeclaredValue::Value(ref origin), &DeclaredValue::Value(ref clip)) => { - use properties::longhands::background_origin::computed_value::T as Origin; - use properties::longhands::background_clip::computed_value::T as Clip; - - try!(write!(dest, " ")); - - match (origin, clip) { - (&Origin::padding_box, &Clip::padding_box) => { - try!(origin.to_css(dest)); - }, - (&Origin::border_box, &Clip::border_box) => { - try!(origin.to_css(dest)); - }, - (&Origin::content_box, &Clip::content_box) => { - try!(origin.to_css(dest)); - }, - _ => { - try!(origin.to_css(dest)); - try!(write!(dest, " ")); - try!(clip.to_css(dest)); + % for name in "image repeat attachment origin clip".split(): + if ${name}.is_none() { + if let Ok(value) = input.try(|input| background_${name}::single_value + ::parse(context, input)) { + ${name} = Some(value); + continue } } - }, - (&DeclaredValue::Value(ref origin), _) => { - try!(write!(dest, " ")); - try!(origin.to_css(dest)); - }, - (_, &DeclaredValue::Value(ref clip)) => { - try!(write!(dest, " ")); - try!(clip.to_css(dest)); - }, - _ => {} - }; + % endfor + break + } + let mut any = false; + % for name in "image position repeat size attachment origin clip".split(): + any = any || ${name}.is_some(); + % endfor + any = any || background_color.is_some(); + if any { + % for name in "image position repeat size attachment origin clip".split(): + if let Some(bg_${name}) = ${name} { + background_${name}.0.push(bg_${name}); + } else { + background_${name}.0.push(background_${name}::single_value + ::get_initial_specified_value()); + } + % endfor + Ok(()) + } else { + Err(()) + } + })); + + Ok(Longhands { + background_color: background_color, + background_image: Some(background_image), + background_position: Some(background_position), + background_repeat: Some(background_repeat), + background_attachment: Some(background_attachment), + background_size: Some(background_size), + background_origin: Some(background_origin), + background_clip: Some(background_clip), + }) + } + + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + // mako doesn't like ampersands following `<` + fn extract_value(x: &DeclaredValue) -> Option< &T> { + match *x { + DeclaredValue::Value(ref val) => Some(val), + _ => None, + } + } + use std::cmp; + let mut len = 0; + % for name in "image position repeat size attachment origin clip".split(): + len = cmp::max(len, extract_value(self.background_${name}).map(|i| i.0.len()) + .unwrap_or(0)); + % endfor + + // There should be at least one declared value + if len == 0 { + return dest.write_str("") + } + + let mut first = true; + for i in 0..len { + % for name in "image position repeat size attachment origin clip".split(): + let ${name} = if let DeclaredValue::Value(ref arr) = *self.background_${name} { + arr.0.get(i % arr.0.len()) + } else { + None + }; + % endfor + let color = if i == len - 1 { + Some(self.background_color) + } else { + None + }; + + if first { + first = false; + } else { + try!(write!(dest, ", ")); + } + match color { + Some(&DeclaredValue::Value(ref color)) => { + try!(color.to_css(dest)); + try!(write!(dest, " ")); + }, + Some(_) => { + try!(write!(dest, "transparent ")); + } + // Not yet the last one + None => () + }; + + + if let Some(image) = image { + try!(image.to_css(dest)); + } else { + try!(write!(dest, "none")); + } + + try!(write!(dest, " ")); + + if let Some(repeat) = repeat { + try!(repeat.to_css(dest)); + } else { + try!(write!(dest, "repeat")); + } + + try!(write!(dest, " ")); + + if let Some(attachment) = attachment { + try!(attachment.to_css(dest)); + } else { + try!(write!(dest, "scroll")); + } + + try!(write!(dest, " ")); + + try!(position.unwrap_or(&background_position::single_value + ::get_initial_specified_value()) + .to_css(dest)); + + if let Some(size) = size { + try!(write!(dest, " / ")); + try!(size.to_css(dest)); + } + + match (origin, clip) { + (Some(origin), Some(clip)) => { + use properties::longhands::background_origin::single_value::computed_value::T as Origin; + use properties::longhands::background_clip::single_value::computed_value::T as Clip; + + try!(write!(dest, " ")); + + match (origin, clip) { + (&Origin::padding_box, &Clip::padding_box) => { + try!(origin.to_css(dest)); + }, + (&Origin::border_box, &Clip::border_box) => { + try!(origin.to_css(dest)); + }, + (&Origin::content_box, &Clip::content_box) => { + try!(origin.to_css(dest)); + }, + _ => { + try!(origin.to_css(dest)); + try!(write!(dest, " ")); + try!(clip.to_css(dest)); + } + } + }, + (Some(origin), _) => { + try!(write!(dest, " ")); + try!(origin.to_css(dest)); + }, + (_, Some(clip)) => { + try!(write!(dest, " ")); + try!(clip.to_css(dest)); + }, + _ => {} + }; + } Ok(()) diff --git a/components/style/properties/shorthand/border.mako.rs b/components/style/properties/shorthand/border.mako.rs index f1d868b26cc..c29a6a2e7fb 100644 --- a/components/style/properties/shorthand/border.mako.rs +++ b/components/style/properties/shorthand/border.mako.rs @@ -25,8 +25,8 @@ ${helpers.four_sides_shorthand("border-style", "border-%s-style", }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { // extract tuple container values so that the different border widths // can be compared via partial eq % for side in ["top", "right", "bottom", "left"]: @@ -102,8 +102,8 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser) }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { super::serialize_directional_border( dest, self.border_${side}_width, @@ -134,8 +134,8 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser) }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { // If all longhands are all present, then all sides should be the same, // so we can just one set of color/style/width super::serialize_directional_border( @@ -170,8 +170,8 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser) // TODO: I do not understand how border radius works with respect to the slashes /, // so putting a default generic impl for now // https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { try!(self.border_top_left_radius.to_css(dest)); try!(write!(dest, " ")); diff --git a/components/style/properties/shorthand/box.mako.rs b/components/style/properties/shorthand/box.mako.rs index 0825a8fc314..2465a91ae8a 100644 --- a/components/style/properties/shorthand/box.mako.rs +++ b/components/style/properties/shorthand/box.mako.rs @@ -19,8 +19,8 @@ // Overflow does not behave like a normal shorthand. When overflow-x and overflow-y are not of equal // values, they no longer use the shared property name "overflow". // Other shorthands do not include their name in the to_css method - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { let x_and_y_equal = match (self.overflow_x, self.overflow_y) { (&DeclaredValue::Value(ref x_value), &DeclaredValue::Value(ref y_container)) => { *x_value == y_container.0 @@ -132,8 +132,8 @@ macro_rules! try_parse_one { }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { try!(self.transition_property.to_css(dest)); try!(write!(dest, " ")); @@ -268,8 +268,8 @@ macro_rules! try_parse_one { }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { try!(self.animation_duration.to_css(dest)); try!(write!(dest, " ")); diff --git a/components/style/properties/shorthand/column.mako.rs b/components/style/properties/shorthand/column.mako.rs index 563ef210fdf..e8ccf00bf47 100644 --- a/components/style/properties/shorthand/column.mako.rs +++ b/components/style/properties/shorthand/column.mako.rs @@ -48,8 +48,8 @@ } } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { try!(self.column_width.to_css(dest)); try!(write!(dest, " ")); diff --git a/components/style/properties/shorthand/font.mako.rs b/components/style/properties/shorthand/font.mako.rs index aae11eb3cf6..555b098e575 100644 --- a/components/style/properties/shorthand/font.mako.rs +++ b/components/style/properties/shorthand/font.mako.rs @@ -68,8 +68,8 @@ } // This may be a bit off, unsure, possibly needs changes - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { if let DeclaredValue::Value(ref style) = *self.font_style { try!(style.to_css(dest)); try!(write!(dest, " ")); diff --git a/components/style/properties/shorthand/inherited_text.mako.rs b/components/style/properties/shorthand/inherited_text.mako.rs index e7c19127a15..64ef03b1138 100644 --- a/components/style/properties/shorthand/inherited_text.mako.rs +++ b/components/style/properties/shorthand/inherited_text.mako.rs @@ -15,8 +15,8 @@ }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { self.overflow_wrap.to_css(dest) } } diff --git a/components/style/properties/shorthand/list.mako.rs b/components/style/properties/shorthand/list.mako.rs index 6e1abf337fe..955d93f7c16 100644 --- a/components/style/properties/shorthand/list.mako.rs +++ b/components/style/properties/shorthand/list.mako.rs @@ -92,8 +92,8 @@ } } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self.list_style_position { DeclaredValue::Initial => try!(write!(dest, "outside")), _ => try!(self.list_style_position.to_css(dest)) diff --git a/components/style/properties/shorthand/outline.mako.rs b/components/style/properties/shorthand/outline.mako.rs index c00294f45df..852fce2b292 100644 --- a/components/style/properties/shorthand/outline.mako.rs +++ b/components/style/properties/shorthand/outline.mako.rs @@ -49,8 +49,8 @@ } } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { try!(self.outline_width.to_css(dest)); try!(write!(dest, " ")); @@ -89,8 +89,8 @@ } // TODO: Border radius for the radius shorthand is not implemented correctly yet - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { try!(self._moz_outline_radius_topleft.to_css(dest)); try!(write!(dest, " ")); diff --git a/components/style/properties/shorthand/position.mako.rs b/components/style/properties/shorthand/position.mako.rs index 9fc33e00f42..5064c375215 100644 --- a/components/style/properties/shorthand/position.mako.rs +++ b/components/style/properties/shorthand/position.mako.rs @@ -38,8 +38,8 @@ } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self.flex_direction { DeclaredValue::Initial => try!(write!(dest, "row")), _ => try!(self.flex_direction.to_css(dest)) @@ -107,8 +107,8 @@ }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { try!(self.flex_grow.to_css(dest)); try!(write!(dest, " ")); diff --git a/components/style/properties/shorthand/text.mako.rs b/components/style/properties/shorthand/text.mako.rs index 2bcfbfcd7b9..d09e00b6ba4 100644 --- a/components/style/properties/shorthand/text.mako.rs +++ b/components/style/properties/shorthand/text.mako.rs @@ -46,8 +46,8 @@ }) } - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { match *self.text_decoration_line { DeclaredValue::Value(ref line) => { try!(line.to_css(dest)); diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 1045d09c615..345083adbf6 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -2119,6 +2119,7 @@ dependencies = [ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", diff --git a/ports/geckolib/Cargo.lock b/ports/geckolib/Cargo.lock index d29ee9a5347..69baaa7d7d3 100644 --- a/ports/geckolib/Cargo.lock +++ b/ports/geckolib/Cargo.lock @@ -234,6 +234,14 @@ dependencies = [ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-integer" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.1.35" @@ -363,6 +371,7 @@ dependencies = [ "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "quickersort 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/geckolib/gecko_bindings/bindings.rs b/ports/geckolib/gecko_bindings/bindings.rs index 78b5d89f25c..35b55f01cf3 100644 --- a/ports/geckolib/gecko_bindings/bindings.rs +++ b/ports/geckolib/gecko_bindings/bindings.rs @@ -335,6 +335,8 @@ extern "C" { pub fn Gecko_DestroyClipPath(clip: *mut StyleClipPath); pub fn Gecko_NewBasicShape(type_: StyleBasicShapeType) -> *mut StyleBasicShape; + pub fn Gecko_FillAllBackgroundLists(layers: *mut nsStyleImageLayers, + maxLen: u32); pub fn Gecko_AddRefCalcArbitraryThread(aPtr: *mut Calc); pub fn Gecko_ReleaseCalcArbitraryThread(aPtr: *mut Calc); pub fn Gecko_Construct_nsStyleFont(ptr: *mut nsStyleFont); diff --git a/ports/geckolib/gecko_bindings/sugar/ns_style_auto_array.rs b/ports/geckolib/gecko_bindings/sugar/ns_style_auto_array.rs index 43728fd979d..b81aa51df52 100644 --- a/ports/geckolib/gecko_bindings/sugar/ns_style_auto_array.rs +++ b/ports/geckolib/gecko_bindings/sugar/ns_style_auto_array.rs @@ -3,13 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::iter::{once, Chain, Once, IntoIterator}; -use std::slice::IterMut; +use std::slice::{Iter, IterMut}; use structs::nsStyleAutoArray; impl nsStyleAutoArray { pub fn iter_mut(&mut self) -> Chain, IterMut> { once(&mut self.mFirstElement).chain(self.mOtherElements.iter_mut()) } + pub fn iter(&self) -> Chain, Iter> { + once(&self.mFirstElement).chain(self.mOtherElements.iter()) + } // Note that often structs containing autoarrays will have // additional member fields that contain the length, which must be kept diff --git a/tests/unit/style/properties/serialization.rs b/tests/unit/style/properties/serialization.rs index 61e49221c9a..7187c92b9f8 100644 --- a/tests/unit/style/properties/serialization.rs +++ b/tests/unit/style/properties/serialization.rs @@ -679,18 +679,44 @@ mod shorthand_serialization { */ mod background { - use style::properties::longhands::background_attachment::computed_value::T as Attachment; - use style::properties::longhands::background_clip::computed_value::T as Clip; - use style::properties::longhands::background_image::SpecifiedValue as ImageContainer; - use style::properties::longhands::background_origin::computed_value::T as Origin; - use style::properties::longhands::background_position::SpecifiedValue as PositionContainer; - use style::properties::longhands::background_repeat::computed_value::T as Repeat; - use style::properties::longhands::background_size::SpecifiedExplicitSize; - use style::properties::longhands::background_size::SpecifiedValue as Size; + use style::properties::longhands::background_attachment as attachment; + use style::properties::longhands::background_clip as clip; + use style::properties::longhands::background_image as image; + use style::properties::longhands::background_origin as origin; + use style::properties::longhands::background_position as position; + use style::properties::longhands::background_repeat as repeat; + use style::properties::longhands::background_size as size; use style::values::specified::Image; use style::values::specified::position::Position; use super::*; - + macro_rules! single_vec_value_typedef { + ($name:ident, $path:expr) => { + DeclaredValue::Value($name::SpecifiedValue( + vec![$path] + )) + }; + } + macro_rules! single_vec_value { + ($name:ident, $path:expr) => { + DeclaredValue::Value($name::SpecifiedValue( + vec![$name::single_value::SpecifiedValue($path)] + )) + }; + } + macro_rules! single_vec_keyword_value { + ($name:ident, $kw:ident) => { + DeclaredValue::Value($name::SpecifiedValue( + vec![$name::single_value::SpecifiedValue::$kw] + )) + }; + } + macro_rules! single_vec_variant_value { + ($name:ident, $variant:expr) => { + DeclaredValue::Value($name::SpecifiedValue( + vec![$variant] + )) + }; + } #[test] fn background_should_serialize_all_available_properties_when_specified() { let mut properties = Vec::new(); @@ -700,29 +726,31 @@ mod shorthand_serialization { authored: None }); - let position = DeclaredValue::Value( + let position = single_vec_value_typedef!(position, Position { horizontal: LengthOrPercentage::Length(Length::from_px(7f32)), vertical: LengthOrPercentage::Length(Length::from_px(4f32)) } ); - let repeat = DeclaredValue::Value(Repeat::repeat_x); - let attachment = DeclaredValue::Value(Attachment::scroll); + let repeat = single_vec_keyword_value!(repeat, repeat_x); + let attachment = single_vec_keyword_value!(attachment, scroll); - let image = DeclaredValue::Value(ImageContainer( - Some(Image::Url(Url::parse("http://servo/test.png").unwrap(), UrlExtraData {})) - )); + let image = single_vec_value!(image, + Some(Image::Url(Url::parse("http://servo/test.png").unwrap(), + UrlExtraData {}))); - let size = DeclaredValue::Value( - Size::Explicit(SpecifiedExplicitSize { - width: LengthOrPercentageOrAuto::Length(Length::from_px(70f32)), - height: LengthOrPercentageOrAuto::Length(Length::from_px(50f32)) - } - )); + let size = single_vec_variant_value!(size, + size::single_value::SpecifiedValue::Explicit( + size::single_value::SpecifiedExplicitSize { + width: LengthOrPercentageOrAuto::Length(Length::from_px(70f32)), + height: LengthOrPercentageOrAuto::Length(Length::from_px(50f32)) + } + ) + ); - let origin = DeclaredValue::Value(Origin::border_box); - let clip = DeclaredValue::Value(Clip::padding_box); + let origin = single_vec_keyword_value!(origin, border_box); + let clip = single_vec_keyword_value!(clip, padding_box); properties.push(PropertyDeclaration::BackgroundColor(color)); properties.push(PropertyDeclaration::BackgroundPosition(position)); @@ -751,29 +779,31 @@ mod shorthand_serialization { authored: None }); - let position = DeclaredValue::Value( + let position = single_vec_value_typedef!(position, Position { horizontal: LengthOrPercentage::Length(Length::from_px(7f32)), vertical: LengthOrPercentage::Length(Length::from_px(4f32)) } ); - let repeat = DeclaredValue::Value(Repeat::repeat_x); - let attachment = DeclaredValue::Value(Attachment::scroll); + let repeat = single_vec_keyword_value!(repeat, repeat_x); + let attachment = single_vec_keyword_value!(attachment, scroll); - let image = DeclaredValue::Value(ImageContainer( - Some(Image::Url(Url::parse("http://servo/test.png").unwrap(), UrlExtraData {})) - )); + let image = single_vec_value!(image, + Some(Image::Url(Url::parse("http://servo/test.png").unwrap(), + UrlExtraData {}))); - let size = DeclaredValue::Value( - Size::Explicit(SpecifiedExplicitSize { - width: LengthOrPercentageOrAuto::Length(Length::from_px(70f32)), - height: LengthOrPercentageOrAuto::Length(Length::from_px(50f32)) - }) + let size = single_vec_variant_value!(size, + size::single_value::SpecifiedValue::Explicit( + size::single_value::SpecifiedExplicitSize { + width: LengthOrPercentageOrAuto::Length(Length::from_px(70f32)), + height: LengthOrPercentageOrAuto::Length(Length::from_px(50f32)) + } + ) ); - let origin = DeclaredValue::Value(Origin::padding_box); - let clip = DeclaredValue::Value(Clip::padding_box); + let origin = single_vec_keyword_value!(origin, padding_box); + let clip = single_vec_keyword_value!(clip, padding_box); properties.push(PropertyDeclaration::BackgroundColor(color)); properties.push(PropertyDeclaration::BackgroundPosition(position)); @@ -801,17 +831,17 @@ mod shorthand_serialization { authored: None }); - let position = DeclaredValue::Value( + let position = single_vec_value_typedef!(position, Position { horizontal: LengthOrPercentage::Length(Length::from_px(0f32)), vertical: LengthOrPercentage::Length(Length::from_px(0f32)) } ); - let repeat = DeclaredValue::Value(Repeat::repeat_x); - let attachment = DeclaredValue::Value(Attachment::scroll); + let repeat = single_vec_keyword_value!(repeat, repeat_x); + let attachment = single_vec_keyword_value!(attachment, scroll); - let image = DeclaredValue::Value(ImageContainer(None)); + let image = single_vec_value!(image, None); let size = DeclaredValue::Initial; diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs index 68e0b8e9577..528dcd67084 100644 --- a/tests/unit/style/stylesheets.rs +++ b/tests/unit/style/stylesheets.rs @@ -191,19 +191,40 @@ fn test_parse_stylesheet() { } )), Importance::Normal), - (PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial), + (PropertyDeclaration::BackgroundPosition(DeclaredValue::Value( + longhands::background_position::SpecifiedValue( + vec![longhands::background_position::single_value + ::get_initial_specified_value()]))), Importance::Normal), - (PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial), + (PropertyDeclaration::BackgroundRepeat(DeclaredValue::Value( + longhands::background_repeat::SpecifiedValue( + vec![longhands::background_repeat::single_value + ::get_initial_specified_value()]))), Importance::Normal), - (PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial), + (PropertyDeclaration::BackgroundAttachment(DeclaredValue::Value( + longhands::background_attachment::SpecifiedValue( + vec![longhands::background_attachment::single_value + ::get_initial_specified_value()]))), Importance::Normal), - (PropertyDeclaration::BackgroundImage(DeclaredValue::Initial), + (PropertyDeclaration::BackgroundImage(DeclaredValue::Value( + longhands::background_image::SpecifiedValue( + vec![longhands::background_image::single_value + ::get_initial_specified_value()]))), Importance::Normal), - (PropertyDeclaration::BackgroundSize(DeclaredValue::Initial), + (PropertyDeclaration::BackgroundSize(DeclaredValue::Value( + longhands::background_size::SpecifiedValue( + vec![longhands::background_size::single_value + ::get_initial_specified_value()]))), Importance::Normal), - (PropertyDeclaration::BackgroundOrigin(DeclaredValue::Initial), + (PropertyDeclaration::BackgroundOrigin(DeclaredValue::Value( + longhands::background_origin::SpecifiedValue( + vec![longhands::background_origin::single_value + ::get_initial_specified_value()]))), Importance::Normal), - (PropertyDeclaration::BackgroundClip(DeclaredValue::Initial), + (PropertyDeclaration::BackgroundClip(DeclaredValue::Value( + longhands::background_clip::SpecifiedValue( + vec![longhands::background_clip::single_value + ::get_initial_specified_value()]))), Importance::Normal), ]), important_count: 0, diff --git a/tests/wpt/metadata-css/cssom-1_dev/html/cssom-setProperty-shorthand.htm.ini b/tests/wpt/metadata-css/cssom-1_dev/html/cssom-setProperty-shorthand.htm.ini deleted file mode 100644 index 0107a131efa..00000000000 --- a/tests/wpt/metadata-css/cssom-1_dev/html/cssom-setProperty-shorthand.htm.ini +++ /dev/null @@ -1,35 +0,0 @@ -[cssom-setProperty-shorthand.htm] - type: testharness - [shorthand font can be set with setProperty] - expected: FAIL - - [shorthand border-top can be set with setProperty] - expected: FAIL - - [shorthand border-right can be set with setProperty] - expected: FAIL - - [shorthand border-bottom can be set with setProperty] - expected: FAIL - - [shorthand border-left can be set with setProperty] - expected: FAIL - - [shorthand border can be set with setProperty] - expected: FAIL - - [shorthand list-style can be set with setProperty] - expected: FAIL - - [shorthand outline can be set with setProperty] - expected: FAIL - - [shorthand background can be set with setProperty] - expected: FAIL - - [shorthand overflow can be set with setProperty] - expected: FAIL - - [shorthand border-radius can be set with setProperty] - expected: FAIL - diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index ee90a1be5d4..0546548d847 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -3564,6 +3564,30 @@ "url": "/_mozilla/css/mix_blend_mode_a.html" } ], + "css/multiple_backgrounds.html": [ + { + "path": "css/multiple_backgrounds.html", + "references": [ + [ + "/_mozilla/css/multiple_backgrounds_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/multiple_backgrounds.html" + } + ], + "css/multiple_backgrounds_ref.html": [ + { + "path": "css/multiple_backgrounds_ref.html", + "references": [ + [ + "/_mozilla/css/multiple_backgrounds_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/multiple_backgrounds_ref.html" + } + ], "css/multiple_css_class_a.html": [ { "path": "css/multiple_css_class_a.html", @@ -12832,6 +12856,30 @@ "url": "/_mozilla/css/mix_blend_mode_a.html" } ], + "css/multiple_backgrounds.html": [ + { + "path": "css/multiple_backgrounds.html", + "references": [ + [ + "/_mozilla/css/multiple_backgrounds_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/multiple_backgrounds.html" + } + ], + "css/multiple_backgrounds_ref.html": [ + { + "path": "css/multiple_backgrounds_ref.html", + "references": [ + [ + "/_mozilla/css/multiple_backgrounds_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/multiple_backgrounds_ref.html" + } + ], "css/multiple_css_class_a.html": [ { "path": "css/multiple_css_class_a.html", diff --git a/tests/wpt/mozilla/tests/css/bubbles.png b/tests/wpt/mozilla/tests/css/bubbles.png new file mode 100644 index 00000000000..dbd4db86005 Binary files /dev/null and b/tests/wpt/mozilla/tests/css/bubbles.png differ diff --git a/tests/wpt/mozilla/tests/css/multiple_backgrounds.html b/tests/wpt/mozilla/tests/css/multiple_backgrounds.html new file mode 100644 index 00000000000..29922d60454 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/multiple_backgrounds.html @@ -0,0 +1,31 @@ + + + + + + + + + +
+
+ + diff --git a/tests/wpt/mozilla/tests/css/multiple_backgrounds_ref.html b/tests/wpt/mozilla/tests/css/multiple_backgrounds_ref.html new file mode 100644 index 00000000000..d3af6fe5881 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/multiple_backgrounds_ref.html @@ -0,0 +1,47 @@ + + + + + + + + + +
+
+
+
+
+
+ + diff --git a/tests/wpt/mozilla/tests/css/rust-logo-256x256.png b/tests/wpt/mozilla/tests/css/rust-logo-256x256.png new file mode 100644 index 00000000000..63506dd85ef Binary files /dev/null and b/tests/wpt/mozilla/tests/css/rust-logo-256x256.png differ