style: Refactor RestyleHint to be a struct.

Later PRs will add additional data to it that is not so easy to
represent using bitflags.
This commit is contained in:
Cameron McCormack 2017-05-19 17:39:15 +08:00 committed by Emilio Cobos Álvarez
parent c13be5cd13
commit a397590838
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
9 changed files with 294 additions and 111 deletions

View file

@ -1100,7 +1100,7 @@ impl LayoutThread {
let el = node.as_element().unwrap(); let el = node.as_element().unwrap();
if let Some(mut d) = element.mutate_data() { if let Some(mut d) = element.mutate_data() {
if d.has_styles() { if d.has_styles() {
d.ensure_restyle().hint.insert(&StoredRestyleHint::subtree()); d.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
} }
} }
if let Some(p) = el.parent_element() { if let Some(p) = el.parent_element() {
@ -1136,7 +1136,7 @@ impl LayoutThread {
if needs_dirtying { if needs_dirtying {
if let Some(mut d) = element.mutate_data() { if let Some(mut d) = element.mutate_data() {
if d.has_styles() { if d.has_styles() {
d.ensure_restyle().hint.insert(&StoredRestyleHint::subtree()); d.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
} }
} }
} }
@ -1184,7 +1184,7 @@ impl LayoutThread {
let mut restyle_data = style_data.ensure_restyle(); let mut restyle_data = style_data.ensure_restyle();
// Stash the data on the element for processing by the style system. // Stash the data on the element for processing by the style system.
restyle_data.hint.insert(&restyle.hint.into()); restyle_data.hint.insert(restyle.hint.into());
restyle_data.damage = restyle.damage; restyle_data.damage = restyle.damage;
debug!("Noting restyle for {:?}: {:?}", el, restyle_data); debug!("Noting restyle for {:?}: {:?}", el, restyle_data);
} }

View file

@ -131,7 +131,7 @@ use std::rc::Rc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use style::attr::AttrValue; use style::attr::AttrValue;
use style::context::{QuirksMode, ReflowGoal}; use style::context::{QuirksMode, ReflowGoal};
use style::restyle_hints::{RestyleHint, RESTYLE_SELF, RESTYLE_STYLE_ATTRIBUTE}; use style::restyle_hints::{RestyleHint, RestyleReplacements, RESTYLE_STYLE_ATTRIBUTE};
use style::selector_parser::{RestyleDamage, Snapshot}; use style::selector_parser::{RestyleDamage, Snapshot};
use style::shared_lock::SharedRwLock as StyleSharedRwLock; use style::shared_lock::SharedRwLock as StyleSharedRwLock;
use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join}; use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join};
@ -2361,14 +2361,14 @@ impl Document {
entry.snapshot = Some(Snapshot::new(el.html_element_in_html_document())); entry.snapshot = Some(Snapshot::new(el.html_element_in_html_document()));
} }
if attr.local_name() == &local_name!("style") { if attr.local_name() == &local_name!("style") {
entry.hint |= RESTYLE_STYLE_ATTRIBUTE; entry.hint.insert(RestyleHint::for_replacements(RESTYLE_STYLE_ATTRIBUTE));
} }
// FIXME(emilio): This should become something like // FIXME(emilio): This should become something like
// element.is_attribute_mapped(attr.local_name()). // element.is_attribute_mapped(attr.local_name()).
if attr.local_name() == &local_name!("width") || if attr.local_name() == &local_name!("width") ||
attr.local_name() == &local_name!("height") { attr.local_name() == &local_name!("height") {
entry.hint |= RESTYLE_SELF; entry.hint.insert(RestyleHint::for_self());
} }
let mut snapshot = entry.snapshot.as_mut().unwrap(); let mut snapshot = entry.snapshot.as_mut().unwrap();

View file

