mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Auto merge of #17703 - servo:code-size, r=emilio
Reduce amount of Mako-generated code in style In multiple places, instead of generated similar code for each CSS property, have a single code path that uses an enum such as `LonghandId`. Fix https://bugzilla.mozilla.org/show_bug.cgi?id=1377262 Machine code size is measured with [bloaty](https://github.com/google/bloaty), looking at the total "VM size" in order to exclude debug info (which is stripped in binaries shipped to users). ``` mach build-geckolib --release bloaty target/geckolib/release/libgeckoservo.a ``` Initial size was 9.56Mi. Successive commits in this PR bring it to 9.55Mi, 9.51Mi, 9.44Mi, 9.31Mi, 8.89Mi, 8.89Mi, and 7.54Mi. Total savings: 2.02 MiB. In an optimized Firefox build with `mach build && mach package`, the size of `obj-x86_64-pc-linux-gnu/dist/firefox/libxul.so` goes from 109.11 MiB to 107.49 MiB, saving 1.62 MiB. I don’t really know how to explain the difference. Does `libgeckoservo.a` contain code that ends up not being used in `libxul.so`? <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17703) <!-- Reviewable:end -->
This commit is contained in:
commit
68ae7ceff6
3 changed files with 439 additions and 448 deletions
|
@ -315,7 +315,7 @@
|
||||||
context: &mut computed::Context,
|
context: &mut computed::Context,
|
||||||
cacheable: &mut bool,
|
cacheable: &mut bool,
|
||||||
cascade_info: &mut Option<<&mut CascadeInfo>) {
|
cascade_info: &mut Option<<&mut CascadeInfo>) {
|
||||||
let declared_value = match *declaration {
|
let value = match *declaration {
|
||||||
PropertyDeclaration::${property.camel_case}(ref value) => {
|
PropertyDeclaration::${property.camel_case}(ref value) => {
|
||||||
DeclaredValue::Value(value)
|
DeclaredValue::Value(value)
|
||||||
},
|
},
|
||||||
|
@ -323,107 +323,98 @@
|
||||||
debug_assert!(id == LonghandId::${property.camel_case});
|
debug_assert!(id == LonghandId::${property.camel_case});
|
||||||
DeclaredValue::CSSWideKeyword(value)
|
DeclaredValue::CSSWideKeyword(value)
|
||||||
},
|
},
|
||||||
PropertyDeclaration::WithVariables(id, ref value) => {
|
PropertyDeclaration::WithVariables(..) => {
|
||||||
debug_assert!(id == LonghandId::${property.camel_case});
|
panic!("variables should already have been substituted")
|
||||||
DeclaredValue::WithVariables(value)
|
}
|
||||||
},
|
|
||||||
_ => panic!("entered the wrong cascade_property() implementation"),
|
_ => panic!("entered the wrong cascade_property() implementation"),
|
||||||
};
|
};
|
||||||
|
|
||||||
% if not property.derived_from:
|
% if not property.derived_from:
|
||||||
{
|
if let Some(ref mut cascade_info) = *cascade_info {
|
||||||
let custom_props = context.style().custom_properties();
|
cascade_info.on_cascade_property(&declaration,
|
||||||
let quirks_mode = context.quirks_mode;
|
&value);
|
||||||
::properties::substitute_variables_${property.ident}(
|
}
|
||||||
&declared_value, &custom_props,
|
% if property.logical:
|
||||||
&mut |value| {
|
let wm = context.style.writing_mode;
|
||||||
if let Some(ref mut cascade_info) = *cascade_info {
|
% endif
|
||||||
cascade_info.on_cascade_property(&declaration,
|
<%
|
||||||
&value);
|
maybe_wm = ", wm" if property.logical else ""
|
||||||
}
|
maybe_cacheable = ", cacheable" if property.has_uncacheable_values == "True" else ""
|
||||||
% if property.logical:
|
props_need_device = "content list_style_type".split() if product == "gecko" else []
|
||||||
let wm = context.style.writing_mode;
|
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
|
% endif
|
||||||
<%
|
% if property.is_vector:
|
||||||
maybe_wm = ", wm" if property.logical else ""
|
// In the case of a vector property we want to pass down
|
||||||
maybe_cacheable = ", cacheable" if property.has_uncacheable_values == "True" else ""
|
// an iterator so that this can be computed without allocation
|
||||||
props_need_device = "content list_style_type".split() if product == "gecko" else []
|
//
|
||||||
maybe_device = ", context.device" if property.ident in props_need_device else ""
|
// However, computing requires a context, but the style struct
|
||||||
%>
|
// being mutated is on the context. We temporarily remove it,
|
||||||
match *value {
|
// mutate it, and then put it back. Vector longhands cannot
|
||||||
DeclaredValue::Value(ref specified_value) => {
|
// touch their own style struct whilst computing, else this will panic.
|
||||||
% if property.ident in SYSTEM_FONT_LONGHANDS and product == "gecko":
|
let mut s = context.mutate_style().take_${data.current_style_struct.name_lower}();
|
||||||
if let Some(sf) = specified_value.get_system() {
|
{
|
||||||
longhands::system_font::resolve_system_font(sf, context);
|
let iter = specified_value.compute_iter(context);
|
||||||
}
|
s.set_${property.ident}(iter ${maybe_cacheable});
|
||||||
% 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
|
|
||||||
}
|
}
|
||||||
DeclaredValue::WithVariables(_) => unreachable!(),
|
context.mutate_style().put_${data.current_style_struct.name_lower}(s);
|
||||||
DeclaredValue::CSSWideKeyword(keyword) => match keyword {
|
% else:
|
||||||
% if not data.current_style_struct.inherited:
|
let computed = specified_value.to_computed_value(context);
|
||||||
CSSWideKeyword::Unset |
|
% if property.ident == "font_size":
|
||||||
% endif
|
longhands::font_size::cascade_specified_font_size(context,
|
||||||
CSSWideKeyword::Initial => {
|
specified_value,
|
||||||
% if property.ident == "font_size":
|
computed,
|
||||||
longhands::font_size::cascade_initial_font_size(context);
|
inherited_style.get_font());
|
||||||
% else:
|
% else:
|
||||||
// We assume that it's faster to use copy_*_from rather than
|
context.mutate_style().mutate_${data.current_style_struct.name_lower}()
|
||||||
// set_*(get_initial_value());
|
.set_${property.ident}(computed ${maybe_device}
|
||||||
let initial_struct = default_style
|
${maybe_cacheable} ${maybe_wm});
|
||||||
.get_${data.current_style_struct.name_lower}();
|
% endif
|
||||||
context.mutate_style().mutate_${data.current_style_struct.name_lower}()
|
% endif
|
||||||
.copy_${property.ident}_from(initial_struct ${maybe_wm});
|
}
|
||||||
% endif
|
DeclaredValue::WithVariables(_) => unreachable!(),
|
||||||
},
|
DeclaredValue::CSSWideKeyword(keyword) => match keyword {
|
||||||
% if data.current_style_struct.inherited:
|
% if not data.current_style_struct.inherited:
|
||||||
CSSWideKeyword::Unset |
|
CSSWideKeyword::Unset |
|
||||||
% endif
|
% endif
|
||||||
CSSWideKeyword::Inherit => {
|
CSSWideKeyword::Initial => {
|
||||||
// This is a bit slow, but this is rare so it shouldn't
|
% if property.ident == "font_size":
|
||||||
// matter.
|
longhands::font_size::cascade_initial_font_size(context);
|
||||||
//
|
% else:
|
||||||
// FIXME: is it still?
|
// We assume that it's faster to use copy_*_from rather than
|
||||||
*cacheable = false;
|
// set_*(get_initial_value());
|
||||||
let inherited_struct =
|
let initial_struct = default_style
|
||||||
inherited_style.get_${data.current_style_struct.name_lower}();
|
.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":
|
% if property.ident == "font_size":
|
||||||
longhands::font_size::cascade_inherit_font_size(context, inherited_struct);
|
longhands::font_size::cascade_inherit_font_size(context, inherited_struct);
|
||||||
% else:
|
% else:
|
||||||
context.mutate_style().mutate_${data.current_style_struct.name_lower}()
|
context.mutate_style().mutate_${data.current_style_struct.name_lower}()
|
||||||
.copy_${property.ident}_from(inherited_struct ${maybe_wm});
|
.copy_${property.ident}_from(inherited_struct ${maybe_wm});
|
||||||
% endif
|
% endif
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, quirks_mode);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
% if property.custom_cascade:
|
% if property.custom_cascade:
|
||||||
|
@ -452,31 +443,7 @@
|
||||||
}
|
}
|
||||||
pub fn parse_declared<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
|
pub fn parse_declared<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||||
-> Result<PropertyDeclaration, ParseError<'i>> {
|
-> Result<PropertyDeclaration, ParseError<'i>> {
|
||||||
match input.try(|i| CSSWideKeyword::parse(context, i)) {
|
parse_specified(context, input).map(PropertyDeclaration::${property.camel_case})
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
% endif
|
% endif
|
||||||
}
|
}
|
||||||
|
@ -765,13 +732,12 @@
|
||||||
pub mod ${shorthand.ident} {
|
pub mod ${shorthand.ident} {
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use parser::ParserContext;
|
use parser::ParserContext;
|
||||||
use properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed};
|
use properties::{PropertyDeclaration, SourcePropertyDeclaration, MaybeBoxed, longhands};
|
||||||
use properties::{ShorthandId, LonghandId, UnparsedValue, longhands};
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use selectors::parser::SelectorParseError;
|
use selectors::parser::SelectorParseError;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use stylearc::Arc;
|
#[allow(unused_imports)]
|
||||||
use style_traits::{ParseError, StyleParseError};
|
use style_traits::{ParseError, StyleParseError};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
@ -850,41 +816,15 @@
|
||||||
/// Parse the given shorthand and fill the result into the
|
/// Parse the given shorthand and fill the result into the
|
||||||
/// `declarations` vector.
|
/// `declarations` vector.
|
||||||
pub fn parse_into<'i, 't>(declarations: &mut SourcePropertyDeclaration,
|
pub fn parse_into<'i, 't>(declarations: &mut SourcePropertyDeclaration,
|
||||||
context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {
|
context: &ParserContext, input: &mut Parser<'i, 't>)
|
||||||
input.look_for_var_functions();
|
-> Result<(), ParseError<'i>> {
|
||||||
let start = input.position();
|
input.parse_entirely(|input| parse_value(context, input)).map(|longhands| {
|
||||||
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 {
|
|
||||||
% for sub_property in shorthand.sub_properties:
|
% for sub_property in shorthand.sub_properties:
|
||||||
declarations.push(PropertyDeclaration::${sub_property.camel_case}(
|
declarations.push(PropertyDeclaration::${sub_property.camel_case}(
|
||||||
value.${sub_property.ident}
|
longhands.${sub_property.ident}
|
||||||
));
|
));
|
||||||
% endfor
|
% 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()}
|
${caller.body()}
|
||||||
|
|
|
@ -524,7 +524,6 @@ impl AnimationValue {
|
||||||
pub fn from_declaration(decl: &PropertyDeclaration, context: &mut Context,
|
pub fn from_declaration(decl: &PropertyDeclaration, context: &mut Context,
|
||||||
initial: &ComputedValues) -> Option<Self> {
|
initial: &ComputedValues) -> Option<Self> {
|
||||||
use properties::LonghandId;
|
use properties::LonghandId;
|
||||||
use properties::DeclaredValue;
|
|
||||||
|
|
||||||
match *decl {
|
match *decl {
|
||||||
% for prop in data.longhands:
|
% for prop in data.longhands:
|
||||||
|
@ -584,40 +583,10 @@ impl AnimationValue {
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PropertyDeclaration::WithVariables(id, ref variables) => {
|
PropertyDeclaration::WithVariables(id, ref unparsed) => {
|
||||||
let custom_props = context.style().custom_properties();
|
let custom_props = context.style().custom_properties();
|
||||||
match id {
|
let substituted = unparsed.substitute_variables(id, &custom_props, context.quirks_mode);
|
||||||
% for prop in data.longhands:
|
AnimationValue::from_declaration(&substituted, context, initial)
|
||||||
% 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
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
_ => None // non animatable properties will get included because of shorthands. ignore.
|
_ => None // non animatable properties will get included because of shorthands. ignore.
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,43 +215,6 @@ pub mod shorthands {
|
||||||
<% data.declare_shorthand("all",
|
<% data.declare_shorthand("all",
|
||||||
[p.name for p in data.longhands if p.name not in ['direction', 'unicode-bidi']],
|
[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") %>
|
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.
|
/// 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" />
|
<%include file="/helpers/animated_properties.mako.rs" />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A longhand or shorthand porperty
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct NonCustomPropertyId(usize);
|
||||||
|
|
||||||
|
impl From<LonghandId> for NonCustomPropertyId {
|
||||||
|
fn from(id: LonghandId) -> Self {
|
||||||
|
NonCustomPropertyId(id as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ShorthandId> 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>
|
||||||
|
|
||||||
|
<%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)}]
|
||||||
|
};
|
||||||
|
</%def>
|
||||||
|
|
||||||
/// A set of longhand properties
|
/// A set of longhand properties
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct LonghandIdSet {
|
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}<F>(
|
|
||||||
% if property.boxed:
|
|
||||||
value: &DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>,
|
|
||||||
% else:
|
|
||||||
value: &DeclaredValue<longhands::${property.ident}::SpecifiedValue>,
|
|
||||||
% endif
|
|
||||||
custom_properties: &Option<Arc<::custom_properties::CustomPropertiesMap>>,
|
|
||||||
f: &mut F,
|
|
||||||
quirks_mode: QuirksMode)
|
|
||||||
% if property.boxed:
|
|
||||||
where F: FnMut(&DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>)
|
|
||||||
% else:
|
|
||||||
where F: FnMut(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
|
|
||||||
% 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<F>(
|
|
||||||
css: &String,
|
|
||||||
first_token_type: TokenSerializationType,
|
|
||||||
url_data: &UrlExtraData,
|
|
||||||
from_shorthand: Option<ShorthandId>,
|
|
||||||
custom_properties: &Option<Arc<::custom_properties::CustomPropertiesMap>>,
|
|
||||||
f: &mut F,
|
|
||||||
quirks_mode: QuirksMode)
|
|
||||||
% if property.boxed:
|
|
||||||
where F: FnMut(&DeclaredValue<Box<longhands::${property.ident}::SpecifiedValue>>)
|
|
||||||
% else:
|
|
||||||
where F: FnMut(&DeclaredValue<longhands::${property.ident}::SpecifiedValue>)
|
|
||||||
% 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.
|
/// An enum to represent a CSS Wide keyword.
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, ToCss)]
|
#[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] {
|
fn shorthands(&self) -> &'static [ShorthandId] {
|
||||||
// first generate longhand to shorthands lookup map
|
// 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<PropertyDeclaration, ParseError<'i>> {
|
||||||
|
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.
|
/// If this is a logical property, return the corresponding physical one in the given writing mode.
|
||||||
/// Otherwise, return unchanged.
|
/// Otherwise, return unchanged.
|
||||||
|
@ -801,6 +736,20 @@ impl ShorthandId {
|
||||||
% endfor
|
% 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
|
/// Servo's representation of a declared value for a given `T`, which is the
|
||||||
|
@ -853,6 +802,64 @@ pub struct UnparsedValue {
|
||||||
from_shorthand: Option<ShorthandId>,
|
from_shorthand: Option<ShorthandId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UnparsedValue {
|
||||||
|
fn substitute_variables(&self, longhand_id: LonghandId,
|
||||||
|
custom_properties: &Option<Arc<::custom_properties::CustomPropertiesMap>>,
|
||||||
|
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> {
|
impl<'a, T: HasViewportPercentage> HasViewportPercentage for DeclaredValue<'a, T> {
|
||||||
fn has_viewport_percentage(&self) -> bool {
|
fn has_viewport_percentage(&self) -> bool {
|
||||||
match *self {
|
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.
|
/// 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
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
impl MallocSizeOf for PropertyDeclaration {
|
impl MallocSizeOf for PropertyDeclaration {
|
||||||
fn malloc_size_of_children(&self, _malloc_size_of: MallocSizeOfFn) -> usize {
|
fn malloc_size_of_children(&self, _malloc_size_of: MallocSizeOfFn) -> usize {
|
||||||
// The variants of PropertyDeclaration mostly (entirely?) contain
|
// The variants of PropertyDeclaration mostly (entirely?) contain
|
||||||
|
@ -1409,6 +1471,7 @@ impl PropertyDeclaration {
|
||||||
rule_type == CssRuleType::Page ||
|
rule_type == CssRuleType::Page ||
|
||||||
rule_type == CssRuleType::Style,
|
rule_type == CssRuleType::Style,
|
||||||
"Declarations are only expected inside a keyframe, page, or style rule.");
|
"Declarations are only expected inside a keyframe, page, or style rule.");
|
||||||
|
id.check_allowed_in(rule_type, context.stylesheet_origin)?;
|
||||||
match id {
|
match id {
|
||||||
PropertyId::Custom(name) => {
|
PropertyId::Custom(name) => {
|
||||||
let value = match input.try(|i| CSSWideKeyword::parse(context, i)) {
|
let value = match input.try(|i| CSSWideKeyword::parse(context, i)) {
|
||||||
|
@ -1421,73 +1484,79 @@ impl PropertyDeclaration {
|
||||||
declarations.push(PropertyDeclaration::Custom(name, value));
|
declarations.push(PropertyDeclaration::Custom(name, value));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
PropertyId::Longhand(id) => match id {
|
PropertyId::Longhand(id) => {
|
||||||
% for property in data.longhands:
|
input.try(|i| CSSWideKeyword::parse(context, i)).map(|keyword| {
|
||||||
LonghandId::${property.camel_case} => {
|
PropertyDeclaration::CSSWideKeyword(id, keyword)
|
||||||
% if not property.derived_from:
|
}).or_else(|_| {
|
||||||
% if not property.allowed_in_keyframe_block:
|
input.look_for_var_functions();
|
||||||
if rule_type == CssRuleType::Keyframe {
|
let start = input.position();
|
||||||
return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock)
|
input.parse_entirely(|input| id.parse_value(context, input))
|
||||||
}
|
.or_else(|_| {
|
||||||
% endif
|
while let Ok(_) = input.next() {} // Look for var() after the error.
|
||||||
% if not property.allowed_in_page_rule:
|
if input.seen_var_functions() {
|
||||||
if rule_type == CssRuleType::Page {
|
input.reset(start);
|
||||||
return Err(PropertyDeclarationParseError::NotAllowedInPageRule)
|
let (first_token_type, css) =
|
||||||
}
|
::custom_properties::parse_non_custom_with_var(input).map_err(|_| {
|
||||||
% endif
|
PropertyDeclarationParseError::InvalidValue(id.name().into())
|
||||||
|
})?;
|
||||||
${property_exposure_check(property)}
|
Ok(PropertyDeclaration::WithVariables(id, Arc::new(UnparsedValue {
|
||||||
|
css: css.into_owned(),
|
||||||
match longhands::${property.ident}::parse_declared(context, input) {
|
first_token_type: first_token_type,
|
||||||
Ok(value) => {
|
url_data: context.url_data.clone(),
|
||||||
declarations.push(value);
|
from_shorthand: None,
|
||||||
Ok(())
|
})))
|
||||||
},
|
} else {
|
||||||
Err(_) => Err(PropertyDeclarationParseError::InvalidValue("${property.ident}".into())),
|
Err(PropertyDeclarationParseError::InvalidValue(id.name().into()))
|
||||||
}
|
}
|
||||||
% else:
|
})
|
||||||
Err(PropertyDeclarationParseError::UnknownProperty)
|
}).map(|declaration| {
|
||||||
% endif
|
declarations.push(declaration)
|
||||||
}
|
})
|
||||||
% endfor
|
}
|
||||||
},
|
PropertyId::Shorthand(id) => {
|
||||||
PropertyId::Shorthand(id) => match id {
|
if let Ok(keyword) = input.try(|i| CSSWideKeyword::parse(context, i)) {
|
||||||
% for shorthand in data.shorthands:
|
if id == ShorthandId::All {
|
||||||
ShorthandId::${shorthand.camel_case} => {
|
declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
|
||||||
% if not shorthand.allowed_in_keyframe_block:
|
} else {
|
||||||
if rule_type == CssRuleType::Keyframe {
|
for &longhand in id.longhands() {
|
||||||
return Err(PropertyDeclarationParseError::AnimationPropertyInKeyframeBlock)
|
declarations.push(PropertyDeclaration::CSSWideKeyword(longhand, keyword))
|
||||||
}
|
|
||||||
% 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()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
let mut font_family = None;
|
||||||
% endif
|
% endif
|
||||||
for (declaration, cascade_level) in iter_declarations() {
|
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() {
|
let longhand_id = match declaration.id() {
|
||||||
PropertyDeclarationId::Longhand(id) => id,
|
PropertyDeclarationId::Longhand(id) => id,
|
||||||
PropertyDeclarationId::Custom(..) => continue,
|
PropertyDeclarationId::Custom(..) => continue,
|
||||||
|
@ -2742,15 +2821,18 @@ pub fn apply_declarations<'a, F, I>(device: &Device,
|
||||||
CascadeLevel::UserNormal |
|
CascadeLevel::UserNormal |
|
||||||
CascadeLevel::UserImportant |
|
CascadeLevel::UserImportant |
|
||||||
CascadeLevel::UAImportant) {
|
CascadeLevel::UAImportant) {
|
||||||
if let PropertyDeclaration::BackgroundColor(ref color) = *declaration {
|
let non_transparent_background = match *declaration {
|
||||||
// Treat background-color a bit differently. If the specified
|
PropertyDeclaration::BackgroundColor(ref color) => {
|
||||||
// color is anything other than a fully transparent color, convert
|
// Treat background-color a bit differently. If the specified
|
||||||
// it into the Device's default background color.
|
// color is anything other than a fully transparent color, convert
|
||||||
if color.is_non_transparent() {
|
// it into the Device's default background color.
|
||||||
declaration = default_background_color_decl.as_ref().unwrap();
|
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 category_to_cascade_now == "early":
|
||||||
if LonghandId::FontSize == longhand_id {
|
if LonghandId::FontSize == longhand_id {
|
||||||
font_size = Some(declaration);
|
font_size = Some(declaration.clone());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if LonghandId::FontFamily == longhand_id {
|
if LonghandId::FontFamily == longhand_id {
|
||||||
font_family = Some(declaration);
|
font_family = Some(declaration.clone());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
let discriminant = longhand_id as usize;
|
let discriminant = longhand_id as usize;
|
||||||
(CASCADE_PROPERTY[discriminant])(declaration,
|
(CASCADE_PROPERTY[discriminant])(&*declaration,
|
||||||
inherited_style,
|
inherited_style,
|
||||||
default_style,
|
default_style,
|
||||||
&mut context,
|
&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 seen.contains(LonghandId::XLang) || font_family.is_some() {
|
||||||
// if just the language changed, the inherited generic is all we need
|
// if just the language changed, the inherited generic is all we need
|
||||||
let mut generic = inherited_style.get_font().gecko().mGenericID;
|
let mut generic = inherited_style.get_font().gecko().mGenericID;
|
||||||
if let Some(declaration) = font_family {
|
if let Some(ref declaration) = font_family {
|
||||||
if let PropertyDeclaration::FontFamily(ref fam) = *declaration {
|
if let PropertyDeclaration::FontFamily(ref fam) = **declaration {
|
||||||
if let Some(id) = fam.single_generic() {
|
if let Some(id) = fam.single_generic() {
|
||||||
generic = id;
|
generic = id;
|
||||||
// In case of a specified font family with a single generic, we will
|
// 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
|
// during the early iteration and cascade them in order
|
||||||
// after it.
|
// after it.
|
||||||
if !_skip_font_family {
|
if !_skip_font_family {
|
||||||
if let Some(declaration) = font_family {
|
if let Some(ref declaration) = font_family {
|
||||||
|
|
||||||
let discriminant = LonghandId::FontFamily as usize;
|
let discriminant = LonghandId::FontFamily as usize;
|
||||||
(CASCADE_PROPERTY[discriminant])(declaration,
|
(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;
|
let discriminant = LonghandId::FontSize as usize;
|
||||||
(CASCADE_PROPERTY[discriminant])(declaration,
|
(CASCADE_PROPERTY[discriminant])(declaration,
|
||||||
inherited_style,
|
inherited_style,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue