author | Xidorn Quan <me@upsuper.org> |
Sun, 29 Apr 2018 09:03:31 +1000 | |
changeset 416174 | 5edb30a5ac7b2382b6d412ce90817e7bc782d529 |
parent 416173 | 44c33a20df275d8b7760493b8427f97f01ea3008 |
child 416175 | cebd5132edfc1f48cba9ebb4561925ed4c710876 |
push id | 33918 |
push user | nerli@mozilla.com |
push date | Sun, 29 Apr 2018 09:47:13 +0000 |
treeherder | mozilla-central@afbec7f03bd8 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | emilio |
bugs | 1434130 |
milestone | 61.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -873,16 +873,18 @@ SERVO_BINDING_FUNC(Servo_ParseFontShorth nsCSSValueBorrowedMut weight); SERVO_BINDING_FUNC(Servo_Property_IsShorthand, bool, const nsACString* name, bool* found); SERVO_BINDING_FUNC(Servo_Property_IsInherited, bool, const nsACString* name); SERVO_BINDING_FUNC(Servo_Property_SupportsType, bool, const nsACString* name, uint32_t ty, bool* found); +SERVO_BINDING_FUNC(Servo_Property_GetCSSValuesForProperty, void, + const nsACString* name, bool* found, nsTArray<nsString>* result) SERVO_BINDING_FUNC(Servo_PseudoClass_GetStates, uint64_t, const nsACString* name) // AddRef / Release functions #define SERVO_ARC_TYPE(name_, type_) \ SERVO_BINDING_FUNC(Servo_##name_##_AddRef, void, type_##Borrowed) \ SERVO_BINDING_FUNC(Servo_##name_##_Release, void, type_##Borrowed) #include "mozilla/ServoArcTypeList.h"
--- a/servo/components/style/properties/properties.mako.rs +++ b/servo/components/style/properties/properties.mako.rs @@ -37,17 +37,17 @@ use logical_geometry::WritingMode; use media_queries::Device; use parser::ParserContext; #[cfg(feature = "gecko")] use properties::longhands::system_font::SystemFont; use rule_cache::{RuleCache, RuleCacheConditions}; use selector_parser::PseudoElement; use selectors::parser::SelectorParseErrorKind; #[cfg(feature = "servo")] use servo_config::prefs::PREFS; use shared_lock::StylesheetGuards; -use style_traits::{CssWriter, ParseError, ParsingMode}; +use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use stylesheets::{CssRuleType, Origin, UrlExtraData}; #[cfg(feature = "servo")] use values::Either; use values::generics::text::LineHeight; use values::computed; use values::computed::NonNegativeLength; use rule_tree::{CascadeLevel, StrongRuleNode}; use self::computed_value_flags::*; @@ -555,16 +555,35 @@ impl NonCustomPropertyId { 0, // 'all' accepts no value other than CSS-wide keywords % else: <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES, % endif % endfor ]; 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 { fn from(id: LonghandId) -> Self { NonCustomPropertyId(id as usize) } } @@ -738,17 +757,18 @@ impl LonghandIdSet { /// Returns whether the set is empty. #[inline] pub fn is_empty(&self) -> bool { self.storage.iter().all(|c| *c == 0) } } /// 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 { /// The `initial` keyword. Initial, /// The `inherit` keyword. Inherit, /// The `unset` keyword. Unset, } @@ -1706,16 +1726,28 @@ impl PropertyId { PropertyId::Custom(_) => return None, PropertyId::Shorthand(shorthand_id) => shorthand_id.into(), PropertyId::Longhand(longhand_id) => longhand_id.into(), PropertyId::ShorthandAlias(_, alias_id) => alias_id.into(), PropertyId::LonghandAlias(_, alias_id) => alias_id.into(), }) } + /// 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 /// stylesheet it was declared on (that is, in practice only checks prefs). #[inline] pub fn enabled_for_all_content(&self) -> bool { let id = match self.non_custom_id() { // Custom properties are allowed everywhere None => return true, Some(id) => id, @@ -1731,24 +1763,29 @@ impl PropertyId { Some(id) => id, }; id.allowed_in(context) } /// Whether the property supports the given CSS type. /// `ty` should a bitflags of constants in style_traits::CssType. pub fn supports_type(&self, ty: u8) -> bool { - let non_custom_id: NonCustomPropertyId = match *self { - PropertyId::Custom(_) => return false, - PropertyId::Shorthand(id) => id.into(), - PropertyId::Longhand(id) => id.into(), - PropertyId::ShorthandAlias(id, _) => id.into(), - PropertyId::LonghandAlias(id, _) => id.into(), - }; - non_custom_id.supported_types() & ty != 0 + let id = self.non_custom_non_alias_id(); + id.map_or(0, |id| id.supported_types()) & ty != 0 + } + + /// Collect supported starting word of values of this property. + /// + /// See style_traits::SpecifiedValueInfo::collect_completion_keywords for more + /// 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); } } /// A declaration using a CSS-wide keyword. #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] #[derive(Clone, PartialEq, ToCss)] pub struct WideKeywordDeclaration { #[css(skip)]
--- a/servo/components/style_derive/specified_value_info.rs +++ b/servo/components/style_derive/specified_value_info.rs @@ -1,68 +1,124 @@ /* 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/. */ use cg; use quote::Tokens; -use syn::{Data, DeriveInput, Fields}; +use syn::{Data, DeriveInput, Fields, Type}; use to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs}; pub fn derive(mut input: DeriveInput) -> Tokens { let attrs = cg::parse_input_attrs::<CssInputAttrs>(&input); - let mut types_value = quote!(0); - // If the whole value is wrapped in a function, value types of its - // fields should not be propagated. - if attrs.function.is_none() { + 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 { + 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(); for param in input.generics.type_params() { cg::add_predicate( &mut where_clause, parse_quote!(#param: ::style_traits::SpecifiedValueInfo), ); } input.generics.where_clause = where_clause; match input.data { Data::Enum(ref e) => { for v in e.variants.iter() { let attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&v); - if attrs.function.is_none() { - derive_struct_fields(&v.fields, &mut types_value); + if attrs.skip { + 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) => { - 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"), } } + 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 (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); quote! { impl #impl_generics ::style_traits::SpecifiedValueInfo for #name #ty_generics #where_clause { 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 { - Fields::Unit => return, + Fields::Unit => return false, Fields::Named(ref fields) => fields.named.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); - if attrs.skip { - return quote!(); + if let Some(if_empty) = attrs.if_empty { + values.push(if_empty); } - let ty = &field.ty; - quote! { - | <#ty as ::style_traits::SpecifiedValueInfo>::SUPPORTED_TYPES + if !attrs.skip { + Some(&field.ty) + } else { + None } })); + true }
--- a/servo/components/style_traits/lib.rs +++ b/servo/components/style_traits/lib.rs @@ -74,17 +74,17 @@ pub enum CSSPixel {} pub mod cursor; pub mod specified_value_info; #[macro_use] pub mod values; #[macro_use] 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}; /// The error type for all CSS parsing routines. pub type ParseError<'i> = cssparser::ParseError<'i, StyleParseErrorKind<'i>>; /// Error in property value parsing pub type ValueParseError<'i> = cssparser::ParseError<'i, ValueParseErrorKind<'i>>;
--- a/servo/components/style_traits/specified_value_info.rs +++ b/servo/components/style_traits/specified_value_info.rs @@ -18,54 +18,80 @@ pub mod CssType { /// <color> pub const COLOR: u8 = 1 << 0; /// <gradient> pub const GRADIENT: u8 = 1 << 1; /// <timing-function> 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. pub trait SpecifiedValueInfo { /// Supported CssTypes by the given value type. /// /// XXX This should be typed CssType when that becomes a bitflags. /// Currently we cannot do so since bitflags cannot be used in constant. 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 f32 {} impl SpecifiedValueInfo for i8 {} impl SpecifiedValueInfo for i32 {} impl SpecifiedValueInfo for u8 {} impl SpecifiedValueInfo for u16 {} impl SpecifiedValueInfo for u32 {} impl SpecifiedValueInfo for str {} impl SpecifiedValueInfo for String {} impl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> { const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES; + fn collect_completion_keywords(f: KeywordsCollectFn) { + T::collect_completion_keywords(f); + } } impl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] { 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 { ($ty:ident<$param:ident>) => { impl<$param: SpecifiedValueInfo> SpecifiedValueInfo for $ty<$param> { const SUPPORTED_TYPES: u8 = $param::SUPPORTED_TYPES; + fn collect_completion_keywords(f: KeywordsCollectFn) { + $param::collect_completion_keywords(f); + } } } } impl_generic_specified_value_info!(Option<T>); impl_generic_specified_value_info!(Vec<T>); impl_generic_specified_value_info!(Arc<T>); impl_generic_specified_value_info!(Range<Idx>); impl<T1, T2> SpecifiedValueInfo for (T1, T2) where T1: SpecifiedValueInfo, T2: SpecifiedValueInfo, { 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); + } }
--- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -1,21 +1,22 @@ /* 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/. */ use cssparser::{ParseErrorKind, Parser, ParserInput, SourceLocation}; use cssparser::ToCss as ParserToCss; use malloc_size_of::MallocSizeOfOps; -use nsstring::nsCString; +use nsstring::{nsCString, nsStringRepr}; use selectors::{NthIndexCache, SelectorList}; use selectors::matching::{MatchingContext, MatchingMode, matches_selector}; use servo_arc::{Arc, ArcBorrow, RawOffsetArc}; use smallvec::SmallVec; use std::cell::RefCell; +use std::collections::BTreeSet; use std::fmt::Write; use std::iter; use std::mem; use std::os::raw::c_void; use std::ptr; use style::applicable_declarations::ApplicableDeclarationBlock; use style::author_styles::AuthorStyles; use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext}; @@ -923,30 +924,46 @@ pub extern "C" fn Servo_ComputedValues_E }; match AnimationValue::from_computed_values(property, &computed_values) { Some(v) => Arc::new(v).into_strong(), None => Strong::null(), } } +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] pub unsafe extern "C" fn Servo_Property_IsShorthand( prop_name: *const nsACString, found: *mut bool ) -> bool { - let prop_id = PropertyId::parse(prop_name.as_ref().unwrap().as_str_unchecked()); - let prop_id = match prop_id { - Ok(ref p) if p.enabled_for_all_content() => p, - _ => { - *found = false; - return false; - } - }; - *found = true; + let prop_id = parse_enabled_property_name!(prop_name, found, false); prop_id.is_shorthand() } #[no_mangle] pub unsafe extern "C" fn Servo_Property_IsInherited( prop_name: *const nsACString, ) -> bool { let prop_name = prop_name.as_ref().unwrap().as_str_unchecked(); @@ -965,40 +982,49 @@ pub unsafe extern "C" fn Servo_Property_ } #[no_mangle] pub unsafe extern "C" fn Servo_Property_SupportsType( prop_name: *const nsACString, ty: u32, found: *mut bool, ) -> bool { - let prop_id = PropertyId::parse(prop_name.as_ref().unwrap().as_str_unchecked()); - let prop_id = match prop_id { - Ok(ref p) if p.enabled_for_all_content() => p, - _ => { - *found = false; - return false; - } - }; - - *found = true; + let prop_id = parse_enabled_property_name!(prop_name, found, false); // This should match the constants in InspectorUtils. // (Let's don't bother importing InspectorUtilsBinding into bindings // because it is not used anywhere else, and issue here would be // caught by the property-db test anyway.) let ty = match ty { 1 => CssType::COLOR, 2 => CssType::GRADIENT, 3 => CssType::TIMING_FUNCTION, _ => unreachable!("unknown CSS 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] pub extern "C" fn Servo_Property_IsAnimatable(property: nsCSSPropertyID) -> bool { use style::properties::animated_properties; animated_properties::nscsspropertyid_is_animatable(property) } #[no_mangle] pub extern "C" fn Servo_Property_IsTransitionable(property: nsCSSPropertyID) -> bool { use style::properties::animated_properties;