@ -102,7 +102,7 @@ use style::context::{QuirksMode, ReflowGoal};
use style::element_state::*; use style::element_state::*;
use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute}; use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x}; use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x};
use style::restyle_hints::RESTYLE_SELF; use style::restyle_hints::RestyleHint;
use style::rule_tree::CascadeLevel; use style::rule_tree::CascadeLevel;
use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser}; use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
use style::shared_lock::{SharedRwLock, Locked}; use style::shared_lock::{SharedRwLock, Locked};
@ -245,7 +245,7 @@ impl Element {
// FIXME(bholley): I think we should probably only do this for // FIXME(bholley): I think we should probably only do this for
// NodeStyleDamaged, but I'm preserving existing behavior. // NodeStyleDamaged, but I'm preserving existing behavior.
restyle.hint |= RESTYLE_SELF; restyle.hint.insert(RestyleHint::for_self());
if damage == NodeDamage::OtherNodeDamage { if damage == NodeDamage::OtherNodeDamage {
restyle.damage = RestyleDamage::rebuild_and_reflow(); restyle.damage = RestyleDamage::rebuild_and_reflow();

View file

@ -10,7 +10,7 @@ use context::SharedStyleContext;
use dom::TElement; use dom::TElement;
use properties::ComputedValues; use properties::ComputedValues;
use properties::longhands::display::computed_value as display; use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; use restyle_hints::{RestyleReplacements, RestyleHint};
use rule_tree::StrongRuleNode; use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage}; use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
use shared_lock::StylesheetGuards; use shared_lock::StylesheetGuards;
@ -198,21 +198,18 @@ impl StoredRestyleHint {
// In the middle of an animation only restyle, we don't need to // In the middle of an animation only restyle, we don't need to
// propagate any restyle hints, and we need to remove ourselves. // propagate any restyle hints, and we need to remove ourselves.
if traversal_flags.for_animation_only() { if traversal_flags.for_animation_only() {
self.0.remove(RestyleHint::for_animations()); self.0.remove_animation_hints();
return Self::empty(); return Self::empty();
} }
debug_assert!(!self.0.intersects(RestyleHint::for_animations()), debug_assert!(!self.0.has_animation_hint(),
"There should not be any animation restyle hints \ "There should not be any animation restyle hints \
during normal traversal"); during normal traversal");
// Else we should clear ourselves, and return the propagated hint. // Else we should clear ourselves, and return the propagated hint.
let hint = mem::replace(&mut self.0, RestyleHint::empty()); let new_hint = mem::replace(&mut self.0, RestyleHint::empty())
StoredRestyleHint(if hint.contains(RESTYLE_DESCENDANTS) { .propagate_for_non_animation_restyle();
RESTYLE_SELF | RESTYLE_DESCENDANTS StoredRestyleHint(new_hint)
} else {
RestyleHint::empty()
})
} }
/// Creates an empty `StoredRestyleHint`. /// Creates an empty `StoredRestyleHint`.
@ -223,25 +220,25 @@ impl StoredRestyleHint {
/// Creates a restyle hint that forces the whole subtree to be restyled, /// Creates a restyle hint that forces the whole subtree to be restyled,
/// including the element. /// including the element.
pub fn subtree() -> Self { pub fn subtree() -> Self {
StoredRestyleHint(RESTYLE_SELF | RESTYLE_DESCENDANTS) StoredRestyleHint(RestyleHint::subtree())
} }
/// Creates a restyle hint that forces the element and all its later /// Creates a restyle hint that forces the element and all its later
/// siblings to have their whole subtrees restyled, including the elements /// siblings to have their whole subtrees restyled, including the elements
/// themselves. /// themselves.
pub fn subtree_and_later_siblings() -> Self { pub fn subtree_and_later_siblings() -> Self {
StoredRestyleHint(RESTYLE_SELF | RESTYLE_DESCENDANTS | RESTYLE_LATER_SIBLINGS) StoredRestyleHint(RestyleHint::subtree_and_later_siblings())
} }
/// Returns true if the hint indicates that our style may be invalidated. /// Returns true if the hint indicates that our style may be invalidated.
pub fn has_self_invalidations(&self) -> bool { pub fn has_self_invalidations(&self) -> bool {
self.0.intersects(RestyleHint::for_self()) self.0.affects_self()
} }
/// Returns true if the hint indicates that our sibling's style may be /// Returns true if the hint indicates that our sibling's style may be
/// invalidated. /// invalidated.
pub fn has_sibling_invalidations(&self) -> bool { pub fn has_sibling_invalidations(&self) -> bool {
self.0.intersects(RESTYLE_LATER_SIBLINGS) self.0.affects_later_siblings()
} }
/// Whether the restyle hint is empty (nothing requires to be restyled). /// Whether the restyle hint is empty (nothing requires to be restyled).
@ -250,13 +247,18 @@ impl StoredRestyleHint {
} }
/// Insert another restyle hint, effectively resulting in the union of both. /// Insert another restyle hint, effectively resulting in the union of both.
pub fn insert(&mut self, other: &Self) { pub fn insert(&mut self, other: Self) {
self.0 |= other.0 self.0.insert(other.0)
}
/// Insert another restyle hint, effectively resulting in the union of both.
pub fn insert_from(&mut self, other: &Self) {
self.0.insert_from(&other.0)
} }
/// Returns true if the hint has animation-only restyle. /// Returns true if the hint has animation-only restyle.
pub fn has_animation_hint(&self) -> bool { pub fn has_animation_hint(&self) -> bool {
self.0.intersects(RestyleHint::for_animations()) self.0.has_animation_hint()
} }
} }
@ -356,7 +358,7 @@ pub enum RestyleKind {
MatchAndCascade, MatchAndCascade,
/// We need to recascade with some replacement rule, such as the style /// We need to recascade with some replacement rule, such as the style
/// attribute, or animation rules. /// attribute, or animation rules.
CascadeWithReplacements(RestyleHint), CascadeWithReplacements(RestyleReplacements),
/// We only need to recascade, for example, because only inherited /// We only need to recascade, for example, because only inherited
/// properties in the parent changed. /// properties in the parent changed.
CascadeOnly, CascadeOnly,
@ -381,7 +383,7 @@ impl ElementData {
context.traversal_flags); context.traversal_flags);
let mut hint = match self.get_restyle() { let mut hint = match self.get_restyle() {
Some(r) => r.hint.0, Some(r) => r.hint.0.clone(),
None => RestyleHint::empty(), None => RestyleHint::empty(),
}; };
@ -393,7 +395,7 @@ impl ElementData {
element.implemented_pseudo_element()); element.implemented_pseudo_element());
if element.has_snapshot() && !element.handled_snapshot() { if element.has_snapshot() && !element.handled_snapshot() {
hint |= context.stylist.compute_restyle_hint(&element, context.snapshot_map); hint.insert(context.stylist.compute_restyle_hint(&element, context.snapshot_map));
unsafe { element.set_handled_snapshot() } unsafe { element.set_handled_snapshot() }
debug_assert!(element.handled_snapshot()); debug_assert!(element.handled_snapshot());
} }
@ -402,8 +404,7 @@ impl ElementData {
// If the hint includes a directive for later siblings, strip it out and // If the hint includes a directive for later siblings, strip it out and
// notify the caller to modify the base hint for future siblings. // notify the caller to modify the base hint for future siblings.
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS); let later_siblings = hint.remove_later_siblings_hint();
hint.remove(RESTYLE_LATER_SIBLINGS);
// Insert the hint, overriding the previous hint. This effectively takes // Insert the hint, overriding the previous hint. This effectively takes
// care of removing the later siblings restyle hint. // care of removing the later siblings restyle hint.
@ -445,13 +446,13 @@ impl ElementData {
debug_assert!(self.restyle.is_some()); debug_assert!(self.restyle.is_some());
let restyle_data = self.restyle.as_ref().unwrap(); let restyle_data = self.restyle.as_ref().unwrap();
let hint = restyle_data.hint.0; let hint = &restyle_data.hint.0;
if hint.contains(RESTYLE_SELF) { if hint.match_self() {
return RestyleKind::MatchAndCascade; return RestyleKind::MatchAndCascade;
} }
if !hint.is_empty() { if !hint.is_empty() {
return RestyleKind::CascadeWithReplacements(hint); return RestyleKind::CascadeWithReplacements(hint.replacements);
} }
debug_assert!(restyle_data.recascade, debug_assert!(restyle_data.recascade,

View file

@ -19,7 +19,7 @@ use font_metrics::FontMetricsProvider;
use log::LogLevel::Trace; use log::LogLevel::Trace;
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
use properties::longhands::display::computed_value as display; use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleHint}; use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleReplacements};
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL}; use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl}; use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
@ -1327,7 +1327,7 @@ pub trait MatchMethods : TElement {
/// the rule tree. Returns RulesChanged which indicates whether the rule nodes changed /// the rule tree. Returns RulesChanged which indicates whether the rule nodes changed
/// and whether the important rules changed. /// and whether the important rules changed.
fn replace_rules(&self, fn replace_rules(&self,
hint: RestyleHint, replacements: RestyleReplacements,
context: &StyleContext<Self>, context: &StyleContext<Self>,
data: &mut AtomicRefMut<ElementData>) data: &mut AtomicRefMut<ElementData>)
-> RulesChanged { -> RulesChanged {
@ -1359,10 +1359,10 @@ pub trait MatchMethods : TElement {
// //
// Non-animation restyle hints will be processed in a subsequent // Non-animation restyle hints will be processed in a subsequent
// normal traversal. // normal traversal.
if hint.intersects(RestyleHint::for_animations()) { if replacements.intersects(RestyleReplacements::for_animations()) {
debug_assert!(context.shared.traversal_flags.for_animation_only()); debug_assert!(context.shared.traversal_flags.for_animation_only());
if hint.contains(RESTYLE_SMIL) { if replacements.contains(RESTYLE_SMIL) {
replace_rule_node(CascadeLevel::SMILOverride, replace_rule_node(CascadeLevel::SMILOverride,
self.get_smil_override(), self.get_smil_override(),
primary_rules); primary_rules);
@ -1378,16 +1378,16 @@ pub trait MatchMethods : TElement {
// Apply Transition rules and Animation rules if the corresponding restyle hint // Apply Transition rules and Animation rules if the corresponding restyle hint
// is contained. // is contained.
if hint.contains(RESTYLE_CSS_TRANSITIONS) { if replacements.contains(RESTYLE_CSS_TRANSITIONS) {
replace_rule_node_for_animation(CascadeLevel::Transitions, replace_rule_node_for_animation(CascadeLevel::Transitions,
primary_rules); primary_rules);
} }
if hint.contains(RESTYLE_CSS_ANIMATIONS) { if replacements.contains(RESTYLE_CSS_ANIMATIONS) {
replace_rule_node_for_animation(CascadeLevel::Animations, replace_rule_node_for_animation(CascadeLevel::Animations,
primary_rules); primary_rules);
} }
} else if hint.contains(RESTYLE_STYLE_ATTRIBUTE) { } else if replacements.contains(RESTYLE_STYLE_ATTRIBUTE) {
let style_attribute = self.style_attribute(); let style_attribute = self.style_attribute();
replace_rule_node(CascadeLevel::StyleAttributeNormal, replace_rule_node(CascadeLevel::StyleAttributeNormal,
style_attribute, style_attribute,

View file

@ -28,32 +28,38 @@ use std::cell::Cell;
use std::clone::Clone; use std::clone::Clone;
use stylist::SelectorMap; use stylist::SelectorMap;
/// When the ElementState of an element (like IN_HOVER_STATE) changes,
/// certain pseudo-classes (like :hover) may require us to restyle that
/// element, its siblings, and/or its descendants. Similarly, when various
/// attributes of an element change, we may also need to restyle things with
/// id, class, and attribute selectors. Doing this conservatively is
/// expensive, and so we use RestyleHints to short-circuit work we know is
/// unnecessary.
#[derive(Debug, Clone, PartialEq)]
pub struct RestyleHint {
/// Rerun selector matching on the element.
match_self: bool,
/// Rerun selector matching on all of the element's descendants.
match_descendants: bool,
/// Rerun selector matching on all later siblings of the element and all
/// of their descendants.
match_later_siblings: bool,
/// Levels of the cascade whose rule nodes should be recomputed and
/// replaced.
pub replacements: RestyleReplacements,
}
bitflags! { bitflags! {
/// When the ElementState of an element (like IN_HOVER_STATE) changes, /// Cascade levels that can be updated for an element by simply replacing
/// certain pseudo-classes (like :hover) may require us to restyle that /// their rule node.
/// element, its siblings, and/or its descendants. Similarly, when various
/// attributes of an element change, we may also need to restyle things with
/// id, class, and attribute selectors. Doing this conservatively is
/// expensive, and so we use RestyleHints to short-circuit work we know is
/// unnecessary.
/// ///
/// Note that the bit values here must be kept in sync with the Gecko /// Note that the bit values here must be kept in sync with the Gecko
/// nsRestyleHint values. If you add more bits with matching values, /// nsRestyleHint values. If you add more bits with matching values,
/// please add assertions to assert_restyle_hints_match below. /// please add assertions to assert_restyle_hints_match below.
pub flags RestyleHint: u32 { pub flags RestyleReplacements: u8 {
/// Rerun selector matching on the element.
const RESTYLE_SELF = 0x01,
/// Rerun selector matching on all of the element's descendants.
///
/// NB: In Gecko, we have RESTYLE_SUBTREE which is inclusive of self,
/// but heycam isn't aware of a good reason for that.
const RESTYLE_DESCENDANTS = 0x04,
/// Rerun selector matching on all later siblings of the element and all
/// of their descendants.
const RESTYLE_LATER_SIBLINGS = 0x08,
/// Replace the style data coming from CSS transitions without updating /// Replace the style data coming from CSS transitions without updating
/// any other style data. This hint is only processed in animation-only /// any other style data. This hint is only processed in animation-only
/// traversal which is prior to normal traversal. /// traversal which is prior to normal traversal.
@ -76,7 +82,7 @@ bitflags! {
} }
} }
/// Asserts that all RestyleHint flags have a matching nsRestyleHint value. /// Asserts that all RestyleReplacements have a matching nsRestyleHint value.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
#[inline] #[inline]
pub fn assert_restyle_hints_match() { pub fn assert_restyle_hints_match() {
@ -85,24 +91,18 @@ pub fn assert_restyle_hints_match() {
macro_rules! check_restyle_hints { macro_rules! check_restyle_hints {
( $( $a:ident => $b:ident ),*, ) => { ( $( $a:ident => $b:ident ),*, ) => {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
let mut hints = RestyleHint::all(); let mut replacements = RestyleReplacements::all();
$( $(
assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b)); assert_eq!(structs::$a.0 as usize, $b.bits() as usize, stringify!($b));
hints.remove($b); replacements.remove($b);
)* )*
assert_eq!(hints, RestyleHint::empty(), "all RestyleHint bits should have an assertion"); assert_eq!(replacements, RestyleReplacements::empty(),
"all RestyleReplacements bits should have an assertion");
} }
} }
} }
check_restyle_hints! { check_restyle_hints! {
nsRestyleHint_eRestyle_Self => RESTYLE_SELF,
// Note that eRestyle_Subtree means "self and descendants", while
// RESTYLE_DESCENDANTS means descendants only. The From impl
// below handles converting eRestyle_Subtree into
// (RESTYLE_SELF | RESTYLE_DESCENDANTS).
nsRestyleHint_eRestyle_Subtree => RESTYLE_DESCENDANTS,
nsRestyleHint_eRestyle_LaterSiblings => RESTYLE_LATER_SIBLINGS,
nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS, nsRestyleHint_eRestyle_CSSTransitions => RESTYLE_CSS_TRANSITIONS,
nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS, nsRestyleHint_eRestyle_CSSAnimations => RESTYLE_CSS_ANIMATIONS,
nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE, nsRestyleHint_eRestyle_StyleAttribute => RESTYLE_STYLE_ATTRIBUTE,
@ -111,39 +111,220 @@ pub fn assert_restyle_hints_match() {
} }
impl RestyleHint { impl RestyleHint {
/// The subset hints that affect the styling of a single element during the /// Creates a new, empty `RestyleHint`, which represents no restyling work
/// traversal. /// needs to be done.
#[inline] #[inline]
pub fn for_self() -> Self { pub fn empty() -> Self {
RESTYLE_SELF | RESTYLE_STYLE_ATTRIBUTE | Self::for_animations() RestyleHint {
match_self: false,
match_descendants: false,
match_later_siblings: false,
replacements: RestyleReplacements::empty(),
}
} }
/// The subset hints that are used for animation restyle. /// Creates a new `RestyleHint` that indicates selector matching must be
/// re-run on the element.
#[inline]
pub fn for_self() -> Self {
RestyleHint {
match_self: true,
match_descendants: false,
match_later_siblings: false,
replacements: RestyleReplacements::empty(),
}
}
/// Creates a new `RestyleHint` that indicates selector matching must be
/// re-run on all of the element's descendants.
#[inline]
pub fn descendants() -> Self {
RestyleHint {
match_self: false,
match_descendants: true,
match_later_siblings: false,
replacements: RestyleReplacements::empty(),
}
}
/// Creates a new `RestyleHint` that indicates selector matching must be
/// re-run on all of the element's later siblings and their descendants.
#[inline]
pub fn later_siblings() -> Self {
RestyleHint {
match_self: false,
match_descendants: false,
match_later_siblings: true,
replacements: RestyleReplacements::empty(),
}
}
/// Creates a new `RestyleHint` that indicates selector matching must be
/// re-run on the element and all of its descendants.
#[inline]
pub fn subtree() -> Self {
RestyleHint {
match_self: true,
match_descendants: true,
match_later_siblings: false,
replacements: RestyleReplacements::empty(),
}
}
/// Creates a new `RestyleHint` that indicates selector matching must be
/// re-run on the element, its descendants, its later siblings, and
/// their descendants.
#[inline]
pub fn subtree_and_later_siblings() -> Self {
RestyleHint {
match_self: true,
match_descendants: true,
match_later_siblings: true,
replacements: RestyleReplacements::empty(),
}
}
/// Creates a new `RestyleHint` that indicates the specified rule node
/// replacements must be performed on the element.
#[inline]
pub fn for_replacements(replacements: RestyleReplacements) -> Self {
RestyleHint {
match_self: false,
match_descendants: false,
match_later_siblings: false,
replacements: replacements,
}
}
/// Returns whether this `RestyleHint` represents no needed restyle work.
#[inline]
pub fn is_empty(&self) -> bool {
*self == RestyleHint::empty()
}
/// Returns whether this `RestyleHint` represents the maximum possible
/// restyle work, and thus any `insert()` calls will have no effect.
#[inline]
pub fn is_maximum(&self) -> bool {
self.match_self && self.match_descendants && self.match_later_siblings && self.replacements.is_all()
}
/// Returns whether the hint specifies that some work must be performed on
/// the current element.
#[inline]
pub fn affects_self(&self) -> bool {
self.match_self || !self.replacements.is_empty()
}
/// Returns whether the hint specifies that later siblings must be restyled.
#[inline]
pub fn affects_later_siblings(&self) -> bool {
self.match_later_siblings
}
/// Returns whether the hint specifies that an animation cascade level must
/// be replaced.
#[inline]
pub fn has_animation_hint(&self) -> bool {
self.replacements.intersects(RestyleReplacements::for_animations())
}
/// Returns whether the hint specifies some restyle work other than an
/// animation cascade level replacement.
#[inline]
pub fn has_non_animation_hint(&self) -> bool {
self.match_self || self.match_descendants || self.match_later_siblings ||
self.replacements.contains(RESTYLE_STYLE_ATTRIBUTE)
}
/// Returns whether the hint specifies that selector matching must be re-run
/// for the element.
#[inline]
pub fn match_self(&self) -> bool {
self.match_self
}
/// Returns a new `RestyleHint` appropriate for children of the current
/// element.
#[inline]
pub fn propagate_for_non_animation_restyle(&self) -> Self {
if self.match_descendants {
Self::subtree()
} else {
Self::empty()
}
}
/// Removes all of the animation-related hints.
#[inline]
pub fn remove_animation_hints(&mut self) {
self.replacements.remove(RestyleReplacements::for_animations());
}
/// Removes the later siblings hint, and returns whether it was present.
pub fn remove_later_siblings_hint(&mut self) -> bool {
let later_siblings = self.match_later_siblings;
self.match_later_siblings = false;
later_siblings
}
/// Unions the specified `RestyleHint` into this one.
#[inline]
pub fn insert_from(&mut self, other: &Self) {
self.match_self |= other.match_self;
self.match_descendants |= other.match_descendants;
self.match_later_siblings |= other.match_later_siblings;
self.replacements.insert(other.replacements);
}
/// Unions the specified `RestyleHint` into this one.
#[inline]
pub fn insert(&mut self, other: Self) {
// A later patch should make it worthwhile to have an insert() function
// that consumes its argument.
self.insert_from(&other)
}
/// Returns whether this `RestyleHint` represents at least as much restyle
/// work as the specified one.
#[inline]
pub fn contains(&mut self, other: &Self) -> bool {
!(other.match_self && !self.match_self) &&
!(other.match_descendants && !self.match_descendants) &&
!(other.match_later_siblings && !self.match_later_siblings) &&
self.replacements.contains(other.replacements)
}
}
impl RestyleReplacements {
/// The replacements for the animation cascade levels.
#[inline] #[inline]
pub fn for_animations() -> Self { pub fn for_animations() -> Self {
RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS RESTYLE_SMIL | RESTYLE_CSS_ANIMATIONS | RESTYLE_CSS_TRANSITIONS
} }
} }
#[cfg(feature = "gecko")]
impl From<nsRestyleHint> for RestyleReplacements {
fn from(raw: nsRestyleHint) -> Self {
Self::from_bits_truncate(raw.0 as u8)
}
}
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
impl From<nsRestyleHint> for RestyleHint { impl From<nsRestyleHint> for RestyleHint {
fn from(raw: nsRestyleHint) -> Self { fn from(raw: nsRestyleHint) -> Self {
let raw_bits: u32 = raw.0; use gecko_bindings::structs::nsRestyleHint_eRestyle_LaterSiblings as eRestyle_LaterSiblings;
use gecko_bindings::structs::nsRestyleHint_eRestyle_Self as eRestyle_Self;
use gecko_bindings::structs::nsRestyleHint_eRestyle_SomeDescendants as eRestyle_SomeDescendants;
use gecko_bindings::structs::nsRestyleHint_eRestyle_Subtree as eRestyle_Subtree;
// FIXME(bholley): Finish aligning the binary representations here and RestyleHint {
// then .expect() the result of the checked version. match_self: (raw.0 & (eRestyle_Self.0 | eRestyle_Subtree.0)) != 0,
if Self::from_bits(raw_bits).is_none() { match_descendants: (raw.0 & (eRestyle_Subtree.0 | eRestyle_SomeDescendants.0)) != 0,
warn!("stylo: dropping unsupported restyle hint bits"); match_later_siblings: (raw.0 & eRestyle_LaterSiblings.0) != 0,
replacements: raw.into(),
} }
let mut bits = Self::from_bits_truncate(raw_bits);
// eRestyle_Subtree converts to (RESTYLE_SELF | RESTYLE_DESCENDANTS).
if bits.contains(RESTYLE_DESCENDANTS) {
bits |= RESTYLE_SELF;
}
bits
} }
} }
@ -437,15 +618,16 @@ fn is_attr_selector(sel: &Component<SelectorImpl>) -> bool {
fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint { fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint {
match combinator { match combinator {
None => RESTYLE_SELF, None => RestyleHint::for_self(),
Some(c) => match c { Some(c) => match c {
// NB: RESTYLE_SELF is needed to handle properly eager pseudos, // NB: RestyleHint::subtree() and not RestyleHint::descendants() is
// otherwise we may leave a stale style on the parent. // needed to handle properly eager pseudos, otherwise we may leave
Combinator::PseudoElement => RESTYLE_SELF | RESTYLE_DESCENDANTS, // a stale style on the parent.
Combinator::Child => RESTYLE_DESCENDANTS, Combinator::PseudoElement => RestyleHint::subtree(),
Combinator::Descendant => RESTYLE_DESCENDANTS, Combinator::Child => RestyleHint::descendants(),
Combinator::NextSibling => RESTYLE_LATER_SIBLINGS, Combinator::Descendant => RestyleHint::descendants(),
Combinator::LaterSibling => RESTYLE_LATER_SIBLINGS, Combinator::NextSibling => RestyleHint::later_siblings(),
Combinator::LaterSibling => RestyleHint::later_siblings(),
} }
} }
} }
@ -700,12 +882,12 @@ impl DependencySet {
return true; return true;
} }
if hint.contains(dep.hint) { if hint.contains(&dep.hint) {
trace!(" > hint was already there"); trace!(" > hint was already there");
return true; return true;
} }
// We can ignore the selector flags, since they would have already // We can ignore the selector replacements, since they would have already
// been set during original matching for any element that might // been set during original matching for any element that might
// change its matching behavior here. // change its matching behavior here.
let matched_then = let matched_then =
@ -717,10 +899,10 @@ impl DependencySet {
&mut matching_context, &mut matching_context,
&mut |_, _| {}); &mut |_, _| {});
if matched_then != matches_now { if matched_then != matches_now {
hint.insert(dep.hint); hint.insert_from(&dep.hint);
} }
!hint.is_all() !hint.is_maximum()
}); });
debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)", debug!("Calculated restyle hint: {:?} for {:?}. (State={:?}, {} Deps)",

View file

@ -9,7 +9,7 @@ use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
use data::{ElementData, ElementStyles, StoredRestyleHint}; use data::{ElementData, ElementStyles, StoredRestyleHint};
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode}; use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
use matching::{ChildCascadeRequirement, MatchMethods, StyleSharingBehavior}; use matching::{ChildCascadeRequirement, MatchMethods, StyleSharingBehavior};
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF}; use restyle_hints::RestyleHint;
use selector_parser::RestyleDamage; use selector_parser::RestyleDamage;
#[cfg(feature = "servo")] use servo_config::opts; #[cfg(feature = "servo")] use servo_config::opts;
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
@ -232,7 +232,7 @@ pub trait DomTraversal<E: TElement> : Sync {
if let Some(next) = root.next_sibling_element() { if let Some(next) = root.next_sibling_element() {
if let Some(mut next_data) = next.mutate_data() { if let Some(mut next_data) = next.mutate_data() {
let hint = StoredRestyleHint::subtree_and_later_siblings(); let hint = StoredRestyleHint::subtree_and_later_siblings();
next_data.ensure_restyle().hint.insert(&hint); next_data.ensure_restyle().hint.insert(hint);
} }
} }
} }
@ -819,11 +819,11 @@ fn preprocess_children<E, D>(traversal: &D,
// Propagate the parent and sibling restyle hint. // Propagate the parent and sibling restyle hint.
if !propagated_hint.is_empty() { if !propagated_hint.is_empty() {
restyle_data.hint.insert(&propagated_hint); restyle_data.hint.insert_from(&propagated_hint);
} }
if later_siblings { if later_siblings {
propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into()); propagated_hint.insert(RestyleHint::subtree().into());
} }
// Store the damage already handled by ancestors. // Store the damage already handled by ancestors.

View file

@ -2116,16 +2116,16 @@ pub extern "C" fn Servo_NoteExplicitHints(element: RawGeckoElementBorrowed,
element, restyle_hint, change_hint); element, restyle_hint, change_hint);
let restyle_hint: RestyleHint = restyle_hint.into(); let restyle_hint: RestyleHint = restyle_hint.into();
debug_assert!(RestyleHint::for_animations().contains(restyle_hint) || debug_assert!(!(restyle_hint.has_animation_hint() &&
!RestyleHint::for_animations().intersects(restyle_hint), restyle_hint.has_non_animation_hint()),
"Animation restyle hints should not appear with non-animation restyle hints"); "Animation restyle hints should not appear with non-animation restyle hints");
let mut maybe_data = element.mutate_data(); let mut maybe_data = element.mutate_data();
let maybe_restyle_data = maybe_data.as_mut().and_then(|d| unsafe { let maybe_restyle_data = maybe_data.as_mut().and_then(|d| unsafe {
maybe_restyle(d, element, restyle_hint.intersects(RestyleHint::for_animations())) maybe_restyle(d, element, restyle_hint.has_animation_hint())
}); });
if let Some(restyle_data) = maybe_restyle_data { if let Some(restyle_data) = maybe_restyle_data {
restyle_data.hint.insert(&restyle_hint.into()); restyle_data.hint.insert(restyle_hint.into());
restyle_data.damage |= damage; restyle_data.damage |= damage;
} else { } else {
debug!("(Element not styled, discarding hints)"); debug!("(Element not styled, discarding hints)");

View file

@ -6,7 +6,7 @@
fn smoke_restyle_hints() { fn smoke_restyle_hints() {
use cssparser::Parser; use cssparser::Parser;
use selectors::parser::SelectorList; use selectors::parser::SelectorList;
use style::restyle_hints::{DependencySet, RESTYLE_LATER_SIBLINGS}; use style::restyle_hints::DependencySet;
use style::selector_parser::SelectorParser; use style::selector_parser::SelectorParser;
use style::stylesheets::{Origin, Namespaces}; use style::stylesheets::{Origin, Namespaces};
let namespaces = Namespaces::default(); let namespaces = Namespaces::default();