servo: Merge #14218 - Implement drop-shadow filter for Stylo (from shinglyu:dropshadow-review); r=Manishearth
authorShing Lyu <shing.lyu@gmail.com>
Tue, 22 Nov 2016 03:25:43 -0600
changeset 340199 d9434f058fc55a97ea4efd69031e7d401396e78b
parent 340198 9fd386e81d6919fd06ba61d1834073ec80fd2ce7
child 340200 1edcbee8585714a0ecc59f31cec7bc1028ebbfa7
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersManishearth
servo: Merge #14218 - Implement drop-shadow filter for Stylo (from shinglyu:dropshadow-review); r=Manishearth <!-- Please describe your changes on the following line: --> This patch got an r+ on [MozReview](https://reviewboard.mozilla.org/r/90560/#issue-summary) --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix Bugzilla [#1315157](https://bugzilla.mozilla.org/show_bug.cgi?id=1315157) <!-- Either: --> - [ ] There are tests for these changes OR - [x] These changes do not require tests because unit test not land-able if not enabled for servo. I'll land the test later when we enable it in Servo <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: f747ff95cdb03cdf66735d3975de4793147ba07f
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/effects.mako.rs
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -26,16 +26,17 @@ use gecko_bindings::bindings::Gecko_Copy
 use gecko_bindings::bindings::Gecko_CopyListStyleTypeFrom;
 use gecko_bindings::bindings::Gecko_CopyMozBindingFrom;
 use gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
 use gecko_bindings::bindings::Gecko_FontFamilyList_AppendGeneric;
 use gecko_bindings::bindings::Gecko_FontFamilyList_AppendNamed;
 use gecko_bindings::bindings::Gecko_FontFamilyList_Clear;
 use gecko_bindings::bindings::Gecko_SetCursorArrayLength;
 use gecko_bindings::bindings::Gecko_SetCursorImage;
+use gecko_bindings::bindings::Gecko_NewCSSShadowArray;
 use gecko_bindings::bindings::Gecko_SetListStyleImage;
 use gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use gecko_bindings::bindings::Gecko_SetListStyleType;
 use gecko_bindings::bindings::Gecko_SetMozBinding;
 use gecko_bindings::bindings::Gecko_SetNullImageValue;
 use gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull;
 use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use gecko_bindings::structs;
