Add a pref checking mechanism for alias properties

This commit is contained in:
Nazım Can Altınova 2017-08-16 12:58:40 -07:00
parent 4d10d39e8f
commit 6893446b71
9 changed files with 161 additions and 44 deletions

View file

@ -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 {

View file

@ -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.

View file

@ -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

View file

@ -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<Importance, ParseError<'i>> {
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) {

View file

@ -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() {

View file

@ -246,10 +246,16 @@ impl From<ShorthandId> for NonCustomPropertyId {
}
}
/// A set of longhand properties
impl From<AliasId> 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<Self, ()> {
/// 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<Self, ()> {
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<AliasId>, 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<ComputedValues>,
}
}
/// 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<nsCSSPropertyID, ()> {
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) => {

View file

@ -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(()) => {

View file

@ -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())

View file

@ -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; }
}