style: Add collect_values function to SpecifiedValueInfo trait for collecting possible values.

This is the basic structure of the stuff. Following patches will fill
the gap between Gecko and Servo on value generating, and finally hook
it into InspectorUtils.

Bug: 1434130
Reviewed-by: emilio
MozReview-Commit-ID: KNLAfFBiY6e
This commit is contained in:
Xidorn Quan 2018-04-29 09:03:31 +10:00 committed by Emilio Cobos Álvarez
parent 3b9c40dd14
commit 26c3aeda97
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
5 changed files with 192 additions and 47 deletions

View file

@ -42,7 +42,7 @@ use selector_parser::PseudoElement;
use selectors::parser::SelectorParseErrorKind; use selectors::parser::SelectorParseErrorKind;
#[cfg(feature = "servo")] use servo_config::prefs::PREFS; #[cfg(feature = "servo")] use servo_config::prefs::PREFS;
use shared_lock::StylesheetGuards; use shared_lock::StylesheetGuards;
use style_traits::{CssWriter, ParseError, ParsingMode}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
use stylesheets::{CssRuleType, Origin, UrlExtraData}; use stylesheets::{CssRuleType, Origin, UrlExtraData};
#[cfg(feature = "servo")] use values::Either; #[cfg(feature = "servo")] use values::Either;
@ -560,6 +560,25 @@ impl NonCustomPropertyId {
]; ];
SUPPORTED_TYPES[self.0] SUPPORTED_TYPES[self.0]
} }
/// See PropertyId::collect_property_completion_keywords.
fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
const COLLECT_FUNCTIONS: [&Fn(KeywordsCollectFn);
${len(data.longhands) + len(data.shorthands)}] = [
% for prop in data.longhands:
&<${prop.specified_type()} as SpecifiedValueInfo>::collect_completion_keywords,
% endfor
% for prop in data.shorthands:
% if prop.name == "all":
&|_f| {}, // 'all' accepts no value other than CSS-wide keywords
% else:
&<shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::
collect_completion_keywords,
% endif
% endfor
];
COLLECT_FUNCTIONS[self.0](f);
}
} }
impl From<LonghandId> for NonCustomPropertyId { impl From<LonghandId> for NonCustomPropertyId {
@ -743,7 +762,8 @@ impl LonghandIdSet {
} }
/// An enum to represent a CSS Wide keyword. /// An enum to represent a CSS Wide keyword.
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)] #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
ToCss)]
pub enum CSSWideKeyword { pub enum CSSWideKeyword {
/// The `initial` keyword. /// The `initial` keyword.
Initial, Initial,
@ -1711,6 +1731,18 @@ impl PropertyId {
}) })
} }
/// Returns non-alias NonCustomPropertyId corresponding to this
/// property id.
fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {
Some(match *self {
PropertyId::Custom(_) => return None,
PropertyId::Shorthand(id) => id.into(),
PropertyId::Longhand(id) => id.into(),
PropertyId::ShorthandAlias(id, _) => id.into(),
PropertyId::LonghandAlias(id, _) => id.into(),
})
}
/// Whether the property is enabled for all content regardless of the /// Whether the property is enabled for all content regardless of the
/// stylesheet it was declared on (that is, in practice only checks prefs). /// stylesheet it was declared on (that is, in practice only checks prefs).
#[inline] #[inline]
@ -1736,14 +1768,19 @@ impl PropertyId {
/// Whether the property supports the given CSS type. /// Whether the property supports the given CSS type.
/// `ty` should a bitflags of constants in style_traits::CssType. /// `ty` should a bitflags of constants in style_traits::CssType.
pub fn supports_type(&self, ty: u8) -> bool { pub fn supports_type(&self, ty: u8) -> bool {
let non_custom_id: NonCustomPropertyId = match *self { let id = self.non_custom_non_alias_id();
PropertyId::Custom(_) => return false, id.map_or(0, |id| id.supported_types()) & ty != 0
PropertyId::Shorthand(id) => id.into(), }
PropertyId::Longhand(id) => id.into(),
PropertyId::ShorthandAlias(id, _) => id.into(), /// Collect supported starting word of values of this property.
PropertyId::LonghandAlias(id, _) => id.into(), ///
}; /// See style_traits::SpecifiedValueInfo::collect_completion_keywords for more
non_custom_id.supported_types() & ty != 0 /// details.
pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {
if let Some(id) = self.non_custom_non_alias_id() {
id.collect_property_completion_keywords(f);
}
CSSWideKeyword::collect_completion_keywords(f);
} }
} }