@@ -1687,27 +1688,30 @@ fn static_assert() {
                 inset: shadow.mInset,
                 color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)),
             }
         }).collect();
         longhands::box_shadow::computed_value::T(buf)
     }
 
     pub fn set_filter(&mut self, v: longhands::filter::computed_value::T) {
+        use cssparser::Color;
         use properties::longhands::filter::computed_value::Filter::*;
+        use gecko_bindings::structs::nsCSSShadowArray;
         use gecko_bindings::structs::nsStyleFilter;
         use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
         use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
         use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
         use gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
         use gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
         use gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
         use gecko_bindings::structs::NS_STYLE_FILTER_SATURATE;
         use gecko_bindings::structs::NS_STYLE_FILTER_SEPIA;
         use gecko_bindings::structs::NS_STYLE_FILTER_HUE_ROTATE;
+        use gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW;
 
         fn fill_filter(m_type: u32, value: CoordDataValue, gecko_filter: &mut nsStyleFilter){
             gecko_filter.mType = m_type;
             gecko_filter.mFilterParameter.set_value(value);
         }
 
         unsafe {
             Gecko_ResetFilters(&mut self.gecko, v.filters.len());
@@ -1739,16 +1743,46 @@ fn static_assert() {
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
                 Saturate(factor)   => fill_filter(NS_STYLE_FILTER_SATURATE,
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
                 Sepia(factor)      => fill_filter(NS_STYLE_FILTER_SEPIA,
                                                   CoordDataValue::Factor(factor),
                                                   gecko_filter),
+                DropShadow(offset_x, offset_y, blur_radius, ref color) => {
+                    gecko_filter.mType = NS_STYLE_FILTER_DROP_SHADOW;
+
+                    fn init_shadow(filter: &mut nsStyleFilter) -> &mut nsCSSShadowArray {
+                        unsafe {
+                            let ref mut union = filter.__bindgen_anon_1;
+                            let mut shadow_array: &mut *mut nsCSSShadowArray = union.mDropShadow.as_mut();
+                            *shadow_array = Gecko_NewCSSShadowArray(1);
+
+                            &mut **shadow_array
+                        }
+                    }
+
+                    let mut gecko_shadow = init_shadow(gecko_filter);
+                    gecko_shadow.mArray[0].mXOffset = offset_x.0;
+                    gecko_shadow.mArray[0].mYOffset = offset_y.0;
+                    gecko_shadow.mArray[0].mRadius = blur_radius.0;
+                    // mSpread is not supported in the spec, so we leave it as 0
+                    gecko_shadow.mArray[0].mInset = false; // Not supported in spec level 1
+
+                    gecko_shadow.mArray[0].mColor = match *color {
+                        Color::RGBA(rgba) => {
+                            gecko_shadow.mArray[0].mHasColor = true;
+                            convert_rgba_to_nscolor(&rgba)
+                        },
+                        // TODO handle currentColor
+                        // https://bugzilla.mozilla.org/show_bug.cgi?id=760345
+                        Color::CurrentColor => 0,
+                    };
+                }
             }
         }
     }
 
     pub fn copy_filter_from(&mut self, other: &Self) {
         unsafe {
             Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut self.gecko);
         }
--- a/servo/components/style/properties/longhand/effects.mako.rs
+++ b/servo/components/style/properties/longhand/effects.mako.rs
@@ -397,31 +397,32 @@
             })))
         })
     }
 </%helpers:longhand>
 
 // FIXME: This prop should be animatable
 <%helpers:longhand name="filter" animatable="False">
     //pub use self::computed_value::T as SpecifiedValue;
+    use cssparser;
     use std::fmt;
-    use style_traits::ToCss;
+    use style_traits::{self, ToCss};
     use values::{CSSFloat, HasViewportPercentage};
-    use values::specified::{Angle, Length};
+    use values::specified::{Angle, CSSColor, Length};
 
     impl HasViewportPercentage for SpecifiedValue {
         fn has_viewport_percentage(&self) -> bool {
             let &SpecifiedValue(ref vec) = self;
             vec.iter().any(|ref x| x.has_viewport_percentage())
         }
     }
 
     #[derive(Debug, Clone, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-    pub struct SpecifiedValue(Vec<SpecifiedFilter>);
