Check modified event state from layout and dirty it there.

This adds some overhead, but also provides the small performance benefit of
avoiding dirtying in the case where an event state is toggled an even
number of times between reflows.

The main benefit here though is that it sets us up to be smarter about
what we mark as dirty using restyle hints.
This commit is contained in:
Bobby Holley 2015-10-09 21:38:24 -07:00
parent 441c84d75d
commit 069c40f788
6 changed files with 102 additions and 13 deletions

View file

@ -45,7 +45,7 @@ use script::dom::bindings::js::LayoutJS;
use script::dom::characterdata::LayoutCharacterDataHelpers;
use script::dom::document::{Document, LayoutDocumentHelpers};
use script::dom::element;
use script::dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
use script::dom::element::{Element, EventState, LayoutElementHelpers, RawLayoutElementHelpers};
use script::dom::htmlcanvaselement::{LayoutHTMLCanvasElementHelpers, HTMLCanvasData};
use script::dom::htmliframeelement::HTMLIFrameElement;
use script::dom::htmlimageelement::LayoutHTMLImageElementHelpers;
@ -59,6 +59,7 @@ use selectors::parser::{AttrSelector, NamespaceConstraint};
use smallvec::VecLike;
use std::borrow::ToOwned;
use std::cell::{Ref, RefMut};
use std::iter::FromIterator;
use std::marker::PhantomData;
use std::mem;
use std::sync::Arc;
@ -365,6 +366,17 @@ impl<'le> LayoutDocument<'le> {
}
node
}
pub fn drain_event_state_changes(&self) -> Vec<(LayoutElement, EventState)> {
unsafe {
let changes = self.document.drain_event_state_changes();
Vec::from_iter(changes.iter().map(|&(el, state)|
(LayoutElement {
element: el,
chain: PhantomData,
}, state)))
}
}
}
/// A wrapper around elements that ensures layout can only ever access safe properties.
@ -387,6 +399,41 @@ impl<'le> LayoutElement<'le> {
chain: PhantomData,
}
}
/// Properly marks nodes as dirty in response to event state changes.
///
/// Currently this implementation is very conservative, and basically mirrors node::dirty_impl.
/// With restyle hints, we can do less work here.
pub fn note_event_state_change(&self) {
let node = self.as_node();
// Bail out if we're already dirty. This won't be valid when we start doing more targeted
// dirtying with restyle hints.
if node.is_dirty() { return }
// Dirty descendants.
fn dirty_subtree(node: LayoutNode) {
// Stop if this subtree is already dirty. This won't be valid with restyle hints, see above.
if node.is_dirty() { return }
unsafe {
node.set_dirty(true);
node.set_dirty_descendants(true);
}
for kid in node.children() {
dirty_subtree(kid);
}
}
dirty_subtree(node);
let mut curr = node;
while let Some(parent) = curr.parent_node() {
if parent.has_dirty_descendants() { break }
unsafe { parent.set_dirty_descendants(true); }
curr = parent;
}
}
}
fn as_element<'le>(node: LayoutJS<Node>) -> Option<LayoutElement<'le>> {