servo: Merge #15697 - Add support for most of the border-image properties, pass to WR (from glennw:border-image); r=jdm,pcwalton

This adds support for:
 * border-image (images, not gradients yet)
 * border-image-source
 * border-image-slice
 * border-image-repeat (stretch + repeat only for now)

Remaining work:
 * Connect border-image-outset (WR supports this).
 * border-image-width

Source-Repo: https://github.com/servo/servo
Source-Revision: 6c9e94b1c07dd148430d175e79c595db82dea742

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 1266344fb19194b3b2500700fb636c577a2d5366
This commit is contained in:
Glenn Watson 2017-02-23 13:40:40 -08:00
parent 5f6e848e09
commit 9e303bf129
6 changed files with 231 additions and 74 deletions

View File

@ -891,15 +891,9 @@ pub struct GradientDisplayItem {
pub stops: Vec<GradientStop>,
}
/// Paints a border.
/// A normal border, supporting CSS border styles.
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct BorderDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
/// Border widths.
pub border_widths: SideOffsets2D<Au>,
pub struct NormalBorder {
/// Border colors.
pub color: SideOffsets2D<ColorF>,
@ -912,6 +906,50 @@ pub struct BorderDisplayItem {
pub radius: BorderRadii<Au>,
}
/// A border that is made of image segments.
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct ImageBorder {
/// The image this border uses, border-image-source.
pub image: WebRenderImageInfo,
/// How to slice the image, as per border-image-slice.
pub slice: SideOffsets2D<u32>,
/// Outsets for the border, as per border-image-outset.
pub outset: SideOffsets2D<f32>,
/// If fill is true, draw the center patch of the image.
pub fill: bool,
/// How to repeat or stretch horizontal edges (border-image-repeat).
#[ignore_heap_size_of = "WebRender traits type, and tiny"]
pub repeat_horizontal: webrender_traits::RepeatMode,
/// How to repeat or stretch vertical edges (border-image-repeat).
#[ignore_heap_size_of = "WebRender traits type, and tiny"]
pub repeat_vertical: webrender_traits::RepeatMode,
}
/// Specifies the type of border
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub enum BorderDetails {
Normal(NormalBorder),
Image(ImageBorder),
}
/// Paints a border.
#[derive(Clone, HeapSizeOf, Deserialize, Serialize)]
pub struct BorderDisplayItem {
/// Fields common to all display items.
pub base: BaseDisplayItem,
/// Border widths.
pub border_widths: SideOffsets2D<Au>,
/// Details for specific border type
pub details: BorderDetails,
}
/// Information about the border radii.
///
/// TODO(pcwalton): Elliptical radii.

View File

@ -20,7 +20,8 @@ use flow::{BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED};
use flow_ref::FlowRef;
use fragment::{CoordinateSystem, Fragment, ImageFragmentInfo, ScannedTextFragmentInfo};
use fragment::{SpecificFragmentInfo, TruncatedFragmentInfo};
use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDetails};
use gfx::display_list::{BorderDisplayItem, ImageBorder, NormalBorder};
use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection};
use gfx::display_list::{GradientDisplayItem, IframeDisplayItem, ImageDisplayItem};
@ -45,13 +46,14 @@ use std::mem;
use std::sync::Arc;
use style::computed_values::{background_attachment, background_clip, background_origin};
use style::computed_values::{background_repeat, background_size, border_style};
use style::computed_values::{cursor, image_rendering, overflow_x};
use style::computed_values::{cursor, image_rendering, overflow_x, border_image_slice};
use style::computed_values::{pointer_events, position, transform_style, visibility};
use style::computed_values::_servo_overflow_clip_box as overflow_clip_box;
use style::computed_values::filter::Filter;
use style::computed_values::text_shadow::TextShadow;
use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use style::properties::{self, ServoComputedValues};
use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
use style::properties::style_structs;
use style::servo::restyle_damage::REPAINT;
use style::values::{RGBA, computed};
@ -60,7 +62,33 @@ use style::values::specified::{HorizontalDirection, VerticalDirection};
use style_traits::PagePx;
use style_traits::cursor::Cursor;
use table_cell::CollapsedBordersForCell;
use webrender_traits::{ColorF, GradientStop, ScrollPolicy};
use webrender_traits::{ColorF, GradientStop, RepeatMode, ScrollPolicy};
trait ResolvePercentage {
fn resolve(&self, length: u32) -> u32;
}
impl ResolvePercentage for border_image_slice::PercentageOrNumber {
fn resolve(&self, length: u32) -> u32 {
match *self {
border_image_slice::PercentageOrNumber::Percentage(p) => {
(p.0 * length as f32).round() as u32
}
border_image_slice::PercentageOrNumber::Number(n) => {
n.round() as u32
}
}
}
}
fn convert_repeat_mode(from: RepeatKeyword) -> RepeatMode {
match from {
RepeatKeyword::Stretch => RepeatMode::Stretch,
RepeatKeyword::Repeat => RepeatMode::Repeat,
RepeatKeyword::Round => RepeatMode::Round,
RepeatKeyword::Space => RepeatMode::Space,
}
}
trait RgbColor {
fn rgb(r: u8, g: u8, b: u8) -> Self;
@ -1056,16 +1084,55 @@ impl FragmentDisplayListBuilding for Fragment {
self.node,
style.get_cursor(Cursor::Default),
display_list_section);
state.add_display_item(DisplayItem::Border(box BorderDisplayItem {
base: base,
border_widths: border.to_physical(style.writing_mode),
color: SideOffsets2D::new(colors.top.to_gfx_color(),
colors.right.to_gfx_color(),
colors.bottom.to_gfx_color(),
colors.left.to_gfx_color()),
style: border_style,
radius: build_border_radius(&bounds, border_style_struct),
}));
match border_style_struct.border_image_source.0 {
None => {
state.add_display_item(DisplayItem::Border(box BorderDisplayItem {
base: base,
border_widths: border.to_physical(style.writing_mode),
details: BorderDetails::Normal(NormalBorder {
color: SideOffsets2D::new(colors.top.to_gfx_color(),
colors.right.to_gfx_color(),
colors.bottom.to_gfx_color(),
colors.left.to_gfx_color()),
style: border_style,
radius: build_border_radius(&bounds, border_style_struct),
}),
}));
}
Some(computed::Image::Gradient(..)) => {
// TODO(gw): Handle border-image with gradient.
}
Some(computed::Image::Url(ref image_url)) => {
if let Some(url) = image_url.url() {
let webrender_image = state.layout_context
.get_webrender_image_for_url(self.node,
url.clone(),
UsePlaceholder::No);
if let Some(webrender_image) = webrender_image {
// The corners array is guaranteed to be len=4 by the css parser.
let corners = &border_style_struct.border_image_slice.corners;
state.add_display_item(DisplayItem::Border(box BorderDisplayItem {
base: base,
border_widths: border.to_physical(style.writing_mode),
details: BorderDetails::Image(ImageBorder {
image: webrender_image,
fill: border_style_struct.border_image_slice.fill,
slice: SideOffsets2D::new(corners[0].resolve(webrender_image.height),
corners[1].resolve(webrender_image.width),
corners[2].resolve(webrender_image.height),
corners[3].resolve(webrender_image.width)),
// TODO(gw): Support border-image-outset
outset: SideOffsets2D::zero(),
repeat_horizontal: convert_repeat_mode(border_style_struct.border_image_repeat.0),
repeat_vertical: convert_repeat_mode(border_style_struct.border_image_repeat.1),
}),
}));
}
}
}
}
}
fn build_display_list_for_outline_if_applicable(&self,
@ -1105,9 +1172,11 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(DisplayItem::Border(box BorderDisplayItem {
base: base,
border_widths: SideOffsets2D::new_all_same(width),
color: SideOffsets2D::new_all_same(color),
style: SideOffsets2D::new_all_same(outline_style),
radius: Default::default(),
details: BorderDetails::Normal(NormalBorder {
color: SideOffsets2D::new_all_same(color),
style: SideOffsets2D::new_all_same(outline_style),
radius: Default::default(),
}),
}));
}
@ -1130,9 +1199,11 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(DisplayItem::Border(box BorderDisplayItem {
base: base,
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
color: SideOffsets2D::new_all_same(ColorF::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::T::solid),
radius: Default::default(),
details: BorderDetails::Normal(NormalBorder {
color: SideOffsets2D::new_all_same(ColorF::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::T::solid),
radius: Default::default(),
}),
}));
// Draw a rectangle representing the baselines.
@ -1168,9 +1239,11 @@ impl FragmentDisplayListBuilding for Fragment {
state.add_display_item(DisplayItem::Border(box BorderDisplayItem {
base: base,
border_widths: SideOffsets2D::new_all_same(Au::from_px(1)),
color: SideOffsets2D::new_all_same(ColorF::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::T::solid),
radius: Default::default(),
details: BorderDetails::Normal(NormalBorder {
color: SideOffsets2D::new_all_same(ColorF::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::T::solid),
radius: Default::default(),
}),
}));
}
@ -2129,9 +2202,11 @@ impl BaseFlowDisplayListBuilding for BaseFlow {
state.add_display_item(DisplayItem::Border(box BorderDisplayItem {
base: base,
border_widths: SideOffsets2D::new_all_same(Au::from_px(2)),
color: SideOffsets2D::new_all_same(color),
style: SideOffsets2D::new_all_same(border_style::T::solid),
radius: BorderRadii::all_same(Au(0)),
details: BorderDetails::Normal(NormalBorder {
color: SideOffsets2D::new_all_same(color),
style: SideOffsets2D::new_all_same(border_style::T::solid),
radius: BorderRadii::all_same(Au(0)),
}),
}));
}
}

