From 0f7f9eebc07fa173487acdefeb8b55ce4b8bef22 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Sun, 29 Apr 2018 09:03:31 +1000 Subject: [PATCH] style: Add some attributes for SpecifiedValueInfo to help deriving more from types. Bug: 1434130 Reviewed-by: emilio MozReview-Commit-ID: IyohSTbUO31 --- .../style/values/generics/basic_shape.rs | 4 + components/style/values/generics/border.rs | 1 + components/style/values/generics/counters.rs | 2 +- components/style/values/generics/effects.rs | 1 + components/style/values/generics/grid.rs | 10 ++- components/style/values/generics/image.rs | 1 + components/style/values/generics/transform.rs | 8 +- components/style/values/specified/box.rs | 13 ++- components/style/values/specified/font.rs | 2 + .../style/values/specified/inherited_box.rs | 1 + components/style/values/specified/list.rs | 3 +- components/style/values/specified/mod.rs | 7 +- components/style/values/specified/position.rs | 1 + .../style/values/specified/transform.rs | 6 +- components/style_derive/lib.rs | 2 +- .../style_derive/specified_value_info.rs | 81 ++++++++++++++++--- .../style_traits/specified_value_info.rs | 30 +++++++ 17 files changed, 137 insertions(+), 36 deletions(-) diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index 80375ad8ffa..3bad738c1e6 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -69,6 +69,7 @@ pub enum BasicShape { /// #[allow(missing_docs)] +#[css(function = "inset")] #[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] pub struct InsetRect { @@ -78,6 +79,7 @@ pub struct InsetRect { /// #[allow(missing_docs)] +#[css(function)] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] pub struct Circle { @@ -87,6 +89,7 @@ pub struct Circle { /// #[allow(missing_docs)] +#[css(function)] #[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] pub struct Ellipse { @@ -110,6 +113,7 @@ pub enum ShapeRadius { /// A generic type for representing the `polygon()` function /// /// +#[css(function)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] pub struct Polygon { diff --git a/components/style/values/generics/border.rs b/components/style/values/generics/border.rs index 79c8713bfba..6230a1cf36a 100644 --- a/components/style/values/generics/border.rs +++ b/components/style/values/generics/border.rs @@ -28,6 +28,7 @@ pub struct BorderImageSlice { /// The offsets. pub offsets: Rect, /// Whether to fill the middle part. + #[value_info(represents_keyword)] pub fill: bool, } diff --git a/components/style/values/generics/counters.rs b/components/style/values/generics/counters.rs index 037834abf6e..7373cd8e947 100644 --- a/components/style/values/generics/counters.rs +++ b/components/style/values/generics/counters.rs @@ -59,7 +59,7 @@ impl Deref for CounterReset { /// Keyword `none` is represented by an empty vector. #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] -pub struct Counters(Box<[(CustomIdent, I)]>); +pub struct Counters(#[css(if_empty = "none")] Box<[(CustomIdent, I)]>); impl Default for Counters { #[inline] diff --git a/components/style/values/generics/effects.rs b/components/style/values/generics/effects.rs index 136f45635c1..7a76cdeb7bc 100644 --- a/components/style/values/generics/effects.rs +++ b/components/style/values/generics/effects.rs @@ -19,6 +19,7 @@ pub struct BoxShadow { pub spread: ShapeLength, /// Whether this is an inset box shadow. #[animation(constant)] + #[value_info(represents_keyword)] pub inset: bool, } diff --git a/components/style/values/generics/grid.rs b/components/style/values/generics/grid.rs index d805fb5e0d0..b9ec85ace22 100644 --- a/components/style/values/generics/grid.rs +++ b/components/style/values/generics/grid.rs @@ -198,10 +198,12 @@ pub enum TrackSize { /// and a flexible `` /// /// + #[css(function)] Minmax(TrackBreadth, TrackBreadth), /// A `fit-content` function. /// /// + #[css(function)] FitContent(L), } @@ -346,8 +348,7 @@ where /// The initial argument of the `repeat` function. /// /// -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, - ToComputedValue, ToCss)] +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] pub enum RepeatCount { /// A positive integer. This is allowed only for `` and `` Number(Integer), @@ -384,6 +385,7 @@ impl Parse for RepeatCount { /// values in its computed form. #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] +#[css(function = "repeat")] pub struct TrackRepeat { /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`) pub count: RepeatCount, @@ -481,8 +483,7 @@ pub enum TrackListValue { /// The type of a `` as determined during parsing. /// /// -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, - ToComputedValue)] +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)] pub enum TrackListType { /// [``](https://drafts.csswg.org/css-grid/#typedef-auto-track-list) /// @@ -510,6 +511,7 @@ pub struct TrackList { /// /// In order to avoid parsing the same value multiple times, this does a single traversal /// and arrives at the type of value it has parsed (or bails out gracefully with an error). + #[css(skip)] pub list_type: TrackListType, /// A vector of ` | ` values. pub values: Vec>, diff --git a/components/style/values/generics/image.rs b/components/style/values/generics/image.rs index 4501f94433f..5017559e00c 100644 --- a/components/style/values/generics/image.rs +++ b/components/style/values/generics/image.rs @@ -26,6 +26,7 @@ pub enum Image { /// A `-moz-image-rect` image. Also fairly large and rare. Rect(Box), /// A `-moz-element(# )` + #[css(function = "-moz-element")] Element(Atom), /// A paint worklet image. /// diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index 2c671541ea4..ecd40f5a55c 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -80,7 +80,9 @@ pub struct TransformOrigin { /// A generic timing function. /// /// -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss)] +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, + ToCss)] +#[value_info(ty = "TIMING_FUNCTION")] pub enum TimingFunction { /// `linear | ease | ease-in | ease-out | ease-in-out` Keyword(TimingKeyword), @@ -95,6 +97,7 @@ pub enum TimingFunction { }, /// `step-start | step-end | steps(, [ start | end ]?)` #[css(comma, function)] + #[value_info(other_values = "step-start,step-end")] Steps(Integer, #[css(skip_if = "is_end")] StepPosition), /// `frames()` #[css(comma, function)] @@ -103,7 +106,8 @@ pub enum TimingFunction { #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)] +#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, + SpecifiedValueInfo, ToComputedValue, ToCss)] pub enum TimingKeyword { Linear, Ease, diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index e26d82c8f67..ab2872d26c3 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -9,7 +9,7 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; +use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use values::{CustomIdent, KeyframesName}; use values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; use values::generics::box_::Perspective as GenericPerspective; @@ -298,6 +298,7 @@ impl AnimationIterationCount { /// A value for the `animation-name` property. #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] +#[value_info(other_values = "none")] pub struct AnimationName(pub Option); impl AnimationName { @@ -420,8 +421,9 @@ impl Parse for WillChange { bitflags! { #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] - #[derive(ToComputedValue)] + #[derive(SpecifiedValueInfo, ToComputedValue)] /// These constants match Gecko's `NS_STYLE_TOUCH_ACTION_*` constants. + #[value_info(other_values = "auto,none,manipulation,pan-x,pan-y")] pub struct TouchAction: u8 { /// `none` variant const TOUCH_ACTION_NONE = 1 << 0; @@ -493,8 +495,6 @@ impl Parse for TouchAction { } } -impl SpecifiedValueInfo for TouchAction {} - #[cfg(feature = "gecko")] impl_bitflags_conversions!(TouchAction); @@ -522,7 +522,8 @@ pub fn assert_touch_action_matches() { } bitflags! { - #[derive(MallocSizeOf, ToComputedValue)] + #[derive(MallocSizeOf, ToComputedValue, SpecifiedValueInfo)] + #[value_info(other_values = "none,strict,layout,style,paint")] /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property pub struct Contain: u8 { /// `layout` variant, turns on layout containment @@ -605,8 +606,6 @@ impl Parse for Contain { } } -impl SpecifiedValueInfo for Contain {} - /// A specified value for the `perspective` property. pub type Perspective = GenericPerspective; diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index fedf748d54f..13ca2280997 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -1945,9 +1945,11 @@ pub struct FontSynthesis { /// If a `font-weight` is requested that the font family does not contain, /// the user agent may synthesize the requested weight from the weights /// that do exist in the font family. + #[value_info(represents_keyword)] pub weight: bool, /// If a font-style is requested that the font family does not contain, /// the user agent may synthesize the requested style from the normal face in the font family. + #[value_info(represents_keyword)] pub style: bool, } diff --git a/components/style/values/specified/inherited_box.rs b/components/style/values/specified/inherited_box.rs index da8da7614be..c7756f974f2 100644 --- a/components/style/values/specified/inherited_box.rs +++ b/components/style/values/specified/inherited_box.rs @@ -21,6 +21,7 @@ pub struct ImageOrientation { pub angle: Option, /// Whether or not "flip" was specified + #[value_info(other_values = "flip,from-image")] pub flipped: bool, } diff --git a/components/style/values/specified/list.rs b/components/style/values/specified/list.rs index 89e4afda6fc..ae61bb1ae9c 100644 --- a/components/style/values/specified/list.rs +++ b/components/style/values/specified/list.rs @@ -78,9 +78,10 @@ impl Parse for ListStyleType { /// /// FIXME(emilio): It's a shame that this allocates all the time it's computed, /// probably should just be refcounted. +/// FIXME This can probably derive ToCss. #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] -pub struct Quotes(pub Box<[(Box, Box)]>); +pub struct Quotes(#[css(if_empty = "none")] pub Box<[(Box, Box)]>); impl ToCss for Quotes { fn to_css(&self, dest: &mut CssWriter) -> fmt::Result diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 9b4edfac413..3f89dae0053 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -570,6 +570,7 @@ pub type GridTemplateComponent = GenericGridTemplateComponent, , , ) used by clip and image-region +#[css(function = "rect")] pub struct ClipRect { /// ( | ) pub top: Option, @@ -757,7 +758,9 @@ impl AllowQuirks { /// An attr(...) rule /// /// `[namespace? `|`]? ident` -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)] +#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, + ToComputedValue)] +#[css(function)] pub struct Attr { /// Optional namespace prefix and URL. pub namespace: Option<(Prefix, Namespace)>, @@ -852,5 +855,3 @@ impl ToCss for Attr { dest.write_str(")") } } - -impl SpecifiedValueInfo for Attr {} diff --git a/components/style/values/specified/position.rs b/components/style/values/specified/position.rs index cb4b4aeb45b..0f2669f0a02 100644 --- a/components/style/values/specified/position.rs +++ b/components/style/values/specified/position.rs @@ -431,6 +431,7 @@ pub struct GridAutoFlow { /// Specifiy how auto-placement algorithm fills each `row` or `column` in turn pub autoflow: AutoFlow, /// Specify use `dense` packing algorithm or not + #[value_info(represents_keyword)] pub dense: bool, } diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs index b66e5744f39..835d505836c 100644 --- a/components/style/values/specified/transform.rs +++ b/components/style/values/specified/transform.rs @@ -7,7 +7,7 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; use selectors::parser::SelectorParseErrorKind; -use style_traits::{CssType, ParseError, SpecifiedValueInfo, StyleParseErrorKind}; +use style_traits::{ParseError, StyleParseErrorKind}; use values::computed::{Context, LengthOrPercentage as ComputedLengthOrPercentage}; use values::computed::{Percentage as ComputedPercentage, ToComputedValue}; use values::computed::transform::TimingFunction as ComputedTimingFunction; @@ -246,10 +246,6 @@ pub enum OriginComponent { /// A specified timing function. pub type TimingFunction = generic::TimingFunction; -impl SpecifiedValueInfo for TimingFunction { - const SUPPORTED_TYPES: u8 = CssType::TIMING_FUNCTION; -} - impl Parse for TransformOrigin { fn parse<'i, 't>( context: &ParserContext, diff --git a/components/style_derive/lib.rs b/components/style_derive/lib.rs index 86442070ca1..88f0bbadfb1 100644 --- a/components/style_derive/lib.rs +++ b/components/style_derive/lib.rs @@ -64,7 +64,7 @@ pub fn derive_to_css(stream: TokenStream) -> TokenStream { to_css::derive(input).into() } -#[proc_macro_derive(SpecifiedValueInfo, attributes(css))] +#[proc_macro_derive(SpecifiedValueInfo, attributes(css, value_info))] pub fn derive_specified_value_info(stream: TokenStream) -> TokenStream { let input = syn::parse(stream).unwrap(); specified_value_info::derive(input).into() diff --git a/components/style_derive/specified_value_info.rs b/components/style_derive/specified_value_info.rs index e5e9ac328da..7168ec2509f 100644 --- a/components/style_derive/specified_value_info.rs +++ b/components/style_derive/specified_value_info.rs @@ -4,17 +4,17 @@ use cg; use quote::Tokens; -use syn::{Data, DeriveInput, Fields, Type}; +use syn::{Data, DeriveInput, Fields, Ident,Type}; use to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs}; pub fn derive(mut input: DeriveInput) -> Tokens { - let attrs = cg::parse_input_attrs::(&input); + let css_attrs = cg::parse_input_attrs::(&input); let mut types = vec![]; let mut values = vec![]; let input_ident = input.ident; let input_name = || cg::to_css_identifier(input_ident.as_ref()); - if let Some(function) = attrs.function { + if let Some(function) = css_attrs.function { values.push(function.explicit().unwrap_or_else(input_name)); // If the whole value is wrapped in a function, value types of // its fields should not be propagated. @@ -31,21 +31,32 @@ pub fn derive(mut input: DeriveInput) -> Tokens { match input.data { Data::Enum(ref e) => { for v in e.variants.iter() { - let attrs = cg::parse_variant_attrs::(&v); - if attrs.skip { + let css_attrs = cg::parse_variant_attrs::(&v); + let info_attrs = cg::parse_variant_attrs::(&v); + if css_attrs.skip { continue; } - if let Some(aliases) = attrs.aliases { + if let Some(aliases) = css_attrs.aliases { for alias in aliases.split(",") { values.push(alias.to_string()); } } - if let Some(keyword) = attrs.keyword { + if let Some(other_values) = info_attrs.other_values { + for value in other_values.split(",") { + values.push(value.to_string()); + } + } + let ident = &v.ident; + let variant_name = || cg::to_css_identifier(ident.as_ref()); + if info_attrs.starts_with_keyword { + values.push(variant_name()); + continue; + } + if let Some(keyword) = css_attrs.keyword { values.push(keyword); continue; } - let variant_name = || cg::to_css_identifier(v.ident.as_ref()); - if let Some(function) = attrs.function { + if let Some(function) = css_attrs.function { values.push(function.explicit().unwrap_or_else(variant_name)); } else { if !derive_struct_fields(&v.fields, &mut types, &mut values) { @@ -63,6 +74,13 @@ pub fn derive(mut input: DeriveInput) -> Tokens { } } + let info_attrs = cg::parse_input_attrs::(&input); + if let Some(other_values) = info_attrs.other_values { + for value in other_values.split(",") { + values.push(value.to_string()); + } + } + let mut types_value = quote!(0); types_value.append_all(types.iter().map(|ty| quote! { | <#ty as ::style_traits::SpecifiedValueInfo>::SUPPORTED_TYPES @@ -73,6 +91,12 @@ pub fn derive(mut input: DeriveInput) -> Tokens { <#ty as ::style_traits::SpecifiedValueInfo>::collect_completion_keywords(_f); })); + if let Some(ty) = info_attrs.ty { + types_value.append_all(quote! { + | ::style_traits::CssType::#ty + }); + } + let append_values = if values.is_empty() { quote!() } else { @@ -110,11 +134,23 @@ fn derive_struct_fields<'a>( Fields::Unnamed(ref fields) => fields.unnamed.iter(), }; types.extend(fields.filter_map(|field| { - let attrs = cg::parse_field_attrs::(field); - if let Some(if_empty) = attrs.if_empty { + let info_attrs = cg::parse_field_attrs::(field); + if let Some(other_values) = info_attrs.other_values { + for value in other_values.split(",") { + values.push(value.to_string()); + } + } + if info_attrs.represents_keyword { + let ident = field.ident.as_ref() + .expect("only named field should use represents_keyword"); + values.push(cg::to_css_identifier(ident.as_ref())); + return None; + } + let css_attrs = cg::parse_field_attrs::(field); + if let Some(if_empty) = css_attrs.if_empty { values.push(if_empty); } - if !attrs.skip { + if !css_attrs.skip { Some(&field.ty) } else { None @@ -122,3 +158,24 @@ fn derive_struct_fields<'a>( })); true } + +#[darling(attributes(value_info), default)] +#[derive(Default, FromDeriveInput)] +struct ValueInfoInputAttrs { + ty: Option, + other_values: Option, +} + +#[darling(attributes(value_info), default)] +#[derive(Default, FromVariant)] +struct ValueInfoVariantAttrs { + starts_with_keyword: bool, + other_values: Option, +} + +#[darling(attributes(value_info), default)] +#[derive(Default, FromField)] +struct ValueInfoFieldAttrs { + represents_keyword: bool, + other_values: Option, +} diff --git a/components/style_traits/specified_value_info.rs b/components/style_traits/specified_value_info.rs index 55690ebea5c..122091fc8e9 100644 --- a/components/style_traits/specified_value_info.rs +++ b/components/style_traits/specified_value_info.rs @@ -27,6 +27,36 @@ pub mod CssType { pub type KeywordsCollectFn<'a> = &'a mut FnMut(&[&'static str]); /// Information of values of a given specified value type. +/// +/// This trait is derivable with `#[derive(SpecifiedValueInfo)]`. +/// +/// The algorithm traverses the type definition. For `SUPPORTED_TYPES`, +/// it puts an or'ed value of `SUPPORTED_TYPES` of all types it finds. +/// For `collect_completion_keywords`, it recursively invokes this +/// method on types found, and lists all keyword values and function +/// names following the same rule as `ToCss` in that method. +/// +/// Some attributes of `ToCss` can affect the behavior, specifically: +/// * If `#[css(function)]` is found, the content inside the annotated +/// variant (or the whole type) isn't traversed, only the function +/// name is listed in `collect_completion_keywords`. +/// * If `#[css(skip)]` is found, the content inside the variant or +/// field is ignored. +/// * Values listed in `#[css(if_empty)]`, `#[css(aliases)]`, and +/// `#[css(keyword)]` are added into `collect_completion_keywords`. +/// +/// In addition to `css` attributes, it also has `value_info` helper +/// attributes, including: +/// * `#[value_info(ty = "TYPE")]` can be used to specify a constant +/// from `CssType` to `SUPPORTED_TYPES`. +/// * `#[value_info(other_values = "value1,value2")]` can be used to +/// add other values related to a field, variant, or the type itself +/// into `collect_completion_keywords`. +/// * `#[value_info(starts_with_keyword)]` can be used on variants to +/// add the name of a non-unit variant (serialized like `ToCss`) into +/// `collect_completion_keywords`. +/// * `#[value_info(represents_keyword)]` can be used on fields into +/// `collect_completion_keywords`. pub trait SpecifiedValueInfo { /// Supported CssTypes by the given value type. ///