+    pub struct SpecifiedValue(pub Vec<SpecifiedFilter>);
 
     impl HasViewportPercentage for SpecifiedFilter {
         fn has_viewport_percentage(&self) -> bool {
             match *self {
                 SpecifiedFilter::Blur(length) => length.has_viewport_percentage(),
                 _ => false
             }
         }
@@ -435,35 +436,42 @@
         Brightness(CSSFloat),
         Contrast(CSSFloat),
         Grayscale(CSSFloat),
         HueRotate(Angle),
         Invert(CSSFloat),
         Opacity(CSSFloat),
         Saturate(CSSFloat),
         Sepia(CSSFloat),
+        % if product == "gecko":
+        DropShadow(Length, Length, Length, Option<CSSColor>),
+        % endif
     }
 
     pub mod computed_value {
         use app_units::Au;
         use values::CSSFloat;
+        use values::computed::CSSColor;
         use values::specified::{Angle};
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub enum Filter {
             Blur(Au),
             Brightness(CSSFloat),
             Contrast(CSSFloat),
             Grayscale(CSSFloat),
             HueRotate(Angle),
             Invert(CSSFloat),
             Opacity(CSSFloat),
             Saturate(CSSFloat),
             Sepia(CSSFloat),
+            % if product == "gecko":
+            DropShadow(Au, Au, Au, CSSColor),
+            % endif
         }
 
         #[derive(Clone, PartialEq, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
         pub struct T { pub filters: Vec<Filter> }
 
         impl T {
             /// Creates a new filter pipeline.
@@ -551,16 +559,29 @@
                     try!(dest.write_str("hue-rotate("));
                     try!(value.to_css(dest));
                     try!(dest.write_str(")"));
                 }
                 computed_value::Filter::Invert(value) => try!(write!(dest, "invert({})", value)),
                 computed_value::Filter::Opacity(value) => try!(write!(dest, "opacity({})", value)),
                 computed_value::Filter::Saturate(value) => try!(write!(dest, "saturate({})", value)),
                 computed_value::Filter::Sepia(value) => try!(write!(dest, "sepia({})", value)),
+                % if product == "gecko":
+                computed_value::Filter::DropShadow(offset_x, offset_y, blur_radius, ref color) => {
+                    try!(dest.write_str("drop-shadow("));
+                    try!(offset_x.to_css(dest));
+                    try!(dest.write_str(", "));
+                    try!(offset_y.to_css(dest));
+                    try!(dest.write_str(", "));
+                    try!(blur_radius.to_css(dest));
+                    try!(dest.write_str(", "));
+                    try!(color.to_css(dest));
+                    try!(dest.write_str(")"));
+                }
+                % endif
             }
             Ok(())
         }
     }
 
     impl ToCss for SpecifiedFilter {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
@@ -576,16 +597,31 @@
                     try!(dest.write_str("hue-rotate("));
                     try!(value.to_css(dest));
                     try!(dest.write_str(")"));
                 }
                 SpecifiedFilter::Invert(value) => try!(write!(dest, "invert({})", value)),
                 SpecifiedFilter::Opacity(value) => try!(write!(dest, "opacity({})", value)),
                 SpecifiedFilter::Saturate(value) => try!(write!(dest, "saturate({})", value)),
                 SpecifiedFilter::Sepia(value) => try!(write!(dest, "sepia({})", value)),
+                % if product == "gecko":
+                SpecifiedFilter::DropShadow(offset_x, offset_y, blur_radius, ref color) => {
+                    try!(dest.write_str("drop-shadow("));
+                    try!(offset_x.to_css(dest));
+                    try!(dest.write_str(", "));
+                    try!(offset_y.to_css(dest));
+                    try!(dest.write_str(", "));
+                    try!(blur_radius.to_css(dest));
+                    if let &Some(ref color) = color {
+                        try!(dest.write_str(", "));
+                        try!(color.to_css(dest));
+                    }
+                    try!(dest.write_str(")"));
+                }
+                % endif
             }
             Ok(())
         }
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T::new(Vec::new())
@@ -604,16 +640,19 @@
                         "brightness" => parse_factor(input).map(SpecifiedFilter::Brightness),
                         "contrast" => parse_factor(input).map(SpecifiedFilter::Contrast),
                         "grayscale" => parse_factor(input).map(SpecifiedFilter::Grayscale),
                         "hue-rotate" => Angle::parse(input).map(SpecifiedFilter::HueRotate),
                         "invert" => parse_factor(input).map(SpecifiedFilter::Invert),
                         "opacity" => parse_factor(input).map(SpecifiedFilter::Opacity),
                         "saturate" => parse_factor(input).map(SpecifiedFilter::Saturate),
                         "sepia" => parse_factor(input).map(SpecifiedFilter::Sepia),
+                        % if product == "gecko":
+                        "drop-shadow" => parse_drop_shadow(input),
+                        % endif
                         _ => Err(())
                     }
                 })));
             } else if filters.is_empty() {
                 return Err(())
             } else {
                 return Ok(SpecifiedValue(filters))
             }
