зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #17402 - Use generics for the filter property (from servo:derive-all-the-things); r=emilio
Source-Repo: https://github.com/servo/servo Source-Revision: fc2c5b7ef4bd7f36b6aa4e0540adc5b8ef3882c0 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : ffccb6f72d2ac1e74cb406e00a940f07c204100f
This commit is contained in:
Родитель
2d93723913
Коммит
565433f368
|
@ -479,7 +479,7 @@ impl StackingContext {
|
||||||
&Rect::zero(),
|
&Rect::zero(),
|
||||||
&Rect::zero(),
|
&Rect::zero(),
|
||||||
0,
|
0,
|
||||||
filter::T::new(Vec::new()),
|
filter::T::none(),
|
||||||
MixBlendMode::Normal,
|
MixBlendMode::Normal,
|
||||||
None,
|
None,
|
||||||
TransformStyle::Flat,
|
TransformStyle::Flat,
|
||||||
|
|
|
@ -50,7 +50,6 @@ use std::sync::Arc;
|
||||||
use style::computed_values::{background_attachment, background_clip, background_origin};
|
use style::computed_values::{background_attachment, background_clip, background_origin};
|
||||||
use style::computed_values::{background_repeat, border_style, cursor};
|
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::{image_rendering, overflow_x, pointer_events, position, visibility};
|
||||||
use style::computed_values::filter::Filter;
|
|
||||||
use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
|
use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
|
||||||
use style::properties::{self, ServoComputedValues};
|
use style::properties::{self, ServoComputedValues};
|
||||||
use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
|
use style::properties::longhands::border_image_repeat::computed_value::RepeatKeyword;
|
||||||
|
@ -61,6 +60,7 @@ use style::values::computed::{Gradient, GradientItem, LengthOrPercentage};
|
||||||
use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position, Shadow};
|
use style::values::computed::{LengthOrPercentageOrAuto, NumberOrPercentage, Position, Shadow};
|
||||||
use style::values::computed::image::{EndingShape, LineDirection};
|
use style::values::computed::image::{EndingShape, LineDirection};
|
||||||
use style::values::generics::background::BackgroundSize;
|
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::{Circle, Ellipse, EndingShape as GenericEndingShape};
|
||||||
use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
|
use style::values::generics::image::{GradientItem as GenericGradientItem, GradientKind};
|
||||||
use style::values::generics::image::{Image, ShapeExtent};
|
use style::values::generics::image::{Image, ShapeExtent};
|
||||||
|
@ -2006,7 +2006,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
|
|
||||||
// Create the filter pipeline.
|
// Create the filter pipeline.
|
||||||
let effects = self.style().get_effects();
|
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 {
|
if effects.opacity != 1.0 {
|
||||||
filters.push(Filter::Opacity(effects.opacity))
|
filters.push(Filter::Opacity(effects.opacity))
|
||||||
}
|
}
|
||||||
|
@ -2022,7 +2022,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
&border_box,
|
&border_box,
|
||||||
&overflow,
|
&overflow,
|
||||||
self.effective_z_index(),
|
self.effective_z_index(),
|
||||||
filters,
|
filters.into(),
|
||||||
self.style().get_effects().mix_blend_mode.to_mix_blend_mode(),
|
self.style().get_effects().mix_blend_mode.to_mix_blend_mode(),
|
||||||
self.transform_matrix(&border_box),
|
self.transform_matrix(&border_box),
|
||||||
self.style().get_used_transform_style().to_transform_style(),
|
self.style().get_used_transform_style().to_transform_style(),
|
||||||
|
|
|
@ -2467,7 +2467,7 @@ impl Fragment {
|
||||||
if self.style().get_effects().opacity != 1.0 {
|
if self.style().get_effects().opacity != 1.0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !self.style().get_effects().filter.is_empty() {
|
if !self.style().get_effects().filter.0.is_empty() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal {
|
if self.style().get_effects().mix_blend_mode != mix_blend_mode::T::normal {
|
||||||
|
|
|
@ -13,8 +13,9 @@ use gfx::display_list::{BorderDetails, BorderRadii, BoxShadowClipMode, ClippingR
|
||||||
use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal, StackingContextType};
|
use gfx::display_list::{DisplayItem, DisplayList, DisplayListTraversal, StackingContextType};
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use style::computed_values::{image_rendering, mix_blend_mode, transform_style};
|
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::computed::BorderStyle;
|
||||||
|
use style::values::generics::effects::Filter;
|
||||||
use webrender_traits::{self, DisplayListBuilder, ExtendMode};
|
use webrender_traits::{self, DisplayListBuilder, ExtendMode};
|
||||||
use webrender_traits::{LayoutTransform, ClipId, ClipRegionToken};
|
use webrender_traits::{LayoutTransform, ClipId, ClipRegionToken};
|
||||||
|
|
||||||
|
@ -203,8 +204,8 @@ trait ToFilterOps {
|
||||||
|
|
||||||
impl ToFilterOps for filter::T {
|
impl ToFilterOps for filter::T {
|
||||||
fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp> {
|
fn to_filter_ops(&self) -> Vec<webrender_traits::FilterOp> {
|
||||||
let mut result = Vec::with_capacity(self.filters.len());
|
let mut result = Vec::with_capacity(self.0.len());
|
||||||
for filter in self.filters.iter() {
|
for filter in self.0.iter() {
|
||||||
match *filter {
|
match *filter {
|
||||||
Filter::Blur(radius) => result.push(webrender_traits::FilterOp::Blur(radius)),
|
Filter::Blur(radius) => result.push(webrender_traits::FilterOp::Blur(radius)),
|
||||||
Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)),
|
Filter::Brightness(amount) => result.push(webrender_traits::FilterOp::Brightness(amount)),
|
||||||
|
@ -215,6 +216,7 @@ impl ToFilterOps for filter::T {
|
||||||
Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())),
|
Filter::Opacity(amount) => result.push(webrender_traits::FilterOp::Opacity(amount.into())),
|
||||||
Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)),
|
Filter::Saturate(amount) => result.push(webrender_traits::FilterOp::Saturate(amount)),
|
||||||
Filter::Sepia(amount) => result.push(webrender_traits::FilterOp::Sepia(amount)),
|
Filter::Sepia(amount) => result.push(webrender_traits::FilterOp::Sepia(amount)),
|
||||||
|
Filter::DropShadow(ref shadow) => match *shadow {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
|
|
@ -8,6 +8,7 @@ use app_units::Au;
|
||||||
use gecko::values::{convert_rgba_to_nscolor, convert_nscolor_to_rgba};
|
use gecko::values::{convert_rgba_to_nscolor, convert_nscolor_to_rgba};
|
||||||
use gecko_bindings::structs::nsCSSShadowItem;
|
use gecko_bindings::structs::nsCSSShadowItem;
|
||||||
use values::computed::{Color, Shadow};
|
use values::computed::{Color, Shadow};
|
||||||
|
use values::computed::effects::DropShadow;
|
||||||
|
|
||||||
impl nsCSSShadowItem {
|
impl nsCSSShadowItem {
|
||||||
/// Set this item to the given shadow value.
|
/// Set this item to the given shadow value.
|
||||||
|
@ -39,4 +40,36 @@ impl nsCSSShadowItem {
|
||||||
color: Color::rgba(convert_nscolor_to_rgba(self.mColor)),
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3444,7 +3444,7 @@ fn static_assert() {
|
||||||
%>
|
%>
|
||||||
|
|
||||||
pub fn set_filter(&mut self, v: longhands::filter::computed_value::T) {
|
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::nsCSSShadowArray;
|
||||||
use gecko_bindings::structs::nsStyleFilter;
|
use gecko_bindings::structs::nsStyleFilter;
|
||||||
use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
|
use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
|
||||||
|
@ -3464,11 +3464,11 @@ fn static_assert() {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
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());
|
debug_assert!(v.0.len() == self.gecko.mFilters.len());
|
||||||
|
|
||||||
for (servo, gecko_filter) in v.filters.into_iter().zip(self.gecko.mFilters.iter_mut()) {
|
for (servo, gecko_filter) in v.0.into_vec().into_iter().zip(self.gecko.mFilters.iter_mut()) {
|
||||||
match servo {
|
match servo {
|
||||||
% for func in FILTER_FUNCTIONS:
|
% for func in FILTER_FUNCTIONS:
|
||||||
${func}(factor) => fill_filter(NS_STYLE_FILTER_${func.upper()},
|
${func}(factor) => fill_filter(NS_STYLE_FILTER_${func.upper()},
|
||||||
|
@ -3497,7 +3497,7 @@ fn static_assert() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut gecko_shadow = init_shadow(gecko_filter);
|
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) => {
|
Url(ref url) => {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -3515,7 +3515,7 @@ fn static_assert() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_filter(&self) -> longhands::filter::computed_value::T {
|
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 values::specified::url::SpecifiedUrl;
|
||||||
use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
|
use gecko_bindings::structs::NS_STYLE_FILTER_BLUR;
|
||||||
use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
|
use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
|
||||||
|
@ -3534,35 +3534,36 @@ fn static_assert() {
|
||||||
match filter.mType {
|
match filter.mType {
|
||||||
% for func in FILTER_FUNCTIONS:
|
% for func in FILTER_FUNCTIONS:
|
||||||
NS_STYLE_FILTER_${func.upper()} => {
|
NS_STYLE_FILTER_${func.upper()} => {
|
||||||
filters.push(${func}(
|
filters.push(Filter::${func}(
|
||||||
GeckoStyleCoordConvertible::from_gecko_style_coord(
|
GeckoStyleCoordConvertible::from_gecko_style_coord(
|
||||||
&filter.mFilterParameter).unwrap()));
|
&filter.mFilterParameter).unwrap()));
|
||||||
},
|
},
|
||||||
% endfor
|
% endfor
|
||||||
NS_STYLE_FILTER_BLUR => {
|
NS_STYLE_FILTER_BLUR => {
|
||||||
filters.push(Blur(Au::from_gecko_style_coord(
|
filters.push(Filter::Blur(Au::from_gecko_style_coord(
|
||||||
&filter.mFilterParameter).unwrap()));
|
&filter.mFilterParameter).unwrap()));
|
||||||
},
|
},
|
||||||
NS_STYLE_FILTER_HUE_ROTATE => {
|
NS_STYLE_FILTER_HUE_ROTATE => {
|
||||||
filters.push(HueRotate(
|
filters.push(Filter::HueRotate(
|
||||||
GeckoStyleCoordConvertible::from_gecko_style_coord(
|
GeckoStyleCoordConvertible::from_gecko_style_coord(
|
||||||
&filter.mFilterParameter).unwrap()));
|
&filter.mFilterParameter).unwrap()));
|
||||||
},
|
},
|
||||||
NS_STYLE_FILTER_DROP_SHADOW => {
|
NS_STYLE_FILTER_DROP_SHADOW => {
|
||||||
filters.push(unsafe {
|
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 => {
|
NS_STYLE_FILTER_URL => {
|
||||||
filters.push(unsafe {
|
filters.push(unsafe {
|
||||||
(Url(SpecifiedUrl::from_url_value_data(
|
Filter::Url(
|
||||||
&(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap()))
|
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>
|
||||||
|
|
|
@ -14,12 +14,9 @@ use euclid::{Point2D, Size2D};
|
||||||
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
|
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
|
||||||
#[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
|
#[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
|
||||||
#[cfg(feature = "gecko")] use gecko_string_cache::Atom;
|
#[cfg(feature = "gecko")] use gecko_string_cache::Atom;
|
||||||
#[cfg(feature = "gecko")] use gecko::url::SpecifiedUrl;
|
|
||||||
use properties::{CSSWideKeyword, PropertyDeclaration};
|
use properties::{CSSWideKeyword, PropertyDeclaration};
|
||||||
use properties::longhands;
|
use properties::longhands;
|
||||||
use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
|
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_weight::computed_value::T as FontWeight;
|
||||||
use properties::longhands::font_stretch::computed_value::T as FontStretch;
|
use properties::longhands::font_stretch::computed_value::T as FontStretch;
|
||||||
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
|
use properties::longhands::text_shadow::computed_value::T as TextShadowList;
|
||||||
|
@ -37,12 +34,14 @@ use std::cmp;
|
||||||
use style_traits::ParseError;
|
use style_traits::ParseError;
|
||||||
use super::ComputedValues;
|
use super::ComputedValues;
|
||||||
use values::{Auto, CSSFloat, CustomIdent, Either};
|
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::{Angle, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
|
||||||
use values::computed::{BorderCornerRadius, ClipRect};
|
use values::computed::{BorderCornerRadius, ClipRect};
|
||||||
use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
|
use values::computed::{CalcLengthOrPercentage, Color, Context, ComputedValueAsSpecified};
|
||||||
use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToComputedValue};
|
use values::computed::{LengthOrPercentage, MaxLength, MozLength, Shadow, ToComputedValue};
|
||||||
use values::generics::{SVGPaint, SVGPaintKind};
|
use values::generics::{SVGPaint, SVGPaintKind};
|
||||||
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
|
use values::generics::border::BorderCornerRadius as GenericBorderCornerRadius;
|
||||||
|
use values::generics::effects::Filter;
|
||||||
use values::generics::position as generic_position;
|
use values::generics::position as generic_position;
|
||||||
use values::specified::length::Percentage;
|
use values::specified::length::Percentage;
|
||||||
|
|
||||||
|
@ -3196,135 +3195,61 @@ impl Animatable for IntermediateShadowList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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',
|
FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
|
||||||
'HueRotate', 'Invert', 'Opacity', 'Saturate',
|
'HueRotate', 'Invert', 'Opacity', 'Saturate',
|
||||||
'Sepia' ]
|
'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
|
/// https://drafts.fxtf.org/filters/#animation-of-filters
|
||||||
fn add_weighted_filter_function_impl(from: &IntermediateFilter,
|
fn add_weighted_filter_function_impl(from: &AnimatedFilter,
|
||||||
to: &IntermediateFilter,
|
to: &AnimatedFilter,
|
||||||
self_portion: f64,
|
self_portion: f64,
|
||||||
other_portion: f64)
|
other_portion: f64)
|
||||||
-> Result<IntermediateFilter, ()> {
|
-> Result<AnimatedFilter, ()> {
|
||||||
match (from, to) {
|
match (from, to) {
|
||||||
% for func in [ 'Blur', 'HueRotate' ]:
|
% for func in [ 'Blur', 'HueRotate' ]:
|
||||||
(&IntermediateFilter::${func}(from_value),
|
(&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
|
||||||
&IntermediateFilter::${func}(to_value)) => {
|
Ok(Filter::${func}(from_value.add_weighted(
|
||||||
Ok(IntermediateFilter::${func}(
|
&to_value,
|
||||||
try!(from_value.add_weighted(&to_value,
|
self_portion,
|
||||||
self_portion,
|
other_portion,
|
||||||
other_portion))))
|
)?))
|
||||||
},
|
},
|
||||||
% endfor
|
% endfor
|
||||||
% for func in [ 'Grayscale', 'Invert', 'Sepia' ]:
|
% for func in [ 'Grayscale', 'Invert', 'Sepia' ]:
|
||||||
(&IntermediateFilter::${func}(from_value),
|
(&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
|
||||||
&IntermediateFilter::${func}(to_value)) => {
|
Ok(Filter::${func}(add_weighted_with_initial_val(
|
||||||
Ok(IntermediateFilter::${func}(try!(
|
&from_value,
|
||||||
add_weighted_with_initial_val(&from_value,
|
&to_value,
|
||||||
&to_value,
|
self_portion,
|
||||||
self_portion,
|
other_portion,
|
||||||
other_portion,
|
&0.0,
|
||||||
&0.0))))
|
)?))
|
||||||
},
|
},
|
||||||
% endfor
|
% endfor
|
||||||
% for func in [ 'Brightness', 'Contrast', 'Opacity', 'Saturate' ]:
|
% for func in [ 'Brightness', 'Contrast', 'Opacity', 'Saturate' ]:
|
||||||
(&IntermediateFilter::${func}(from_value),
|
(&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
|
||||||
&IntermediateFilter::${func}(to_value)) => {
|
Ok(Filter::${func}(add_weighted_with_initial_val(
|
||||||
Ok(IntermediateFilter::${func}(try!(
|
&from_value,
|
||||||
add_weighted_with_initial_val(&from_value,
|
&to_value,
|
||||||
&to_value,
|
self_portion,
|
||||||
self_portion,
|
other_portion,
|
||||||
other_portion,
|
&1.0,
|
||||||
&1.0))))
|
)?))
|
||||||
},
|
},
|
||||||
% endfor
|
% endfor
|
||||||
% if product == "gecko":
|
% if product == "gecko":
|
||||||
(&IntermediateFilter::DropShadow(from_value),
|
(&Filter::DropShadow(ref from_value), &Filter::DropShadow(ref to_value)) => {
|
||||||
&IntermediateFilter::DropShadow(to_value)) => {
|
Ok(Filter::DropShadow(from_value.add_weighted(
|
||||||
Ok(IntermediateFilter::DropShadow(try!(
|
&to_value,
|
||||||
from_value.add_weighted(&to_value,
|
self_portion,
|
||||||
self_portion,
|
other_portion,
|
||||||
other_portion))))
|
)?))
|
||||||
},
|
},
|
||||||
(&IntermediateFilter::Url(_),
|
(&Filter::Url(_), &Filter::Url(_)) => {
|
||||||
&IntermediateFilter::Url(_)) => {
|
Err(())
|
||||||
Err(())
|
},
|
||||||
},
|
|
||||||
% endif
|
% endif
|
||||||
_ => {
|
_ => {
|
||||||
// If specified the different filter functions,
|
// If specified the different filter functions,
|
||||||
|
@ -3335,10 +3260,10 @@ fn add_weighted_filter_function_impl(from: &IntermediateFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://drafts.fxtf.org/filters/#animation-of-filters
|
/// https://drafts.fxtf.org/filters/#animation-of-filters
|
||||||
fn add_weighted_filter_function(from: Option<<&IntermediateFilter>,
|
fn add_weighted_filter_function(from: Option<<&AnimatedFilter>,
|
||||||
to: Option<<&IntermediateFilter>,
|
to: Option<<&AnimatedFilter>,
|
||||||
self_portion: f64,
|
self_portion: f64,
|
||||||
other_portion: f64) -> Result<IntermediateFilter, ()> {
|
other_portion: f64) -> Result<AnimatedFilter, ()> {
|
||||||
match (from, to) {
|
match (from, to) {
|
||||||
(Some(f), Some(t)) => {
|
(Some(f), Some(t)) => {
|
||||||
add_weighted_filter_function_impl(f, t, self_portion, other_portion)
|
add_weighted_filter_function_impl(f, t, self_portion, other_portion)
|
||||||
|
@ -3353,19 +3278,18 @@ fn add_weighted_filter_function(from: Option<<&IntermediateFilter>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_filter_square_distance(from: &IntermediateFilter,
|
fn compute_filter_square_distance(from: &AnimatedFilter,
|
||||||
to: &IntermediateFilter)
|
to: &AnimatedFilter)
|
||||||
-> Result<f64, ()> {
|
-> Result<f64, ()> {
|
||||||
match (from, to) {
|
match (from, to) {
|
||||||
% for func in FILTER_FUNCTIONS :
|
% for func in FILTER_FUNCTIONS :
|
||||||
(&IntermediateFilter::${func}(f),
|
(&Filter::${func}(f),
|
||||||
&IntermediateFilter::${func}(t)) => {
|
&Filter::${func}(t)) => {
|
||||||
Ok(try!(f.compute_squared_distance(&t)))
|
Ok(try!(f.compute_squared_distance(&t)))
|
||||||
},
|
},
|
||||||
% endfor
|
% endfor
|
||||||
% if product == "gecko":
|
% if product == "gecko":
|
||||||
(&IntermediateFilter::DropShadow(f),
|
(&Filter::DropShadow(ref f), &Filter::DropShadow(ref t)) => {
|
||||||
&IntermediateFilter::DropShadow(t)) => {
|
|
||||||
Ok(try!(f.compute_squared_distance(&t)))
|
Ok(try!(f.compute_squared_distance(&t)))
|
||||||
},
|
},
|
||||||
% endif
|
% endif
|
||||||
|
@ -3375,38 +3299,34 @@ fn compute_filter_square_distance(from: &IntermediateFilter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Animatable for IntermediateFilters {
|
impl Animatable for AnimatedFilterList {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add_weighted(&self, other: &Self,
|
fn add_weighted(&self, other: &Self,
|
||||||
self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
||||||
let mut filters: IntermediateFilters = Vec::new();
|
let mut filters = vec![];
|
||||||
let mut from_iter = self.iter();
|
let mut from_iter = self.0.iter();
|
||||||
let mut to_iter = (&other).iter();
|
let mut to_iter = other.0.iter();
|
||||||
|
|
||||||
let mut from = from_iter.next();
|
let mut from = from_iter.next();
|
||||||
let mut to = to_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,
|
filters.push(try!(add_weighted_filter_function(from,
|
||||||
to,
|
to,
|
||||||
self_portion,
|
self_portion,
|
||||||
other_portion)));
|
other_portion)));
|
||||||
if from != None {
|
if from.is_some() {
|
||||||
from = from_iter.next();
|
from = from_iter.next();
|
||||||
}
|
}
|
||||||
if to != None {
|
if to.is_some() {
|
||||||
to = to_iter.next();
|
to = to_iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(filters)
|
Ok(filters.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(&self, other: &Self) -> Result<Self, ()> {
|
fn add(&self, other: &Self) -> Result<Self, ()> {
|
||||||
let from_list = &self;
|
Ok(self.0.iter().chain(other.0.iter()).cloned().collect::<Vec<_>>().into())
|
||||||
let to_list = &other;
|
|
||||||
let filters: IntermediateFilters =
|
|
||||||
vec![&from_list[..], &to_list[..]].concat();
|
|
||||||
Ok(filters)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -3417,20 +3337,20 @@ impl Animatable for IntermediateFilters {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
fn compute_squared_distance(&self, other: &Self) -> Result<f64, ()> {
|
||||||
let mut square_distance: f64 = 0.0;
|
let mut square_distance: f64 = 0.0;
|
||||||
let mut from_iter = self.iter();
|
let mut from_iter = self.0.iter();
|
||||||
let mut to_iter = (&other).iter();
|
let mut to_iter = other.0.iter();
|
||||||
|
|
||||||
let mut from = from_iter.next();
|
let mut from = from_iter.next();
|
||||||
let mut to = to_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 ;
|
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));
|
let none = try!(add_weighted_filter_function(to, to, 0.0, 0.0));
|
||||||
current_square_distance =
|
current_square_distance =
|
||||||
compute_filter_square_distance(&none, &(to.unwrap())).unwrap();
|
compute_filter_square_distance(&none, &(to.unwrap())).unwrap();
|
||||||
|
|
||||||
to = to_iter.next();
|
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));
|
let none = try!(add_weighted_filter_function(from, from, 0.0, 0.0));
|
||||||
current_square_distance =
|
current_square_distance =
|
||||||
compute_filter_square_distance(&none, &(from.unwrap())).unwrap();
|
compute_filter_square_distance(&none, &(from.unwrap())).unwrap();
|
||||||
|
|
|
@ -41,339 +41,15 @@ ${helpers.predefined_type("clip",
|
||||||
allow_quirks=True,
|
allow_quirks=True,
|
||||||
spec="https://drafts.fxtf.org/css-masking/#clip-property")}
|
spec="https://drafts.fxtf.org/css-masking/#clip-property")}
|
||||||
|
|
||||||
<%helpers:longhand name="filter" animation_value_type="IntermediateFilters" extra_prefixes="webkit"
|
${helpers.predefined_type(
|
||||||
flags="CREATES_STACKING_CONTEXT FIXPOS_CB"
|
"filter",
|
||||||
spec="https://drafts.fxtf.org/filters/#propdef-filter">
|
"FilterList",
|
||||||
//pub use self::computed_value::T as SpecifiedValue;
|
"computed::FilterList::none()",
|
||||||
use std::fmt;
|
animation_value_type="AnimatedFilterList",
|
||||||
use style_traits::{HasViewportPercentage, ToCss};
|
extra_prefixes="webkit",
|
||||||
use values::CSSFloat;
|
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
|
||||||
use values::specified::{Angle, Length};
|
spec="https://drafts.fxtf.org/filters/#propdef-filter",
|
||||||
#[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.single_keyword("mix-blend-mode",
|
${helpers.single_keyword("mix-blend-mode",
|
||||||
"""normal multiply screen overlay darken lighten color-dodge
|
"""normal multiply screen overlay darken lighten color-dodge
|
||||||
|
|
|
@ -2064,7 +2064,7 @@ impl ComputedValues {
|
||||||
let effects = self.get_effects();
|
let effects = self.get_effects();
|
||||||
// TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
|
// TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.
|
||||||
effects.opacity < 1.0 ||
|
effects.opacity < 1.0 ||
|
||||||
!effects.filter.is_empty() ||
|
!effects.filter.0.is_empty() ||
|
||||||
!effects.clip.is_auto() ||
|
!effects.clip.is_auto() ||
|
||||||
effects.mix_blend_mode != mix_blend_mode::T::normal
|
effects.mix_blend_mode != mix_blend_mode::T::normal
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ pub use self::background::BackgroundSize;
|
||||||
pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
|
pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
|
||||||
pub use self::border::{BorderRadius, BorderCornerRadius};
|
pub use self::border::{BorderRadius, BorderCornerRadius};
|
||||||
pub use self::color::{Color, RGBAColor};
|
pub use self::color::{Color, RGBAColor};
|
||||||
|
pub use self::effects::FilterList;
|
||||||
pub use self::flex::FlexBasis;
|
pub use self::flex::FlexBasis;
|
||||||
pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect};
|
pub use self::image::{Gradient, GradientItem, ImageLayer, LineDirection, Image, ImageRect};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -49,6 +50,7 @@ pub mod background;
|
||||||
pub mod basic_shape;
|
pub mod basic_shape;
|
||||||
pub mod border;
|
pub mod border;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
|
pub mod effects;
|
||||||
pub mod flex;
|
pub mod flex;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -249,6 +251,22 @@ impl<T> ToComputedValue for Vec<T>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
/// A marker trait to represent that the specified value is also the computed
|
||||||
/// value.
|
/// value.
|
||||||
pub trait ComputedValueAsSpecified {}
|
pub trait ComputedValueAsSpecified {}
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ use values::specified::url::SpecifiedUrl;
|
||||||
pub mod background;
|
pub mod background;
|
||||||
pub mod basic_shape;
|
pub mod basic_shape;
|
||||||
pub mod border;
|
pub mod border;
|
||||||
|
pub mod effects;
|
||||||
pub mod flex;
|
pub mod flex;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub mod gecko;
|
pub mod gecko;
|
||||||
|
|
|
@ -18,6 +18,7 @@ use std::fmt::{self, Debug};
|
||||||
use std::hash;
|
use std::hash;
|
||||||
use style_traits::{ToCss, ParseError, StyleParseError};
|
use style_traits::{ToCss, ParseError, StyleParseError};
|
||||||
|
|
||||||
|
pub mod animated;
|
||||||
pub mod computed;
|
pub mod computed;
|
||||||
pub mod generics;
|
pub mod generics;
|
||||||
pub mod specified;
|
pub mod specified;
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -704,7 +704,7 @@ impl<T: Parse> Either<Length, T> {
|
||||||
/// This is not a regression, and that's a non-standard extension anyway, so I'm
|
/// This is not a regression, and that's a non-standard extension anyway, so I'm
|
||||||
/// not implementing it for now.
|
/// not implementing it for now.
|
||||||
#[derive(Clone, Copy, Debug, Default, HasViewportPercentage, PartialEq)]
|
#[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);
|
pub struct Percentage(pub CSSFloat);
|
||||||
|
|
||||||
impl ToCss for Percentage {
|
impl ToCss for Percentage {
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub use self::background::BackgroundSize;
|
||||||
pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
||||||
pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth};
|
pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth};
|
||||||
pub use self::color::{Color, RGBAColor};
|
pub use self::color::{Color, RGBAColor};
|
||||||
|
pub use self::effects::FilterList;
|
||||||
pub use self::flex::FlexBasis;
|
pub use self::flex::FlexBasis;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use self::gecko::ScrollSnapPoint;
|
pub use self::gecko::ScrollSnapPoint;
|
||||||
|
@ -56,6 +57,7 @@ pub mod basic_shape;
|
||||||
pub mod border;
|
pub mod border;
|
||||||
pub mod calc;
|
pub mod calc;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
|
pub mod effects;
|
||||||
pub mod flex;
|
pub mod flex;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub mod gecko;
|
pub mod gecko;
|
||||||
|
|
|
@ -41,7 +41,10 @@ macro_rules! no_viewport_percentage {
|
||||||
|
|
||||||
no_viewport_percentage!(bool, f32);
|
no_viewport_percentage!(bool, f32);
|
||||||
|
|
||||||
impl<T: HasViewportPercentage> HasViewportPercentage for Box<T> {
|
impl<T> HasViewportPercentage for Box<T>
|
||||||
|
where
|
||||||
|
T: ?Sized + HasViewportPercentage
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_viewport_percentage(&self) -> bool {
|
fn has_viewport_percentage(&self) -> bool {
|
||||||
(**self).has_viewport_percentage()
|
(**self).has_viewport_percentage()
|
||||||
|
@ -69,6 +72,13 @@ impl<T: HasViewportPercentage> HasViewportPercentage for Vec<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
/// A set of viewport descriptors:
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-device-adapt/#viewport-desc
|
/// https://drafts.csswg.org/css-device-adapt/#viewport-desc
|
||||||
|
|
Загрузка…
Ссылка в новой задаче