View File

@ -8,8 +8,8 @@
// completely converting layout to directly generate WebRender display lists, for example.
use app_units::Au;
use euclid::{Point2D, Rect, Size2D};
use gfx::display_list::{BorderRadii, BoxShadowClipMode, ClippingRegion};
use euclid::{Point2D, Rect, SideOffsets2D, Size2D};
use gfx::display_list::{BorderDetails, BorderRadii, BoxShadowClipMode, ClippingRegion};
use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal, StackingContextType};
use gfx_traits::{FragmentType, ScrollRootId};
use msg::constellation_msg::PipelineId;
@ -46,6 +46,22 @@ impl ToBorderStyle for BorderStyle {
}
}
}
trait ToBorderWidths {
fn to_border_widths(&self) -> webrender_traits::BorderWidths;
}
impl ToBorderWidths for SideOffsets2D<Au> {
fn to_border_widths(&self) -> webrender_traits::BorderWidths {
webrender_traits::BorderWidths {
left: self.left.to_f32_px(),
top: self.top.to_f32_px(),
right: self.right.to_f32_px(),
bottom: self.bottom.to_f32_px(),
}
}
}
trait ToBoxShadowClipMode {
fn to_clip_mode(&self) -> webrender_traits::BoxShadowClipMode;
}
@ -270,41 +286,57 @@ impl WebRenderDisplayItemConverter for DisplayItem {
}
DisplayItem::Border(ref item) => {
let rect = item.base.bounds.to_rectf();
let widths = webrender_traits::BorderWidths {
left: item.border_widths.left.to_f32_px(),
top: item.border_widths.top.to_f32_px(),
right: item.border_widths.right.to_f32_px(),
bottom: item.border_widths.bottom.to_f32_px(),
};
let left = webrender_traits::BorderSide {
color: item.color.left,
style: item.style.left.to_border_style(),
};
let top = webrender_traits::BorderSide {
color: item.color.top,
style: item.style.top.to_border_style(),
};
let right = webrender_traits::BorderSide {
color: item.color.right,
style: item.style.right.to_border_style(),
};
let bottom = webrender_traits::BorderSide {
color: item.color.bottom,
style: item.style.bottom.to_border_style(),
};
let radius = item.radius.to_border_radius();
let widths = item.border_widths.to_border_widths();
let clip = item.base.clip.to_clip_region(builder);
let details = webrender_traits::NormalBorder {
left: left,
top: top,
right: right,
bottom: bottom,
radius: radius,
let details = match item.details {
BorderDetails::Normal(ref border) => {
let left = webrender_traits::BorderSide {
color: border.color.left,
style: border.style.left.to_border_style(),
};
let top = webrender_traits::BorderSide {
color: border.color.top,
style: border.style.top.to_border_style(),
};
let right = webrender_traits::BorderSide {
color: border.color.right,
style: border.style.right.to_border_style(),
};
let bottom = webrender_traits::BorderSide {
color: border.color.bottom,
style: border.style.bottom.to_border_style(),
};
let radius = border.radius.to_border_radius();
webrender_traits::BorderDetails::Normal(webrender_traits::NormalBorder {
left: left,
top: top,
right: right,
bottom: bottom,
radius: radius,
})
}
BorderDetails::Image(ref image) => {
match image.image.key {
None => return,
Some(key) => {
webrender_traits::BorderDetails::Image(webrender_traits::ImageBorder {
image_key: key,
patch: webrender_traits::NinePatchDescriptor {
width: image.image.width,
height: image.image.height,
slice: image.slice,
},
outset: image.outset,
repeat_horizontal: image.repeat_horizontal,
repeat_vertical: image.repeat_vertical,
})
}
}
}
};
builder.push_border(rect,
clip,
widths,
webrender_traits::BorderDetails::Normal(details));
builder.push_border(rect, clip, widths, details);
}
DisplayItem::Gradient(ref item) => {
let rect = item.base.bounds.to_rectf();

View File

@ -106,6 +106,18 @@ partial interface CSSStyleDeclaration {
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top-style;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderTopWidth;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-top-width;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-image-source;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderImageSource;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-image-slice;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderImageSlice;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-image-repeat;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderImageRepeat;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-image-outset;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderImageOutset;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-image-width;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderImageWidth;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-image;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderImage;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString border-block-start-color;
[SetterThrows, TreatNullAs=EmptyString] attribute DOMString borderBlockStartColor;

View File

@ -86,7 +86,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)",
animatable=False)}
<%helpers:longhand name="border-image-source" products="gecko" animatable="False" boxed="True"
<%helpers:longhand name="border-image-source" animatable="False" boxed="True"
spec="https://drafts.csswg.org/css-backgrounds/#border-image-source">
use std::fmt;
use style_traits::ToCss;
@ -162,7 +162,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
}
</%helpers:longhand>
<%helpers:longhand name="border-image-outset" products="gecko" animatable="False"
<%helpers:longhand name="border-image-outset" animatable="False"
spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset">
use std::fmt;
use style_traits::ToCss;
@ -278,7 +278,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
}
</%helpers:longhand>
<%helpers:longhand name="border-image-repeat" products="gecko" animatable="False"
<%helpers:longhand name="border-image-repeat" animatable="False"
spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
use std::fmt;
use style_traits::ToCss;
@ -356,7 +356,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
}
</%helpers:longhand>
<%helpers:longhand name="border-image-width" products="gecko" animatable="False"
<%helpers:longhand name="border-image-width" animatable="False"
spec="https://drafts.csswg.org/css-backgrounds/#border-image-width">
use std::fmt;
use style_traits::ToCss;
@ -556,7 +556,7 @@ ${helpers.single_keyword("-moz-float-edge", "content-box margin-box",
}
</%helpers:longhand>
<%helpers:longhand name="border-image-slice" products="gecko" animatable="False"
<%helpers:longhand name="border-image-slice" animatable="False"
spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice">
use std::fmt;
use style_traits::ToCss;

View File

@ -184,7 +184,7 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser)
}
</%helpers:shorthand>
<%helpers:shorthand name="border-image" products="gecko" sub_properties="border-image-outset
<%helpers:shorthand name="border-image" sub_properties="border-image-outset
border-image-repeat border-image-slice border-image-source border-image-width"
extra_prefixes="moz webkit" spec="https://drafts.csswg.org/css-backgrounds-3/#border-image">
use properties::longhands::{border_image_outset, border_image_repeat, border_image_slice};