@@ -624,32 +663,54 @@
         use cssparser::Token;
         match input.next() {
             Ok(Token::Number(value)) => Ok(value.value),
             Ok(Token::Percentage(value)) => Ok(value.unit_value),
             _ => Err(())
         }
     }
 
+    % if product == "gecko":
+    fn parse_drop_shadow(input: &mut Parser) -> Result<SpecifiedFilter, ()> {
+        let offset_x = try!(specified::Length::parse(input));
+        let offset_y = try!(specified::Length::parse(input));
+        let blur_radius = input.try(specified::Length::parse).unwrap_or(specified::Length::from_px(0.0));
+        let color = input.try(specified::CSSColor::parse).ok();
+        Ok(SpecifiedFilter::DropShadow(offset_x, offset_y, blur_radius, color))
+    }
+    % endif
+
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             computed_value::T{ filters: self.0.iter().map(|value| {
                 match *value {
                     SpecifiedFilter::Blur(factor) =>
                         computed_value::Filter::Blur(factor.to_computed_value(context)),
                     SpecifiedFilter::Brightness(factor) => computed_value::Filter::Brightness(factor),
                     SpecifiedFilter::Contrast(factor) => computed_value::Filter::Contrast(factor),
                     SpecifiedFilter::Grayscale(factor) => computed_value::Filter::Grayscale(factor),
                     SpecifiedFilter::HueRotate(factor) => computed_value::Filter::HueRotate(factor),
                     SpecifiedFilter::Invert(factor) => computed_value::Filter::Invert(factor),
                     SpecifiedFilter::Opacity(factor) => computed_value::Filter::Opacity(factor),
                     SpecifiedFilter::Saturate(factor) => computed_value::Filter::Saturate(factor),
                     SpecifiedFilter::Sepia(factor) => computed_value::Filter::Sepia(factor),
+                    % if product == "gecko":
+                    SpecifiedFilter::DropShadow(offset_x, offset_y, blur_radius, ref color) => {
+                        computed_value::Filter::DropShadow(
+                            offset_x.to_computed_value(context),
+                            offset_y.to_computed_value(context),
+                            blur_radius.to_computed_value(context),
+                            color.as_ref()
+                                 .map(|color| color.parsed)
+                                 .unwrap_or(cssparser::Color::CurrentColor),
+                        )
+                    }
+                    % endif
                 }
             }).collect() }
         }
 
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(computed.filters.iter().map(|value| {
                 match *value {
                     computed_value::Filter::Blur(factor) =>
@@ -657,16 +718,26 @@
                     computed_value::Filter::Brightness(factor) => SpecifiedFilter::Brightness(factor),
                     computed_value::Filter::Contrast(factor) => SpecifiedFilter::Contrast(factor),
                     computed_value::Filter::Grayscale(factor) => SpecifiedFilter::Grayscale(factor),
                     computed_value::Filter::HueRotate(factor) => SpecifiedFilter::HueRotate(factor),
                     computed_value::Filter::Invert(factor) => SpecifiedFilter::Invert(factor),
                     computed_value::Filter::Opacity(factor) => SpecifiedFilter::Opacity(factor),
                     computed_value::Filter::Saturate(factor) => SpecifiedFilter::Saturate(factor),
                     computed_value::Filter::Sepia(factor) => SpecifiedFilter::Sepia(factor),
+                    % if product == "gecko":
+                    computed_value::Filter::DropShadow(offset_x, offset_y, blur_radius, color) => {
+                        SpecifiedFilter::DropShadow(
+                            ToComputedValue::from_computed_value(&offset_x),
+                            ToComputedValue::from_computed_value(&offset_y),
+                            ToComputedValue::from_computed_value(&blur_radius),
+                            Some(ToComputedValue::from_computed_value(&color)),
+                        )
+                    }
+                    % endif
                 }
             }).collect())
         }
     }
 </%helpers:longhand>
 
 pub struct OriginParseResult {
     pub horizontal: Option<specified::LengthOrPercentage>,