Expand the role of modified_elements to include explicit restyle hints and damage.

This will take the place of setting IS_DIRTY and HAS_CHANGED.
This commit is contained in:
Bobby Holley 2016-11-14 13:29:41 -08:00
parent dda2928386
commit 0547a6b313
5 changed files with 93 additions and 27 deletions

View file

@ -1117,11 +1117,41 @@ impl LayoutThread {
} }
} }
let modified_elements = document.drain_modified_elements(); let restyles = document.drain_pending_restyles();
if !needs_dirtying { if !needs_dirtying {
for (el, snapshot) in modified_elements { for (el, restyle) in restyles {
let hint = rw_data.stylist.compute_restyle_hint(&el, &snapshot, el.get_state()); if el.get_data().is_none() {
el.note_restyle_hint::<RecalcStyleAndConstructFlows>(hint); // If we haven't styled this node yet, we can ignore the restyle.
continue;
}
// Start with the explicit hint, if any.
let mut hint = restyle.hint;
// Expand any snapshots.
if let Some(s) = restyle.snapshot {
hint |= rw_data.stylist.compute_restyle_hint(&el, &s, el.get_state());
}
// Apply the cumulative hint.
if !hint.is_empty() {
el.note_restyle_hint::<RecalcStyleAndConstructFlows>(hint);
}
// Apply explicit damage, if any.
if !restyle.damage.is_empty() {
let mut d = el.mutate_layout_data().unwrap();
d.base.restyle_damage |= restyle.damage;
}
// Propagate the descendant bit up the ancestors.
if !hint.is_empty() || !restyle.damage.is_empty() {
let curr = el;
while let Some(curr) = curr.parent_element() {
if curr.has_dirty_descendants() { break }
unsafe { curr.set_dirty_descendants(); }
}
}
} }
} }

View file

@ -41,6 +41,7 @@ use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::{Reflectable, Reflector}; use dom::bindings::reflector::{Reflectable, Reflector};
use dom::bindings::str::{DOMString, USVString}; use dom::bindings::str::{DOMString, USVString};
use dom::bindings::utils::WindowProxyHandler; use dom::bindings::utils::WindowProxyHandler;
use dom::document::PendingRestyle;
use encoding::types::EncodingRef; use encoding::types::EncodingRef;
use euclid::{Matrix2D, Matrix4D, Point2D}; use euclid::{Matrix2D, Matrix4D, Point2D};
use euclid::length::Length as EuclidLength; use euclid::length::Length as EuclidLength;
@ -348,6 +349,7 @@ no_jsmanaged_fields!(Mime);
no_jsmanaged_fields!(AttrIdentifier); no_jsmanaged_fields!(AttrIdentifier);
no_jsmanaged_fields!(AttrValue); no_jsmanaged_fields!(AttrValue);
no_jsmanaged_fields!(Snapshot); no_jsmanaged_fields!(Snapshot);
no_jsmanaged_fields!(PendingRestyle);
no_jsmanaged_fields!(HttpsState); no_jsmanaged_fields!(HttpsState);
no_jsmanaged_fields!(Request); no_jsmanaged_fields!(Request);
no_jsmanaged_fields!(RequestInit); no_jsmanaged_fields!(RequestInit);

View file

