aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/cell.rs7
-rw-r--r--components/script/dom/document.rs34
-rw-r--r--components/script/dom/element.rs5
-rw-r--r--components/script/dom/window.rs12
4 files changed, 46 insertions, 12 deletions
diff --git a/components/script/dom/bindings/cell.rs b/components/script/dom/bindings/cell.rs
index 9ec04e358e5..dabc88284b6 100644
--- a/components/script/dom/bindings/cell.rs
+++ b/components/script/dom/bindings/cell.rs
@@ -94,6 +94,13 @@ impl<T> DOMRefCell<T> {
_ => None,
}
}
+
+ /// Version of the above that we use during restyle while the script task
+ /// is blocked.
+ pub fn borrow_mut_for_layout(&self) -> RefMut<T> {
+ debug_assert!(task_state::get().is_layout());
+ self.value.borrow_mut()
+ }
}
impl<T: JSTraceable> JSTraceable for DOMRefCell<T> {
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index b8819fc4c86..f9028e3616e 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -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));
+ }
+ }
}
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 1be58932305..0bdca0714a7 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -1836,6 +1836,9 @@ impl Element {
fn set_state(&self, which: EventState, value: bool) {
let mut state = self.event_state.get();
+ if state.contains(which) == value {
+ return
+ }
match value {
true => state.insert(which),
false => state.remove(which),
@@ -1843,7 +1846,7 @@ impl Element {
self.event_state.set(state);
let node = self.upcast::<Node>();
- node.dirty(NodeDamage::NodeStyleDamaged);
+ node.owner_doc().record_event_state_change(self, which);
}
pub fn get_active_state(&self) -> bool {
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 761ede6edf8..abe693696fd 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -957,16 +957,8 @@ impl Window {
///
/// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout.
pub fn reflow(&self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) {
- let document = self.Document();
- let root = document.r().GetDocumentElement();
- let root = match root.r() {
- Some(root) => root,
- None => return,
- };
-
- let root = root.upcast::<Node>();
- if query_type == ReflowQueryType::NoQuery && !root.get_has_dirty_descendants() {
- debug!("root has no dirty descendants; avoiding reflow (reason {:?})", reason);
+ if query_type == ReflowQueryType::NoQuery && !self.Document().needs_reflow() {
+ debug!("Document doesn't need reflow - skipping it (reason {:?})", reason);
return
}