diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 22c51479375..e59be799737 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -315,7 +315,7 @@ context: &mut computed::Context, cacheable: &mut bool, cascade_info: &mut Option<<&mut CascadeInfo>) { - let declared_value = match *declaration { + let value = match *declaration { PropertyDeclaration::${property.camel_case}(ref value) => { DeclaredValue::Value(value) }, @@ -323,107 +323,98 @@ debug_assert!(id == LonghandId::${property.camel_case}); DeclaredValue::CSSWideKeyword(value) }, - PropertyDeclaration::WithVariables(id, ref value) => { - debug_assert!(id == LonghandId::${property.camel_case}); - DeclaredValue::WithVariables(value) - }, + PropertyDeclaration::WithVariables(..) => { + panic!("variables should already have been substituted") + } _ => panic!("entered the wrong cascade_property() implementation"), }; % if not property.derived_from: - { - let custom_props = context.style().custom_properties(); - let quirks_mode = context.quirks_mode; - ::properties::substitute_variables_${property.ident}( - &declared_value, &custom_props, - &mut |value| { - if let Some(ref mut cascade_info) = *cascade_info { - cascade_info.on_cascade_property(&declaration, - &value); - } - % if property.logical: - let wm = context.style.writing_mode; + if let Some(ref mut cascade_info) = *cascade_info { + cascade_info.on_cascade_property(&declaration, + &value); + } + % if property.logical: + let wm = context.style.writing_mode; + % endif + <% + maybe_wm = ", wm" if property.logical else "" + maybe_cacheable = ", cacheable" if property.has_uncacheable_values == "True" else "" + props_need_device = "content list_style_type".split() if product == "gecko" else [] + maybe_device = ", context.device" if property.ident in props_need_device else "" + %> + match value { + DeclaredValue::Value(ref specified_value) => { + % if property.ident in SYSTEM_FONT_LONGHANDS and product == "gecko": + if let Some(sf) = specified_value.get_system() { + longhands::system_font::resolve_system_font(sf, context); + } % endif - <% - maybe_wm = ", wm" if property.logical else "" - maybe_cacheable = ", cacheable" if property.has_uncacheable_values == "True" else "" - props_need_device = "content list_style_type".split() if product == "gecko" else [] - maybe_device = ", context.device" if property.ident in props_need_device else "" - %> - match *value { - DeclaredValue::Value(ref specified_value) => { - % if property.ident in SYSTEM_FONT_LONGHANDS and product == "gecko": - if let Some(sf) = specified_value.get_system() { - longhands::system_font::resolve_system_font(sf, context); - } - % endif - % if property.is_vector: - // In the case of a vector property we want to pass down - // an iterator so that this can be computed without allocation - // - // However, computing requires a context, but the style struct - // being mutated is on the context. We temporarily remove it, - // mutate it, and then put it back. Vector longhands cannot - // touch their own style struct whilst computing, else this will panic. - let mut s = context.mutate_style().take_${data.current_style_struct.name_lower}(); - { - let iter = specified_value.compute_iter(context); - s.set_${property.ident}(iter ${maybe_cacheable}); - } - context.mutate_style().put_${data.current_style_struct.name_lower}(s); - % else: - let computed = specified_value.to_computed_value(context); - % if property.ident == "font_size": - longhands::font_size::cascade_specified_font_size(context, - specified_value, - computed, - inherited_style.get_font()); - % else: - context.mutate_style().mutate_${data.current_style_struct.name_lower}() - .set_${property.ident}(computed ${maybe_device} - ${maybe_cacheable} ${maybe_wm}); - % endif - % endif + % if property.is_vector: + // In the case of a vector property we want to pass down + // an iterator so that this can be computed without allocation + // + // However, computing requires a context, but the style struct + // being mutated is on the context. We temporarily remove it, + // mutate it, and then put it back. Vector longhands cannot + // touch their own style struct whilst computing, else this will panic. + let mut s = context.mutate_style().take_${data.current_style_struct.name_lower}(); + { + let iter = specified_value.compute_iter(context); + s.set_${property.ident}(iter ${maybe_cacheable}); } - DeclaredValue::WithVariables(_) => unreachable!(), - DeclaredValue::CSSWideKeyword(keyword) => match keyword { - % if not data.current_style_struct.inherited: - CSSWideKeyword::Unset | - % endif - CSSWideKeyword::Initial => { - % if property.ident == "font_size": - longhands::font_size::cascade_initial_font_size(context); - % else: - // We assume that it's faster to use copy_*_from rather than - // set_*(get_initial_value()); - let initial_struct = default_style - .get_${data.current_style_struct.name_lower}(); - context.mutate_style().mutate_${data.current_style_struct.name_lower}() - .copy_${property.ident}_from(initial_struct ${maybe_wm}); - % endif - }, - % if data.current_style_struct.inherited: - CSSWideKeyword::Unset | - % endif - CSSWideKeyword::Inherit => { - // This is a bit slow, but this is rare so it shouldn't - // matter. - // - // FIXME: is it still? - *cacheable = false; - let inherited_struct = - inherited_style.get_${data.current_style_struct.name_lower}(); + context.mutate_style().put_${data.current_style_struct.name_lower}(s); + % else: + let computed = specified_value.to_computed_value(context); + % if property.ident == "font_size": + longhands::font_size::cascade_specified_font_size(context, + specified_value, + computed, + inherited_style.get_font()); + % else: + context.mutate_style().mutate_${data.current_style_struct.name_lower}() + .set_${property.ident}(computed ${maybe_device} + ${maybe_cacheable} ${maybe_wm}); + % endif + % endif + } + DeclaredValue::WithVariables(_) => unreachable!(), + DeclaredValue::CSSWideKeyword(keyword) => match keyword { + % if not data.current_style_struct.inherited: + CSSWideKeyword::Unset | + % endif + CSSWideKeyword::Initial => { + % if property.ident == "font_size": + longhands::font_size::cascade_initial_font_size(context); + % else: + // We assume that it's faster to use copy_*_from rather than + // set_*(get_initial_value()); + let initial_struct = default_style + .get_${data.current_style_struct.name_lower}(); + context.mutate_style().mutate_${data.current_style_struct.name_lower}() + .copy_${property.ident}_from(initial_struct ${maybe_wm}); + % endif + }, + % if data.current_style_struct.inherited: + CSSWideKeyword::Unset | + % endif + CSSWideKeyword::Inherit => { + // This is a bit slow, but this is rare so it shouldn't + // matter. + // + // FIXME: is it still? + *cacheable = false; + let inherited_struct = + inherited_style.get_${data.current_style_struct.name_lower}(); - % if property.ident == "font_size": - longhands::font_size::cascade_inherit_font_size(context, inherited_struct); - % else: - context.mutate_style().mutate_${data.current_style_struct.name_lower}() - .copy_${property.ident}_from(inherited_struct ${maybe_wm}); - % endif - } - } + % if property.ident == "font_size": + longhands::font_size::cascade_inherit_font_size(context, inherited_struct); + % else: + context.mutate_style().mutate_${data.current_style_struct.name_lower}() + .copy_${property.ident}_from(inherited_struct ${maybe_wm}); + % endif } - }, quirks_mode); + } } % if property.custom_cascade: @@ -452,31 +443,7 @@ } pub fn parse_declared<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { - match input.try(|i| CSSWideKeyword::parse(context, i)) { - Ok(keyword) => Ok(PropertyDeclaration::CSSWideKeyword(LonghandId::${property.camel_case}, keyword)), - Err(_) => { - input.look_for_var_functions(); - let start = input.position(); - let specified = parse_specified(context, input); - if specified.is_err() { - while let Ok(_) = input.next() {} // Look for var() after the error. - } - let var = input.seen_var_functions(); - if specified.is_err() && var { - input.reset(start); - let (first_token_type, css) = - ::custom_properties::parse_non_custom_with_var(input)?; - return Ok(PropertyDeclaration::WithVariables(LonghandId::${property.camel_case}, - Arc::new(UnparsedValue { - css: css.into_owned(), - first_token_type: first_token_type, - url_data: context.url_data.clone(), - from_shorthand: None, - }))) - } - specified.map(|s| PropertyDeclaration::${property.camel_case}(s)) - } - } + parse_specified(context, input).map(PropertyDeclaration::${property.camel_case}) } % endif } @@ -765,13 +732,12 @@ pub mod ${shorthand.ident} { use cssparser::Parser; use parser::ParserContext; - use properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed}; - use properties::{ShorthandId, LonghandId, UnparsedValue, longhands}; + use properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed, longhands}; #[allow(unused_imports)] use selectors::parser::SelectorParseError; #[allow(unused_imports)] use std::fmt; - use stylearc::Arc; + #[allow(unused_imports)] use style_traits::{ParseError, StyleParseError}; #[allow(unused_imports)] use style_traits::ToCss; @@ -850,41 +816,15 @@ /// Parse the given shorthand and fill the result into the /// `declarations` vector. pub fn parse_into<'i, 't>(declarations: &mut SourcePropertyDeclaration, - context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> { - input.look_for_var_functions(); - let start = input.position(); - let value = input.parse_entirely(|input| parse_value(context, input)); - if value.is_err() { - while let Ok(_) = input.next() {} // Look for var() after the error. - } - let var = input.seen_var_functions(); - if let Ok(value) = value { + context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result<(), ParseError<'i>> { + input.parse_entirely(|input| parse_value(context, input)).map(|longhands| { % for sub_property in shorthand.sub_properties: declarations.push(PropertyDeclaration::${sub_property.camel_case}( - value.${sub_property.ident} + longhands.${sub_property.ident} )); % endfor - Ok(()) - } else if var { - input.reset(start); - let (first_token_type, css) = - ::custom_properties::parse_non_custom_with_var(input)?; - let unparsed = Arc::new(UnparsedValue { - css: css.into_owned(), - first_token_type: first_token_type, - url_data: context.url_data.clone(), - from_shorthand: Some(ShorthandId::${shorthand.camel_case}), - }); - % for sub_property in shorthand.sub_properties: - declarations.push(PropertyDeclaration::WithVariables( - LonghandId::${sub_property.camel_case}, - unparsed.clone() - )); - % endfor - Ok(()) - } else { - Err(StyleParseError::UnspecifiedError.into()) - } + }) } ${caller.body()} diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index 959b55b0a60..abceb90629d 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -524,7 +524,6 @@ impl AnimationValue { pub fn from_declaration(decl: &PropertyDeclaration, context: &mut Context, initial: &ComputedValues) -> Option { use properties::LonghandId; - use properties::DeclaredValue; match *decl { % for prop in data.longhands: @@ -584,40 +583,10 @@ impl AnimationValue { % endfor } }, - PropertyDeclaration::WithVariables(id, ref variables) => { + PropertyDeclaration::WithVariables(id, ref unparsed) => { let custom_props = context.style().custom_properties(); - match id { - % for prop in data.longhands: - % if prop.animatable: - LonghandId::${prop.camel_case} => { - let mut result = None; - let quirks_mode = context.quirks_mode; - ::properties::substitute_variables_${prop.ident}_slow( - &variables.css, - variables.first_token_type, - &variables.url_data, - variables.from_shorthand, - &custom_props, - &mut |v| { - let declaration = match *v { - DeclaredValue::Value(value) => { - PropertyDeclaration::${prop.camel_case}(value.clone()) - }, - DeclaredValue::CSSWideKeyword(keyword) => { - PropertyDeclaration::CSSWideKeyword(id, keyword) - }, - DeclaredValue::WithVariables(_) => unreachable!(), - }; - result = AnimationValue::from_declaration(&declaration, context, initial); - }, - quirks_mode); - result - }, - % else: - LonghandId::${prop.camel_case} => None, - % endif - % endfor - } + let substituted = unparsed.substitute_variables(id, &custom_props, context.quirks_mode); + AnimationValue::from_declaration(&substituted, context, initial) }, _ => None // non animatable properties will get included because of shorthands. ignore. } diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 58f0b3bcc68..25c6de05f11 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -215,43 +215,6 @@ pub mod shorthands { <% data.declare_shorthand("all", [p.name for p in data.longhands if p.name not in ['direction', 'unicode-bidi']], spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand") %> - pub mod all { - use cssparser::Parser; - use parser::ParserContext; - use properties::{SourcePropertyDeclaration, AllShorthand, ShorthandId, UnparsedValue}; - use stylearc::Arc; - use style_traits::{ParseError, StyleParseError}; - - pub fn parse_into<'i, 't>(declarations: &mut SourcePropertyDeclaration, - context: &ParserContext, input: &mut Parser<'i, 't>) - -> Result<(), ParseError<'i>> { - // This function is like the parse() that is generated by - // helpers:shorthand, but since the only values for the 'all' - // shorthand when not just a single CSS-wide keyword is one - // with variable references, we can make this function a - // little simpler. - // - // FIXME(heycam) Try to share code with the helpers:shorthand - // definition. - input.look_for_var_functions(); - let start = input.position(); - while let Ok(_) = input.next() {} // Look for var() - if input.seen_var_functions() { - input.reset(start); - let (first_token_type, css) = - ::custom_properties::parse_non_custom_with_var(input)?; - declarations.all_shorthand = AllShorthand::WithVariables(Arc::new(UnparsedValue { - css: css.into_owned(), - first_token_type: first_token_type, - url_data: context.url_data.clone(), - from_shorthand: Some(ShorthandId::All), - })); - Ok(()) - } else { - Err(StyleParseError::UnspecifiedError.into()) - } - } - } } /// A module with all the code related to animated properties. @@ -262,6 +225,61 @@ pub mod animated_properties { <%include file="/helpers/animated_properties.mako.rs" /> } +/// A longhand or shorthand porperty +#[derive(Copy, Clone, Debug)] +pub struct NonCustomPropertyId(usize); + +impl From for NonCustomPropertyId { + fn from(id: LonghandId) -> Self { + NonCustomPropertyId(id as usize) + } +} + +impl From for NonCustomPropertyId { + fn from(id: ShorthandId) -> Self { + NonCustomPropertyId((id as usize) + ${len(data.longhands)}) + } +} + +/// A set of longhand properties +#[derive(Clone, PartialEq)] +pub struct NonCustomPropertyIdSet { + storage: [u32; (${len(data.longhands) + len(data.shorthands)} - 1 + 32) / 32] +} + +impl NonCustomPropertyIdSet { + /// Return whether the given property is in the set + #[inline] + pub fn contains(&self, id: NonCustomPropertyId) -> bool { + let bit = id.0; + (self.storage[bit / 32] & (1 << (bit % 32))) != 0 + } +} + +<%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) + for i, property in enumerate(data.longhands + data.shorthands): + if is_member(property): + storage[i / 32] |= 1 << (i % 32) + %> + storage: [${", ".join("0x%x" % word for word in storage)}] +}; + + +<%def name="static_longhand_id_set(name, is_member)"> +static ${name}: LonghandIdSet = LonghandIdSet { + <% + storage = [0] * ((len(data.longhands) - 1 + 32) / 32) + for i, property in enumerate(data.longhands): + if is_member(property): + storage[i / 32] |= 1 << (i % 32) + %> + storage: [${", ".join("0x%x" % word for word in storage)}] +}; + + /// A set of longhand properties #[derive(Clone, PartialEq)] pub struct LonghandIdSet { @@ -365,108 +383,6 @@ impl PropertyDeclarationIdSet { } } -% for property in data.longhands: - % if not property.derived_from: - /// Perform CSS variable substitution if needed, and execute `f` with - /// the resulting declared value. - #[allow(non_snake_case)] - fn substitute_variables_${property.ident}( - % if property.boxed: - value: &DeclaredValue>, - % else: - value: &DeclaredValue, - % endif - custom_properties: &Option>, - f: &mut F, - quirks_mode: QuirksMode) - % if property.boxed: - where F: FnMut(&DeclaredValue>) - % else: - where F: FnMut(&DeclaredValue) - % endif - { - if let DeclaredValue::WithVariables(ref with_variables) = *value { - substitute_variables_${property.ident}_slow(&with_variables.css, - with_variables.first_token_type, - &with_variables.url_data, - with_variables.from_shorthand, - custom_properties, - f, - quirks_mode); - } else { - f(value); - } - } - - #[allow(non_snake_case)] - #[inline(never)] - fn substitute_variables_${property.ident}_slow( - css: &String, - first_token_type: TokenSerializationType, - url_data: &UrlExtraData, - from_shorthand: Option, - custom_properties: &Option>, - f: &mut F, - quirks_mode: QuirksMode) - % if property.boxed: - where F: FnMut(&DeclaredValue>) - % else: - where F: FnMut(&DeclaredValue) - % endif - { - f(& - ::custom_properties::substitute(css, first_token_type, custom_properties) - .ok() - .and_then(|css| { - // As of this writing, only the base URL is used for property values: - let reporter = NullReporter; - let context = ParserContext::new(Origin::Author, - url_data, - &reporter, - None, - PARSING_MODE_DEFAULT, - quirks_mode); - let mut input = ParserInput::new(&css); - Parser::new(&mut input).parse_entirely(|input| { - match from_shorthand { - None => { - longhands::${property.ident} - ::parse_specified(&context, input).map(DeclaredValueOwned::Value) - } - Some(ShorthandId::All) => { - // No need to parse the 'all' shorthand as anything other than a CSS-wide - // keyword, after variable substitution. - Err(SelectorParseError::UnexpectedIdent("all".into()).into()) - } - % for shorthand in data.shorthands_except_all(): - % if property in shorthand.sub_properties: - Some(ShorthandId::${shorthand.camel_case}) => { - shorthands::${shorthand.ident}::parse_value(&context, input) - .map(|result| { - DeclaredValueOwned::Value(result.${property.ident}) - }) - } - % endif - % endfor - _ => unreachable!() - } - }).ok() - }) - .unwrap_or( - // Invalid at computed-value time. - DeclaredValueOwned::CSSWideKeyword( - % if property.style_struct.inherited: - CSSWideKeyword::Inherit - % else: - CSSWideKeyword::Initial - % endif - ) - ).borrow() - ); - } - % endif -% endfor - /// An enum to represent a CSS Wide keyword. #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[derive(Copy, Clone, Debug, Eq, PartialEq, ToCss)] @@ -546,6 +462,11 @@ impl LonghandId { } } + fn inherited(&self) -> bool { + ${static_longhand_id_set("INHERITED", lambda p: p.style_struct.inherited)} + INHERITED.contains(*self) + } + fn shorthands(&self) -> &'static [ShorthandId] { // first generate longhand to shorthands lookup map // @@ -581,6 +502,20 @@ impl LonghandId { } } + fn parse_value<'i, 't>(&self, context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result> { + match *self { + % for property in data.longhands: + LonghandId::${property.camel_case} => { + % if not property.derived_from: + longhands::${property.ident}::parse_declared(context, input) + % else: + Err(PropertyDeclarationParseError::UnknownProperty.into()) + % endif + } + % endfor + } + } /// If this is a logical property, return the corresponding physical one in the given writing mode. /// Otherwise, return unchanged. @@ -801,6 +736,20 @@ impl ShorthandId { % endfor } } + + fn parse_into<'i, 't>(&self, declarations: &mut SourcePropertyDeclaration, + context: &ParserContext, input: &mut Parser<'i, 't>) + -> Result<(), ParseError<'i>> { + match *self { + % for shorthand in data.shorthands_except_all(): + ShorthandId::${shorthand.camel_case} => { + shorthands::${shorthand.ident}::parse_into(declarations, context, input) + } + % endfor + // 'all' accepts no value other than CSS-wide keywords + ShorthandId::All => Err(StyleParseError::UnspecifiedError.into()) + } + } } /// Servo's representation of a declared value for a given `T`, which is the @@ -853,6 +802,64 @@ pub struct UnparsedValue { from_shorthand: Option, } +impl UnparsedValue { + fn substitute_variables(&self, longhand_id: LonghandId, + custom_properties: &Option>, + quirks_mode: QuirksMode) + -> PropertyDeclaration { + ::custom_properties::substitute(&self.css, self.first_token_type, custom_properties) + .ok() + .and_then(|css| { + // As of this writing, only the base URL is used for property values: + let reporter = NullReporter; + let context = ParserContext::new(Origin::Author, + &self.url_data, + &reporter, + None, + PARSING_MODE_DEFAULT, + quirks_mode); + let mut input = ParserInput::new(&css); + Parser::new(&mut input).parse_entirely(|input| { + match self.from_shorthand { + None => longhand_id.parse_value(&context, input), + Some(ShorthandId::All) => { + // No need to parse the 'all' shorthand as anything other than a CSS-wide + // keyword, after variable substitution. + Err(SelectorParseError::UnexpectedIdent("all".into()).into()) + } + % for shorthand in data.shorthands_except_all(): + Some(ShorthandId::${shorthand.camel_case}) => { + shorthands::${shorthand.ident}::parse_value(&context, input) + .map(|longhands| { + match longhand_id { + % for property in shorthand.sub_properties: + LonghandId::${property.camel_case} => { + PropertyDeclaration::${property.camel_case}( + longhands.${property.ident} + ) + } + % endfor + _ => unreachable!() + } + }) + } + % endfor + } + }) + .ok() + }) + .unwrap_or_else(|| { + // Invalid at computed-value time. + let keyword = if longhand_id.inherited() { + CSSWideKeyword::Inherit + } else { + CSSWideKeyword::Initial + }; + PropertyDeclaration::CSSWideKeyword(longhand_id, keyword) + }) + } +} + impl<'a, T: HasViewportPercentage> HasViewportPercentage for DeclaredValue<'a, T> { fn has_viewport_percentage(&self) -> bool { match *self { @@ -1092,6 +1099,104 @@ impl PropertyId { } } } + + fn check_allowed_in(&self, rule_type: CssRuleType, stylesheet_origin: Origin) + -> Result<(), PropertyDeclarationParseError> { + let id: NonCustomPropertyId; + 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(), + } + + <% 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 { + CssRuleType::Keyframe if DISALLOWED_IN_KEYFRAME_BLOCK.contains(id) => { + return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock) + } + CssRuleType::Page if DISALLOWED_IN_PAGE_RULE.contains(id) => { + return Err(PropertyDeclarationParseError::NotAllowedInPageRule) + } + _ => {} + } + + + // 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 + // availability in non-UA sheets (and in UA sheets it is always available). + ${id_set("INTERNAL", lambda p: p.internal)} + + % if product == "servo": + ${id_set("EXPERIMENTAL", lambda p: p.experimental)} + % endif + % if product == "gecko": + use gecko_bindings::structs::root::mozilla; + static EXPERIMENTAL: NonCustomPropertyIdSet = NonCustomPropertyIdSet { + <% + grouped = [] + properties = data.longhands + data.shorthands + while properties: + grouped.append(properties[:32]) + properties = properties[32:] + %> + storage: [ + % for group in grouped: + (0 + % for i, property in enumerate(group): + | ((mozilla::SERVO_PREF_ENABLED_${property.gecko_pref_ident} as u32) << ${i}) + % endfor + ), + % endfor + ] + }; + % endif + + let passes_pref_check = || { + % if product == "servo": + static PREF_NAME: [Option< &str>; ${len(data.longhands) + len(data.shorthands)}] = [ + % for property in data.longhands + data.shorthands: + % if property.experimental: + Some("${property.experimental}"), + % else: + None, + % endif + % endfor + ]; + match PREF_NAME[id.0] { + None => true, + Some(pref) => PREFS.get(pref).as_boolean().unwrap_or(false) + } + % endif + % if product == "gecko": + let id = self.to_nscsspropertyid().unwrap(); + unsafe { bindings::Gecko_PropertyId_IsPrefEnabled(id) } + % endif + }; + + if INTERNAL.contains(id) { + if stylesheet_origin != Origin::UserAgent { + if EXPERIMENTAL.contains(id) { + if !passes_pref_check() { + return Err(PropertyDeclarationParseError::ExperimentalProperty); + } + } else { + return Err(PropertyDeclarationParseError::UnknownProperty); + } + } + } else { + if EXPERIMENTAL.contains(id) && !passes_pref_check() { + return Err(PropertyDeclarationParseError::ExperimentalProperty); + } + } + + Ok(()) + } } /// Servo's representation for a property declaration. @@ -1175,49 +1280,6 @@ impl ToCss for PropertyDeclaration { } } -<%def name="property_exposure_check(property)"> - // 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 - // availability in non-UA sheets (and in UA sheets it is always available). - let is_experimental = - % if property.experimental and product == "servo": - true; - % elif product == "gecko": - structs::root::mozilla::SERVO_PREF_ENABLED_${property.gecko_pref_ident}; - % else: - false; - % endif - - let passes_pref_check = - % if property.experimental and product == "servo": - PREFS.get("${property.experimental}").as_boolean().unwrap_or(false); - % elif product == "gecko": - { - let id = structs::${helpers.to_nscsspropertyid(property.ident)}; - unsafe { bindings::Gecko_PropertyId_IsPrefEnabled(id) } - }; - % else: - true; - % endif - - % if property.internal: - if context.stylesheet_origin != Origin::UserAgent { - if is_experimental { - if !passes_pref_check { - return Err(PropertyDeclarationParseError::ExperimentalProperty); - } - } else { - return Err(PropertyDeclarationParseError::UnknownProperty); - } - } - % else: - if is_experimental && !passes_pref_check { - return Err(PropertyDeclarationParseError::ExperimentalProperty); - } - % endif - - impl MallocSizeOf for PropertyDeclaration { fn malloc_size_of_children(&self, _malloc_size_of: MallocSizeOfFn) -> usize { // The variants of PropertyDeclaration mostly (entirely?) contain @@ -1409,6 +1471,7 @@ impl PropertyDeclaration { 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(context, i)) { @@ -1421,73 +1484,79 @@ impl PropertyDeclaration { declarations.push(PropertyDeclaration::Custom(name, value)); Ok(()) } - PropertyId::Longhand(id) => match id { - % for property in data.longhands: - LonghandId::${property.camel_case} => { - % if not property.derived_from: - % if not property.allowed_in_keyframe_block: - if rule_type == CssRuleType::Keyframe { - return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock) - } - % endif - % if not property.allowed_in_page_rule: - if rule_type == CssRuleType::Page { - return Err(PropertyDeclarationParseError::NotAllowedInPageRule) - } - % endif - - ${property_exposure_check(property)} - - match longhands::${property.ident}::parse_declared(context, input) { - Ok(value) => { - declarations.push(value); - Ok(()) - }, - Err(_) => Err(PropertyDeclarationParseError::InvalidValue("${property.ident}".into())), + PropertyId::Longhand(id) => { + input.try(|i| CSSWideKeyword::parse(context, i)).map(|keyword| { + PropertyDeclaration::CSSWideKeyword(id, keyword) + }).or_else(|_| { + input.look_for_var_functions(); + let start = input.position(); + input.parse_entirely(|input| id.parse_value(context, input)) + .or_else(|_| { + while let Ok(_) = input.next() {} // Look for var() after the error. + if input.seen_var_functions() { + input.reset(start); + let (first_token_type, css) = + ::custom_properties::parse_non_custom_with_var(input).map_err(|_| { + PropertyDeclarationParseError::InvalidValue(id.name().into()) + })?; + Ok(PropertyDeclaration::WithVariables(id, Arc::new(UnparsedValue { + css: css.into_owned(), + first_token_type: first_token_type, + url_data: context.url_data.clone(), + from_shorthand: None, + }))) + } else { + Err(PropertyDeclarationParseError::InvalidValue(id.name().into())) } - % else: - Err(PropertyDeclarationParseError::UnknownProperty) - % endif - } - % endfor - }, - PropertyId::Shorthand(id) => match id { - % for shorthand in data.shorthands: - ShorthandId::${shorthand.camel_case} => { - % if not shorthand.allowed_in_keyframe_block: - if rule_type == CssRuleType::Keyframe { - return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock) - } - % endif - % if not shorthand.allowed_in_page_rule: - if rule_type == CssRuleType::Page { - return Err(PropertyDeclarationParseError::NotAllowedInPageRule) - } - % endif - - ${property_exposure_check(shorthand)} - - match input.try(|i| CSSWideKeyword::parse(context, i)) { - Ok(keyword) => { - % if shorthand.name == "all": - declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword); - % else: - % for sub_property in shorthand.sub_properties: - declarations.push(PropertyDeclaration::CSSWideKeyword( - LonghandId::${sub_property.camel_case}, - keyword, - )); - % endfor - % endif - Ok(()) - }, - Err(_) => { - shorthands::${shorthand.ident}::parse_into(declarations, context, input) - .map_err(|_| PropertyDeclarationParseError::InvalidValue("${shorthand.ident}".into())) + }) + }).map(|declaration| { + declarations.push(declaration) + }) + } + PropertyId::Shorthand(id) => { + if let Ok(keyword) = input.try(|i| CSSWideKeyword::parse(context, i)) { + if id == ShorthandId::All { + declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword) + } else { + for &longhand in id.longhands() { + declarations.push(PropertyDeclaration::CSSWideKeyword(longhand, keyword)) } } + Ok(()) + } else { + input.look_for_var_functions(); + let start = input.position(); + // Not using parse_entirely here: each ${shorthand.ident}::parse_into function + // needs to do so *before* pushing to `declarations`. + id.parse_into(declarations, context, input).or_else(|_| { + while let Ok(_) = input.next() {} // Look for var() after the error. + if input.seen_var_functions() { + input.reset(start); + let (first_token_type, css) = + ::custom_properties::parse_non_custom_with_var(input).map_err(|_| { + PropertyDeclarationParseError::InvalidValue(id.name().into()) + })?; + let unparsed = Arc::new(UnparsedValue { + css: css.into_owned(), + first_token_type: first_token_type, + url_data: context.url_data.clone(), + from_shorthand: Some(id), + }); + if id == ShorthandId::All { + declarations.all_shorthand = AllShorthand::WithVariables(unparsed) + } else { + for &longhand in id.longhands() { + declarations.push( + PropertyDeclaration::WithVariables(longhand, unparsed.clone()) + ) + } + } + Ok(()) + } else { + Err(PropertyDeclarationParseError::InvalidValue(id.name().into())) + } + }) } - % endfor } } } @@ -2718,7 +2787,17 @@ pub fn apply_declarations<'a, F, I>(device: &Device, let mut font_family = None; % endif for (declaration, cascade_level) in iter_declarations() { - let mut declaration = declaration; + let mut declaration = match *declaration { + PropertyDeclaration::WithVariables(id, ref unparsed) => { + Cow::Owned(unparsed.substitute_variables( + id, + &context.style.custom_properties, + context.quirks_mode + )) + } + ref d => Cow::Borrowed(d) + }; + let longhand_id = match declaration.id() { PropertyDeclarationId::Longhand(id) => id, PropertyDeclarationId::Custom(..) => continue, @@ -2742,15 +2821,18 @@ pub fn apply_declarations<'a, F, I>(device: &Device, CascadeLevel::UserNormal | CascadeLevel::UserImportant | CascadeLevel::UAImportant) { - if let PropertyDeclaration::BackgroundColor(ref color) = *declaration { - // Treat background-color a bit differently. If the specified - // color is anything other than a fully transparent color, convert - // it into the Device's default background color. - if color.is_non_transparent() { - declaration = default_background_color_decl.as_ref().unwrap(); + let non_transparent_background = match *declaration { + PropertyDeclaration::BackgroundColor(ref color) => { + // Treat background-color a bit differently. If the specified + // color is anything other than a fully transparent color, convert + // it into the Device's default background color. + color.is_non_transparent() } - } else { - continue + _ => continue + }; + // FIXME: moving this out of `match` is a work around for borrows being lexical. + if non_transparent_background { + declaration = Cow::Borrowed(default_background_color_decl.as_ref().unwrap()); } } @@ -2772,17 +2854,17 @@ pub fn apply_declarations<'a, F, I>(device: &Device, % if category_to_cascade_now == "early": if LonghandId::FontSize == longhand_id { - font_size = Some(declaration); + font_size = Some(declaration.clone()); continue; } if LonghandId::FontFamily == longhand_id { - font_family = Some(declaration); + font_family = Some(declaration.clone()); continue; } % endif let discriminant = longhand_id as usize; - (CASCADE_PROPERTY[discriminant])(declaration, + (CASCADE_PROPERTY[discriminant])(&*declaration, inherited_style, default_style, &mut context, @@ -2806,8 +2888,8 @@ pub fn apply_declarations<'a, F, I>(device: &Device, if seen.contains(LonghandId::XLang) || font_family.is_some() { // if just the language changed, the inherited generic is all we need let mut generic = inherited_style.get_font().gecko().mGenericID; - if let Some(declaration) = font_family { - if let PropertyDeclaration::FontFamily(ref fam) = *declaration { + if let Some(ref declaration) = font_family { + if let PropertyDeclaration::FontFamily(ref fam) = **declaration { if let Some(id) = fam.single_generic() { generic = id; // In case of a specified font family with a single generic, we will @@ -2852,7 +2934,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device, // during the early iteration and cascade them in order // after it. if !_skip_font_family { - if let Some(declaration) = font_family { + if let Some(ref declaration) = font_family { let discriminant = LonghandId::FontFamily as usize; (CASCADE_PROPERTY[discriminant])(declaration, @@ -2867,7 +2949,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device, } } - if let Some(declaration) = font_size { + if let Some(ref declaration) = font_size { let discriminant = LonghandId::FontSize as usize; (CASCADE_PROPERTY[discriminant])(declaration, inherited_style,