View file

@ -4,15 +4,21 @@
use cg; use cg;
use quote::Tokens; use quote::Tokens;
use syn::{Data, DeriveInput, Fields}; use syn::{Data, DeriveInput, Fields, Type};
use to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs}; use to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs};
pub fn derive(mut input: DeriveInput) -> Tokens { pub fn derive(mut input: DeriveInput) -> Tokens {
let attrs = cg::parse_input_attrs::<CssInputAttrs>(&input); let attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
let mut types_value = quote!(0); let mut types = vec![];
// If the whole value is wrapped in a function, value types of its let mut values = vec![];
// fields should not be propagated.
if attrs.function.is_none() { let input_ident = input.ident;
let input_name = || cg::to_css_identifier(input_ident.as_ref());
if let Some(function) = 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.
} else {
let mut where_clause = input.generics.where_clause.take(); let mut where_clause = input.generics.where_clause.take();
for param in input.generics.type_params() { for param in input.generics.type_params() {
cg::add_predicate( cg::add_predicate(
@ -26,18 +32,55 @@ pub fn derive(mut input: DeriveInput) -> Tokens {
Data::Enum(ref e) => { Data::Enum(ref e) => {
for v in e.variants.iter() { for v in e.variants.iter() {
let attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&v); let attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&v);
if attrs.function.is_none() { if attrs.skip {
derive_struct_fields(&v.fields, &mut types_value); continue;
}
if let Some(aliases) = attrs.aliases {
for alias in aliases.split(",") {
values.push(alias.to_string());
}
}
if let Some(keyword) = attrs.keyword {
values.push(keyword);
continue;
}
let variant_name = || cg::to_css_identifier(v.ident.as_ref());
if let Some(function) = attrs.function {
values.push(function.explicit().unwrap_or_else(variant_name));
} else {
if !derive_struct_fields(&v.fields, &mut types, &mut values) {
values.push(variant_name());
}
} }
} }
} }
Data::Struct(ref s) => { Data::Struct(ref s) => {
derive_struct_fields(&s.fields, &mut types_value) if !derive_struct_fields(&s.fields, &mut types, &mut values) {
values.push(input_name());
}
} }
Data::Union(_) => unreachable!("union is not supported"), Data::Union(_) => unreachable!("union is not supported"),
} }
} }
let mut types_value = quote!(0);
types_value.append_all(types.iter().map(|ty| quote! {
| <#ty as ::style_traits::SpecifiedValueInfo>::SUPPORTED_TYPES
}));
let mut nested_collects = quote!();
nested_collects.append_all(types.iter().map(|ty| quote! {
<#ty as ::style_traits::SpecifiedValueInfo>::collect_completion_keywords(_f);
}));
let append_values = if values.is_empty() {
quote!()
} else {
let mut value_list = quote!();
value_list.append_separated(values.iter(), quote!(,));
quote! { _f(&[#value_list]); }
};
let name = &input.ident; let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
quote! { quote! {
@ -45,24 +88,37 @@ pub fn derive(mut input: DeriveInput) -> Tokens {
#where_clause #where_clause
{ {
const SUPPORTED_TYPES: u8 = #types_value; const SUPPORTED_TYPES: u8 = #types_value;
fn collect_completion_keywords(_f: &mut FnMut(&[&'static str])) {
#nested_collects
#append_values
}
} }
} }
} }
fn derive_struct_fields(fields: &Fields, supports_body: &mut Tokens) { /// Derive from the given fields. Return false if the fields is a Unit,
/// true otherwise.
fn derive_struct_fields<'a>(
fields: &'a Fields,
types: &mut Vec<&'a Type>,
values: &mut Vec<String>,
) -> bool {
let fields = match *fields { let fields = match *fields {
Fields::Unit => return, Fields::Unit => return false,
Fields::Named(ref fields) => fields.named.iter(), Fields::Named(ref fields) => fields.named.iter(),
Fields::Unnamed(ref fields) => fields.unnamed.iter(), Fields::Unnamed(ref fields) => fields.unnamed.iter(),
}; };
supports_body.append_all(fields.map(|field| { types.extend(fields.filter_map(|field| {
let attrs = cg::parse_field_attrs::<CssFieldAttrs>(field); let attrs = cg::parse_field_attrs::<CssFieldAttrs>(field);
if attrs.skip { if let Some(if_empty) = attrs.if_empty {
return quote!(); values.push(if_empty);
} }
let ty = &field.ty; if !attrs.skip {
quote! { Some(&field.ty)
| <#ty as ::style_traits::SpecifiedValueInfo>::SUPPORTED_TYPES } else {
None
} }
})); }));
true
} }

View file

@ -79,7 +79,7 @@ pub mod values;
#[macro_use] #[macro_use]
pub mod viewport; pub mod viewport;
pub use specified_value_info::{CssType, SpecifiedValueInfo}; pub use specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo};
pub use values::{Comma, CommaWithSpace, CssWriter, OneOrMoreSeparated, Separator, Space, ToCss}; pub use values::{Comma, CommaWithSpace, CssWriter, OneOrMoreSeparated, Separator, Space, ToCss};
/// The error type for all CSS parsing routines. /// The error type for all CSS parsing routines.

View file

@ -23,6 +23,9 @@ pub mod CssType {
pub const TIMING_FUNCTION: u8 = 1 << 2; pub const TIMING_FUNCTION: u8 = 1 << 2;
} }
/// See SpecifiedValueInfo::collect_completion_keywords.
pub type KeywordsCollectFn<'a> = &'a mut FnMut(&[&'static str]);
/// Information of values of a given specified value type. /// Information of values of a given specified value type.
pub trait SpecifiedValueInfo { pub trait SpecifiedValueInfo {
/// Supported CssTypes by the given value type. /// Supported CssTypes by the given value type.
@ -30,6 +33,15 @@ pub trait SpecifiedValueInfo {
/// XXX This should be typed CssType when that becomes a bitflags. /// XXX This should be typed CssType when that becomes a bitflags.
/// Currently we cannot do so since bitflags cannot be used in constant. /// Currently we cannot do so since bitflags cannot be used in constant.
const SUPPORTED_TYPES: u8 = 0; const SUPPORTED_TYPES: u8 = 0;
/// Collect value starting words for the given specified value type.
/// This includes keyword and function names which can appear at the
/// beginning of a value of this type.
///
/// Caller should pass in a callback function to accept the list of
/// values. The callback function can be called multiple times, and
/// some values passed to the callback may be duplicate.
fn collect_completion_keywords(_f: KeywordsCollectFn) {}
} }
impl SpecifiedValueInfo for bool {} impl SpecifiedValueInfo for bool {}
@ -44,16 +56,25 @@ impl SpecifiedValueInfo for String {}
impl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> { impl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> {
const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES; const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
fn collect_completion_keywords(f: KeywordsCollectFn) {
T::collect_completion_keywords(f);
}
} }
impl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] { impl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] {
const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES; const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
fn collect_completion_keywords(f: KeywordsCollectFn) {
T::collect_completion_keywords(f);
}
} }
macro_rules! impl_generic_specified_value_info { macro_rules! impl_generic_specified_value_info {
($ty:ident<$param:ident>) => { ($ty:ident<$param:ident>) => {
impl<$param: SpecifiedValueInfo> SpecifiedValueInfo for $ty<$param> { impl<$param: SpecifiedValueInfo> SpecifiedValueInfo for $ty<$param> {
const SUPPORTED_TYPES: u8 = $param::SUPPORTED_TYPES; const SUPPORTED_TYPES: u8 = $param::SUPPORTED_TYPES;
fn collect_completion_keywords(f: KeywordsCollectFn) {
$param::collect_completion_keywords(f);
}
} }
} }
} }
@ -68,4 +89,9 @@ where
T2: SpecifiedValueInfo, T2: SpecifiedValueInfo,
{ {
const SUPPORTED_TYPES: u8 = T1::SUPPORTED_TYPES | T2::SUPPORTED_TYPES; const SUPPORTED_TYPES: u8 = T1::SUPPORTED_TYPES | T2::SUPPORTED_TYPES;
fn collect_completion_keywords(f: KeywordsCollectFn) {
T1::collect_completion_keywords(f);
T2::collect_completion_keywords(f);
}
} }

View file

@ -5,12 +5,13 @@
use cssparser::{ParseErrorKind, Parser, ParserInput, SourceLocation}; use cssparser::{ParseErrorKind, Parser, ParserInput, SourceLocation};
use cssparser::ToCss as ParserToCss; use cssparser::ToCss as ParserToCss;
use malloc_size_of::MallocSizeOfOps; use malloc_size_of::MallocSizeOfOps;
use nsstring::nsCString; use nsstring::{nsCString, nsStringRepr};
use selectors::{NthIndexCache, SelectorList}; use selectors::{NthIndexCache, SelectorList};
use selectors::matching::{MatchingContext, MatchingMode, matches_selector}; use selectors::matching::{MatchingContext, MatchingMode, matches_selector};
use servo_arc::{Arc, ArcBorrow, RawOffsetArc}; use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::BTreeSet;
use std::fmt::Write; use std::fmt::Write;
use std::iter; use std::iter;
use std::mem; use std::mem;
@ -928,20 +929,36 @@ pub extern "C" fn Servo_ComputedValues_ExtractAnimationValue(
} }
} }
macro_rules! parse_enabled_property_name {
($prop_name:ident, $found:ident, $default:expr) => {{
let prop_name = $prop_name.as_ref().unwrap().as_str_unchecked();
// XXX This can be simplified once Option::filter is stable.
let prop_id = PropertyId::parse(prop_name).ok().and_then(|p| {
if p.enabled_for_all_content() {
Some(p)
} else {
None
}
});
match prop_id {
Some(p) => {
*$found = true;
p
}
None => {
*$found = false;
return $default;
}
}
}}
}
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn Servo_Property_IsShorthand( pub unsafe extern "C" fn Servo_Property_IsShorthand(
prop_name: *const nsACString, prop_name: *const nsACString,
found: *mut bool found: *mut bool
) -> bool { ) -> bool {
let prop_id = PropertyId::parse(prop_name.as_ref().unwrap().as_str_unchecked()); let prop_id = parse_enabled_property_name!(prop_name, found, false);
let prop_id = match prop_id {
Ok(ref p) if p.enabled_for_all_content() => p,
_ => {
*found = false;
return false;
}
};
*found = true;
prop_id.is_shorthand() prop_id.is_shorthand()
} }
@ -970,16 +987,7 @@ pub unsafe extern "C" fn Servo_Property_SupportsType(
ty: u32, ty: u32,
found: *mut bool, found: *mut bool,
) -> bool { ) -> bool {
let prop_id = PropertyId::parse(prop_name.as_ref().unwrap().as_str_unchecked()); let prop_id = parse_enabled_property_name!(prop_name, found, false);
let prop_id = match prop_id {
Ok(ref p) if p.enabled_for_all_content() => p,
_ => {
*found = false;
return false;
}
};
*found = true;
// This should match the constants in InspectorUtils. // This should match the constants in InspectorUtils.
// (Let's don't bother importing InspectorUtilsBinding into bindings // (Let's don't bother importing InspectorUtilsBinding into bindings
// because it is not used anywhere else, and issue here would be // because it is not used anywhere else, and issue here would be
@ -993,6 +1001,24 @@ pub unsafe extern "C" fn Servo_Property_SupportsType(
prop_id.supports_type(ty) prop_id.supports_type(ty)
} }
#[no_mangle]
pub unsafe extern "C" fn Servo_Property_GetCSSValuesForProperty(
prop_name: *const nsACString,
found: *mut bool,
result: *mut nsTArray<nsStringRepr>,
) {
let prop_id = parse_enabled_property_name!(prop_name, found, ());
// Use B-tree set for unique and sorted result.
let mut values = BTreeSet::<&'static str>::new();
prop_id.collect_property_completion_keywords(&mut |list| values.extend(list.iter()));
let result = result.as_mut().unwrap();
bindings::Gecko_ResizeTArrayForStrings(result, values.len() as u32);
for (src, dest) in values.iter().zip(result.iter_mut()) {
dest.write_str(src).unwrap();
}
}
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_Property_IsAnimatable(property: nsCSSPropertyID) -> bool { pub extern "C" fn Servo_Property_IsAnimatable(property: nsCSSPropertyID) -> bool {
use style::properties::animated_properties; use style::properties::animated_properties;