Refactor cascade() and fix cascade_with_cached_declarations()

* Expand the apply() and apply_cached() templates.
  Their two invocations each were different enough
  that this improves readability IMO.
* Create computed::Context from inherited and cascaded values
  rather than computed value, as much as possible.
* Centralize this creation rather than making it per-property,
  making 'needed_for_context' not needed anymore.
* Pass a context to cascade_with_cached_declarations() rather than
  duplicate the creation code.
This commit is contained in:
Simon Sapin 2014-02-15 14:02:39 +00:00
parent 85ef67ef14
commit 83510ebbff
2 changed files with 171 additions and 267 deletions

View file

@ -163,14 +163,13 @@ pub mod computed {
pub use CSSColor = cssparser::Color; pub use CSSColor = cssparser::Color;
pub use compute_CSSColor = super::super::longhands::computed_as_specified; pub use compute_CSSColor = super::super::longhands::computed_as_specified;
use super::*; use super::*;
use super::super::{longhands, style_structs}; use super::super::longhands;
use servo_util::cowarc::CowArc;
pub use servo_util::geometry::Au; pub use servo_util::geometry::Au;
pub struct Context { pub struct Context {
color: longhands::color::computed_value::T, color: longhands::color::computed_value::T,
parent_font_weight: longhands::font_weight::computed_value::T, inherited_font_weight: longhands::font_weight::computed_value::T,
parent_font_size: longhands::font_size::computed_value::T, inherited_font_size: longhands::font_size::computed_value::T,
font_size: longhands::font_size::computed_value::T, font_size: longhands::font_size::computed_value::T,
positioned: bool, positioned: bool,
floated: bool, floated: bool,
@ -179,109 +178,23 @@ pub mod computed {
border_bottom_present: bool, border_bottom_present: bool,
border_left_present: bool, border_left_present: bool,
is_root_element: bool, is_root_element: bool,
use_parent_font_size: bool,
// TODO, as needed: root font size, viewport size, etc. // TODO, as needed: root font size, viewport size, etc.
} }
fn border_is_present(border_style: longhands::border_top_style::computed_value::T) -> bool {
match border_style {
longhands::border_top_style::none | longhands::border_top_style::hidden => false,
_ => true,
}
}
impl Context {
#[inline]
pub fn new(color: &CowArc<style_structs::Color>,
font: &CowArc<style_structs::Font>,
css_box: &CowArc<style_structs::Box>,
border: &CowArc<style_structs::Border>,
is_root_element: bool)
-> Context {
let mut context = Context {
color: color.get().color,
parent_font_weight: font.get().font_weight,
parent_font_size: font.get().font_size,
font_size: font.get().font_size,
positioned: false,
floated: false,
border_top_present: false,
border_right_present: false,
border_bottom_present: false,
border_left_present: false,
is_root_element: is_root_element,
use_parent_font_size: true,
};
context.set_position(css_box.get().position);
context.set_float(css_box.get().float);
context.set_border_top_style(border.get().border_top_style);
context.set_border_right_style(border.get().border_right_style);
context.set_border_bottom_style(border.get().border_bottom_style);
context.set_border_left_style(border.get().border_left_style);
context
}
pub fn set_color(&mut self, color: longhands::color::computed_value::T) {
self.color = color
}
pub fn set_position(&mut self, position: longhands::position::computed_value::T) {
self.positioned = match position {
longhands::position::absolute | longhands::position::fixed => true,
_ => false,
}
}
pub fn set_font_size(&mut self, font_size: longhands::font_size::computed_value::T) {
self.font_size = font_size
}
pub fn set_float(&mut self, float: longhands::float::computed_value::T) {
self.floated = float != longhands::float::none
}
pub fn set_border_top_style(&mut self,
style: longhands::border_top_style::computed_value::T) {
self.border_top_present = border_is_present(style)
}
pub fn set_border_right_style(&mut self,
style: longhands::border_top_style::computed_value::T) {
self.border_right_present = border_is_present(style)
}
pub fn set_border_bottom_style(&mut self,
style: longhands::border_top_style::computed_value::T) {
self.border_bottom_present = border_is_present(style)
}
pub fn set_border_left_style(&mut self,
style: longhands::border_top_style::computed_value::T) {
self.border_left_present = border_is_present(style)
}
}
#[inline] #[inline]
pub fn compute_Au(value: specified::Length, context: &Context) -> Au { pub fn compute_Au(value: specified::Length, context: &Context) -> Au {
match value { compute_Au_with_font_size(value, context.font_size)
specified::Au_(value) => value,
specified::Em(value) => context.font_size.scale_by(value),
specified::Ex(value) => {
let x_height = 0.5; // TODO: find that from the font
context.font_size.scale_by(value * x_height)
},
}
} }
/// A special version of `compute_Au` used for `font-size`. /// A special version of `compute_Au` used for `font-size`.
#[inline] #[inline]
pub fn compute_Au_from_parent(value: specified::Length, context: &Context) -> Au { pub fn compute_Au_with_font_size(value: specified::Length, reference_font_size: Au) -> Au {
match value { match value {
specified::Au_(value) => value, specified::Au_(value) => value,
specified::Em(value) => context.parent_font_size.scale_by(value), specified::Em(value) => reference_font_size.scale_by(value),
specified::Ex(value) => { specified::Ex(value) => {
let x_height = 0.5; // TODO: find that from the font let x_height = 0.5; // TODO: find that from the font
context.font_size.scale_by(value * x_height) reference_font_size.scale_by(value * x_height)
}, },
} }
} }

View file

@ -27,11 +27,10 @@ def to_rust_ident(name):
return name return name
class Longhand(object): class Longhand(object):
def __init__(self, name, needed_for_context): def __init__(self, name):
self.name = name self.name = name
self.ident = to_rust_ident(name) self.ident = to_rust_ident(name)
self.style_struct = THIS_STYLE_STRUCT self.style_struct = THIS_STYLE_STRUCT
self.needed_for_context = needed_for_context
class Shorthand(object): class Shorthand(object):
def __init__(self, name, sub_properties): def __init__(self, name, sub_properties):
@ -77,9 +76,9 @@ pub mod longhands {
value value
} }
<%def name="raw_longhand(name, needed_for_context=False, no_super=False)"> <%def name="raw_longhand(name, no_super=False)">
<% <%
property = Longhand(name, needed_for_context) property = Longhand(name)
THIS_STYLE_STRUCT.longhands.append(property) THIS_STYLE_STRUCT.longhands.append(property)
LONGHANDS.append(property) LONGHANDS.append(property)
LONGHANDS_BY_NAME[name] = property LONGHANDS_BY_NAME[name] = property
@ -102,8 +101,8 @@ pub mod longhands {
} }
</%def> </%def>
<%def name="longhand(name, needed_for_context=False, no_super=False)"> <%def name="longhand(name, no_super=False)">
<%self:raw_longhand name="${name}" needed_for_context="${needed_for_context}"> <%self:raw_longhand name="${name}">
${caller.body()} ${caller.body()}
pub fn parse_specified(input: &[ComponentValue]) pub fn parse_specified(input: &[ComponentValue])
-> Option<DeclaredValue<SpecifiedValue>> { -> Option<DeclaredValue<SpecifiedValue>> {
@ -112,8 +111,8 @@ pub mod longhands {
</%self:raw_longhand> </%self:raw_longhand>
</%def> </%def>
<%def name="single_component_value(name, needed_for_context=False)"> <%def name="single_component_value(name)">
<%self:longhand name="${name}" needed_for_context="${needed_for_context}"> <%self:longhand name="${name}">
${caller.body()} ${caller.body()}
pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> { pub fn parse(input: &[ComponentValue]) -> Option<SpecifiedValue> {
one_component_value(input).and_then(from_component_value) one_component_value(input).and_then(from_component_value)
@ -121,8 +120,8 @@ pub mod longhands {
</%self:longhand> </%self:longhand>
</%def> </%def>
<%def name="single_keyword_computed(name, values, needed_for_context=False)"> <%def name="single_keyword_computed(name, values)">
<%self:single_component_value name="${name}" needed_for_context="${needed_for_context}"> <%self:single_component_value name="${name}">
${caller.body()} ${caller.body()}
pub mod computed_value { pub mod computed_value {
#[deriving(Eq, Clone, FromPrimitive)] #[deriving(Eq, Clone, FromPrimitive)]
@ -149,10 +148,9 @@ pub mod longhands {
</%self:single_component_value> </%self:single_component_value>
</%def> </%def>
<%def name="single_keyword(name, values, needed_for_context=False)"> <%def name="single_keyword(name, values)">
<%self:single_keyword_computed name="${name}" <%self:single_keyword_computed name="${name}"
values="${values}" values="${values}">
needed_for_context="${needed_for_context}">
// The computed value is the same as the specified value. // The computed value is the same as the specified value.
pub use to_computed_value = super::computed_as_specified; pub use to_computed_value = super::computed_as_specified;
</%self:single_keyword_computed> </%self:single_keyword_computed>
@ -197,11 +195,10 @@ pub mod longhands {
% endfor % endfor
// double groove ridge insed outset // double groove ridge insed outset
${single_keyword("border-top-style", values="none solid dotted dashed hidden", \ ${single_keyword("border-top-style", values="none solid dotted dashed hidden")}
needed_for_context=True)}
% for side in ["right", "bottom", "left"]: % for side in ["right", "bottom", "left"]:
<%self:longhand name="border-${side}-style", no_super="True", needed_for_context="True"> <%self:longhand name="border-${side}-style", no_super="True">
pub use super::border_top_style::{get_initial_value, parse, to_computed_value}; pub use super::border_top_style::{get_initial_value, parse, to_computed_value};
pub type SpecifiedValue = super::border_top_style::SpecifiedValue; pub type SpecifiedValue = super::border_top_style::SpecifiedValue;
pub mod computed_value { pub mod computed_value {
@ -290,8 +287,8 @@ pub mod longhands {
} }
</%self:single_keyword_computed> </%self:single_keyword_computed>
${single_keyword("position", "static absolute relative fixed", needed_for_context="True")} ${single_keyword("position", "static absolute relative fixed")}
${single_keyword("float", "none left right", needed_for_context="True")} ${single_keyword("float", "none left right")}
${single_keyword("clear", "none left right both")} ${single_keyword("clear", "none left right both")}
// CSS 2.1, Section 10 - Visual formatting model details // CSS 2.1, Section 10 - Visual formatting model details
@ -480,7 +477,7 @@ pub mod longhands {
${new_style_struct("Color", is_inherited=True)} ${new_style_struct("Color", is_inherited=True)}
<%self:raw_longhand name="color" needed_for_context="True"> <%self:raw_longhand name="color">
pub use to_computed_value = super::computed_as_specified; pub use to_computed_value = super::computed_as_specified;
pub type SpecifiedValue = RGBA; pub type SpecifiedValue = RGBA;
pub mod computed_value { pub mod computed_value {
@ -646,7 +643,7 @@ pub mod longhands {
% for weight in range(100, 901, 100): % for weight in range(100, 901, 100):
SpecifiedWeight${weight} => Weight${weight}, SpecifiedWeight${weight} => Weight${weight},
% endfor % endfor
Bolder => match context.parent_font_weight { Bolder => match context.inherited_font_weight {
Weight100 => Weight400, Weight100 => Weight400,
Weight200 => Weight400, Weight200 => Weight400,
Weight300 => Weight400, Weight300 => Weight400,
@ -657,7 +654,7 @@ pub mod longhands {
Weight800 => Weight900, Weight800 => Weight900,
Weight900 => Weight900, Weight900 => Weight900,
}, },
Lighter => match context.parent_font_weight { Lighter => match context.inherited_font_weight {
Weight100 => Weight100, Weight100 => Weight100,
Weight200 => Weight100, Weight200 => Weight100,
Weight300 => Weight100, Weight300 => Weight100,
@ -672,8 +669,7 @@ pub mod longhands {
} }
</%self:single_component_value> </%self:single_component_value>
<%self:single_component_value name="font-size" needed_for_context="True"> <%self:single_component_value name="font-size">
use super::super::common_types::computed::compute_Au_from_parent;
pub type SpecifiedValue = specified::Length; // Percentages are the same as em. pub type SpecifiedValue = specified::Length; // Percentages are the same as em.
pub mod computed_value { pub mod computed_value {
use super::super::Au; use super::super::Au;
@ -683,13 +679,10 @@ pub mod longhands {
Au::from_px(16) // medium Au::from_px(16) // medium
} }
#[inline] #[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context) pub fn to_computed_value(_value: SpecifiedValue, context: &computed::Context)
-> computed_value::T { -> computed_value::T {
if !context.use_parent_font_size { // We already computed this element's font size; no need to compute it again.
// We already computed this element's font size; no need to compute it again. return context.font_size
return context.font_size
}
compute_Au_from_parent(value, context)
} }
/// <length> | <percentage> /// <length> | <percentage>
/// TODO: support <absolute-size> and <relative-size> /// TODO: support <absolute-size> and <relative-size>
@ -771,7 +764,7 @@ pub mod shorthands {
pub use super::*; pub use super::*;
pub use super::longhands::*; pub use super::longhands::*;
<%def name="shorthand(name, sub_properties, needed_for_context=False)"> <%def name="shorthand(name, sub_properties)">
<% <%
shorthand = Shorthand(name, sub_properties.split()) shorthand = Shorthand(name, sub_properties.split())
SHORTHANDS.append(shorthand) SHORTHANDS.append(shorthand)
@ -1156,74 +1149,53 @@ pub fn initial_values() -> ComputedValues {
} }
/// Fast path for the function below. Only computes new inherited styles. /// Fast path for the function below. Only computes new inherited styles.
#[allow(unused_mut)]
fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty], fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty],
shareable: bool, shareable: bool,
parent_style: &ComputedValues, parent_style: &ComputedValues,
cached_style: &ComputedValues) cached_style: &ComputedValues,
context: &computed::Context)
-> ComputedValues { -> ComputedValues {
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
% if style_struct.inherited: % if style_struct.inherited:
let mut style_${style_struct.name} = parent_style.${style_struct.name}.clone(); let mut style_${style_struct.name} = parent_style.${style_struct.name}.clone();
% else: % else:
let mut style_${style_struct.name} = cached_style.${style_struct.name}.clone(); let style_${style_struct.name} = cached_style.${style_struct.name}.clone();
% endif % endif
% endfor % endfor
let mut context = computed::Context::new(&style_Color, for sub_list in applicable_declarations.iter() {
&style_Font, for declaration in sub_list.declarations.get().iter() {
&style_Box, match *declaration {
&style_Border, % for style_struct in STYLE_STRUCTS:
false); % if style_struct.inherited:
% for property in style_struct.longhands:
<%def name="apply_cached(priority)"> ${property.ident}_declaration(ref declared_value) => {
for sub_list in applicable_declarations.iter() { style_${style_struct.name}.get_mut().${property.ident} =
for declaration in sub_list.declarations.get().iter() { match *declared_value {
match declaration { SpecifiedValue(ref specified_value)
% for style_struct in STYLE_STRUCTS: => longhands::${property.ident}::to_computed_value(
% if style_struct.inherited: (*specified_value).clone(),
% for property in style_struct.longhands: context
% if (property.needed_for_context and needed_for_context) or not \ ),
needed_for_context: CSSWideKeyword(Initial)
&${property.ident}_declaration(SpecifiedValue(ref value)) => { => longhands::${property.ident}::get_initial_value(),
% if property.needed_for_context and needed_for_context: CSSWideKeyword(Inherit) => {
context.set_${property.ident}(computed_value) // This is a bit slow, but this is rare so it shouldn't matter.
% elif not needed_for_context: // FIXME: is it still?
// Overwrite earlier declarations. parent_style.${style_struct.name}
let computed_value = .get()
longhands::${property.ident}::to_computed_value( .${property.ident}
(*value).clone(), .clone()
&context);
style_${style_struct.name}.get_mut()
.${property.ident} =
computed_value
% endif
} }
&${property.ident}_declaration(CSSWideKeyword(Initial)) => { }
let computed_value = }
longhands::${property.ident}::get_initial_value(); % endfor
% if property.needed_for_context and needed_for_context: % endif
context.set_${property.ident}(computed_value) % endfor
% elif not needed_for_context: _ => {}
// Overwrite earlier declarations.
style_${style_struct.name}.get_mut()
.${property.ident} =
computed_value
% endif
}
% endif
% endfor
% endif
% endfor
_ => {}
}
} }
} }
</%def> }
${apply_cached(True)}
context.use_parent_font_size = false;
${apply_cached(False)}
ComputedValues { ComputedValues {
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
@ -1257,113 +1229,132 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
initial_values: &ComputedValues, initial_values: &ComputedValues,
cached_style: Option< &ComputedValues >) cached_style: Option< &ComputedValues >)
-> (ComputedValues, bool) { -> (ComputedValues, bool) {
let (is_root_element, inherited_style) = match parent_style {
Some(parent_style) => (false, parent_style),
None => (true, initial_values),
};
let mut context = {
let inherited_font_style = inherited_style.Font.get();
computed::Context {
is_root_element: is_root_element,
inherited_font_weight: inherited_font_style.font_weight,
inherited_font_size: inherited_font_style.font_size,
// To be overridden by applicable declarations:
font_size: inherited_font_style.font_size,
color: inherited_style.Color.get().color,
positioned: false,
floated: false,
border_top_present: false,
border_right_present: false,
border_bottom_present: false,
border_left_present: false,
}
};
// This assumes that the computed and specified values have the same Rust type.
macro_rules! get_specified(
($style_struct: ident, $property: ident, $declared_value: expr) => {
match *$declared_value {
SpecifiedValue(specified_value) => specified_value,
CSSWideKeyword(Initial) => longhands::$property::get_initial_value(),
CSSWideKeyword(Inherit) => inherited_style.$style_struct.get().$property.clone(),
}
};
)
// Initialize `context`
for sub_list in applicable_declarations.iter() {
for declaration in sub_list.declarations.get().iter() {
match *declaration {
font_size_declaration(ref value) => {
context.font_size = match *value {
SpecifiedValue(specified_value) => computed::compute_Au_with_font_size(
specified_value, context.inherited_font_size),
CSSWideKeyword(Initial) => longhands::font_size::get_initial_value(),
CSSWideKeyword(Inherit) => context.inherited_font_size,
}
}
color_declaration(ref value) => {
context.color = get_specified!(Color, color, value);
}
position_declaration(ref value) => {
context.positioned = match get_specified!(Box, position, value) {
longhands::position::absolute | longhands::position::fixed => true,
_ => false,
}
}
float_declaration(ref value) => {
context.floated = get_specified!(Box, float, value) != longhands::float::none;
}
% for side in ["top", "right", "bottom", "left"]:
border_${side}_style_declaration(ref value) => {
context.border_${side}_present =
match get_specified!(Border, border_${side}_style, value) {
longhands::border_top_style::none |
longhands::border_top_style::hidden => false,
_ => true,
};
}
% endfor
_ => {}
}
}
}
match (cached_style, parent_style) { match (cached_style, parent_style) {
(Some(cached_style), Some(parent_style)) => { (Some(cached_style), Some(parent_style)) => {
return (cascade_with_cached_declarations(applicable_declarations, return (cascade_with_cached_declarations(applicable_declarations,
shareable, shareable,
parent_style, parent_style,
cached_style), false) cached_style,
&context), false)
} }
(_, _) => {} (_, _) => {}
} }
let is_root_element; // Set computed values, overwriting earlier declarations for the same property.
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
let mut style_${style_struct.name}; let mut style_${style_struct.name} =
% if style_struct.inherited:
inherited_style
% else:
initial_values
% endif
.${style_struct.name}.clone();
% endfor % endfor
match parent_style {
Some(parent_style) => {
is_root_element = false;
% for style_struct in STYLE_STRUCTS:
% if style_struct.inherited:
style_${style_struct.name} = parent_style.${style_struct.name}.clone();
% else:
style_${style_struct.name} = initial_values.${style_struct.name}.clone();
% endif
% endfor
}
None => {
is_root_element = true;
% for style_struct in STYLE_STRUCTS:
style_${style_struct.name} = initial_values.${style_struct.name}.clone();
% endfor
}
}
let mut context = computed::Context::new(&style_Color,
&style_Font,
&style_Box,
&style_Border,
is_root_element);
let mut cacheable = true; let mut cacheable = true;
<%def name="apply(needed_for_context)"> for sub_list in applicable_declarations.iter() {
for sub_list in applicable_declarations.iter() { for declaration in sub_list.declarations.get().iter() {
for declaration in sub_list.declarations.get().iter() { match *declaration {
match declaration { % for style_struct in STYLE_STRUCTS:
% for style_struct in STYLE_STRUCTS: % for property in style_struct.longhands:
% for property in style_struct.longhands: ${property.ident}_declaration(ref declared_value) => {
% if (property.needed_for_context and needed_for_context) or not \ style_${style_struct.name}.get_mut().${property.ident} =
needed_for_context: match *declared_value {
&${property.ident}_declaration(SpecifiedValue(ref value)) => { SpecifiedValue(ref specified_value)
let computed_value = => longhands::${property.ident}::to_computed_value(
longhands::${property.ident}::to_computed_value( (*specified_value).clone(),
(*value).clone(), &context
&context); ),
% if property.needed_for_context and needed_for_context: CSSWideKeyword(Initial)
context.set_${property.ident}(computed_value) => longhands::${property.ident}::get_initial_value(),
% elif not needed_for_context: CSSWideKeyword(Inherit) => {
// Overwrite earlier declarations.
style_${style_struct.name}.get_mut().${property.ident} =
computed_value
% endif
}
&${property.ident}_declaration(CSSWideKeyword(Initial)) => {
let computed_value =
longhands::${property.ident}::get_initial_value();
% if property.needed_for_context and needed_for_context:
context.set_${property.ident}(computed_value)
% elif not needed_for_context:
// Overwrite earlier declarations.
style_${style_struct.name}.get_mut().${property.ident} =
computed_value
% endif
}
% endif
% if not needed_for_context:
&${property.ident}_declaration(CSSWideKeyword(Inherit)) => {
// This is a bit slow, but this is rare so it shouldn't matter. // This is a bit slow, but this is rare so it shouldn't matter.
// FIXME: is it still?
cacheable = false; cacheable = false;
match parent_style { inherited_style.${style_struct.name}
None => { .get()
style_${style_struct.name}.get_mut() .${property.ident}
.${property.ident} = .clone()
longhands::${property.ident}::get_initial_value()
}
Some(ref parent_style) => {
style_${style_struct.name}.get_mut()
.${property.ident} =
parent_style.${style_struct.name}
.get()
.${property.ident}
.clone()
}
}
} }
% endif }
% endfor }
% endfor % endfor
% if needed_for_context: % endfor
_ => {}
% endif
}
} }
} }
</%def> }
${apply(True)}
context.use_parent_font_size = false;
${apply(False)}
(ComputedValues { (ComputedValues {
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS: