Skip to content

Commit

Permalink
Centralize note_dirty_descendants implementation, and fully propagate…
Browse files Browse the repository at this point in the history
… dirty_descendants in resolve_style.

The current code can leave the tree in an inconsistent state, with the dirty
descendants bit not fully propagated.

MozReview-Commit-ID: ALI6etmlrDa
  • Loading branch information
bholley committed Mar 28, 2017
1 parent 185d31f commit 7c58483
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 27 deletions.
20 changes: 6 additions & 14 deletions components/script/layout_wrapper.rs
Expand Up @@ -63,7 +63,8 @@ use style::attr::AttrValue;
use style::computed_values::display;
use style::context::{QuirksMode, SharedStyleContext};
use style::data::ElementData;
use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, PresentationalHintsSynthetizer, TElement, TNode};
use style::dom::{DirtyDescendants, LayoutIterator, NodeInfo, OpaqueNode, PresentationalHintsSynthetizer};
use style::dom::{TElement, TNode};
use style::dom::UnsafeNode;
use style::element_state::*;
use style::properties::{ComputedValues, PropertyDeclarationBlock};
Expand Down Expand Up @@ -487,6 +488,9 @@ impl<'le> ServoLayoutElement<'le> {
}
}

// FIXME(bholley): This should be merged with TElement::note_dirty_descendants,
// but that requires re-testing and possibly fixing the broken callers given
// the FIXME below, which I don't have time to do right now.
pub unsafe fn note_dirty_descendant(&self) {
use ::selectors::Element;

Expand All @@ -501,19 +505,7 @@ impl<'le> ServoLayoutElement<'le> {
current = el.parent_element();
}

debug_assert!(self.dirty_descendants_bit_is_propagated());
}

fn dirty_descendants_bit_is_propagated(&self) -> bool {
use ::selectors::Element;

let mut current = Some(*self);
while let Some(el) = current {
if !el.has_dirty_descendants() { return false; }
current = el.parent_element();
}

true
debug_assert!(self.descendants_bit_is_propagated::<DirtyDescendants>());
}
}

Expand Down
53 changes: 53 additions & 0 deletions components/style/dom.rs
Expand Up @@ -22,6 +22,7 @@ use std::fmt::Debug;
use std::ops::Deref;
use std::sync::Arc;
use stylist::ApplicableDeclarationBlock;
use thread_state;

pub use style_traits::UnsafeNode;

Expand Down Expand Up @@ -305,6 +306,36 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
/// Only safe to call with exclusive access to the element.
unsafe fn set_dirty_descendants(&self);

/// Flag that this element has a descendant for style processing, propagating
/// the bit up to the root as needed.
///
/// This is _not_ safe to call during the parallel traversal.
unsafe fn note_descendants<B: DescendantsBit<Self>>(&self) {
debug_assert!(!thread_state::get().is_worker());
let mut curr = Some(*self);
while curr.is_some() && !B::has(curr.unwrap()) {
B::set(curr.unwrap());
curr = curr.unwrap().parent_element();
}

// Note: We disable this assertion on servo because of bugs. See the
// comment around note_dirty_descendant in layout/wrapper.rs.
if cfg!(feature = "gecko") {
debug_assert!(self.descendants_bit_is_propagated::<B>());
}
}

/// Debug helper to be sure the bit is propagated.
fn descendants_bit_is_propagated<B: DescendantsBit<Self>>(&self) -> bool {
let mut current = Some(*self);
while let Some(el) = current {
if !B::has(el) { return false; }
current = el.parent_element();
}

true
}

/// Flag that this element has no descendant for style processing.
///
/// Only safe to call with exclusive access to the element.
Expand Down Expand Up @@ -391,6 +422,28 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
}
}

/// Trait abstracting over different kinds of dirty-descendants bits.
pub trait DescendantsBit<E: TElement> {
/// Returns true if the Element has the bit.
fn has(el: E) -> bool;
/// Sets the bit on the Element.
unsafe fn set(el: E);
}

/// Implementation of DescendantsBit for the regular dirty descendants bit.
pub struct DirtyDescendants;
impl<E: TElement> DescendantsBit<E> for DirtyDescendants {
fn has(el: E) -> bool { el.has_dirty_descendants() }
unsafe fn set(el: E) { el.set_dirty_descendants(); }
}

/// Implementation of DescendantsBit for the animation-only dirty descendants bit.
pub struct AnimationOnlyDirtyDescendants;
impl<E: TElement> DescendantsBit<E> for AnimationOnlyDirtyDescendants {
fn has(el: E) -> bool { el.has_animation_only_dirty_descendants() }
unsafe fn set(el: E) { el.set_animation_only_dirty_descendants(); }
}

/// TNode and TElement aren't Send because we want to be careful and explicit
/// about our parallel traversal. However, there are certain situations
/// (including but not limited to the traversal) where we need to send DOM
Expand Down
4 changes: 2 additions & 2 deletions components/style/traversal.rs
Expand Up @@ -9,7 +9,7 @@
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
use data::{ElementData, ElementStyles, StoredRestyleHint};
use dom::{NodeInfo, TElement, TNode};
use dom::{DirtyDescendants, NodeInfo, TElement, TNode};
use matching::{MatchMethods, MatchResults};
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
use selector_parser::RestyleDamage;
Expand Down Expand Up @@ -388,7 +388,7 @@ fn resolve_style_internal<E, F>(context: &mut StyleContext<E>,
// Conservatively mark us as having dirty descendants, since there might
// be other unstyled siblings we miss when walking straight up the parent
// chain.
unsafe { element.set_dirty_descendants() };
unsafe { element.note_descendants::<DirtyDescendants>() };
}

// If we're display:none and none of our ancestors are, we're the root
Expand Down
18 changes: 7 additions & 11 deletions ports/geckolib/glue.rs
Expand Up @@ -18,6 +18,7 @@ use style::arc_ptr_eq;
use style::context::{QuirksMode, SharedStyleContext, StyleContext};
use style::context::{ThreadLocalStyleContext, ThreadLocalStyleContextCreationInfo};
use style::data::{ElementData, ElementStyles, RestyleData};
use style::dom::{AnimationOnlyDirtyDescendants, DirtyDescendants};
use style::dom::{ShowSubtreeData, TElement, TNode};
use style::error_reporting::StdoutErrorReporter;
use style::gecko::data::{PerDocumentStyleData, PerDocumentStyleDataImpl};
Expand Down Expand Up @@ -1384,17 +1385,12 @@ unsafe fn maybe_restyle<'a>(data: &'a mut AtomicRefMut<ElementData>,
}

// Propagate the bit up the chain.
let mut curr = element;
while let Some(parent) = curr.parent_element() {
curr = parent;
if animation_only {
if curr.has_animation_only_dirty_descendants() { break; }
curr.set_animation_only_dirty_descendants();
} else {
if curr.has_dirty_descendants() { break; }
curr.set_dirty_descendants();
}
}
if animation_only {
element.parent_element().map(|p| p.note_descendants::<AnimationOnlyDirtyDescendants>());
} else {
element.parent_element().map(|p| p.note_descendants::<DirtyDescendants>());
};

bindings::Gecko_SetOwnerDocumentNeedsStyleFlush(element.0);

// Ensure and return the RestyleData.
Expand Down

0 comments on commit 7c58483

Please sign in to comment.