Skip to content

Commit

Permalink
Expand the role of modified_elements to include explicit restyle hint…
Browse files Browse the repository at this point in the history
…s and damage.

This will take the place of setting IS_DIRTY and HAS_CHANGED.
  • Loading branch information
bholley committed Nov 15, 2016
1 parent dda2928 commit 0547a6b
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 27 deletions.
38 changes: 34 additions & 4 deletions components/layout_thread/lib.rs
Expand Up @@ -1117,11 +1117,41 @@ impl LayoutThread {
}
}

let modified_elements = document.drain_modified_elements();
let restyles = document.drain_pending_restyles();
if !needs_dirtying {
for (el, snapshot) in modified_elements {
let hint = rw_data.stylist.compute_restyle_hint(&el, &snapshot, el.get_state());
el.note_restyle_hint::<RecalcStyleAndConstructFlows>(hint);
for (el, restyle) in restyles {
if el.get_data().is_none() {
// 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(); }
}
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions components/script/dom/bindings/trace.rs
Expand Up @@ -41,6 +41,7 @@ use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::bindings::str::{DOMString, USVString};
use dom::bindings::utils::WindowProxyHandler;
use dom::document::PendingRestyle;
use encoding::types::EncodingRef;
use euclid::{Matrix2D, Matrix4D, Point2D};
use euclid::length::Length as EuclidLength;
Expand Down Expand Up @@ -348,6 +349,7 @@ no_jsmanaged_fields!(Mime);
no_jsmanaged_fields!(AttrIdentifier);
no_jsmanaged_fields!(AttrValue);
no_jsmanaged_fields!(Snapshot);
no_jsmanaged_fields!(PendingRestyle);
no_jsmanaged_fields!(HttpsState);
no_jsmanaged_fields!(Request);
no_jsmanaged_fields!(RequestInit);
Expand Down
67 changes: 48 additions & 19 deletions components/script/dom/document.rs
Expand Up @@ -125,7 +125,8 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use style::attr::AttrValue;
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::stylesheets::Stylesheet;
use time;
Expand Down Expand Up @@ -158,6 +159,29 @@ struct StylesheetInDocument {
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
#[dom_struct]
pub struct Document {
Expand Down Expand Up @@ -234,9 +258,9 @@ 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>>,
/// For each element that has had a state or attribute change since the last restyle,
/// track the original condition of the element.
modified_elements: DOMRefCell<HashMap<JS<Element>, Snapshot>>,
/// Information on elements needing restyle to ship over to the layout thread when the
/// time comes.
pending_restyles: DOMRefCell<HashMap<JS<Element>, PendingRestyle>>,
/// This flag will be true if layout suppressed a reflow attempt that was
/// needed in order for the page to be painted.
needs_paint: Cell<bool>,
Expand Down Expand Up @@ -410,7 +434,7 @@ impl Document {
Some(root) => {
root.upcast::<Node>().is_dirty() ||
root.upcast::<Node>().has_dirty_descendants() ||
!self.modified_elements.borrow().is_empty() ||
!self.pending_restyles.borrow().is_empty() ||
self.needs_paint()
}
None => false,
Expand Down Expand Up @@ -1708,7 +1732,7 @@ pub enum DocumentSource {
#[allow(unsafe_code)]
pub trait LayoutDocumentHelpers {
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 will_paint(&self);
}
Expand All @@ -1722,8 +1746,8 @@ impl LayoutDocumentHelpers for LayoutJS<Document> {

#[inline]
#[allow(unrooted_must_root)]
unsafe fn drain_modified_elements(&self) -> Vec<(LayoutJS<Element>, Snapshot)> {
let mut elements = (*self.unsafe_get()).modified_elements.borrow_mut_for_layout();
unsafe fn drain_pending_restyles(&self) -> Vec<(LayoutJS<Element>, PendingRestyle)> {
let mut elements = (*self.unsafe_get()).pending_restyles.borrow_mut_for_layout();
let result = elements.drain().map(|(k, v)| (k.to_layout(), v)).collect();
result
}
Expand Down Expand Up @@ -1830,7 +1854,7 @@ impl Document {
reflow_timeout: Cell::new(None),
base_element: 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),
active_touch_points: DOMRefCell::new(Vec::new()),
dom_loading: Cell::new(Default::default()),
Expand Down Expand Up @@ -1966,23 +1990,28 @@ impl Document {
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) {
let mut map = self.modified_elements.borrow_mut();
let snapshot = map.entry(JS::from_ref(el))
.or_insert_with(|| {
Snapshot::new(el.html_element_in_html_document())
});
let mut snapshot = self.ensure_snapshot(el);
if snapshot.state.is_none() {
snapshot.state = Some(el.state());
}
}

pub fn element_attr_will_change(&self, el: &Element) {
let mut map = self.modified_elements.borrow_mut();
let mut snapshot = map.entry(JS::from_ref(el))
.or_insert_with(|| {
Snapshot::new(el.html_element_in_html_document())
});
let mut snapshot = self.ensure_snapshot(el);
if snapshot.attrs.is_none() {
let attrs = el.attrs()
.iter()
Expand Down
8 changes: 4 additions & 4 deletions components/script/layout_wrapper.rs
Expand Up @@ -34,7 +34,7 @@ use dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
use dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::LayoutJS;
use dom::characterdata::LayoutCharacterDataHelpers;
use dom::document::{Document, LayoutDocumentHelpers};
use dom::document::{Document, LayoutDocumentHelpers, PendingRestyle};
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::{LayoutNodeHelpers, Node};
Expand Down Expand Up @@ -65,7 +65,7 @@ use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, PresentationalHintsSynthe
use style::dom::{TRestyleDamage, UnsafeNode};
use style::element_state::*;
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::sink::Push;
use style::str::is_whitespace;
Expand Down Expand Up @@ -377,8 +377,8 @@ impl<'ld> ServoLayoutDocument<'ld> {
self.as_node().children().find(ServoLayoutNode::is_element)
}

pub fn drain_modified_elements(&self) -> Vec<(ServoLayoutElement<'ld>, Snapshot)> {
let elements = unsafe { self.document.drain_modified_elements() };
pub fn drain_pending_restyles(&self) -> Vec<(ServoLayoutElement<'ld>, PendingRestyle)> {
let elements = unsafe { self.document.drain_pending_restyles() };
elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()
}

Expand Down
5 changes: 5 additions & 0 deletions components/style/servo/restyle_damage.rs
Expand Up @@ -4,6 +4,7 @@

use computed_values::display;
use dom::TRestyleDamage;
use heapsize::HeapSizeOf;
use properties::ServoComputedValues;
use std::fmt;
use std::sync::Arc;
Expand Down Expand Up @@ -48,6 +49,10 @@ bitflags! {
}
}

impl HeapSizeOf for ServoRestyleDamage {
fn heap_size_of_children(&self) -> usize { 0 }
}

impl TRestyleDamage for ServoRestyleDamage {
/// For Servo the style source is always the computed values.
type PreExistingComputedValues = Arc<ServoComputedValues>;
Expand Down

0 comments on commit 0547a6b

Please sign in to comment.