@ -125,7 +125,8 @@ use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use style::attr::AttrValue; use style::attr::AttrValue;
use style::context::ReflowGoal; use style::context::ReflowGoal;
use style::selector_impl::Snapshot; use style::restyle_hints::RestyleHint;
use style::selector_impl::{RestyleDamage, Snapshot};
use style::str::{split_html_space_chars, str_join}; use style::str::{split_html_space_chars, str_join};
use style::stylesheets::Stylesheet; use style::stylesheets::Stylesheet;
use time; use time;
@ -158,6 +159,29 @@ struct StylesheetInDocument {
stylesheet: Arc<Stylesheet>, stylesheet: Arc<Stylesheet>,
} }
#[derive(Debug, HeapSizeOf)]
pub struct PendingRestyle {
/// If this element had a state or attribute change since the last restyle, track
/// the original condition of the element.
pub snapshot: Option<Snapshot>,
/// Any explicit restyles hints that have been accumulated for this element.
pub hint: RestyleHint,
/// Any explicit restyles damage that have been accumulated for this element.
pub damage: RestyleDamage,
}
impl PendingRestyle {
pub fn new() -> Self {
PendingRestyle {
snapshot: None,
hint: RestyleHint::empty(),
damage: RestyleDamage::empty(),
}
}
}
// https://dom.spec.whatwg.org/#document // https://dom.spec.whatwg.org/#document
#[dom_struct] #[dom_struct]
pub struct Document { pub struct Document {
@ -234,9 +258,9 @@ pub struct Document {
/// This field is set to the document itself for inert documents. /// This field is set to the document itself for inert documents.
/// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document /// https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document
appropriate_template_contents_owner_document: MutNullableHeap<JS<Document>>, appropriate_template_contents_owner_document: MutNullableHeap<JS<Document>>,
/// For each element that has had a state or attribute change since the last restyle, /// Information on elements needing restyle to ship over to the layout thread when the
/// track the original condition of the element. /// time comes.
modified_elements: DOMRefCell<HashMap<JS<Element>, Snapshot>>, pending_restyles: DOMRefCell<HashMap<JS<Element>, PendingRestyle>>,
/// This flag will be true if layout suppressed a reflow attempt that was /// This flag will be true if layout suppressed a reflow attempt that was
/// needed in order for the page to be painted. /// needed in order for the page to be painted.
needs_paint: Cell<bool>, needs_paint: Cell<bool>,
@ -410,7 +434,7 @@ impl Document {
Some(root) => { Some(root) => {
root.upcast::<Node>().is_dirty() || root.upcast::<Node>().is_dirty() ||
root.upcast::<Node>().has_dirty_descendants() || root.upcast::<Node>().has_dirty_descendants() ||
!self.modified_elements.borrow().is_empty() || !self.pending_restyles.borrow().is_empty() ||
self.needs_paint() self.needs_paint()
} }
None => false, None => false,
@ -1708,7 +1732,7 @@ pub enum DocumentSource {
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub trait LayoutDocumentHelpers { pub trait LayoutDocumentHelpers {
unsafe fn is_html_document_for_layout(&self) -> bool; unsafe fn is_html_document_for_layout(&self) -> bool;
unsafe fn drain_modified_elements(&self) -> Vec<(LayoutJS<Element>, Snapshot)>; unsafe fn drain_pending_restyles(&self) -> Vec<(LayoutJS<Element>, PendingRestyle)>;
unsafe fn needs_paint_from_layout(&self); unsafe fn needs_paint_from_layout(&self);
unsafe fn will_paint(&self); unsafe fn will_paint(&self);
} }
@ -1722,8 +1746,8 @@ impl LayoutDocumentHelpers for LayoutJS<Document> {
#[inline] #[inline]
#[allow(unrooted_must_root)] #[allow(unrooted_must_root)]
unsafe fn drain_modified_elements(&self) -> Vec<(LayoutJS<Element>, Snapshot)> { unsafe fn drain_pending_restyles(&self) -> Vec<(LayoutJS<Element>, PendingRestyle)> {
let mut elements = (*self.unsafe_get()).modified_elements.borrow_mut_for_layout(); let mut elements = (*self.unsafe_get()).pending_restyles.borrow_mut_for_layout();
let result = elements.drain().map(|(k, v)| (k.to_layout(), v)).collect(); let result = elements.drain().map(|(k, v)| (k.to_layout(), v)).collect();
result result
} }
@ -1830,7 +1854,7 @@ impl Document {
reflow_timeout: Cell::new(None), reflow_timeout: Cell::new(None),
base_element: Default::default(), base_element: Default::default(),
appropriate_template_contents_owner_document: Default::default(), appropriate_template_contents_owner_document: Default::default(),
modified_elements: DOMRefCell::new(HashMap::new()), pending_restyles: DOMRefCell::new(HashMap::new()),
needs_paint: Cell::new(false), needs_paint: Cell::new(false),
active_touch_points: DOMRefCell::new(Vec::new()), active_touch_points: DOMRefCell::new(Vec::new()),
dom_loading: Cell::new(Default::default()), dom_loading: Cell::new(Default::default()),
@ -1966,23 +1990,28 @@ impl Document {
self.id_map.borrow().get(&id).map(|ref elements| Root::from_ref(&*(*elements)[0])) self.id_map.borrow().get(&id).map(|ref elements| Root::from_ref(&*(*elements)[0]))
} }
fn ensure_pending_restyle(&self, el: &Element) -> RefMut<PendingRestyle> {
let map = self.pending_restyles.borrow_mut();
RefMut::map(map, |m| m.entry(JS::from_ref(el)).or_insert_with(PendingRestyle::new))
}
fn ensure_snapshot(&self, el: &Element) -> RefMut<Snapshot> {
let mut entry = self.ensure_pending_restyle(el);
if entry.snapshot.is_none() {
entry.snapshot = Some(Snapshot::new(el.html_element_in_html_document()));
}
RefMut::map(entry, |e| e.snapshot.as_mut().unwrap())
}
pub fn element_state_will_change(&self, el: &Element) { pub fn element_state_will_change(&self, el: &Element) {
let mut map = self.modified_elements.borrow_mut(); let mut snapshot = self.ensure_snapshot(el);
let snapshot = map.entry(JS::from_ref(el))
.or_insert_with(|| {
Snapshot::new(el.html_element_in_html_document())
});
if snapshot.state.is_none() { if snapshot.state.is_none() {
snapshot.state = Some(el.state()); snapshot.state = Some(el.state());
} }
} }
pub fn element_attr_will_change(&self, el: &Element) { pub fn element_attr_will_change(&self, el: &Element) {
let mut map = self.modified_elements.borrow_mut(); let mut snapshot = self.ensure_snapshot(el);
let mut snapshot = map.entry(JS::from_ref(el))
.or_insert_with(|| {
Snapshot::new(el.html_element_in_html_document())
});
if snapshot.attrs.is_none() { if snapshot.attrs.is_none() {
let attrs = el.attrs() let attrs = el.attrs()
.iter() .iter()

View file

@ -34,7 +34,7 @@ use dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId}; use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::LayoutJS; use dom::bindings::js::LayoutJS;
use dom::characterdata::LayoutCharacterDataHelpers; use dom::characterdata::LayoutCharacterDataHelpers;
use dom::document::{Document, LayoutDocumentHelpers}; use dom::document::{Document, LayoutDocumentHelpers, PendingRestyle};
use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers}; use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
use dom::node::{CAN_BE_FRAGMENTED, DIRTY_ON_VIEWPORT_SIZE_CHANGE, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; use dom::node::{CAN_BE_FRAGMENTED, DIRTY_ON_VIEWPORT_SIZE_CHANGE, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY};
use dom::node::{LayoutNodeHelpers, Node}; use dom::node::{LayoutNodeHelpers, Node};
@ -65,7 +65,7 @@ use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, PresentationalHintsSynthe
use style::dom::{TRestyleDamage, UnsafeNode}; use style::dom::{TRestyleDamage, UnsafeNode};
use style::element_state::*; use style::element_state::*;
use style::properties::{ComputedValues, PropertyDeclarationBlock}; use style::properties::{ComputedValues, PropertyDeclarationBlock};
use style::selector_impl::{NonTSPseudoClass, PseudoElement, RestyleDamage, ServoSelectorImpl, Snapshot}; use style::selector_impl::{NonTSPseudoClass, PseudoElement, RestyleDamage, ServoSelectorImpl};
use style::selector_matching::ApplicableDeclarationBlock; use style::selector_matching::ApplicableDeclarationBlock;
use style::sink::Push; use style::sink::Push;
use style::str::is_whitespace; use style::str::is_whitespace;
@ -377,8 +377,8 @@ impl<'ld> ServoLayoutDocument<'ld> {
self.as_node().children().find(ServoLayoutNode::is_element) self.as_node().children().find(ServoLayoutNode::is_element)
} }
pub fn drain_modified_elements(&self) -> Vec<(ServoLayoutElement<'ld>, Snapshot)> { pub fn drain_pending_restyles(&self) -> Vec<(ServoLayoutElement<'ld>, PendingRestyle)> {
let elements = unsafe { self.document.drain_modified_elements() }; let elements = unsafe { self.document.drain_pending_restyles() };
elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect() elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()
} }

View file

@ -4,6 +4,7 @@
use computed_values::display; use computed_values::display;
use dom::TRestyleDamage; use dom::TRestyleDamage;
use heapsize::HeapSizeOf;
use properties::ServoComputedValues; use properties::ServoComputedValues;
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
@ -48,6 +49,10 @@ bitflags! {
} }
} }
impl HeapSizeOf for ServoRestyleDamage {
fn heap_size_of_children(&self) -> usize { 0 }
}
impl TRestyleDamage for ServoRestyleDamage { impl TRestyleDamage for ServoRestyleDamage {
/// For Servo the style source is always the computed values. /// For Servo the style source is always the computed values.
type PreExistingComputedValues = Arc<ServoComputedValues>; type PreExistingComputedValues = Arc<ServoComputedValues>;