From 13f6cf6e3a1094f4cbfed0395cfebf459b83b1c5 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Tue, 18 Apr 2017 14:25:12 +0800 Subject: [PATCH] Stylo: add -moz-border-*-colors support In Gecko, we use double pointers to nsBorderColors to store -moz-border-*-colors. We can simplify the implementation of computed value in Servo by using Option. As to passing computed values from Servo to Gecko, we might need to use some binding functions (pre-added in the same Gecko bug, see Bug 1348173). Note that we added -moz-border-*-colors as sub_properties of the 'border' shorthand, so we can make the 'border' shorthand reset -moz-border-*-colors (See Gecko Bug 482692). However, since they are Gecko only non-standard properties, we should skip these sub_properties while doing unit testing in Servo. The test part should be convered by Stylo/Gecko already. A bit refactoring of replacing all ["top", "right", "bottom", "left"] with PHYSICAL_SIDES is included in this patch, since we've already had PHYSICAL_SIDES for a while. --- components/style/build_gecko.rs | 1 + components/style/properties/gecko.mako.rs | 42 +++++- .../style/properties/longhand/border.mako.rs | 124 ++++++++++++++++++ .../style/properties/shorthand/border.mako.rs | 22 +++- 4 files changed, 182 insertions(+), 7 deletions(-) diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index 92d512ee75c..00aac6d8526 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -624,6 +624,7 @@ mod bindings { .whitelisted_function("Gecko_.*"); let structs_types = [ "mozilla::css::URLValue", + "mozilla::Side", "RawGeckoAnimationPropertySegment", "RawGeckoComputedTiming", "RawGeckoDocument", diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 9ab78fa0bba..3c2ab7280be 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -827,9 +827,13 @@ fn static_assert() { for y in ["color", "style", "width"]] + ["border-{0}-radius".format(x.ident.replace("_", "-")) for x in CORNERS]) %> + +<% skip_moz_border_color_longhands = " ".join("-moz-border-{0}-colors".format(x.ident) + for x in SIDES) %> <%self:impl_trait style_struct_name="Border" skip_longhands="${skip_border_longhands} border-image-source border-image-outset - border-image-repeat border-image-width border-image-slice" + border-image-repeat border-image-width border-image-slice + ${skip_moz_border_color_longhands}" skip_additionals="*"> % for side in SIDES: @@ -880,6 +884,42 @@ fn static_assert() { pub fn border_${side.ident}_has_nonzero_width(&self) -> bool { self.gecko.mComputedBorder.${side.ident} != 0 } + + #[allow(non_snake_case)] + pub fn set__moz_border_${side.ident}_colors(&mut self, + v: longhands::_moz_border_${side.ident}_colors::computed_value::T) { + match v.0 { + None => { + unsafe { + bindings::Gecko_ClearMozBorderColors(&mut self.gecko, + structs::Side::eSide${to_camel_case(side.ident)}); + } + }, + Some(ref colors) => { + unsafe { + bindings::Gecko_EnsureMozBorderColors(&mut self.gecko); + bindings::Gecko_ClearMozBorderColors(&mut self.gecko, + structs::Side::eSide${to_camel_case(side.ident)}); + } + for color in colors { + let c = color_to_nscolor_zero_currentcolor(*color); + unsafe { + bindings::Gecko_AppendMozBorderColors(&mut self.gecko, + structs::Side::eSide${to_camel_case(side.ident)}, + c); + } + } + } + } + } + + #[allow(non_snake_case)] + pub fn copy__moz_border_${side.ident}_colors_from(&mut self, other: &Self) { + unsafe { + bindings::Gecko_CopyMozBorderColors(&mut self.gecko, &other.gecko, + structs::Side::eSide${to_camel_case(side.ident)}); + } + } % endfor % for corner in CORNERS: diff --git a/components/style/properties/longhand/border.mako.rs b/components/style/properties/longhand/border.mako.rs index cad750ba165..64c64c700fb 100644 --- a/components/style/properties/longhand/border.mako.rs +++ b/components/style/properties/longhand/border.mako.rs @@ -73,6 +73,130 @@ ${helpers.gecko_keyword_conversion(Keyword('border-style', animation_value_type="ComputedValue")} % endfor +/// -moz-border-*-colors: color, string, enum, none, inherit/initial +/// These non-spec properties are just for Gecko (Stylo) internal use. +% for side in PHYSICAL_SIDES: + <%helpers:longhand name="-moz-border-${side}-colors" animation_value_type="none" + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-border-*-colors)" + products="gecko"> + use std::fmt; + use style_traits::ToCss; + use values::HasViewportPercentage; + use values::specified::CSSColor; + no_viewport_percentage!(SpecifiedValue); + + pub mod computed_value { + use values::computed::CSSColor; + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub struct T(pub Option>); + } + + #[derive(Debug, Clone, PartialEq)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub enum SpecifiedValue { + None, + Colors(Vec), + } + + impl ToCss for computed_value::T { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match self.0 { + None => return dest.write_str("none"), + Some(ref vec) => { + let mut first = true; + for ref color in vec { + if !first { + try!(dest.write_str(" ")); + } + first = false; + try!(color.to_css(dest)) + } + Ok(()) + } + } + } + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + SpecifiedValue::None => return dest.write_str("none"), + SpecifiedValue::Colors(ref vec) => { + let mut first = true; + for ref color in vec { + if !first { + try!(dest.write_str(" ")); + } + first = false; + try!(color.to_css(dest)) + } + Ok(()) + } + } + } + } + + #[inline] pub fn get_initial_value() -> computed_value::T { + computed_value::T(None) + } + + #[inline] pub fn get_initial_specified_value() -> SpecifiedValue { + SpecifiedValue::None + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + match *self { + SpecifiedValue::Colors(ref vec) => { + computed_value::T(Some(vec.iter() + .map(|c| c.to_computed_value(context)) + .collect())) + }, + SpecifiedValue::None => { + computed_value::T(None) + } + } + } + #[inline] + fn from_computed_value(computed: &computed_value::T) -> Self { + match *computed { + computed_value::T(Some(ref vec)) => { + SpecifiedValue::Colors(vec.iter() + .map(|c| ToComputedValue::from_computed_value((c))) + .collect()) + }, + computed_value::T(None) => { + SpecifiedValue::None + } + } + } + } + + #[inline] + pub fn parse(context: &ParserContext, input: &mut Parser) + -> Result { + if input.try(|input| input.expect_ident_matching("none")).is_ok() { + return Ok(SpecifiedValue::None) + } + + let mut result = Vec::new(); + while let Ok(value) = input.try(|i| CSSColor::parse(context, i)) { + result.push(value); + } + + if !result.is_empty() { + Ok(SpecifiedValue::Colors(result)) + } else { + Err(()) + } + } + +% endfor + ${helpers.single_keyword("box-decoration-break", "slice clone", gecko_enum_prefix="StyleBoxDecorationBreak", gecko_inexhaustive=True, diff --git a/components/style/properties/shorthand/border.mako.rs b/components/style/properties/shorthand/border.mako.rs index 1ee9d3ff8da..35afe623205 100644 --- a/components/style/properties/shorthand/border.mako.rs +++ b/components/style/properties/shorthand/border.mako.rs @@ -14,7 +14,7 @@ ${helpers.four_sides_shorthand("border-style", "border-%s-style", <%helpers:shorthand name="border-width" sub_properties="${ ' '.join('border-%s-width' % side - for side in ['top', 'right', 'bottom', 'left'])}" + for side in PHYSICAL_SIDES)}" spec="https://drafts.csswg.org/css-backgrounds/#border-width"> use super::parse_four_sides; use parser::Parse; @@ -23,7 +23,7 @@ ${helpers.four_sides_shorthand("border-style", "border-%s-style", pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result { let (top, right, bottom, left) = try!(parse_four_sides(input, |i| specified::BorderWidth::parse(context, i))); Ok(Longhands { - % for side in ["top", "right", "bottom", "left"]: + % for side in PHYSICAL_SIDES: ${to_rust_ident('border-%s-width' % side)}: ${side}, % endfor }) @@ -31,7 +31,7 @@ ${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 { - % for side in ["top", "right", "bottom", "left"]: + % for side in PHYSICAL_SIDES: let ${side} = self.border_${side}_width.clone(); % endfor @@ -124,22 +124,32 @@ pub fn parse_border(context: &ParserContext, input: &mut Parser) <%helpers:shorthand name="border" sub_properties="${' '.join('border-%s-%s' % (side, prop) - for side in ['top', 'right', 'bottom', 'left'] + for side in PHYSICAL_SIDES for prop in ['color', 'style', 'width'])} ${' '.join('border-image-%s' % name - for name in ['outset', 'repeat', 'slice', 'source', 'width'])}" + for name in ['outset', 'repeat', 'slice', 'source', 'width'])} + ${' '.join('-moz-border-%s-colors' % side + for side in PHYSICAL_SIDES) if product == 'gecko' else ''}" spec="https://drafts.csswg.org/css-backgrounds/#border"> + % if product == "gecko": + use properties::longhands::{_moz_border_top_colors, _moz_border_right_colors, + _moz_border_bottom_colors, _moz_border_left_colors}; + % endif + pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result { use properties::longhands::{border_image_outset, border_image_repeat, border_image_slice}; use properties::longhands::{border_image_source, border_image_width}; let (color, style, width) = try!(super::parse_border(context, input)); Ok(Longhands { - % for side in ["top", "right", "bottom", "left"]: + % for side in PHYSICAL_SIDES: border_${side}_color: color.clone(), border_${side}_style: style, border_${side}_width: width.clone(), + % if product == "gecko": + _moz_border_${side}_colors: _moz_border_${side}_colors::get_initial_specified_value(), + % endif % endfor // The ‘border’ shorthand resets ‘border-image’ to its initial value.