Auto merge of #12945 - Manishearth:bgarray, r=SimonSapin

Support multiple backgrounds in both servo and stylo

(Commits do not build individually, but split up for review)

These patches make all of the background- properties accept multiple values, and add the layout code to display them.

Still needs some cleanup, and some testing, but it seems to work.

r? @SimonSapin

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12945)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-08-22 22:09:23 -05:00 committed by GitHub
commit c5e81f8361
32 changed files with 806 additions and 440 deletions

View file

@ -60,6 +60,10 @@ use table_cell::CollapsedBordersForCell;
use url::Url;
use util::opts;
fn get_cyclic<T>(arr: &[T], index: usize) -> &T {
&arr[index % arr.len()]
}
pub struct DisplayListBuildState<'a> {
pub layout_context: &'a LayoutContext<'a>,
pub items: Vec<DisplayItem>,
@ -146,7 +150,7 @@ pub trait FragmentDisplayListBuilding {
fn compute_background_image_size(&self,
style: &ServoComputedValues,
bounds: &Rect<Au>,
image: &WebRenderImageInfo)
image: &WebRenderImageInfo, index: usize)
-> Size2D<Au>;
/// 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<Au>,
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<Au>,
image: &WebRenderImageInfo)
image: &WebRenderImageInfo,
index: usize)
-> Size2D<Au> {
// 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<Au>,
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(),
}));
}
}

View file

@ -362,8 +362,11 @@ impl LayoutElementHelpers for LayoutJS<Element> {
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::<HTMLFontElement>() {

View file

@ -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",

View file

@ -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"

View file

@ -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;

View file

@ -937,6 +937,32 @@ fn static_assert() {
</%self:impl_trait>
<%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()}
};
}
}
</%def>
// 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:simple_background_array_property>
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,
};
}
}
</%self:simple_background_array_property>
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,
};
}
}
</%self:simple_background_array_property>
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,
};
}
}
</%self:simple_background_array_property>
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>
<%self:impl_trait style_struct_name="List" skip_longhands="list-style-type" skip_additionals="*">

View file

@ -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<single_value::computed_value::T>);
pub struct T(pub Vec<single_value::T>);
}
impl ToCss for computed_value::T {
@ -285,8 +285,8 @@
}
</%def>
<%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 @@
</%call>
</%def>
<%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<SpecifiedValue, ()> {
computed_value::T::parse(input)
}
</%call>
</%def>
% if vector:
<%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
${inner_body()}
${caller.body()}
</%call>
% else:
<%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
${inner_body()}
${caller.body()}
</%call>
% endif
</%def>
<%def name="keyword_list(name, values, **kwargs)">
@ -464,6 +479,36 @@
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&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<PropertyDeclaration>)
@ -526,8 +571,8 @@
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
super::serialize_four_sides(
dest,
self.${to_rust_ident(sub_property_pattern % 'top')},

View file

@ -155,6 +155,18 @@ pub trait Interpolate: Sized {
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()>;
}
/// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
pub trait RepeatableListInterpolate: Interpolate {}
impl<T: RepeatableListInterpolate> Interpolate for Vec<T> {
fn interpolate(&self, other: &Self, time: f64) -> Result<Self, ()> {
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, ()> {
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<Self, ()> {
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))))
}
}

View file

