Skip to content

Commit

Permalink
style: Refactor the traversal so it's more easy to read and straight-…
Browse files Browse the repository at this point in the history
…forward.
  • Loading branch information
emilio committed Feb 2, 2017
1 parent 8859aa0 commit 2594cb9
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 113 deletions.
71 changes: 50 additions & 21 deletions components/style/data.rs
Expand Up @@ -9,7 +9,7 @@
use dom::TElement;
use properties::ComputedValues;
use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RESTYLE_STYLE_ATTRIBUTE, RestyleHint};
use restyle_hints::{RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
use rule_tree::StrongRuleNode;
use selector_parser::{PseudoElement, RestyleDamage, Snapshot};
use std::collections::HashMap;
Expand Down Expand Up @@ -168,7 +168,7 @@ pub struct StoredRestyleHint {
///
/// This hint is stripped down, and only contains hints that are a subset of
/// RestyleHint::for_single_element().
pub for_self: RestyleHint,
pub self_: RestyleHint,
/// Whether the descendants of this element need to be restyled.
pub descendants: DescendantRestyleHint,
}
Expand All @@ -177,7 +177,7 @@ impl StoredRestyleHint {
/// Propagates this restyle hint to a child element.
pub fn propagate(&self) -> Self {
StoredRestyleHint {
for_self: if self.descendants == DescendantRestyleHint::Empty {
self_: if self.descendants == DescendantRestyleHint::Empty {
RestyleHint::empty()
} else {
RESTYLE_SELF
Expand All @@ -189,7 +189,7 @@ impl StoredRestyleHint {
/// Creates an empty `StoredRestyleHint`.
pub fn empty() -> Self {
StoredRestyleHint {
for_self: RestyleHint::empty(),
self_: RestyleHint::empty(),
descendants: DescendantRestyleHint::Empty,
}
}
Expand All @@ -198,24 +198,25 @@ impl StoredRestyleHint {
/// including the element.
pub fn subtree() -> Self {
StoredRestyleHint {
for_self: RESTYLE_SELF,
self_: RESTYLE_SELF,
descendants: DescendantRestyleHint::Descendants,
}
}

/// Whether the restyle hint is empty (nothing requires to be restyled).
pub fn is_empty(&self) -> bool {
!self.needs_restyle_self() && self.descendants == DescendantRestyleHint::Empty
/// Returns true if the hint indicates that our style may be invalidated.
pub fn has_self_invalidations(&self) -> bool {
!self.self_.is_empty()
}

/// Whether the element holding the hint needs to be restyled on some way.
pub fn needs_restyle_self(&self) -> bool {
!self.for_self.is_empty()
/// Whether the restyle hint is empty (nothing requires to be restyled).
pub fn is_empty(&self) -> bool {
!self.has_self_invalidations() &&
self.descendants == DescendantRestyleHint::Empty
}

/// Insert another restyle hint, effectively resulting in the union of both.
pub fn insert(&mut self, other: &Self) {
self.for_self |= other.for_self;
self.self_ |= other.self_;
self.descendants = self.descendants.union(other.descendants);
}
}
Expand All @@ -232,7 +233,7 @@ impl From<RestyleHint> for StoredRestyleHint {
use self::DescendantRestyleHint::*;
debug_assert!(!hint.contains(RESTYLE_LATER_SIBLINGS), "Caller should apply sibling hints");
StoredRestyleHint {
for_self: hint & RestyleHint::for_single_element(),
self_: hint & RestyleHint::for_self(),
descendants: if hint.contains(RESTYLE_DESCENDANTS) { Descendants } else { Empty },
}
}
Expand Down Expand Up @@ -348,7 +349,7 @@ impl RestyleData {

/// Returns true if this RestyleData might invalidate the current style.
pub fn has_invalidations(&self) -> bool {
self.hint.needs_restyle_self() ||
self.hint.has_self_invalidations() ||
self.recascade ||
self.snapshot.is_some()
}
Expand All @@ -369,6 +370,19 @@ pub struct ElementData {
restyle: Option<Box<RestyleData>>,
}

/// The kind of restyle that a single element should do.
pub enum RestyleKind {
/// We need to run selector matching plus re-cascade, that is, a full
/// restyle.
MatchAndCascade,
/// We need to recascade with some replacement rule, such as the style
/// attribute, or animation rules.
CascadeWithReplacements(RestyleHint),
/// We only need to recascade, for example, because only inherited
/// properties in the parent changed.
CascadeOnly,
}

impl ElementData {
/// Trivially construct an ElementData.
pub fn new(existing: Option<ElementStyles>) -> Self {
Expand All @@ -387,16 +401,31 @@ impl ElementData {
/// invalidation.
pub fn has_current_styles(&self) -> bool {
self.has_styles() &&
self.restyle.as_ref().map_or(true, |r| !r.has_invalidations())
self.restyle.as_ref().map_or(true, |r| !r.has_invalidations())
}

/// Returns true if this element and its pseudo elements do not need
/// selector matching, and can just go on with a style attribute update in
/// the rule tree plus re-cascade.
pub fn needs_only_style_attribute_update(&self) -> bool {
/// Returns the kind of restyling that we're going to need to do on this
/// element, based of the stored restyle hint.
pub fn restyle_kind(&self) -> RestyleKind {
debug_assert!(!self.has_current_styles(), "Should've stopped earlier");
self.has_styles() &&
self.restyle.as_ref().map_or(false, |r| r.hint.for_self == RESTYLE_STYLE_ATTRIBUTE)
if !self.has_styles() {
return RestyleKind::MatchAndCascade;
}

debug_assert!(self.restyle.is_some());
let restyle_data = self.restyle.as_ref().unwrap();
let hint = restyle_data.hint.self_;
if hint.contains(RESTYLE_SELF) {
return RestyleKind::MatchAndCascade;
}

if !hint.is_empty() {
return RestyleKind::CascadeWithReplacements(hint);
}

debug_assert!(restyle_data.recascade,
"We definitely need to do something!");
return RestyleKind::CascadeOnly;
}

/// Gets the element styles, if any.
Expand Down
64 changes: 44 additions & 20 deletions components/style/matching.rs
Expand Up @@ -17,6 +17,7 @@ use data::{ComputedStyle, ElementData, ElementStyles, PseudoRuleNodes, PseudoSty
use dom::{SendElement, TElement, TNode};
use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RestyleHint};
use rule_tree::{CascadeLevel, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::MatchAttr;
Expand Down Expand Up @@ -627,31 +628,50 @@ pub trait MatchMethods : TElement {
}
}

/// Updates the style attribute rule nodes without re-running selector
/// matching, using just the rule tree.
fn update_style_attribute(&self,
context: &StyleContext<Self>,
data: &mut AtomicRefMut<ElementData>)
-> MatchResults {
let style_attribute = self.style_attribute();
/// Get the appropriate MatchResults from the current styles, to perform a
/// recascade.
///
/// TODO(emilio): Stop using `MachResults`, use an enum, or something.
fn match_results_from_current_style(&self, data: &ElementData) -> MatchResults {
let rule_node = data.styles().primary.rules.clone();
MatchResults {
primary: rule_node,
// FIXME(emilio): Same concern as below.
relations: StyleRelations::empty(),
// The per-pseudo rule-nodes haven't changed, but still need to be
// recascaded.
per_pseudo: data.styles().pseudos.get_rules(),
}

let mut new_rule_node = data.styles().primary.rules.clone();
}

new_rule_node = context.shared.stylist.rule_tree
.replace_rule_at_level_if_applicable(CascadeLevel::StyleAttributeNormal,
style_attribute,
new_rule_node);
/// Updates the rule nodes without re-running selector matching, using just
/// the rule tree.
fn cascade_with_replacements(&self,
hint: RestyleHint,
context: &StyleContext<Self>,
data: &mut AtomicRefMut<ElementData>)
-> MatchResults {
let mut rule_node = data.styles().primary.rules.clone();

new_rule_node = context.shared.stylist.rule_tree
.replace_rule_at_level_if_applicable(CascadeLevel::StyleAttributeImportant,
style_attribute,
new_rule_node);
if hint.contains(RESTYLE_STYLE_ATTRIBUTE) {
let style_attribute = self.style_attribute();

rule_node = context.shared.stylist.rule_tree
.update_rule_at_level(CascadeLevel::StyleAttributeNormal,
style_attribute,
rule_node);

rule_node = context.shared.stylist.rule_tree
.update_rule_at_level(CascadeLevel::StyleAttributeImportant,
style_attribute,
rule_node);
}

MatchResults {
primary: new_rule_node,
// FIXME(emilio): This is ok, for now at least, because it disables
// style sharing. That being said, it's not ideal, probably should
// be optional?
primary: rule_node,
// FIXME(emilio): This is ok, for now, we shouldn't need to fake
// this.
relations: AFFECTED_BY_STYLE_ATTRIBUTE,
// The per-pseudo rule-nodes haven't changed, but still need to be
// recomputed.
Expand All @@ -675,6 +695,10 @@ pub trait MatchMethods : TElement {
return StyleSharingResult::CannotShare
}

if self.parent_element().is_none() {
return StyleSharingResult::CannotShare
}

if self.style_attribute().is_some() {
return StyleSharingResult::CannotShare
}
Expand Down
2 changes: 1 addition & 1 deletion components/style/restyle_hints.rs
Expand Up @@ -53,7 +53,7 @@ bitflags! {
impl RestyleHint {
/// The subset hints that affect the styling of a single element during the
/// traversal.
pub fn for_single_element() -> Self {
pub fn for_self() -> Self {
RESTYLE_SELF | RESTYLE_STYLE_ATTRIBUTE
}
}
Expand Down
12 changes: 5 additions & 7 deletions components/style/rule_tree/mod.rs
Expand Up @@ -184,13 +184,11 @@ impl RuleTree {
/// Replaces a rule in a given level (if present) for another rule.
///
/// Returns the resulting node that represents the new path.
///
/// TODO(emilio): Maybe this should be in `StrongRuleNode`?
pub fn replace_rule_at_level_if_applicable(&self,
level: CascadeLevel,
pdb: Option<&Arc<RwLock<PropertyDeclarationBlock>>>,
path: StrongRuleNode)
-> StrongRuleNode {
pub fn update_rule_at_level(&self,
level: CascadeLevel,
pdb: Option<&Arc<RwLock<PropertyDeclarationBlock>>>,
path: StrongRuleNode)
-> StrongRuleNode {
debug_assert!(level.is_unique_per_element());
// TODO(emilio): Being smarter with lifetimes we could avoid a bit of
// the refcount churn.
Expand Down

0 comments on commit 2594cb9

Please sign in to comment.