diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index bf150468dac..736826ece07 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -744,7 +744,8 @@ impl LayoutThread { Msg::RegisterPaint(name, mut properties, painter) => { debug!("Registering the painter"); let properties = properties.drain(..) - .filter_map(|name| PropertyId::parse(&*name).ok().map(|id| (name.clone(), id))) + .filter_map(|name| PropertyId::parse(&*name, None) + .ok().map(|id| (name.clone(), id))) .filter(|&(_, ref id)| id.as_shorthand().is_err()) .collect(); let registered_painter = RegisteredPainterImpl { diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index ddf085b3491..f87c184ef63 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -296,7 +296,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue fn GetPropertyValue(&self, property: DOMString) -> DOMString { - let id = if let Ok(id) = PropertyId::parse(&property) { + let id = if let Ok(id) = PropertyId::parse(&property, None) { id } else { // Unkwown property @@ -307,7 +307,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority fn GetPropertyPriority(&self, property: DOMString) -> DOMString { - let id = if let Ok(id) = PropertyId::parse(&property) { + let id = if let Ok(id) = PropertyId::parse(&property, None) { id } else { // Unkwown property @@ -331,7 +331,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { priority: DOMString) -> ErrorResult { // Step 3 - let id = if let Ok(id) = PropertyId::parse(&property) { + let id = if let Ok(id) = PropertyId::parse(&property, None) { id } else { // Unknown property @@ -348,7 +348,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { } // Step 2 & 3 - let id = match PropertyId::parse(&property) { + let id = match PropertyId::parse(&property, None) { Ok(id) => id, Err(..) => return Ok(()), // Unkwown property }; @@ -380,7 +380,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { return Err(Error::NoModificationAllowed); } - let id = if let Ok(id) = PropertyId::parse(&property) { + let id = if let Ok(id) = PropertyId::parse(&property, None) { id } else { // Unkwown property, cannot be there to remove. diff --git a/components/style/properties/data.py b/components/style/properties/data.py index fd4b83ffc73..5d126e91f50 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -265,6 +265,18 @@ class Shorthand(object): transitionable = property(get_transitionable) +class Alias(object): + def __init__(self, name, original): + self.name = name + self.ident = to_rust_ident(name) + self.camel_case = to_camel_case(self.ident) + self.gecko_pref_ident = to_rust_ident(name) + self.internal = original.internal + self.experimental = original.experimental + self.allowed_in_page_rule = original.allowed_in_page_rule + self.allowed_in_keyframe_block = original.allowed_in_keyframe_block + + class Method(object): def __init__(self, name, return_type=None, arg_types=None, is_mut=False): self.name = name @@ -311,7 +323,9 @@ class PropertiesData(object): self.longhands = [] self.longhands_by_name = {} self.derived_longhands = {} + self.longhand_aliases = [] self.shorthands = [] + self.shorthand_aliases = [] def new_style_struct(self, *args, **kwargs): style_struct = StyleStruct(*args, **kwargs) @@ -335,6 +349,7 @@ class PropertiesData(object): longhand = Longhand(self.current_style_struct, name, **kwargs) self.add_prefixed_aliases(longhand) + self.longhand_aliases += list(map(lambda x: Alias(x, longhand), longhand.alias)) self.current_style_struct.longhands.append(longhand) self.longhands.append(longhand) self.longhands_by_name[name] = longhand @@ -352,8 +367,12 @@ class PropertiesData(object): sub_properties = [self.longhands_by_name[s] for s in sub_properties] shorthand = Shorthand(name, sub_properties, *args, **kwargs) self.add_prefixed_aliases(shorthand) + self.shorthand_aliases += list(map(lambda x: Alias(x, shorthand), shorthand.alias)) self.shorthands.append(shorthand) return shorthand def shorthands_except_all(self): return [s for s in self.shorthands if s.name != "all"] + + def all_aliases(self): + return self.longhand_aliases + self.shorthand_aliases diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index 586c5099c11..625ef96465e 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -947,7 +947,8 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> { fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>) -> Result> { - let id = match PropertyId::parse(&name) { + let prop_context = PropertyParserContext::new(self.context); + let id = match PropertyId::parse(&name, Some(&prop_context)) { Ok(id) => id, Err(()) => { return Err(if is_non_mozilla_vendor_identifier(&name) { diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index f3397ead5c4..6d3b1cc4ce8 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -3505,7 +3505,7 @@ fn static_assert() { Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr()); } - if let Ok(prop_id) = PropertyId::parse(&feature.0.to_string()) { + if let Ok(prop_id) = PropertyId::parse(&feature.0.to_string(), None) { match prop_id.as_shorthand() { Ok(shorthand) => { for longhand in shorthand.longhands() { diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 4eaeb467fc8..f64117e1bfc 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -246,10 +246,16 @@ impl From for NonCustomPropertyId { } } -/// A set of longhand properties +impl From for NonCustomPropertyId { + fn from(id: AliasId) -> Self { + NonCustomPropertyId(id as usize + ${len(data.longhands) + len(data.shorthands)}) + } +} + +/// A set of all properties #[derive(Clone, PartialEq)] pub struct NonCustomPropertyIdSet { - storage: [u32; (${len(data.longhands) + len(data.shorthands)} - 1 + 32) / 32] + storage: [u32; (${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())} - 1 + 32) / 32] } impl NonCustomPropertyIdSet { @@ -264,7 +270,7 @@ impl NonCustomPropertyIdSet { <%def name="static_non_custom_property_id_set(name, is_member)"> static ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet { <% - storage = [0] * ((len(data.longhands) + len(data.shorthands) - 1 + 32) / 32) + storage = [0] * ((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32) for i, property in enumerate(data.longhands + data.shorthands): if is_member(property): storage[i / 32] |= 1 << (i % 32) @@ -1003,33 +1009,75 @@ impl PropertyId { /// Returns a given property from the string `s`. /// /// Returns Err(()) for unknown non-custom properties - pub fn parse(property_name: &str) -> Result { + /// If caller wants to provide a different context, it can be provided with + /// Some(context), if None is given, default setting for PropertyParserContext + /// will be used. It is `Origin::Author` for stylesheet_origin and + /// `CssRuleType::Style` for rule_type. + pub fn parse(property_name: &str, context: Option< &PropertyParserContext>) -> Result { if let Ok(name) = ::custom_properties::parse_name(property_name) { return Ok(PropertyId::Custom(::custom_properties::Name::from(name))) } // FIXME(https://github.com/rust-lang/rust/issues/33156): remove this enum and use PropertyId // when stable Rust allows destructors in statics. + // ShorthandAlias is not used in servo build. That's why we need to allow dead_code. + #[allow(dead_code)] pub enum StaticId { Longhand(LonghandId), Shorthand(ShorthandId), + LonghandAlias(LonghandId, AliasId), + ShorthandAlias(ShorthandId, AliasId), } ascii_case_insensitive_phf_map! { static_id -> StaticId = { % for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]: % for property in properties: - % for name in [property.name] + property.alias: - "${name}" => StaticId::${kind}(${kind}Id::${property.camel_case}), + "${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}), + % for name in property.alias: + "${name}" => { + StaticId::${kind}Alias(${kind}Id::${property.camel_case}, + AliasId::${to_camel_case(name)}) + }, % endfor % endfor % endfor } } - match static_id(property_name) { - Some(&StaticId::Longhand(id)) => Ok(PropertyId::Longhand(id)), - Some(&StaticId::Shorthand(id)) => Ok(PropertyId::Shorthand(id)), - None => Err(()), - } + + let default; + let context = match context { + Some(context) => context, + None => { + default = PropertyParserContext { + stylesheet_origin: Origin::Author, + rule_type: CssRuleType::Style, + }; + &default + } + }; + let rule_type = context.rule_type; + debug_assert!(matches!(rule_type, CssRuleType::Keyframe | + CssRuleType::Page | + CssRuleType::Style), + "Declarations are only expected inside a keyframe, page, or style rule."); + + let (id, alias) = match static_id(property_name) { + Some(&StaticId::Longhand(id)) => { + (PropertyId::Longhand(id), None) + }, + Some(&StaticId::Shorthand(id)) => { + (PropertyId::Shorthand(id), None) + }, + Some(&StaticId::LonghandAlias(id, alias)) => { + (PropertyId::Longhand(id), Some(alias)) + }, + Some(&StaticId::ShorthandAlias(id, alias)) => { + (PropertyId::Shorthand(id), Some(alias)) + }, + None => return Err(()), + }; + id.check_allowed_in(alias, context).map_err(|_| ())?; + Ok(id) } /// Returns a property id from Gecko's nsCSSPropertyID. @@ -1111,22 +1159,26 @@ impl PropertyId { } } - fn check_allowed_in(&self, rule_type: CssRuleType, stylesheet_origin: Origin) + fn check_allowed_in(&self, alias: Option, context: &PropertyParserContext) -> Result<(), PropertyDeclarationParseError<'static>> { let id: NonCustomPropertyId; - match *self { - // Custom properties are allowed everywhere - PropertyId::Custom(_) => return Ok(()), + if let Some(alias_id) = alias { + id = alias_id.into(); + } else { + match *self { + // Custom properties are allowed everywhere + PropertyId::Custom(_) => return Ok(()), - PropertyId::Shorthand(shorthand_id) => id = shorthand_id.into(), - PropertyId::Longhand(longhand_id) => id = longhand_id.into(), + PropertyId::Shorthand(shorthand_id) => id = shorthand_id.into(), + PropertyId::Longhand(longhand_id) => id = longhand_id.into(), + } } <% id_set = static_non_custom_property_id_set %> ${id_set("DISALLOWED_IN_KEYFRAME_BLOCK", lambda p: not p.allowed_in_keyframe_block)} ${id_set("DISALLOWED_IN_PAGE_RULE", lambda p: not p.allowed_in_page_rule)} - match rule_type { + match context.rule_type { CssRuleType::Keyframe if DISALLOWED_IN_KEYFRAME_BLOCK.contains(id) => { return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock) } @@ -1136,7 +1188,6 @@ impl PropertyId { _ => {} } - // For properties that are experimental but not internal, the pref will // control its availability in all sheets. For properties that are // both experimental and internal, the pref only controls its @@ -1151,7 +1202,7 @@ impl PropertyId { static EXPERIMENTAL: NonCustomPropertyIdSet = NonCustomPropertyIdSet { <% grouped = [] - properties = data.longhands + data.shorthands + properties = data.longhands + data.shorthands + data.all_aliases() while properties: grouped.append(properties[:32]) properties = properties[32:] @@ -1185,14 +1236,16 @@ impl PropertyId { } % endif % if product == "gecko": - use gecko_bindings::structs; - let id = self.to_nscsspropertyid().unwrap(); + let id = match alias { + Some(alias_id) => alias_id.to_nscsspropertyid().unwrap(), + None => self.to_nscsspropertyid().unwrap(), + }; unsafe { structs::nsCSSProps_gPropertyEnabled[id as usize] } % endif }; if INTERNAL.contains(id) { - if stylesheet_origin != Origin::UserAgent { + if context.stylesheet_origin != Origin::UserAgent { if EXPERIMENTAL.contains(id) { if !passes_pref_check() { return Err(PropertyDeclarationParseError::ExperimentalProperty); @@ -1211,6 +1264,25 @@ impl PropertyId { } } +/// Parsing Context for PropertyId. +pub struct PropertyParserContext { + /// The Origin of the stylesheet, whether it's a user, + /// author or user-agent stylesheet. + pub stylesheet_origin: Origin, + /// The current rule type, if any. + pub rule_type: CssRuleType, +} + +impl PropertyParserContext { + /// Creates a PropertyParserContext with given stylesheet origin and rule type. + pub fn new(context: &ParserContext) -> Self { + Self { + stylesheet_origin: context.stylesheet_origin, + rule_type: context.rule_type(), + } + } +} + /// Servo's representation for a property declaration. #[derive(PartialEq, Clone)] pub enum PropertyDeclaration { @@ -1478,12 +1550,6 @@ impl PropertyDeclaration { id: PropertyId, context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<(), PropertyDeclarationParseError<'i>> { assert!(declarations.is_empty()); - let rule_type = context.rule_type(); - debug_assert!(rule_type == CssRuleType::Keyframe || - rule_type == CssRuleType::Page || - rule_type == CssRuleType::Style, - "Declarations are only expected inside a keyframe, page, or style rule."); - id.check_allowed_in(rule_type, context.stylesheet_origin)?; match id { PropertyId::Custom(name) => { let value = match input.try(|i| CSSWideKeyword::parse(i)) { @@ -3428,6 +3494,33 @@ pub fn modify_border_style_for_inline_sides(style: &mut Arc, } } +/// An identifier for a given alias property. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum AliasId { + % for i, property in enumerate(data.all_aliases()): + /// ${property.name} + ${property.camel_case} = ${i}, + % endfor +} + +impl AliasId { + /// Returns an nsCSSPropertyID. + #[cfg(feature = "gecko")] + #[allow(non_upper_case_globals)] + pub fn to_nscsspropertyid(&self) -> Result { + use gecko_bindings::structs::*; + + match *self { + % for property in data.all_aliases(): + AliasId::${property.camel_case} => { + Ok(${helpers.alias_to_nscsspropertyid(property.ident)}) + }, + % endfor + } + } +} + #[macro_export] macro_rules! css_properties_accessors { ($macro_name: ident) => { diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs index 0691cdcde41..7c9af1e13f7 100644 --- a/components/style/stylesheets/keyframes_rule.rs +++ b/components/style/stylesheets/keyframes_rule.rs @@ -8,7 +8,7 @@ use cssparser::{AtRuleParser, Parser, QualifiedRuleParser, RuleListParser, Parse use cssparser::{DeclarationListParser, DeclarationParser, parse_one_rule, SourceLocation}; use error_reporting::{NullReporter, ContextualParseError}; use parser::ParserContext; -use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId}; +use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, PropertyId, PropertyParserContext}; use properties::{PropertyDeclarationId, LonghandId, SourcePropertyDeclaration}; use properties::LonghandIdSet; use properties::animated_properties::AnimatableLonghand; @@ -532,7 +532,9 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeDeclarationParser<'a, 'b> { fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> { - let id = PropertyId::parse(&name) + let property_context = PropertyParserContext::new(self.context); + + let id = PropertyId::parse(&name, Some(&property_context)) .map_err(|()| PropertyDeclarationParseError::UnknownProperty(name))?; match PropertyDeclaration::parse_into(self.declarations, id, self.context, input) { Ok(()) => { diff --git a/components/style/stylesheets/supports_rule.rs b/components/style/stylesheets/supports_rule.rs index 0915b65d36a..e9d36dc471e 100644 --- a/components/style/stylesheets/supports_rule.rs +++ b/components/style/stylesheets/supports_rule.rs @@ -7,7 +7,7 @@ use cssparser::{BasicParseError, ParseError as CssParseError, ParserInput}; use cssparser::{Delimiter, parse_important, Parser, SourceLocation, Token}; use parser::ParserContext; -use properties::{PropertyId, PropertyDeclaration, SourcePropertyDeclaration}; +use properties::{PropertyId, PropertyDeclaration, PropertyParserContext, SourcePropertyDeclaration}; use selectors::parser::SelectorParseError; use servo_arc::Arc; use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; @@ -247,10 +247,11 @@ impl Declaration { input.parse_entirely(|input| { let prop = input.expect_ident().unwrap().as_ref().to_owned(); input.expect_colon().unwrap(); - let id = PropertyId::parse(&prop) - .map_err(|_| StyleParseError::UnspecifiedError)?; - let mut declarations = SourcePropertyDeclaration::new(); let context = ParserContext::new_with_rule_type(cx, Some(CssRuleType::Style)); + let property_context = PropertyParserContext::new(&context); + let id = PropertyId::parse(&prop, Some(&property_context)) + .map_err(|_| StyleParseError::UnspecifiedError)?; + let mut declarations = SourcePropertyDeclaration::new(); input.parse_until_before(Delimiter::Bang, |input| { PropertyDeclaration::parse_into(&mut declarations, id, &context, input) .map_err(|e| StyleParseError::PropertyDeclaration(e).into()) diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 15fea3f5d63..efcea6f685d 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -2190,7 +2190,7 @@ pub extern "C" fn Servo_DeclarationBlock_GetNthProperty(declarations: RawServoDe macro_rules! get_property_id_from_property { ($property: ident, $ret: expr) => {{ let property = unsafe { $property.as_ref().unwrap().as_str_unchecked() }; - match PropertyId::parse(property.into()) { + match PropertyId::parse(property.into(), None) { Ok(property_id) => property_id, Err(_) => { return $ret; } }