@ -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<SpecifiedValue, ()> {
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:vector_longhand>
<%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<SpecifiedValue, ()> {
Ok(try!(Position::parse(input)))
}
</%helpers:longhand>
</%helpers:vector_longhand>
${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<Self, ()> {
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<SpecifiedValue,()> {
let width;
@ -274,4 +316,4 @@ ${helpers.single_keyword("background-origin",
height: height,
}))
}
</%helpers:longhand>
</%helpers:vector_longhand>

View file

@ -2034,6 +2034,10 @@ pub fn cascade(viewport_size: Size2D<Au>,
}
% 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() {

View file

@ -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<Longhands, ()> {
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<W>(&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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
// mako doesn't like ampersands following `<`
fn extract_value<T>(x: &DeclaredValue<T>) -> 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(())

View file

@ -25,8 +25,8 @@ ${helpers.four_sides_shorthand("border-style", "border-%s-style",
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.border_top_left_radius.to_css(dest));
try!(write!(dest, " "));

View file

@ -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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.animation_duration.to_css(dest));
try!(write!(dest, " "));

View file

@ -48,8 +48,8 @@
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.column_width.to_css(dest));
try!(write!(dest, " "));

View file

@ -68,8 +68,8 @@
}
// This may be a bit off, unsure, possibly needs changes
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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, " "));

View file

@ -15,8 +15,8 @@
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.overflow_wrap.to_css(dest)
}
}

View file

@ -92,8 +92,8 @@
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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))

View file

@ -49,8 +49,8 @@
}
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self._moz_outline_radius_topleft.to_css(dest));
try!(write!(dest, " "));

View file

@ -38,8 +38,8 @@
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&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<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
try!(self.flex_grow.to_css(dest));
try!(write!(dest, " "));

View file

@ -46,8 +46,8 @@
})
}
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
impl<'a> LonghandsToSerialize<'a> {
fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self.text_decoration_line {
DeclaredValue::Value(ref line) => {
try!(line.to_css(dest));

1
ports/cef/Cargo.lock generated
View file

@ -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",

View file

@ -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)",

View file

@ -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);

View file

@ -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<T> nsStyleAutoArray<T> {
pub fn iter_mut(&mut self) -> Chain<Once<&mut T>, IterMut<T>> {
once(&mut self.mFirstElement).chain(self.mOtherElements.iter_mut())
}
pub fn iter(&self) -> Chain<Once<&T>, Iter<T>> {
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

View file

@ -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;

View file

@ -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,

View file

@ -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

View file

@ -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",

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View file

@ -0,0 +1,31 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="match" href="multiple_backgrounds_ref.html">
<style type="text/css">
/* Example from
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Background_and_Borders/Using_CSS_multiple_backgrounds
(Public domain)
bubbles.png is from the above MDN page, by Mozilla contributors,
licensed under CC-BY-SA 2.5
Rust logo from https://www.rust-lang.org/logos/rust-logo-256x256.png,
licensed as CC-BY-SA 4.0, owned by Mozilla
*/
#multibg {
width: 700px;
height: 400px;
background: url(rust-logo-256x256.png) no-repeat bottom right / 256px 256px,
url(bubbles.png) no-repeat left / 700px 100%,
linear-gradient(to right, rgba(30, 75, 115, 1), rgba(255, 255, 255, 0)) no-repeat;
}
</style>
</head>
<body>
<div id="multibg">
</div>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="match" href="multiple_backgrounds_ref.html">
<style type="text/css">
/* Example from
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Background_and_Borders/Using_CSS_multiple_backgrounds
(Public domain)
bubbles.png is from the above MDN page, by Mozilla contributors,
licensed under CC-BY-SA 2.5
Rust logo from https://www.rust-lang.org/logos/rust-logo-256x256.png,
licensed as CC-BY-SA 4.0, owned by Mozilla
*/
#gradientbg {
width: 700px;
height: 400px;
background: linear-gradient(to right, rgba(30, 75, 115, 1), rgba(255, 255, 255, 0)) no-repeat;
}
#bubblesbg {
width: 100%;
height: 100%;
background: url(bubbles.png) no-repeat left / 700px 100%;
position: relative;
}
#rustbg {
width: 256px;
height: 256px;
background: url(rust-logo-256x256.png) no-repeat left / 256px 256px;
position: absolute;
bottom: 0;
right: 0;
}
</style>
</head>
<body>
<div id="gradientbg">
<div id="bubblesbg">
<div id="rustbg">
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB