author | Anthony Ramine <n.oxyde@gmail.com> |
Tue, 20 Jun 2017 06:23:17 -0700 | |
changeset 365068 | 034c0bb6fb56fe265f3929dbad480c8d8476eebf |
parent 365067 | e5fb1c36d5d6fc4420d4d9afa324e2b33cf6dd08 |
child 365069 | 6731e174280419faf890edb613d553b5c6642089 |
push id | 91680 |
push user | kwierso@gmail.com |
push date | Wed, 21 Jun 2017 01:32:01 +0000 |
treeherder | mozilla-inbound@f7b9dc31956c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | emilio |
milestone | 56.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/servo/components/gfx/display_list/mod.rs +++ b/servo/components/gfx/display_list/mod.rs @@ -474,17 +474,17 @@ impl StackingContext { #[inline] pub fn root(pipeline_id: PipelineId) -> StackingContext { StackingContext::new(StackingContextId::root(), StackingContextType::Real, &Rect::zero(), &Rect::zero(), 0, - filter::T::new(Vec::new()), + filter::T::none(), MixBlendMode::Normal, None, TransformStyle::Flat, None, ScrollPolicy::Scrollable, pipeline_id.root_scroll_node()) }
--- a/servo/components/layout/display_list_builder.rs +++ b/servo/components/layout/display_list_builder.rs @@ -45,27 +45,27 @@ use servo_url::ServoUrl; use std::{cmp, f32}; use std::collections::HashMap; use std::default::Default; use std::mem; use std::sync::Arc; use style::computed_values::{background_attachment, background_clip, background_origin}; use style::computed_values::{background_repeat, border_style, cursor}; use style::computed_values::{image_rendering, overflow_x, pointer_events, position, visibility}; -use style::computed_values::filter::Filter; 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::{Either, RGBA}; use style::values::computed::{Gradient, GradientItem, LengthOrPercentage}; use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position, Shadow}; use style::values::computed::image::{EndingShape, LineDirection}; use style::values::generics::background::BackgroundSize; +use style::values::generics::effects::Filter; use style::values::generics::image::{Circle, Ellipse, EndingShape as GenericEndingShape}; use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind}; use style::values::generics::image::{Image, ShapeExtent}; use style::values::generics::image::PaintWorklet; use style::values::specified::length::Percentage; use style::values::specified::position::{X, Y}; use style_traits::CSSPixel; use style_traits::cursor::Cursor; @@ -2001,33 +2001,33 @@ impl FragmentDisplayListBuilding for Fra // from our flow origin, since that is what `BaseFlow::overflow` is relative to. let border_box_offset = border_box.translate(&-base_flow.stacking_relative_position).origin; // Then, using that, compute our overflow region relative to our border box. let overflow = base_flow.overflow.paint.translate(&-border_box_offset.to_vector()); // Create the filter pipeline. let effects = self.style().get_effects(); - let mut filters = effects.filter.clone(); + let mut filters = effects.filter.clone().0.into_vec(); if effects.opacity != 1.0 { filters.push(Filter::Opacity(effects.opacity)) } let context_type = match mode { StackingContextCreationMode::PseudoFloat => StackingContextType::PseudoFloat, StackingContextCreationMode::PseudoPositioned => StackingContextType::PseudoPositioned, _ => StackingContextType::Real, }; StackingContext::new(id, context_type, &border_box, &overflow, self.effective_z_index(), - filters, + filters.into(), self.style().get_effects().mix_blend_mode.to_mix_blend_mode(), self.transform_matrix(&border_box), self.style().get_used_transform_style().to_transform_style(), self.perspective_matrix(&border_box), scroll_policy, parent_scroll_id) }
--- a/servo/components/layout/fragment.rs +++ b/servo/components/layout/fragment.rs @@ -2462,17 +2462,17 @@ impl Fragment { SpecificFragmentInfo::ScannedText(_) | SpecificFragmentInfo::UnscannedText(_) => return false, _ => {} } if self.style().get_effects().opacity != 1.0 { return true } - if !self.style().get_effects().filter.is_empty() { + if !self.style().get_effects().filter.0.is_empty() { return true } if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal { return true } if self.style().get_box().transform.0.is_some() || self.style().get_box().transform_style == transform_style::T::preserve_3d ||
--- a/servo/components/layout/webrender_helpers.rs +++ b/servo/components/layout/webrender_helpers.rs @@ -8,18 +8,19 @@ // completely converting layout to directly generate WebRender display lists, for example. use app_units::Au; use euclid::{Point2D, Vector2D, Rect, SideOffsets2D, Size2D}; use gfx::display_list::{BorderDetails, BorderRadii, BoxShadowClipMode, ClippingRegion}; use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal, StackingContextType}; use msg::constellation_msg::PipelineId; use style::computed_values::{image_rendering, mix_blend_mode, transform_style}; -use style::computed_values::filter::{self, Filter}; +use style::computed_values::filter; use style::values::computed::BorderStyle; +use style::values::generics::effects::Filter; use webrender_traits::{self, DisplayListBuilder, ExtendMode}; use webrender_traits::{LayoutTransform, ClipId, ClipRegionToken}; pub trait WebRenderDisplayListConverter { fn convert_to_webrender(&self, pipeline_id: PipelineId) -> DisplayListBuilder; } trait WebRenderDisplayItemConverter { @@ -198,28 +199,29 @@ impl ToImageRendering for image_renderin } trait ToFilterOps { fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp>; } impl ToFilterOps for filter::T { fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp> { - let mut result = Vec::with_capacity(self.filters.len()); - for filter in self.filters.iter() { + let mut result = Vec::with_capacity(self.0.len()); + for filter in self.0.iter() { match *filter { Filter::Blur(radius) => result.push(webrender_traits::FilterOp::Blur(radius)), Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)), Filter::Contrast(amount) => result.push(webrender_traits::FilterOp::Contrast(amount)), Filter::Grayscale(amount) => result.push(webrender_traits::FilterOp::Grayscale(amount)), Filter::HueRotate(angle) => result.push(webrender_traits::FilterOp::HueRotate(angle.radians())), Filter::Invert(amount) => result.push(webrender_traits::FilterOp::Invert(amount)), Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())), Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)), Filter::Sepia(amount) => result.push(webrender_traits::FilterOp::Sepia(amount)), + Filter::DropShadow(ref shadow) => match *shadow {}, } } result } } pub trait ToTransformStyle { fn to_transform_style(&self) -> webrender_traits::TransformStyle;
--- a/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs +++ b/servo/components/style/gecko_bindings/sugar/ns_css_shadow_item.rs @@ -3,16 +3,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! Rust helpers for Gecko's `nsCSSShadowItem`. use app_units::Au; use gecko::values::{convert_rgba_to_nscolor, convert_nscolor_to_rgba}; use gecko_bindings::structs::nsCSSShadowItem; use values::computed::{Color, Shadow}; +use values::computed::effects::DropShadow; impl nsCSSShadowItem { /// Set this item to the given shadow value. pub fn set_from_shadow(&mut self, other: Shadow) { self.mXOffset = other.offset_x.0; self.mYOffset = other.offset_y.0; self.mRadius = other.blur_radius.0; self.mSpread = other.spread_radius.0; @@ -34,9 +35,41 @@ impl nsCSSShadowItem { offset_x: Au(self.mXOffset), offset_y: Au(self.mYOffset), blur_radius: Au(self.mRadius), spread_radius: Au(self.mSpread), inset: self.mInset, color: Color::rgba(convert_nscolor_to_rgba(self.mColor)), } } + + /// Sets this item from the given drop shadow. + #[inline] + pub fn set_from_drop_shadow(&mut self, shadow: DropShadow) { + self.mXOffset = shadow.horizontal.0; + self.mYOffset = shadow.vertical.0; + self.mRadius = shadow.blur.0; + self.mSpread = 0; + self.mInset = false; + if shadow.color.is_currentcolor() { + // TODO handle currentColor + // https://bugzilla.mozilla.org/show_bug.cgi?id=760345 + self.mHasColor = false; + self.mColor = 0; + } else { + self.mHasColor = true; + self.mColor = convert_rgba_to_nscolor(&shadow.color.color); + } + } + + /// Returns this item as a drop shadow. + #[inline] + pub fn to_drop_shadow(&self) -> DropShadow { + debug_assert_eq!(self.mSpread, 0); + debug_assert_eq!(self.mInset, false); + DropShadow { + color: Color::rgba(convert_nscolor_to_rgba(self.mColor)), + horizontal: Au(self.mXOffset), + vertical: Au(self.mYOffset), + blur: Au(self.mRadius), + } + } }
--- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -3439,17 +3439,17 @@ fn static_assert() { # number value for function of clone / set. # The setting / cloning process of other function(e.g. Blur / HueRotate) is # different from these function. So this array don't include such function. FILTER_FUNCTIONS = [ 'Brightness', 'Contrast', 'Grayscale', 'Invert', 'Opacity', 'Saturate', 'Sepia' ] %> pub fn set_filter(&mut self, v: longhands::filter::computed_value::T) { - use properties::longhands::filter::computed_value::Filter::*; + use values::generics::effects::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; @@ -3459,21 +3459,21 @@ fn static_assert() { 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()); + Gecko_ResetFilters(&mut self.gecko, v.0.len()); } - debug_assert!(v.filters.len() == self.gecko.mFilters.len()); - - for (servo, gecko_filter) in v.filters.into_iter().zip(self.gecko.mFilters.iter_mut()) { + debug_assert!(v.0.len() == self.gecko.mFilters.len()); + + for (servo, gecko_filter) in v.0.into_vec().into_iter().zip(self.gecko.mFilters.iter_mut()) { match servo { % for func in FILTER_FUNCTIONS: ${func}(factor) => fill_filter(NS_STYLE_FILTER_${func.upper()}, CoordDataValue::Factor(factor), gecko_filter), % endfor Blur(length) => fill_filter(NS_STYLE_FILTER_BLUR, CoordDataValue::Coord(length.0), @@ -3492,17 +3492,17 @@ fn static_assert() { 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].set_from_shadow(shadow); + gecko_shadow.mArray[0].set_from_drop_shadow(shadow); }, Url(ref url) => { unsafe { bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.for_ffi()); } }, } } @@ -3510,17 +3510,17 @@ fn static_assert() { pub fn copy_filter_from(&mut self, other: &Self) { unsafe { Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut self.gecko); } } pub fn clone_filter(&self) -> longhands::filter::computed_value::T { - use properties::longhands::filter::computed_value::Filter::*; + use values::generics::effects::{Filter, FilterList}; use values::specified::url::SpecifiedUrl; 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; @@ -3529,45 +3529,46 @@ fn static_assert() { use gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW; use gecko_bindings::structs::NS_STYLE_FILTER_URL; let mut filters = Vec::new(); for filter in self.gecko.mFilters.iter(){ match filter.mType { % for func in FILTER_FUNCTIONS: NS_STYLE_FILTER_${func.upper()} => { - filters.push(${func}( + filters.push(Filter::${func}( GeckoStyleCoordConvertible::from_gecko_style_coord( &filter.mFilterParameter).unwrap())); }, % endfor NS_STYLE_FILTER_BLUR => { - filters.push(Blur(Au::from_gecko_style_coord( + filters.push(Filter::Blur(Au::from_gecko_style_coord( &filter.mFilterParameter).unwrap())); }, NS_STYLE_FILTER_HUE_ROTATE => { - filters.push(HueRotate( + filters.push(Filter::HueRotate( GeckoStyleCoordConvertible::from_gecko_style_coord( &filter.mFilterParameter).unwrap())); }, NS_STYLE_FILTER_DROP_SHADOW => { filters.push(unsafe { - DropShadow((**filter.__bindgen_anon_1.mDropShadow.as_ref()).mArray[0].to_shadow()) + Filter::DropShadow((**filter.__bindgen_anon_1.mDropShadow.as_ref()).mArray[0].to_drop_shadow()) }); }, NS_STYLE_FILTER_URL => { filters.push(unsafe { - (Url(SpecifiedUrl::from_url_value_data( - &(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap())) + Filter::Url( + SpecifiedUrl::from_url_value_data(&(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap() + ) }); } _ => {}, } } - longhands::filter::computed_value::T::new(filters) + FilterList(filters.into_boxed_slice()) } </%self:impl_trait> <%self:impl_trait style_struct_name="InheritedBox" skip_longhands="image-orientation"> // FIXME: Gecko uses a tricky way to store computed value of image-orientation // within an u8. We could inline following glue codes by implementing all
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs +++ b/servo/components/style/properties/helpers/animated_properties.mako.rs @@ -9,22 +9,19 @@ use app_units::Au; use cssparser::{Parser, RGBA}; use euclid::{Point2D, Size2D}; #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap; #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4; #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID; #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI}; #[cfg(feature = "gecko")] use gecko_string_cache::Atom; -#[cfg(feature = "gecko")] use gecko::url::SpecifiedUrl; use properties::{CSSWideKeyword, PropertyDeclaration}; use properties::longhands; use properties::longhands::background_size::computed_value::T as BackgroundSizeList; -use properties::longhands::filter::computed_value::Filter; -use properties::longhands::filter::computed_value::T as Filters; use properties::longhands::font_weight::computed_value::T as FontWeight; use properties::longhands::font_stretch::computed_value::T as FontStretch; use properties::longhands::text_shadow::computed_value::T as TextShadowList; use properties::longhands::box_shadow::computed_value::T as BoxShadowList; use properties::longhands::transform::computed_value::ComputedMatrix; use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation; use properties::longhands::transform::computed_value::T as TransformList; use properties::longhands::vertical_align::computed_value::T as VerticalAlign; @@ -32,22 +29,24 @@ use properties::longhands::visibility::c #[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId}; use selectors::parser::SelectorParseError; use smallvec::SmallVec; use std::cmp; #[cfg(feature = "gecko")] use std::collections::HashMap; use style_traits::ParseError; use super::ComputedValues; use values::{Auto, CSSFloat, CustomIdent, Either}; +use values::animated::effects::{Filter as AnimatedFilter, FilterList as AnimatedFilterList}; use values::computed::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; use values::computed::{BorderCornerRadius, ClipRect}; use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified}; use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToComputedValue}; use values::generics::{SVGPaint, SVGPaintKind}; use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius; +use values::generics::effects::Filter; use values::generics::position as generic_position; use values::specified::length::Percentage; /// A longhand property whose animation type is not "none". /// /// NOTE: This includes the 'display' property since it is animatable from SMIL even though it is /// not animatable from CSS animations or Web Animations. CSS transitions also does not allow @@ -3191,251 +3190,172 @@ impl Animatable for IntermediateShadowLi result.extend(self.0.iter().cloned()); result.extend(other.0.iter().cloned()); Ok(IntermediateShadowList(result)) } } -/// Intermediate type for filter property. -/// The difference from normal filter type is that this structure uses -/// IntermediateColor into DropShadow's value. -pub type IntermediateFilters = Vec<IntermediateFilter>; - -#[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] -#[allow(missing_docs)] -pub enum IntermediateFilter { - Blur(Au), - Brightness(CSSFloat), - Contrast(CSSFloat), - Grayscale(CSSFloat), - HueRotate(Angle), - Invert(CSSFloat), - Opacity(CSSFloat), - Saturate(CSSFloat), - Sepia(CSSFloat), - % if product == "gecko": - DropShadow(IntermediateShadow), - Url(SpecifiedUrl), - % endif -} - -impl From<Filters> for IntermediateFilters { - fn from(filters: Filters) -> IntermediateFilters { - filters.filters.into_iter().map(|f| f.into()).collect() - } -} - -impl From<IntermediateFilters> for Filters { - fn from(filters: IntermediateFilters) -> Filters { - Filters::new(filters.into_iter().map(|f| f.into()).collect()) - } -} - <% FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale', 'HueRotate', 'Invert', 'Opacity', 'Saturate', 'Sepia' ] %> -impl From<Filter> for IntermediateFilter { - fn from(filter: Filter) -> IntermediateFilter { - use properties::longhands::filter::computed_value::Filter::*; - match filter { - % for func in FILTER_FUNCTIONS: - ${func}(val) => IntermediateFilter::${func}(val), - % endfor - % if product == "gecko": - DropShadow(shadow) => { - IntermediateFilter::DropShadow(shadow.into()) - }, - Url(ref url) => { - IntermediateFilter::Url(url.clone()) - }, - % endif - } - } -} - -impl From<IntermediateFilter> for Filter { - fn from(filter: IntermediateFilter) -> Filter { - match filter { - % for func in FILTER_FUNCTIONS: - IntermediateFilter::${func}(val) => Filter::${func}(val), - % endfor - % if product == "gecko": - IntermediateFilter::DropShadow(shadow) => { - Filter::DropShadow(shadow.into()) - }, - IntermediateFilter::Url(ref url) => { - Filter::Url(url.clone()) - }, - % endif - } - } -} - /// https://drafts.fxtf.org/filters/#animation-of-filters -fn add_weighted_filter_function_impl(from: &IntermediateFilter, - to: &IntermediateFilter, +fn add_weighted_filter_function_impl(from: &AnimatedFilter, + to: &AnimatedFilter, self_portion: f64, other_portion: f64) - -> Result<IntermediateFilter, ()> { + -> Result<AnimatedFilter, ()> { match (from, to) { % for func in [ 'Blur', 'HueRotate' ]: - (&IntermediateFilter::${func}(from_value), - &IntermediateFilter::${func}(to_value)) => { - Ok(IntermediateFilter::${func}( - try!(from_value.add_weighted(&to_value, - self_portion, - other_portion)))) + (&Filter::${func}(from_value), &Filter::${func}(to_value)) => { + Ok(Filter::${func}(from_value.add_weighted( + &to_value, + self_portion, + other_portion, + )?)) }, % endfor % for func in [ 'Grayscale', 'Invert', 'Sepia' ]: - (&IntermediateFilter::${func}(from_value), - &IntermediateFilter::${func}(to_value)) => { - Ok(IntermediateFilter::${func}(try!( - add_weighted_with_initial_val(&from_value, - &to_value, - self_portion, - other_portion, - &0.0)))) + (&Filter::${func}(from_value), &Filter::${func}(to_value)) => { + Ok(Filter::${func}(add_weighted_with_initial_val( + &from_value, + &to_value, + self_portion, + other_portion, + &0.0, + )?)) }, % endfor % for func in [ 'Brightness', 'Contrast', 'Opacity', 'Saturate' ]: - (&IntermediateFilter::${func}(from_value), - &IntermediateFilter::${func}(to_value)) => { - Ok(IntermediateFilter::${func}(try!( - add_weighted_with_initial_val(&from_value, - &to_value, - self_portion, - other_portion, - &1.0)))) + (&Filter::${func}(from_value), &Filter::${func}(to_value)) => { + Ok(Filter::${func}(add_weighted_with_initial_val( + &from_value, + &to_value, + self_portion, + other_portion, + &1.0, + )?)) }, % endfor % if product == "gecko": - (&IntermediateFilter::DropShadow(from_value), - &IntermediateFilter::DropShadow(to_value)) => { - Ok(IntermediateFilter::DropShadow(try!( - from_value.add_weighted(&to_value, - self_portion, - other_portion)))) - }, - (&IntermediateFilter::Url(_), - &IntermediateFilter::Url(_)) => { - Err(()) - }, + (&Filter::DropShadow(ref from_value), &Filter::DropShadow(ref to_value)) => { + Ok(Filter::DropShadow(from_value.add_weighted( + &to_value, + self_portion, + other_portion, + )?)) + }, + (&Filter::Url(_), &Filter::Url(_)) => { + Err(()) + }, % endif _ => { // If specified the different filter functions, // we will need to interpolate as discreate. Err(()) }, } } /// https://drafts.fxtf.org/filters/#animation-of-filters -fn add_weighted_filter_function(from: Option<<&IntermediateFilter>, - to: Option<<&IntermediateFilter>, +fn add_weighted_filter_function(from: Option<<&AnimatedFilter>, + to: Option<<&AnimatedFilter>, self_portion: f64, - other_portion: f64) -> Result<IntermediateFilter, ()> { + other_portion: f64) -> Result<AnimatedFilter, ()> { match (from, to) { (Some(f), Some(t)) => { add_weighted_filter_function_impl(f, t, self_portion, other_portion) }, (Some(f), None) => { add_weighted_filter_function_impl(f, f, self_portion, 0.0) }, (None, Some(t)) => { add_weighted_filter_function_impl(t, t, other_portion, 0.0) }, _ => { Err(()) } } } -fn compute_filter_square_distance(from: &IntermediateFilter, - to: &IntermediateFilter) +fn compute_filter_square_distance(from: &AnimatedFilter, + to: &AnimatedFilter) -> Result<f64, ()> { match (from, to) { % for func in FILTER_FUNCTIONS : - (&IntermediateFilter::${func}(f), - &IntermediateFilter::${func}(t)) => { + (&Filter::${func}(f), + &Filter::${func}(t)) => { Ok(try!(f.compute_squared_distance(&t))) }, % endfor % if product == "gecko": - (&IntermediateFilter::DropShadow(f), - &IntermediateFilter::DropShadow(t)) => { + (&Filter::DropShadow(ref f), &Filter::DropShadow(ref t)) => { Ok(try!(f.compute_squared_distance(&t))) }, % endif _ => { Err(()) } } } -impl Animatable for IntermediateFilters { +impl Animatable for AnimatedFilterList { #[inline] fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { - let mut filters: IntermediateFilters = Vec::new(); - let mut from_iter = self.iter(); - let mut to_iter = (&other).iter(); + let mut filters = vec![]; + let mut from_iter = self.0.iter(); + let mut to_iter = other.0.iter(); let mut from = from_iter.next(); let mut to = to_iter.next(); - while (from,to) != (None, None) { + while from.is_some() || to.is_some() { filters.push(try!(add_weighted_filter_function(from, to, self_portion, other_portion))); - if from != None { + if from.is_some() { from = from_iter.next(); } - if to != None { + if to.is_some() { to = to_iter.next(); } } - Ok(filters) + Ok(filters.into()) } fn add(&self, other: &Self) -> Result<Self, ()> { - let from_list = &self; - let to_list = &other; - let filters: IntermediateFilters = - vec![&from_list[..], &to_list[..]].concat(); - Ok(filters) + Ok(self.0.iter().chain(other.0.iter()).cloned().collect::<Vec<_>>().into()) } #[inline] fn compute_distance(&self, other: &Self) -> Result<f64, ()> { self.compute_squared_distance(other).map(|sd| sd.sqrt()) } #[inline] fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { let mut square_distance: f64 = 0.0; - let mut from_iter = self.iter(); - let mut to_iter = (&other).iter(); + let mut from_iter = self.0.iter(); + let mut to_iter = other.0.iter(); let mut from = from_iter.next(); let mut to = to_iter.next(); - while (from,to) != (None, None) { + while from.is_some() || to.is_some() { let current_square_distance: f64 ; - if from == None { + if from.is_none() { let none = try!(add_weighted_filter_function(to, to, 0.0, 0.0)); current_square_distance = compute_filter_square_distance(&none, &(to.unwrap())).unwrap(); to = to_iter.next(); - } else if to == None { + } else if to.is_none() { let none = try!(add_weighted_filter_function(from, from, 0.0, 0.0)); current_square_distance = compute_filter_square_distance(&none, &(from.unwrap())).unwrap(); from = from_iter.next(); } else { current_square_distance = compute_filter_square_distance(&(from.unwrap()),
--- a/servo/components/style/properties/longhand/effects.mako.rs +++ b/servo/components/style/properties/longhand/effects.mako.rs @@ -36,349 +36,25 @@ ${helpers.predefined_type("clip", "ClipRectOrAuto", "computed::ClipRectOrAuto::auto()", animation_value_type="ComputedValue", boxed="True", allow_quirks=True, spec="https://drafts.fxtf.org/css-masking/#clip-property")} -<%helpers:longhand name="filter" animation_value_type="IntermediateFilters" extra_prefixes="webkit" - flags="CREATES_STACKING_CONTEXT FIXPOS_CB" - spec="https://drafts.fxtf.org/filters/#propdef-filter"> - //pub use self::computed_value::T as SpecifiedValue; - use std::fmt; - use style_traits::{HasViewportPercentage, ToCss}; - use values::CSSFloat; - use values::specified::{Angle, Length}; - #[cfg(feature = "gecko")] - use values::specified::Shadow; - #[cfg(feature = "gecko")] - use values::specified::url::SpecifiedUrl; - - #[derive(Clone, Debug, HasViewportPercentage, PartialEq)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub struct SpecifiedValue(pub Vec<SpecifiedFilter>); - - impl HasViewportPercentage for SpecifiedFilter { - fn has_viewport_percentage(&self) -> bool { - match *self { - SpecifiedFilter::Blur(ref length) => length.has_viewport_percentage(), - _ => false - } - } - } - - // TODO(pcwalton): `drop-shadow` - #[derive(Clone, PartialEq, Debug)] - #[cfg_attr(feature = "servo", derive(HeapSizeOf))] - pub enum SpecifiedFilter { - Blur(Length), - Brightness(CSSFloat), - Contrast(CSSFloat), - Grayscale(CSSFloat), - HueRotate(Angle), - Invert(CSSFloat), - Opacity(CSSFloat), - Saturate(CSSFloat), - Sepia(CSSFloat), - % if product == "gecko": - DropShadow(Shadow), - Url(SpecifiedUrl), - % endif - } - - pub mod computed_value { - use app_units::Au; - use values::CSSFloat; - #[cfg(feature = "gecko")] - use values::computed::Shadow; - use values::computed::Angle; - #[cfg(feature = "gecko")] - use values::specified::url::SpecifiedUrl; - - #[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(Shadow), - Url(SpecifiedUrl), - % 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. - #[inline] - pub fn new(filters: Vec<Filter>) -> T { - T - { - filters: filters, - } - } - - /// Adds a new filter to the filter pipeline. - #[inline] - pub fn push(&mut self, filter: Filter) { - self.filters.push(filter) - } - - /// Returns true if this filter pipeline is empty and false otherwise. - #[inline] - pub fn is_empty(&self) -> bool { - self.filters.is_empty() - } - - /// Returns the resulting opacity of this filter pipeline. - #[inline] - pub fn opacity(&self) -> CSSFloat { - let mut opacity = 1.0; - - for filter in &self.filters { - if let Filter::Opacity(ref opacity_value) = *filter { - opacity *= *opacity_value - } - } - opacity - } - } - } - - impl ToCss for computed_value::T { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - let mut iter = self.filters.iter(); - if let Some(filter) = iter.next() { - filter.to_css(dest)?; - } else { - dest.write_str("none")?; - return Ok(()) - } - for filter in iter { - dest.write_str(" ")?; - filter.to_css(dest)?; - } - Ok(()) - } - } - - impl ToCss for SpecifiedValue { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - let mut iter = self.0.iter(); - if let Some(filter) = iter.next() { - filter.to_css(dest)?; - } else { - dest.write_str("none")?; - return Ok(()) - } - for filter in iter { - dest.write_str(" ")?; - filter.to_css(dest)?; - } - Ok(()) - } - } - - impl ToCss for computed_value::Filter { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - computed_value::Filter::Blur(ref value) => { - dest.write_str("blur(")?; - value.to_css(dest)?; - dest.write_str(")")?; - } - computed_value::Filter::Brightness(value) => write!(dest, "brightness({})", value)?, - computed_value::Filter::Contrast(value) => write!(dest, "contrast({})", value)?, - computed_value::Filter::Grayscale(value) => write!(dest, "grayscale({})", value)?, - computed_value::Filter::HueRotate(value) => { - dest.write_str("hue-rotate(")?; - value.to_css(dest)?; - dest.write_str(")")?; - } - computed_value::Filter::Invert(value) => write!(dest, "invert({})", value)?, - computed_value::Filter::Opacity(value) => write!(dest, "opacity({})", value)?, - computed_value::Filter::Saturate(value) => write!(dest, "saturate({})", value)?, - computed_value::Filter::Sepia(value) => write!(dest, "sepia({})", value)?, - % if product == "gecko": - computed_value::Filter::DropShadow(shadow) => { - dest.write_str("drop-shadow(")?; - shadow.to_css(dest)?; - dest.write_str(")")?; - } - computed_value::Filter::Url(ref url) => { - url.to_css(dest)?; - } - % endif - } - Ok(()) - } - } - - impl ToCss for SpecifiedFilter { - fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - SpecifiedFilter::Blur(ref value) => { - dest.write_str("blur(")?; - value.to_css(dest)?; - dest.write_str(")")?; - } - SpecifiedFilter::Brightness(value) => write!(dest, "brightness({})", value)?, - SpecifiedFilter::Contrast(value) => write!(dest, "contrast({})", value)?, - SpecifiedFilter::Grayscale(value) => write!(dest, "grayscale({})", value)?, - SpecifiedFilter::HueRotate(value) => { - dest.write_str("hue-rotate(")?; - value.to_css(dest)?; - dest.write_str(")")?; - } - SpecifiedFilter::Invert(value) => write!(dest, "invert({})", value)?, - SpecifiedFilter::Opacity(value) => write!(dest, "opacity({})", value)?, - SpecifiedFilter::Saturate(value) => write!(dest, "saturate({})", value)?, - SpecifiedFilter::Sepia(value) => write!(dest, "sepia({})", value)?, - % if product == "gecko": - SpecifiedFilter::DropShadow(ref shadow) => { - dest.write_str("drop-shadow(")?; - shadow.to_css(dest)?; - dest.write_str(")")?; - } - SpecifiedFilter::Url(ref url) => { - url.to_css(dest)?; - } - % endif - } - Ok(()) - } - } - - #[inline] - pub fn get_initial_value() -> computed_value::T { - computed_value::T::new(Vec::new()) - } - - pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) - -> Result<SpecifiedValue, ParseError<'i>> { - let mut filters = Vec::new(); - if input.try(|input| input.expect_ident_matching("none")).is_ok() { - return Ok(SpecifiedValue(filters)) - } - loop { - % if product == "gecko": - if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { - filters.push(SpecifiedFilter::Url(url)); - } else - % endif - if let Ok(function_name) = input.try(|input| input.expect_function()) { - filters.push(input.parse_nested_block(|input| { - match_ignore_ascii_case! { &function_name, - "blur" => specified::Length::parse_non_negative(context, input).map(SpecifiedFilter::Blur), - "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(context, 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" => specified::Shadow::parse(context, input, true) - .map(SpecifiedFilter::DropShadow), - % endif - _ => Err(StyleParseError::UnexpectedFunction(function_name.clone()).into()) - } - })?); - } else if filters.is_empty() { - return Err(StyleParseError::UnspecifiedError.into()) - } else { - return Ok(SpecifiedValue(filters)) - } - } - } - - fn parse_factor<'i, 't>(input: &mut Parser<'i, 't>) -> Result<::values::CSSFloat, ParseError<'i>> { - use cssparser::Token; - match input.next() { - Ok(Token::Number { value, .. }) if value.is_sign_positive() => Ok(value), - Ok(Token::Percentage { unit_value, .. }) if unit_value.is_sign_positive() => Ok(unit_value), - Ok(t) => Err(BasicParseError::UnexpectedToken(t).into()), - Err(e) => Err(e.into()) - } - } - - 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(ref 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(ref factor) => { - computed_value::Filter::HueRotate(factor.to_computed_value(context)) - }, - 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(ref shadow) => { - computed_value::Filter::DropShadow(shadow.to_computed_value(context)) - }, - SpecifiedFilter::Url(ref url) => { - computed_value::Filter::Url(url.to_computed_value(context)) - } - % endif - } - }).collect() } - } - - fn from_computed_value(computed: &computed_value::T) -> Self { - SpecifiedValue(computed.filters.iter().map(|value| { - match *value { - computed_value::Filter::Blur(factor) => - SpecifiedFilter::Blur(ToComputedValue::from_computed_value(&factor)), - 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(ref factor) => { - SpecifiedFilter::HueRotate(ToComputedValue::from_computed_value(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(ref shadow) => { - SpecifiedFilter::DropShadow( - ToComputedValue::from_computed_value(shadow), - ) - } - computed_value::Filter::Url(ref url) => { - SpecifiedFilter::Url( - ToComputedValue::from_computed_value(url), - ) - } - % endif - } - }).collect()) - } - } -</%helpers:longhand> +${helpers.predefined_type( + "filter", + "FilterList", + "computed::FilterList::none()", + animation_value_type="AnimatedFilterList", + extra_prefixes="webkit", + flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + spec="https://drafts.fxtf.org/filters/#propdef-filter", +)} ${helpers.single_keyword("mix-blend-mode", """normal multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue saturation color luminosity""", gecko_constant_prefix="NS_STYLE_BLEND", animation_value_type="discrete", flags="CREATES_STACKING_CONTEXT", spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode")}
--- a/servo/components/style/properties/properties.mako.rs +++ b/servo/components/style/properties/properties.mako.rs @@ -2059,17 +2059,17 @@ impl ComputedValues { /// Return true if the effects force the transform style to be Flat pub fn overrides_transform_style(&self) -> bool { use computed_values::mix_blend_mode; let effects = self.get_effects(); // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported. effects.opacity < 1.0 || - !effects.filter.is_empty() || + !effects.filter.0.is_empty() || !effects.clip.is_auto() || effects.mix_blend_mode != mix_blend_mode::T::normal } /// https://drafts.csswg.org/css-transforms/#grouping-property-values pub fn get_used_transform_style(&self) -> computed_values::transform_style::T { use computed_values::transform_style;
new file mode 100644 --- /dev/null +++ b/servo/components/style/values/animated/effects.rs @@ -0,0 +1,202 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Animated types for CSS values related to effects. + +use properties::animated_properties::Animatable; +#[cfg(feature = "gecko")] +use properties::animated_properties::IntermediateColor; +use values::computed::{Angle, Number}; +use values::computed::effects::DropShadow as ComputedDropShadow; +use values::computed::effects::Filter as ComputedFilter; +use values::computed::effects::FilterList as ComputedFilterList; +use values::computed::length::Length; +use values::generics::effects::Filter as GenericFilter; +use values::generics::effects::FilterList as GenericFilterList; + +/// An animated value for the `filter` property. +pub type FilterList = GenericFilterList<Filter>; + +/// An animated value for a single `filter`. +pub type Filter = GenericFilter< + Angle, + // FIXME: Should be `NumberOrPercentage`. + Number, + Length, + DropShadow +>; + +/// An animated value for the `drop-shadow()` filter. +/// +/// Currently unsupported outside of Gecko. +#[cfg(not(feature = "gecko"))] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[derive(Clone, Debug, PartialEq)] +pub enum DropShadow {} + +/// An animated value for the `drop-shadow()` filter. +/// +/// Contrary to the canonical order from the spec, the color is serialised +/// first, like in Gecko and Webkit. +#[cfg(feature = "gecko")] +#[derive(Clone, Debug, PartialEq)] +pub struct DropShadow { + /// Color. + pub color: IntermediateColor, + /// Horizontal radius. + pub horizontal: Length, + /// Vertical radius. + pub vertical: Length, + /// Blur radius. + pub blur: Length, +} + +impl From<ComputedFilterList> for FilterList { + #[inline] + fn from(filters: ComputedFilterList) -> Self { + filters.0.into_vec().into_iter().map(|f| f.into()).collect::<Vec<_>>().into() + } +} + +impl From<FilterList> for ComputedFilterList { + #[inline] + fn from(filters: FilterList) -> Self { + filters.0.into_vec().into_iter().map(|f| f.into()).collect::<Vec<_>>().into() + } +} + +impl From<ComputedFilter> for Filter { + #[inline] + fn from(filter: ComputedFilter) -> Self { + match filter { + GenericFilter::Blur(angle) => GenericFilter::Blur(angle), + GenericFilter::Brightness(factor) => GenericFilter::Brightness(factor), + GenericFilter::Contrast(factor) => GenericFilter::Contrast(factor), + GenericFilter::Grayscale(factor) => GenericFilter::Grayscale(factor), + GenericFilter::HueRotate(factor) => GenericFilter::HueRotate(factor), + GenericFilter::Invert(factor) => GenericFilter::Invert(factor), + GenericFilter::Opacity(factor) => GenericFilter::Opacity(factor), + GenericFilter::Saturate(factor) => GenericFilter::Saturate(factor), + GenericFilter::Sepia(factor) => GenericFilter::Sepia(factor), + GenericFilter::DropShadow(shadow) => { + GenericFilter::DropShadow(shadow.into()) + }, + #[cfg(feature = "gecko")] + GenericFilter::Url(url) => GenericFilter::Url(url), + } + } +} + +impl From<Filter> for ComputedFilter { + #[inline] + fn from(filter: Filter) -> Self { + match filter { + GenericFilter::Blur(angle) => GenericFilter::Blur(angle), + GenericFilter::Brightness(factor) => GenericFilter::Brightness(factor), + GenericFilter::Contrast(factor) => GenericFilter::Contrast(factor), + GenericFilter::Grayscale(factor) => GenericFilter::Grayscale(factor), + GenericFilter::HueRotate(factor) => GenericFilter::HueRotate(factor), + GenericFilter::Invert(factor) => GenericFilter::Invert(factor), + GenericFilter::Opacity(factor) => GenericFilter::Opacity(factor), + GenericFilter::Saturate(factor) => GenericFilter::Saturate(factor), + GenericFilter::Sepia(factor) => GenericFilter::Sepia(factor), + GenericFilter::DropShadow(shadow) => { + GenericFilter::DropShadow(shadow.into()) + }, + #[cfg(feature = "gecko")] + GenericFilter::Url(url) => GenericFilter::Url(url.clone()) + } + } +} + +impl From<ComputedDropShadow> for DropShadow { + #[cfg(not(feature = "gecko"))] + #[inline] + fn from(shadow: ComputedDropShadow) -> Self { + match shadow {} + } + + #[cfg(feature = "gecko")] + #[inline] + fn from(shadow: ComputedDropShadow) -> Self { + DropShadow { + color: shadow.color.into(), + horizontal: shadow.horizontal, + vertical: shadow.vertical, + blur: shadow.blur, + } + } +} + +impl From<DropShadow> for ComputedDropShadow { + #[cfg(not(feature = "gecko"))] + #[inline] + fn from(shadow: DropShadow) -> Self { + match shadow {} + } + + #[cfg(feature = "gecko")] + #[inline] + fn from(shadow: DropShadow) -> Self { + ComputedDropShadow { + color: shadow.color.into(), + horizontal: shadow.horizontal, + vertical: shadow.vertical, + blur: shadow.blur, + } + } +} + +impl Animatable for DropShadow { + #[cfg(not(feature = "gecko"))] + #[inline] + fn add_weighted(&self, _other: &Self, _self_portion: f64, _other_portion: f64) -> Result<Self, ()> { + match *self {} + } + + #[cfg(feature = "gecko")] + #[inline] + fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> { + let color = self.color.add_weighted(&other.color, self_portion, other_portion)?; + let horizontal = self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?; + let vertical = self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?; + let blur = self.blur.add_weighted(&other.blur, self_portion, other_portion)?; + + Ok(DropShadow { + color: color, + horizontal: horizontal, + vertical: vertical, + blur: blur, + }) + } + + #[cfg(not(feature = "gecko"))] + #[inline] + fn compute_distance(&self, _other: &Self) -> Result<f64, ()> { + match *self {} + } + + #[cfg(feature = "gecko")] + #[inline] + fn compute_distance(&self, other: &Self) -> Result<f64, ()> { + self.compute_squared_distance(other).map(|sd| sd.sqrt()) + } + + #[cfg(not(feature = "gecko"))] + #[inline] + fn compute_squared_distance(&self, _other: &Self) -> Result<f64, ()> { + match *self {} + } + + #[cfg(feature = "gecko")] + #[inline] + fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> { + Ok( + self.color.compute_squared_distance(&other.color)? + + self.horizontal.compute_squared_distance(&other.horizontal)? + + self.vertical.compute_squared_distance(&other.vertical)? + + self.blur.compute_squared_distance(&other.blur)? + ) + } +}
new file mode 100644 --- /dev/null +++ b/servo/components/style/values/animated/mod.rs @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Animated values. +//! +//! Some values, notably colors, cannot be interpolated directly with their +//! computed values and need yet another intermediate representation. This +//! module's raison d'être is to ultimately contain all these types. + +pub mod effects;
new file mode 100644 --- /dev/null +++ b/servo/components/style/values/computed/effects.rs @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Computed types for CSS values related to effects. + +use values::computed::{Angle, Number}; +#[cfg(feature = "gecko")] +use values::computed::color::Color; +use values::computed::length::Length; +use values::generics::effects::Filter as GenericFilter; +use values::generics::effects::FilterList as GenericFilterList; + +/// A computed value for the `filter` property. +pub type FilterList = GenericFilterList<Filter>; + +/// A computed value for a single `filter`. +pub type Filter = GenericFilter< + Angle, + // FIXME: Should be `NumberOrPercentage`. + Number, + Length, + DropShadow, +>; + +/// A computed value for the `drop-shadow()` filter. +/// +/// Currently unsupported outside of Gecko. +#[cfg(not(feature = "gecko"))] +#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] +#[derive(Clone, Debug, PartialEq, ToCss)] +pub enum DropShadow {} + +/// A computed value for the `drop-shadow()` filter. +/// +/// Contrary to the canonical order from the spec, the color is serialised +/// first, like in Gecko and Webkit. +#[cfg(feature = "gecko")] +#[derive(Clone, Debug, PartialEq, ToCss)] +pub struct DropShadow { + /// Color. + pub color: Color, + /// Horizontal radius. + pub horizontal: Length, + /// Vertical radius. + pub vertical: Length, + /// Blur radius. + pub blur: Length, +} + +impl FilterList { + /// Returns the resulting opacity of this filter pipeline. + pub fn opacity(&self) -> Number { + let mut opacity = 0.; + for filter in &*self.0 { + if let GenericFilter::Opacity(factor) = *filter { + opacity *= factor + } + } + opacity + } +}
--- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -23,16 +23,17 @@ use super::generics::grid::TrackList as use super::specified; pub use app_units::Au; pub use properties::animated_properties::TransitionProperty; pub use self::background::BackgroundSize; pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth}; pub use self::border::{BorderRadius, BorderCornerRadius}; pub use self::color::{Color, RGBAColor}; +pub use self::effects::FilterList; pub use self::flex::FlexBasis; pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect}; #[cfg(feature = "gecko")] pub use self::gecko::ScrollSnapPoint; pub use self::rect::LengthOrNumberRect; pub use super::{Auto, Either, None_}; #[cfg(feature = "gecko")] pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; @@ -44,16 +45,17 @@ pub use self::length::{LengthOrPercentag pub use self::position::Position; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing}; pub use self::transform::{TimingFunction, TransformOrigin}; pub mod background; pub mod basic_shape; pub mod border; pub mod color; +pub mod effects; pub mod flex; pub mod image; #[cfg(feature = "gecko")] pub mod gecko; pub mod length; pub mod position; pub mod rect; pub mod text; @@ -244,16 +246,32 @@ impl<T> ToComputedValue for Vec<T> } #[inline] fn from_computed_value(computed: &Self::ComputedValue) -> Self { computed.iter().map(T::from_computed_value).collect() } } +impl<T> ToComputedValue for Box<[T]> + where T: ToComputedValue +{ + type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + self.iter().map(|item| item.to_computed_value(context)).collect::<Vec<_>>().into_boxed_slice() + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + computed.iter().map(T::from_computed_value).collect::<Vec<_>>().into_boxed_slice() + } +} + /// A marker trait to represent that the specified value is also the computed /// value. pub trait ComputedValueAsSpecified {} impl<T> ToComputedValue for T where T: ComputedValueAsSpecified + Clone, { type ComputedValue = T;
new file mode 100644 --- /dev/null +++ b/servo/components/style/values/generics/counters.rs @@ -0,0 +1,51 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Generic types for counters-related CSS values. + +use std::fmt; +use style_traits::{HasViewportPercentage, ToCss}; +use values::CustomIdent; + +/// A generic value for the `counter-increment` property. +/// +/// Keyword `none` is represented by an empty slice. +#[derive(Clone, Debug, PartialEq, ToComputedValue)] +pub struct CounterIncrement<Integer>(Box<[(CustomIdent, Integer)]); + +impl<I> CounterIncrement<I> { + /// Returns `none`. + #[inline] + pub fn none() -> Self { + CounterIncrement(vec![].into_boxed_slice()) + } +} + +impl<I> HasViewportPercentage for CounterIncrement<I> { + #[inline] fn has_viewport_percentage(&self) -> bool { false } +} + +impl<I> ToCss for CounterIncrement<I> +where + I: ToCss, +{ + #[inline] + fn to_css<W>(&self, dest: &mut W) -> fmt::Result + where + W: fmt::Write, + { + if self.0.is_empty() { + return dest.write_str("none"); + } + for (&(ref name, ref value), i) in self.0.iter().enumerate() { + if i != 0 { + dest.write_str(" ")?; + } + name.to_css(dest)?; + dest.write_str(" ")?; + value.to_css(dest)?; + } + Ok(()) + } +}
new file mode 100644 --- /dev/null +++ b/servo/components/style/values/generics/effects.rs @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Generic types for CSS values related to effects. + +use std::fmt; +use style_traits::ToCss; +#[cfg(feature = "gecko")] +use values::specified::url::SpecifiedUrl; + +/// A generic value for the `filter` property. +/// +/// Keyword `none` is represented by an empty slice. +#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] +#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue)] +pub struct FilterList<Filter>(pub Box<[Filter]>); + +/// A generic value for a single `filter`. +#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] +#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)] +pub enum Filter<Angle, Factor, Length, DropShadow> { + /// `blur(<length>)` + #[css(function)] + Blur(Length), + /// `brightness(<factor>)` + #[css(function)] + Brightness(Factor), + /// `contrast(<factor>)` + #[css(function)] + Contrast(Factor), + /// `grayscale(<factor>)` + #[css(function)] + Grayscale(Factor), + /// `hue-rotate(<angle>)` + #[css(function)] + HueRotate(Angle), + /// `invert(<factor>)` + #[css(function)] + Invert(Factor), + /// `opacity(<factor>)` + #[css(function)] + Opacity(Factor), + /// `saturate(<factor>)` + #[css(function)] + Saturate(Factor), + /// `sepia(<factor>)` + #[css(function)] + Sepia(Factor), + /// `drop-shadow(...)` + #[css(function)] + DropShadow(DropShadow), + /// `<url>` + #[cfg(feature = "gecko")] + Url(SpecifiedUrl), +} + +impl<F> FilterList<F> { + /// Returns `none`. + #[inline] + pub fn none() -> Self { + FilterList(vec![].into_boxed_slice()) + } +} + +impl<F> From<Vec<F>> for FilterList<F> { + #[inline] + fn from(vec: Vec<F>) -> Self { + FilterList(vec.into_boxed_slice()) + } +} + +impl<F> ToCss for FilterList<F> +where + F: ToCss, +{ + fn to_css<W>(&self, dest: &mut W) -> fmt::Result + where + W: fmt::Write + { + if let Some((first, rest)) = self.0.split_first() { + first.to_css(dest)?; + for filter in rest { + dest.write_str(" ")?; + filter.to_css(dest)?; + } + Ok(()) + } else { + dest.write_str("none") + } + } +}
--- a/servo/components/style/values/generics/mod.rs +++ b/servo/components/style/values/generics/mod.rs @@ -11,16 +11,17 @@ use parser::{Parse, ParserContext}; use std::fmt; use style_traits::{OneOrMoreSeparated, CommaSeparator, ToCss, ParseError, StyleParseError}; use super::CustomIdent; use values::specified::url::SpecifiedUrl; pub mod background; pub mod basic_shape; pub mod border; +pub mod effects; pub mod flex; #[cfg(feature = "gecko")] pub mod gecko; pub mod grid; pub mod image; pub mod position; pub mod rect; pub mod text;
--- a/servo/components/style/values/mod.rs +++ b/servo/components/style/values/mod.rs @@ -13,16 +13,17 @@ pub use cssparser::{RGBA, Token, Parser, use parser::{Parse, ParserContext}; use selectors::parser::SelectorParseError; use std::ascii::AsciiExt; use std::borrow::Cow; use std::fmt::{self, Debug}; use std::hash; use style_traits::{ToCss, ParseError, StyleParseError}; +pub mod animated; pub mod computed; pub mod generics; pub mod specified; /// A CSS float value. pub type CSSFloat = f32; /// A CSS integer value.
new file mode 100644 --- /dev/null +++ b/servo/components/style/values/specified/effects.rs @@ -0,0 +1,244 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Specified types for CSS values related to effects. + +use cssparser::{BasicParseError, Parser, Token}; +use parser::{Parse, ParserContext}; +#[cfg(feature = "gecko")] +use std::fmt; +use style_traits::ParseError; +#[cfg(not(feature = "gecko"))] +use style_traits::StyleParseError; +#[cfg(feature = "gecko")] +use style_traits::ToCss; +use values::computed::{Context, Number as ComputedNumber, ToComputedValue}; +use values::computed::effects::DropShadow as ComputedDropShadow; +use values::generics::effects::Filter as GenericFilter; +use values::generics::effects::FilterList as GenericFilterList; +use values::specified::{Angle, Percentage}; +#[cfg(feature = "gecko")] +use values::specified::color::Color; +use values::specified::length::Length; +#[cfg(feature = "gecko")] +use values::specified::url::SpecifiedUrl; + +/// A specified value for the `filter` property. +pub type FilterList = GenericFilterList<Filter>; + +/// A specified value for a single `filter`. +pub type Filter = GenericFilter<Angle, Factor, Length, DropShadow>; + +/// A value for the `<factor>` parts in `Filter`. +/// +/// FIXME: Should be `NumberOrPercentage`, but Gecko doesn't support that yet. +#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] +#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)] +pub enum Factor { + /// Literal number. + Number(ComputedNumber), + /// Literal percentage. + Percentage(Percentage), +} + +/// A specified value for the `drop-shadow()` filter. +/// +/// Currently unsupported outside of Gecko. +#[cfg(not(feature = "gecko"))] +#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] +#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)] +pub enum DropShadow {} + +/// A specified value for the `drop-shadow()` filter. +/// +/// Contrary to the canonical order from the spec, the color is serialised +/// first, like in Gecko's computed values and in all Webkit's values. +#[cfg(feature = "gecko")] +#[derive(Clone, Debug, HasViewportPercentage, PartialEq)] +pub struct DropShadow { + /// Color. + pub color: Option<Color>, + /// Horizontal radius. + pub horizontal: Length, + /// Vertical radius. + pub vertical: Length, + /// Blur radius. + pub blur: Option<Length>, +} + +impl Parse for FilterList { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result<Self, ParseError<'i>> { + let mut filters = vec![]; + while let Ok(filter) = input.try(|i| Filter::parse(context, i)) { + filters.push(filter); + } + if filters.is_empty() { + input.expect_ident_matching("none")?; + } + Ok(GenericFilterList(filters.into_boxed_slice())) + } +} + +impl Parse for Filter { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result<Self, ParseError<'i>> { + #[cfg(feature = "gecko")] + { + if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { + return Ok(GenericFilter::Url(url)); + } + } + let function = input.expect_function()?; + input.parse_nested_block(|i| { + try_match_ident_ignore_ascii_case! { function, + "blur" => Ok(GenericFilter::Blur(Length::parse_non_negative(context, i)?)), + "brightness" => Ok(GenericFilter::Brightness(Factor::parse(context, i)?)), + "contrast" => Ok(GenericFilter::Contrast(Factor::parse(context, i)?)), + "grayscale" => Ok(GenericFilter::Grayscale(Factor::parse(context, i)?)), + "hue-rotate" => Ok(GenericFilter::HueRotate(Angle::parse(context, i)?)), + "invert" => Ok(GenericFilter::Invert(Factor::parse(context, i)?)), + "opacity" => Ok(GenericFilter::Opacity(Factor::parse(context, i)?)), + "saturate" => Ok(GenericFilter::Saturate(Factor::parse(context, i)?)), + "sepia" => Ok(GenericFilter::Sepia(Factor::parse(context, i)?)), + "drop-shadow" => Ok(GenericFilter::DropShadow(DropShadow::parse(context, i)?)), + } + }) + } +} + +impl Parse for Factor { + #[inline] + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result<Self, ParseError<'i>> { + match input.next()? { + Token::Number { value, .. } if value.is_sign_positive() => { + Ok(Factor::Number(value)) + }, + Token::Percentage { unit_value, .. } if unit_value.is_sign_positive() => { + Ok(Factor::Percentage(Percentage(unit_value))) + }, + other => Err(BasicParseError::UnexpectedToken(other).into()), + } + } +} + +impl ToComputedValue for Factor { + /// This should actually be `ComputedNumberOrPercentage`, but layout uses + /// `computed::effects::FilterList` directly in `StackingContext`. + type ComputedValue = ComputedNumber; + + #[inline] + fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue { + match *self { + Factor::Number(number) => number, + Factor::Percentage(percentage) => percentage.0, + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + Factor::Number(*computed) + } +} + +impl Parse for DropShadow { + #[cfg(not(feature = "gecko"))] + #[inline] + fn parse<'i, 't>( + _context: &ParserContext, + _input: &mut Parser<'i, 't> + ) -> Result<Self, ParseError<'i>> { + Err(StyleParseError::UnspecifiedError.into()) + } + + #[cfg(feature = "gecko")] + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result<Self, ParseError<'i>> { + let color = input.try(|i| Color::parse(context, i)).ok(); + let horizontal = Length::parse(context, input)?; + let vertical = Length::parse(context, input)?; + let blur = input.try(|i| Length::parse_non_negative(context, i)).ok(); + let color = color.or_else(|| input.try(|i| Color::parse(context, i)).ok()); + Ok(DropShadow { + color: color, + horizontal: horizontal, + vertical: vertical, + blur: blur, + }) + } +} + +impl ToComputedValue for DropShadow { + type ComputedValue = ComputedDropShadow; + + #[cfg(not(feature = "gecko"))] + #[inline] + fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue { + match *self {} + } + + #[cfg(feature = "gecko")] + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + ComputedDropShadow { + color: + self.color.as_ref().unwrap_or(&Color::CurrentColor).to_computed_value(context), + horizontal: self.horizontal.to_computed_value(context), + vertical: self.vertical.to_computed_value(context), + blur: + self.blur.as_ref().unwrap_or(&Length::zero()).to_computed_value(context), + } + } + + #[cfg(not(feature = "gecko"))] + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed {} + } + + #[cfg(feature = "gecko")] + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + DropShadow { + color: Some(ToComputedValue::from_computed_value(&computed.color)), + horizontal: ToComputedValue::from_computed_value(&computed.horizontal), + vertical: ToComputedValue::from_computed_value(&computed.vertical), + blur: Some(ToComputedValue::from_computed_value(&computed.blur)), + } + } +} + +#[cfg(feature = "gecko")] +impl ToCss for DropShadow { + #[inline] + fn to_css<W>(&self, dest: &mut W) -> fmt::Result + where + W: fmt::Write, + { + if let Some(ref color) = self.color { + color.to_css(dest)?; + dest.write_str(" ")?; + } + self.horizontal.to_css(dest)?; + dest.write_str(" ")?; + self.vertical.to_css(dest)?; + if let Some(ref blur) = self.blur { + dest.write_str(" ")?; + blur.to_css(dest)?; + } + Ok(()) + } +}
--- a/servo/components/style/values/specified/length.rs +++ b/servo/components/style/values/specified/length.rs @@ -699,17 +699,17 @@ impl<T: Parse> Either<Length, T> { /// to add special handling for `calc()` and percentages in here in the same way /// as for `Angle` and `Time`, but the lack of this this is otherwise /// undistinguishable (we handle it correctly from `CalcLengthOrPercentage`). /// /// As of today, only `-moz-image-rect` supports percentages without length. /// This is not a regression, and that's a non-standard extension anyway, so I'm /// not implementing it for now. #[derive(Clone, Copy, Debug, Default, HasViewportPercentage, PartialEq)] -#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))] pub struct Percentage(pub CSSFloat); impl ToCss for Percentage { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, { write!(dest, "{}%", self.0 * 100.) }
--- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -28,16 +28,17 @@ use values::specified::calc::CalcNode; pub use properties::animated_properties::TransitionProperty; #[cfg(feature = "gecko")] pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems}; pub use self::background::BackgroundSize; pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth}; pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth}; pub use self::color::{Color, RGBAColor}; +pub use self::effects::FilterList; pub use self::flex::FlexBasis; #[cfg(feature = "gecko")] pub use self::gecko::ScrollSnapPoint; pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient}; pub use self::image::{GradientItem, GradientKind, Image, ImageRect, ImageLayer}; pub use self::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth}; pub use self::length::{FontRelativeLength, Length, LengthOrNone, LengthOrNumber}; pub use self::length::{LengthOrPercentage, LengthOrPercentageOrAuto}; @@ -51,16 +52,17 @@ pub use super::generics::grid::GridLine; #[cfg(feature = "gecko")] pub mod align; pub mod background; pub mod basic_shape; pub mod border; pub mod calc; pub mod color; +pub mod effects; pub mod flex; #[cfg(feature = "gecko")] pub mod gecko; pub mod grid; pub mod image; pub mod length; pub mod position; pub mod rect;
--- a/servo/components/style_traits/viewport.rs +++ b/servo/components/style_traits/viewport.rs @@ -36,17 +36,20 @@ macro_rules! no_viewport_percentage { false } })+ }; } no_viewport_percentage!(bool, f32); -impl<T: HasViewportPercentage> HasViewportPercentage for Box<T> { +impl<T> HasViewportPercentage for Box<T> +where + T: ?Sized + HasViewportPercentage +{ #[inline] fn has_viewport_percentage(&self) -> bool { (**self).has_viewport_percentage() } } impl<T: HasViewportPercentage> HasViewportPercentage for Option<T> { #[inline] @@ -64,16 +67,23 @@ impl<T: HasViewportPercentage, U> HasVie impl<T: HasViewportPercentage> HasViewportPercentage for Vec<T> { #[inline] fn has_viewport_percentage(&self) -> bool { self.iter().any(T::has_viewport_percentage) } } +impl<T: HasViewportPercentage> HasViewportPercentage for [T] { + #[inline] + fn has_viewport_percentage(&self) -> bool { + self.iter().any(T::has_viewport_percentage) + } +} + /// A set of viewport descriptors: /// /// https://drafts.csswg.org/css-device-adapt/#viewport-desc #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, HeapSizeOf))] pub struct ViewportConstraints { /// Width and height: /// * https://drafts.csswg.org/css-device-adapt/#width-desc