mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #14214 - bholley:dirtiness_overhaul, r=emilio
Overhaul dirtiness handling in Servo to prepare for the new incremental restyle architecture <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14214) <!-- Reviewable:end -->
This commit is contained in:
commit
e4a27c4d16
12 changed files with 192 additions and 183 deletions
|
@ -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;
|
||||
|
@ -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);
|
||||
|
|
|
@ -122,7 +122,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;
|
||||
|
@ -155,6 +156,29 @@ pub struct StylesheetInDocument {
|
|||
pub 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 {
|
||||
|
@ -232,9 +256,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>,
|
||||
|
@ -408,7 +432,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,
|
||||
|
@ -451,7 +475,7 @@ impl Document {
|
|||
}
|
||||
|
||||
pub fn content_and_heritage_changed(&self, node: &Node, damage: NodeDamage) {
|
||||
node.force_dirty_ancestors(damage);
|
||||
node.dirty(damage);
|
||||
}
|
||||
|
||||
/// Reflows and disarms the timer if the reflow timer has expired.
|
||||
|
@ -1706,7 +1730,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);
|
||||
}
|
||||
|
@ -1720,8 +1744,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
|
||||
}
|
||||
|
@ -1829,7 +1853,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()),
|
||||
|
@ -1967,23 +1991,28 @@ impl Document {
|
|||
self.id_map.borrow().get(&id).map(|ref elements| Root::from_ref(&*(*elements)[0]))
|
||||
}
|
||||
|
||||
pub 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))
|
||||
}
|
||||
|
||||
pub 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()
|
||||
|
|
|
@ -84,13 +84,15 @@ use std::fmt;
|
|||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use style::attr::{AttrValue, LengthOrPercentageOrAuto};
|
||||
use style::dom::TRestyleDamage;
|
||||
use style::element_state::*;
|
||||
use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
|
||||
use style::parser::ParserContextExtraData;
|
||||
use style::properties::{DeclaredValue, Importance};
|
||||
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
|
||||
use style::properties::longhands::{background_image, border_spacing, font_family, font_size, overflow_x};
|
||||
use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl};
|
||||
use style::restyle_hints::RESTYLE_SELF;
|
||||
use style::selector_impl::{NonTSPseudoClass, RestyleDamage, ServoSelectorImpl};
|
||||
use style::selector_matching::ApplicableDeclarationBlock;
|
||||
use style::sink::Push;
|
||||
use style::values::CSSFloat;
|
||||
|
@ -201,6 +203,19 @@ impl Element {
|
|||
ElementBinding::Wrap)
|
||||
}
|
||||
|
||||
pub fn restyle(&self, damage: NodeDamage) {
|
||||
let doc = self.node.owner_doc();
|
||||
let mut restyle = doc.ensure_pending_restyle(self);
|
||||
|
||||
// FIXME(bholley): I think we should probably only do this for
|
||||
// NodeStyleDamaged, but I'm preserving existing behavior.
|
||||
restyle.hint |= RESTYLE_SELF;
|
||||
|
||||
if damage == NodeDamage::OtherNodeDamage {
|
||||
restyle.damage = RestyleDamage::rebuild_and_reflow();
|
||||
}
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom-view/#css-layout-box
|
||||
// Elements that have a computed value of the display property
|
||||
// that is table-column or table-column-group
|
||||
|
|
|
@ -149,8 +149,6 @@ bitflags! {
|
|||
pub flags NodeFlags: u8 {
|
||||
#[doc = "Specifies whether this node is in a document."]
|
||||
const IS_IN_DOC = 0x01,
|
||||
#[doc = "Specifies whether this node _must_ be reflowed regardless of style differences."]
|
||||
const HAS_CHANGED = 0x02,
|
||||
#[doc = "Specifies whether this node needs style recalc on next reflow."]
|
||||
const IS_DIRTY = 0x04,
|
||||
#[doc = "Specifies whether this node has descendants (inclusive of itself) which \
|
||||
|
@ -175,7 +173,7 @@ bitflags! {
|
|||
|
||||
impl NodeFlags {
|
||||
pub fn new() -> NodeFlags {
|
||||
HAS_CHANGED | IS_DIRTY | HAS_DIRTY_DESCENDANTS
|
||||
IS_DIRTY
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,6 +249,8 @@ impl Node {
|
|||
let parent_in_doc = self.is_in_doc();
|
||||
for node in new_child.traverse_preorder() {
|
||||
node.set_flag(IS_IN_DOC, parent_in_doc);
|
||||
// Out-of-document elements never have the descendants flag set.
|
||||
debug_assert!(!node.get_flag(HAS_DIRTY_DESCENDANTS));
|
||||
vtable_for(&&*node).bind_to_tree(parent_in_doc);
|
||||
}
|
||||
let document = new_child.owner_doc();
|
||||
|
@ -289,7 +289,8 @@ impl Node {
|
|||
self.children_count.set(self.children_count.get() - 1);
|
||||
|
||||
for node in child.traverse_preorder() {
|
||||
node.set_flag(IS_IN_DOC, false);
|
||||
// Out-of-document elements never have the descendants flag set.
|
||||
node.set_flag(IS_IN_DOC | HAS_DIRTY_DESCENDANTS, false);
|
||||
vtable_for(&&*node).unbind_from_tree(&context);
|
||||
node.style_and_layout_data.get().map(|d| node.dispose(d));
|
||||
}
|
||||
|
@ -428,14 +429,6 @@ impl Node {
|
|||
self.flags.set(flags);
|
||||
}
|
||||
|
||||
pub fn has_changed(&self) -> bool {
|
||||
self.get_flag(HAS_CHANGED)
|
||||
}
|
||||
|
||||
pub fn set_has_changed(&self, state: bool) {
|
||||
self.set_flag(HAS_CHANGED, state)
|
||||
}
|
||||
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
self.get_flag(IS_DIRTY)
|
||||
}
|
||||
|
@ -452,10 +445,6 @@ impl Node {
|
|||
self.set_flag(HAS_DIRTY_DESCENDANTS, state)
|
||||
}
|
||||
|
||||
pub fn force_dirty_ancestors(&self, damage: NodeDamage) {
|
||||
self.dirty_impl(damage, true)
|
||||
}
|
||||
|
||||
pub fn rev_version(&self) {
|
||||
// The new version counter is 1 plus the max of the node's current version counter,
|
||||
// its descendants version, and the document's version. Normally, this will just be
|
||||
|
@ -470,30 +459,18 @@ impl Node {
|
|||
}
|
||||
|
||||
pub fn dirty(&self, damage: NodeDamage) {
|
||||
self.dirty_impl(damage, false)
|
||||
}
|
||||
|
||||
pub fn dirty_impl(&self, damage: NodeDamage, force_ancestors: bool) {
|
||||
// 0. Set version counter
|
||||
self.rev_version();
|
||||
|
||||
// 1. Dirty self.
|
||||
match damage {
|
||||
NodeDamage::NodeStyleDamaged => {}
|
||||
NodeDamage::OtherNodeDamage => self.set_has_changed(true),
|
||||
if !self.is_in_doc() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.is_dirty() && !force_ancestors {
|
||||
return
|
||||
}
|
||||
|
||||
self.set_flag(IS_DIRTY, true);
|
||||
|
||||
// 4. Dirty ancestors.
|
||||
for ancestor in self.ancestors() {
|
||||
if !force_ancestors && ancestor.has_dirty_descendants() { break }
|
||||
ancestor.set_has_dirty_descendants(true);
|
||||
}
|
||||
match self.type_id() {
|
||||
NodeTypeId::CharacterData(CharacterDataTypeId::Text) =>
|
||||
self.parent_node.get().unwrap().downcast::<Element>().unwrap().restyle(damage),
|
||||
NodeTypeId::Element(_) =>
|
||||
self.downcast::<Element>().unwrap().restyle(damage),
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
||||
/// The maximum version number of this node's descendants, including itself
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue