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

@ -32,7 +32,7 @@ use dom::customevent::CustomEvent;
use dom::documentfragment::DocumentFragment;
use dom::documenttype::DocumentType;
use dom::domimplementation::DOMImplementation;
use dom::element::{Element, ElementCreator};
use dom::element::{Element, ElementCreator, EventState};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::{EventTarget};
use dom::htmlanchorelement::HTMLAnchorElement;
@ -174,6 +174,8 @@ pub struct Document {
/// This field is set to the document itself for inert documents.
/// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document
appropriate_template_contents_owner_document: MutNullableHeap<JS<Document>>,
// The collection of EventStates that have been changed since the last restyle.
event_state_changes: DOMRefCell<HashMap<JS<Element>, EventState>>,
}
impl PartialEq for Document {
@ -301,6 +303,11 @@ impl Document {
}
}
pub fn needs_reflow(&self) -> bool {
self.GetDocumentElement().is_some() &&
(self.upcast::<Node>().get_has_dirty_descendants() || !self.event_state_changes.borrow().is_empty())
}
/// Returns the first `base` element in the DOM that has an `href` attribute.
pub fn base_element(&self) -> Option<Root<HTMLBaseElement>> {
self.base_element.get_rooted()
@ -1178,6 +1185,7 @@ pub enum DocumentSource {
#[allow(unsafe_code)]
pub trait LayoutDocumentHelpers {
unsafe fn is_html_document_for_layout(&self) -> bool;
unsafe fn drain_event_state_changes(&self) -> Vec<(LayoutJS<Element>, EventState)>;
}
#[allow(unsafe_code)]
@ -1186,6 +1194,15 @@ impl LayoutDocumentHelpers for LayoutJS<Document> {
unsafe fn is_html_document_for_layout(&self) -> bool {
(*self.unsafe_get()).is_html_document
}
#[inline]
#[allow(unrooted_must_root)]
unsafe fn drain_event_state_changes(&self) -> Vec<(LayoutJS<Element>, EventState)> {
let mut changes = (*self.unsafe_get()).event_state_changes.borrow_mut_for_layout();
let drain = changes.drain();
let layout_drain = drain.map(|(k, v)| (k.to_layout(), v));
Vec::from_iter(layout_drain)
}
}
impl Document {
@ -1251,6 +1268,7 @@ impl Document {
reflow_timeout: Cell::new(None),
base_element: Default::default(),
appropriate_template_contents_owner_document: Default::default(),
event_state_changes: DOMRefCell::new(HashMap::new()),
}
}
@ -1315,6 +1333,20 @@ impl Document {
pub fn get_element_by_id(&self, id: &Atom) -> Option<Root<Element>> {
self.idmap.borrow().get(&id).map(|ref elements| (*elements)[0].root())
}
pub fn record_event_state_change(&self, el: &Element, which: EventState) {
let mut map = self.event_state_changes.borrow_mut();
let empty;
{
let states = map.entry(JS::from_ref(el))
.or_insert(EventState::empty());
states.toggle(which);
empty = states.is_empty();
}
if empty {
map.remove(&JS::from_ref(el));
}
}
}