diff options
-rw-r--r-- | components/layout/traversal.rs | 19 | ||||
-rw-r--r-- | components/layout_thread/lib.rs | 46 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 2 | ||||
-rw-r--r-- | components/script/dom/document.rs | 69 | ||||
-rw-r--r-- | components/script/dom/element.rs | 17 | ||||
-rw-r--r-- | components/script/dom/node.rs | 51 | ||||
-rw-r--r-- | components/script/layout_wrapper.rs | 87 | ||||
-rw-r--r-- | components/script_layout_interface/lib.rs | 6 | ||||
-rw-r--r-- | components/script_layout_interface/wrapper_traits.rs | 8 | ||||
-rw-r--r-- | components/style/dom.rs | 24 | ||||
-rw-r--r-- | components/style/gecko/wrapper.rs | 41 | ||||
-rw-r--r-- | components/style/servo/restyle_damage.rs | 5 |
12 files changed, 192 insertions, 183 deletions
diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index b054568d5d5..849e7b2087a 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -10,7 +10,7 @@ use display_list_builder::DisplayListBuildState; use flow::{self, PreorderFlowTraversal}; use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal}; use gfx::display_list::OpaqueNode; -use script_layout_interface::wrapper_traits::{LayoutElement, LayoutNode, ThreadSafeLayoutNode}; +use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; use std::mem; use style::atomic_refcell::AtomicRefCell; use style::context::{LocalStyleContext, SharedStyleContext, StyleContext}; @@ -32,7 +32,7 @@ pub struct RecalcStyleAndConstructFlows<'lc> { #[allow(unsafe_code)] impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc> where N: LayoutNode + TNode, - N::ConcreteElement: LayoutElement + N::ConcreteElement: TElement { type SharedContext = SharedLayoutContext; @@ -118,16 +118,13 @@ impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc> return false; } - // If this node has been marked as damaged in some way, we need to - // traverse it for layout. - if child.has_changed() { - return true; - } - match child.as_element() { - Some(el) => el.styling_mode() != StylingMode::Stop, - // Aside from the has_changed case above, we want to traverse non-element children - // in two additional cases: + // Elements should be traversed if they need styling or flow construction. + Some(el) => el.styling_mode() != StylingMode::Stop || + el.as_node().to_threadsafe().restyle_damage() != RestyleDamage::empty(), + + // Text nodes never need styling. However, there are two cases they may need + // flow construction: // (1) They child doesn't yet have layout data (preorder traversal initializes it). // (2) The parent element has restyle damage (so the text flow also needs fixup). None => child.get_raw_data().is_none() || diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index fbb7d2fb724..8aa51683f1a 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -104,7 +104,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::{Receiver, Sender, channel}; use style::animation::Animation; use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext}; -use style::dom::{TDocument, TElement, TNode}; +use style::dom::{TElement, TNode}; use style::error_reporting::{ParseErrorReporter, StdoutErrorReporter}; use style::logical_geometry::LogicalPoint; use style::media_queries::{Device, MediaType}; @@ -1063,11 +1063,8 @@ impl LayoutThread { // NB: The dirty bit is propagated down the tree. unsafe { node.set_dirty(); } - let mut current = node.parent_node().and_then(|n| n.as_element()); - while let Some(el) = current { - if el.has_dirty_descendants() { break; } - unsafe { el.set_dirty_descendants(); } - current = el.parent_element(); + if let Some(p) = node.parent_node().and_then(|n| n.as_element()) { + unsafe { p.note_dirty_descendant() }; } next = iter.next_skipping_children(); @@ -1096,11 +1093,40 @@ 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 { + // Propagate the descendant bit up the ancestors. Do this before + // the restyle calculation so that we can also do it for new + // unstyled nodes, which the descendants bit helps us find. + if let Some(parent) = el.parent_element() { + unsafe { parent.note_dirty_descendant() }; + } + + if el.get_data().is_none() { + // If we haven't styled this node yet, we don't need to track + // a 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; + } } } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index b38d5d4ec91..661ecadde9d 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -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); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 7326a207c31..d13d94f8099 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -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() diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index ac65c01e24c..b348220c0fb 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -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 diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index f6389b57efe..ea00f62f43d 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -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 diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 00c7ce1413c..5dd5e5082ce 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -34,9 +34,9 @@ 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::{CAN_BE_FRAGMENTED, DIRTY_ON_VIEWPORT_SIZE_CHANGE, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; use dom::node::{LayoutNodeHelpers, Node}; use dom::text::Text; use gfx_traits::ByteIndex; @@ -46,7 +46,7 @@ use parking_lot::RwLock; use range::Range; use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData}; -use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutElement, LayoutNode}; +use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use selectors::matching::ElementFlags; use selectors::parser::{AttrSelector, NamespaceConstraint}; @@ -61,11 +61,11 @@ use style::attr::AttrValue; use style::computed_values::display; use style::context::SharedStyleContext; use style::data::ElementData; -use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, PresentationalHintsSynthetizer, TDocument, TElement, TNode}; -use style::dom::{TRestyleDamage, UnsafeNode}; +use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, PresentationalHintsSynthetizer, TElement, TNode}; +use style::dom::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; @@ -112,6 +112,10 @@ impl<'ln> ServoLayoutNode<'ln> { self.node.type_id_for_layout() } } + + pub fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> { + self.node.downcast().map(ServoLayoutDocument::from_layout_js) + } } impl<'ln> NodeInfo for ServoLayoutNode<'ln> { @@ -128,7 +132,6 @@ impl<'ln> NodeInfo for ServoLayoutNode<'ln> { impl<'ln> TNode for ServoLayoutNode<'ln> { type ConcreteElement = ServoLayoutElement<'ln>; - type ConcreteDocument = ServoLayoutDocument<'ln>; type ConcreteChildrenIterator = ServoChildrenIterator<'ln>; fn to_unsafe(&self) -> UnsafeNode { @@ -177,10 +180,6 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { as_element(self.node) } - fn as_document(&self) -> Option<ServoLayoutDocument<'ln>> { - self.node.downcast().map(ServoLayoutDocument::from_layout_js) - } - fn needs_dirty_on_viewport_size_changed(&self) -> bool { unsafe { self.node.get_flag(DIRTY_ON_VIEWPORT_SIZE_CHANGE) } } @@ -260,12 +259,7 @@ impl<'ln> LayoutNode for ServoLayoutNode<'ln> { self.get_jsmanaged().take_style_and_layout_data() } - fn has_changed(&self) -> bool { - unsafe { self.node.get_flag(HAS_CHANGED) } - } - unsafe fn clear_dirty_bits(&self) { - self.node.set_flag(HAS_CHANGED, false); self.node.set_flag(IS_DIRTY, false); self.node.set_flag(HAS_DIRTY_DESCENDANTS, false); } @@ -336,8 +330,8 @@ impl<'ln> ServoLayoutNode<'ln> { } fn debug_str(self) -> String { - format!("{:?}: changed={} dirty={} dirty_descendants={}", - self.script_type_id(), self.has_changed(), + format!("{:?}: dirty={} dirty_descendants={}", + self.script_type_id(), self.as_element().map_or(false, |el| el.deprecated_dirty_bit_is_set()), self.as_element().map_or(false, |el| el.has_dirty_descendants())) } @@ -369,34 +363,29 @@ pub struct ServoLayoutDocument<'ld> { chain: PhantomData<&'ld ()>, } -impl<'ld> TDocument for ServoLayoutDocument<'ld> { - type ConcreteNode = ServoLayoutNode<'ld>; - type ConcreteElement = ServoLayoutElement<'ld>; - +impl<'ld> ServoLayoutDocument<'ld> { fn as_node(&self) -> ServoLayoutNode<'ld> { ServoLayoutNode::from_layout_js(self.document.upcast()) } - fn root_node(&self) -> Option<ServoLayoutNode<'ld>> { + pub fn root_node(&self) -> Option<ServoLayoutNode<'ld>> { self.as_node().children().find(ServoLayoutNode::is_element) } - 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() } - fn needs_paint_from_layout(&self) { + pub fn needs_paint_from_layout(&self) { unsafe { self.document.needs_paint_from_layout(); } } - fn will_paint(&self) { + pub fn will_paint(&self) { unsafe { self.document.will_paint(); } } -} -impl<'ld> ServoLayoutDocument<'ld> { - fn from_layout_js(doc: LayoutJS<Document>) -> ServoLayoutDocument<'ld> { + pub fn from_layout_js(doc: LayoutJS<Document>) -> ServoLayoutDocument<'ld> { ServoLayoutDocument { document: doc, chain: PhantomData, @@ -433,7 +422,6 @@ impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> { impl<'le> TElement for ServoLayoutElement<'le> { type ConcreteNode = ServoLayoutNode<'le>; - type ConcreteDocument = ServoLayoutDocument<'le>; fn as_node(&self) -> ServoLayoutNode<'le> { ServoLayoutNode::from_layout_js(self.element.upcast()) @@ -460,7 +448,7 @@ impl<'le> TElement for ServoLayoutElement<'le> { } fn set_restyle_damage(self, damage: RestyleDamage) { - self.get_partial_layout_data().unwrap().borrow_mut().restyle_damage = damage; + self.get_partial_layout_data().unwrap().borrow_mut().restyle_damage |= damage; } #[inline] @@ -536,6 +524,35 @@ impl<'le> ServoLayoutElement<'le> { self.get_style_and_layout_data().map(|d| &**d.ptr) } } + + pub unsafe fn note_dirty_descendant(&self) { + use ::selectors::Element; + + let mut current = Some(*self); + while let Some(el) = current { + // FIXME(bholley): Ideally we'd have the invariant that any element + // with has_dirty_descendants also has the bit set on all its ancestors. + // However, there are currently some corner-cases where we get that wrong. + // I have in-flight patches to fix all this stuff up, so we just always + // propagate this bit for now. + el.set_dirty_descendants(); + current = el.parent_element(); + } + + debug_assert!(self.dirty_descendants_bit_is_propagated()); + } + + fn dirty_descendants_bit_is_propagated(&self) -> bool { + use ::selectors::Element; + + let mut current = Some(*self); + while let Some(el) = current { + if !el.has_dirty_descendants() { return false; } + current = el.parent_element(); + } + + true + } } fn as_element<'le>(node: LayoutJS<Node>) -> Option<ServoLayoutElement<'le>> { @@ -857,9 +874,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { } fn restyle_damage(self) -> RestyleDamage { - if self.node.has_changed() { - RestyleDamage::rebuild_and_reflow() - } else if self.is_text_node() { + if self.is_text_node() { let parent = self.node.parent_node().unwrap().as_element().unwrap(); let parent_data = parent.get_partial_layout_data().unwrap().borrow(); parent_data.restyle_damage @@ -1069,8 +1084,6 @@ impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> { } } -impl<'le> LayoutElement for ServoLayoutElement<'le> {} - /// This implementation of `::selectors::Element` is used for implementing lazy /// pseudo-elements. /// diff --git a/components/script_layout_interface/lib.rs b/components/script_layout_interface/lib.rs index 284f6f33498..ff86579631c 100644 --- a/components/script_layout_interface/lib.rs +++ b/components/script_layout_interface/lib.rs @@ -51,6 +51,7 @@ use libc::c_void; use std::sync::atomic::AtomicIsize; use style::atomic_refcell::AtomicRefCell; use style::data::ElementData; +use style::dom::TRestyleDamage; use style::selector_impl::RestyleDamage; pub struct PartialPersistentLayoutData { @@ -71,7 +72,10 @@ impl PartialPersistentLayoutData { pub fn new() -> Self { PartialPersistentLayoutData { style_data: ElementData::new(), - restyle_damage: RestyleDamage::empty(), + // FIXME(bholley): This is needed for now to make sure we do frame + // construction after initial styling. This will go away shortly when + // we move restyle damage into the style system. + restyle_damage: RestyleDamage::rebuild_and_reflow(), parallel: DomParallelInfo::new(), } } diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index 9123c0c1f45..283e8def80a 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -18,7 +18,7 @@ use style::atomic_refcell::AtomicRefCell; use style::computed_values::display; use style::context::SharedStyleContext; use style::data::ElementData; -use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthetizer, TElement, TNode}; +use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthetizer, TNode}; use style::dom::OpaqueNode; use style::properties::ServoComputedValues; use style::selector_impl::{PseudoElement, PseudoElementCascadeType, RestyleDamage, ServoSelectorImpl}; @@ -86,8 +86,6 @@ pub trait LayoutNode: GetLayoutData + TNode { unsafe fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData); unsafe fn take_style_and_layout_data(&self) -> OpaqueStyleAndLayoutData; - fn has_changed(&self) -> bool; - unsafe fn clear_dirty_bits(&self); fn rev_children(self) -> LayoutIterator<ReverseChildrenIterator<Self>> { @@ -274,9 +272,6 @@ pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode { unsafe fn dangerous_next_sibling(&self) -> Option<Self>; } -pub trait LayoutElement: Clone + Copy + Sized + Debug + GetLayoutData + TElement { -} - pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug + ::selectors::Element<Impl=ServoSelectorImpl> + GetLayoutData + @@ -435,5 +430,4 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug + .get(&other.style_pseudo_element()).unwrap().0.clone(), } } - } diff --git a/components/style/dom.rs b/components/style/dom.rs index 388a7310ab0..7b15e820edf 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -14,7 +14,7 @@ use parking_lot::RwLock; use properties::{ComputedValues, PropertyDeclarationBlock}; use properties::longhands::display::computed_value as display; use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; -use selector_impl::{ElementExt, PseudoElement, RestyleDamage, Snapshot}; +use selector_impl::{ElementExt, PseudoElement, RestyleDamage}; use selector_matching::ApplicableDeclarationBlock; use sink::Push; use std::fmt::Debug; @@ -105,8 +105,7 @@ impl<T, I> Iterator for LayoutIterator<T> where T: Iterator<Item=I>, I: NodeInfo } pub trait TNode : Sized + Copy + Clone + NodeInfo { - type ConcreteElement: TElement<ConcreteNode = Self, ConcreteDocument = Self::ConcreteDocument>; - type ConcreteDocument: TDocument<ConcreteNode = Self, ConcreteElement = Self::ConcreteElement>; + type ConcreteElement: TElement<ConcreteNode = Self>; type ConcreteChildrenIterator: Iterator<Item = Self>; fn to_unsafe(&self) -> UnsafeNode; @@ -130,8 +129,6 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { fn as_element(&self) -> Option<Self::ConcreteElement>; - fn as_document(&self) -> Option<Self::ConcreteDocument>; - fn needs_dirty_on_viewport_size_changed(&self) -> bool; unsafe fn set_dirty_on_viewport_size_changed(&self); @@ -151,28 +148,13 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { fn next_sibling(&self) -> Option<Self>; } -pub trait TDocument : Sized + Copy + Clone { - type ConcreteNode: TNode<ConcreteElement = Self::ConcreteElement, ConcreteDocument = Self>; - type ConcreteElement: TElement<ConcreteNode = Self::ConcreteNode, ConcreteDocument = Self>; - - fn as_node(&self) -> Self::ConcreteNode; - - fn root_node(&self) -> Option<Self::ConcreteNode>; - - fn drain_modified_elements(&self) -> Vec<(Self::ConcreteElement, Snapshot)>; - - fn needs_paint_from_layout(&self); - fn will_paint(&self); -} - pub trait PresentationalHintsSynthetizer { fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) where V: Push<ApplicableDeclarationBlock>; } pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer { - type ConcreteNode: TNode<ConcreteElement = Self, ConcreteDocument = Self::ConcreteDocument>; - type ConcreteDocument: TDocument<ConcreteNode = Self::ConcreteNode, ConcreteElement = Self>; + type ConcreteNode: TNode<ConcreteElement = Self>; fn as_node(&self) -> Self::ConcreteNode; diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index cc40f22ae97..4f36b722626 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -7,22 +7,20 @@ use atomic_refcell::{AtomicRef, AtomicRefCell}; use data::ElementData; -use dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, UnsafeNode}; +use dom::{LayoutIterator, NodeInfo, TElement, TNode, UnsafeNode}; use dom::{OpaqueNode, PresentationalHintsSynthetizer}; use element_state::ElementState; use error_reporting::StdoutErrorReporter; use gecko::restyle_damage::GeckoRestyleDamage; use gecko::selector_impl::{GeckoSelectorImpl, NonTSPseudoClass, PseudoElement}; -use gecko::snapshot::GeckoElementSnapshot; use gecko::snapshot_helpers; use gecko_bindings::bindings; use gecko_bindings::bindings::{Gecko_DropStyleChildrenIterator, Gecko_MaybeCreateStyleChildrenIterator}; -use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentElement}; -use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetNextStyleChild}; +use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetLastChild, Gecko_GetNextStyleChild}; use gecko_bindings::bindings::{Gecko_GetServoDeclarationBlock, Gecko_IsHTMLElementInHTMLDocument}; use gecko_bindings::bindings::{Gecko_IsLink, Gecko_IsRootElement}; use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink, Gecko_Namespace}; -use gecko_bindings::bindings::{RawGeckoDocument, RawGeckoElement, RawGeckoNode}; +use gecko_bindings::bindings::{RawGeckoElement, RawGeckoNode}; use gecko_bindings::bindings::Gecko_ClassOrClassList; use gecko_bindings::bindings::Gecko_GetStyleContext; use gecko_bindings::bindings::Gecko_SetNodeFlags; @@ -78,7 +76,6 @@ impl<'ln> NodeInfo for GeckoNode<'ln> { } impl<'ln> TNode for GeckoNode<'ln> { - type ConcreteDocument = GeckoDocument<'ln>; type ConcreteElement = GeckoElement<'ln>; type ConcreteChildrenIterator = GeckoChildrenIterator<'ln>; @@ -137,10 +134,6 @@ impl<'ln> TNode for GeckoNode<'ln> { } } - fn as_document(&self) -> Option<GeckoDocument<'ln>> { - unimplemented!() - } - fn can_be_fragmented(&self) -> bool { // FIXME(SimonSapin): Servo uses this to implement CSS multicol / fragmentation // Maybe this isn’t useful for Gecko? @@ -219,33 +212,6 @@ impl<'a> Iterator for GeckoChildrenIterator<'a> { } #[derive(Clone, Copy)] -pub struct GeckoDocument<'ld>(pub &'ld RawGeckoDocument); - -impl<'ld> TDocument for GeckoDocument<'ld> { - type ConcreteNode = GeckoNode<'ld>; - type ConcreteElement = GeckoElement<'ld>; - - fn as_node(&self) -> GeckoNode<'ld> { - unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) } - } - - fn root_node(&self) -> Option<GeckoNode<'ld>> { - unsafe { - Gecko_GetDocumentElement(self.0).map(|el| GeckoElement(el).as_node()) - } - } - - fn drain_modified_elements(&self) -> Vec<(GeckoElement<'ld>, GeckoElementSnapshot)> { - unimplemented!() - /* - let elements = unsafe { self.0.drain_modified_elements() }; - elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()*/ - } - fn will_paint(&self) { unimplemented!() } - fn needs_paint_from_layout(&self) { unimplemented!() } -} - -#[derive(Clone, Copy)] pub struct GeckoElement<'le>(pub &'le RawGeckoElement); impl<'le> fmt::Debug for GeckoElement<'le> { @@ -322,7 +288,6 @@ lazy_static! { impl<'le> TElement for GeckoElement<'le> { type ConcreteNode = GeckoNode<'le>; - type ConcreteDocument = GeckoDocument<'le>; fn as_node(&self) -> Self::ConcreteNode { unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) } diff --git a/components/style/servo/restyle_damage.rs b/components/style/servo/restyle_damage.rs index 616c3506173..6cabb7eb485 100644 --- a/components/style/servo/restyle_damage.rs +++ b/components/style/servo/restyle_damage.rs @@ -4,6 +4,7 @@ use computed_values::display; use dom::TRestyleDamage; +use heapsize::HeapSizeOf; use properties::ServoComputedValues; use std::fmt; 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 { /// For Servo the style source is always the computed values. type PreExistingComputedValues = Arc<ServoComputedValues>; |