diff --git a/components/layout/lib.rs b/components/layout/lib.rs index b272ad503de0..bff6eeca1acd 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -46,7 +46,6 @@ extern crate range; extern crate rustc_serialize; extern crate script_layout_interface; extern crate script_traits; -extern crate selectors; extern crate smallvec; #[macro_use(atom, ns)] extern crate string_cache; extern crate style; diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 2f6c49b8ea82..54bf5da3b449 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -11,13 +11,12 @@ use flow::{self, PreorderFlowTraversal}; use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal}; use gfx::display_list::OpaqueNode; use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; -use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; +use script_layout_interface::wrapper_traits::{LayoutElement, LayoutNode, ThreadSafeLayoutNode}; use std::mem; use style::atomic_refcell::AtomicRefCell; use style::context::{LocalStyleContext, SharedStyleContext, StyleContext}; use style::data::NodeData; -use style::dom::TNode; -use style::selector_impl::ServoSelectorImpl; +use style::dom::{StylingMode, TElement, TNode}; use style::traversal::{DomTraversalContext, put_thread_local_bloom_filter}; use style::traversal::{recalc_style_at, remove_from_bloom_filter}; use style::traversal::RestyleResult; @@ -32,7 +31,7 @@ pub struct RecalcStyleAndConstructFlows<'lc> { impl<'lc, N> DomTraversalContext for RecalcStyleAndConstructFlows<'lc> where N: LayoutNode + TNode, - N::ConcreteElement: ::selectors::Element + N::ConcreteElement: LayoutElement { type SharedContext = SharedLayoutContext; @@ -114,9 +113,27 @@ impl<'lc, N> DomTraversalContext for RecalcStyleAndConstructFlows<'lc> construct_flows_at(&self.context, self.root, node); } - fn ensure_node_data(node: &N) -> &AtomicRefCell { - node.initialize_data(); - node.get_style_data().unwrap() + fn should_traverse_child(parent: N::ConcreteElement, child: N) -> bool { + // If this node has been marked as damaged in some way, we need to + // traverse it unconditionally 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: + // (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() || + parent.as_node().to_threadsafe().restyle_damage() != RestyleDamage::empty(), + } + } + + fn ensure_element_data(element: &N::ConcreteElement) -> &AtomicRefCell { + element.as_node().initialize_data(); + element.get_style_data().unwrap() } fn local_context(&self) -> &LocalStyleContext { @@ -140,8 +157,8 @@ fn construct_flows_at<'a, N: LayoutNode>(context: &'a LayoutContext<'a>, root: O // Always reconstruct if incremental layout is turned off. let nonincremental_layout = opts::get().nonincremental_layout; - if nonincremental_layout || node.has_dirty_descendants() || - tnode.restyle_damage() != RestyleDamage::empty() { + if nonincremental_layout || tnode.restyle_damage() != RestyleDamage::empty() || + node.as_element().map_or(false, |el| el.has_dirty_descendants()) { let mut flow_constructor = FlowConstructor::new(context); if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) { flow_constructor.process(&tnode); diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index c37499a12afd..6d1b9ce4379d 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -33,12 +33,12 @@ use core::nonzero::NonZero; use data::{LayoutDataFlags, PersistentLayoutData}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData}; -use script_layout_interface::wrapper_traits::{GetLayoutData, LayoutNode}; use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode}; +use script_layout_interface::wrapper_traits::GetLayoutData; use style::atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use style::computed_values::content::{self, ContentItem}; -pub type NonOpaqueStyleAndLayoutData = *mut AtomicRefCell; +pub type NonOpaqueStyleAndLayoutData = AtomicRefCell; pub trait LayoutNodeLayoutData { /// Similar to borrow_data*, but returns the full PersistentLayoutData rather @@ -50,21 +50,11 @@ pub trait LayoutNodeLayoutData { impl LayoutNodeLayoutData for T { fn borrow_layout_data(&self) -> Option> { - unsafe { - self.get_style_and_layout_data().map(|opaque| { - let container = *opaque.ptr as NonOpaqueStyleAndLayoutData; - (*container).borrow() - }) - } + self.get_raw_data().map(|d| d.borrow()) } fn mutate_layout_data(&self) -> Option> { - unsafe { - self.get_style_and_layout_data().map(|opaque| { - let container = *opaque.ptr as NonOpaqueStyleAndLayoutData; - (*container).borrow_mut() - }) - } + self.get_raw_data().map(|d| d.borrow_mut()) } fn flow_debug_id(self) -> usize { @@ -73,19 +63,27 @@ impl LayoutNodeLayoutData for T { } pub trait LayoutNodeHelpers { - fn initialize_data(self); + fn initialize_data(&self); + fn get_raw_data(&self) -> Option<&NonOpaqueStyleAndLayoutData>; } -impl LayoutNodeHelpers for T { - fn initialize_data(self) { - if self.borrow_layout_data().is_none() { - let ptr: NonOpaqueStyleAndLayoutData = +impl LayoutNodeHelpers for T { + fn initialize_data(&self) { + if self.get_raw_data().is_none() { + let ptr: *mut NonOpaqueStyleAndLayoutData = Box::into_raw(box AtomicRefCell::new(PersistentLayoutData::new())); let opaque = OpaqueStyleAndLayoutData { ptr: unsafe { NonZero::new(ptr as *mut AtomicRefCell) } }; self.init_style_and_layout_data(opaque); - } + }; + } + + fn get_raw_data(&self) -> Option<&NonOpaqueStyleAndLayoutData> { + self.get_style_and_layout_data().map(|opaque| { + let container = *opaque.ptr as *mut NonOpaqueStyleAndLayoutData; + unsafe { &*container } + }) } } diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml index e41b6d3b0902..37998d1b48b8 100644 --- a/components/layout_thread/Cargo.toml +++ b/components/layout_thread/Cargo.toml @@ -30,6 +30,7 @@ profile_traits = {path = "../profile_traits"} script = {path = "../script"} script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} +selectors = "0.14" serde_derive = "0.8" serde_json = "0.8" style = {path = "../style"} diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 51ec52cb0d04..a2ad05025a6b 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -37,6 +37,7 @@ extern crate profile_traits; extern crate script; extern crate script_layout_interface; extern crate script_traits; +extern crate selectors; extern crate serde_json; extern crate style; extern crate url; @@ -94,6 +95,7 @@ use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowR use script_layout_interface::wrapper_traits::LayoutNode; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; use script_traits::{StackingContextScrollState, UntrustedNodeAddress}; +use selectors::Element; use std::borrow::ToOwned; use std::collections::HashMap; use std::hash::BuildHasherDefault; @@ -1109,11 +1111,11 @@ impl LayoutThread { // NB: The dirty bit is propagated down the tree. unsafe { node.set_dirty(); } - let mut current = node.parent_node(); - while let Some(node) = current { - if node.has_dirty_descendants() { break; } - unsafe { node.set_dirty_descendants(); } - current = node.parent_node(); + 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(); } next = iter.next_skipping_children(); @@ -1155,7 +1157,8 @@ impl LayoutThread { viewport_size_changed, data.reflow_info.goal); - if node.is_dirty() || node.has_dirty_descendants() { + let el = node.as_element(); + if el.is_some() && (el.unwrap().deprecated_dirty_bit_is_set() || el.unwrap().has_dirty_descendants()) { // Recalculate CSS styles and rebuild flows and fragments. profile(time::ProfilerCategory::LayoutStyleRecalc, self.profiler_metadata(), @@ -1485,7 +1488,7 @@ impl LayoutThread { /// because the struct type is transmuted to a different type on the script side. unsafe fn handle_reap_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) { let ptr: *mut AtomicRefCell = *data.ptr; - let non_opaque: NonOpaqueStyleAndLayoutData = ptr as *mut _; + let non_opaque: *mut NonOpaqueStyleAndLayoutData = ptr as *mut _; let _ = Box::from_raw(non_opaque); } diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 73d06eec76aa..34fa3e10a3a6 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -46,7 +46,7 @@ use range::Range; use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData}; use script_layout_interface::restyle_damage::RestyleDamage; -use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode}; +use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutElement, LayoutNode}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use selectors::matching::ElementFlags; use selectors::parser::{AttrSelector, NamespaceConstraint}; @@ -107,10 +107,6 @@ impl<'ln> ServoLayoutNode<'ln> { } } - pub fn mutate_data(&self) -> Option> { - self.get_style_data().map(|d| d.borrow_mut()) - } - fn script_type_id(&self) -> NodeTypeId { unsafe { self.node.type_id_for_layout() @@ -165,11 +161,11 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { unsafe { self.get_jsmanaged().opaque() } } - fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option> { + fn layout_parent_element(self, reflow_root: OpaqueNode) -> Option> { if self.opaque() == reflow_root { None } else { - self.parent_node() + self.parent_node().and_then(|x| x.as_element()) } } @@ -185,18 +181,6 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { self.node.downcast().map(ServoLayoutDocument::from_layout_js) } - fn deprecated_dirty_bit_is_set(&self) -> bool { - unsafe { self.node.get_flag(IS_DIRTY) } - } - - fn has_dirty_descendants(&self) -> bool { - unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) } - } - - unsafe fn set_dirty_descendants(&self) { - self.node.set_flag(HAS_DIRTY_DESCENDANTS, true) - } - fn needs_dirty_on_viewport_size_changed(&self) -> bool { unsafe { self.node.get_flag(DIRTY_ON_VIEWPORT_SIZE_CHANGE) } } @@ -213,34 +197,6 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { self.node.set_flag(CAN_BE_FRAGMENTED, value) } - fn store_children_to_process(&self, n: isize) { - let data = self.get_partial_layout_data().unwrap().borrow(); - data.parallel.children_to_process.store(n, Ordering::Relaxed); - } - - fn did_process_child(&self) -> isize { - let data = self.get_partial_layout_data().unwrap().borrow(); - let old_value = data.parallel.children_to_process.fetch_sub(1, Ordering::Relaxed); - debug_assert!(old_value >= 1); - old_value - 1 - } - - fn begin_styling(&self) -> AtomicRefMut { - let mut data = self.mutate_data().unwrap(); - data.gather_previous_styles(|| None); - data - } - - fn style_text_node(&self, style: Arc) { - debug_assert!(self.is_text_node()); - let mut data = self.get_partial_layout_data().unwrap().borrow_mut(); - data.style_data.style_text_node(style); - } - - fn borrow_data(&self) -> Option> { - self.get_style_data().map(|d| d.borrow()) - } - fn parent_node(&self) -> Option> { unsafe { self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(&node)) @@ -305,16 +261,12 @@ impl<'ln> LayoutNode for ServoLayoutNode<'ln> { self.node.set_flag(IS_DIRTY, false); self.node.set_flag(HAS_DIRTY_DESCENDANTS, false); } +} - // NB: This duplicates the get_style_data on ThreadSafeLayoutElement, but - // will go away soon. - fn get_style_data(&self) -> Option<&AtomicRefCell> { +impl<'ln> GetLayoutData for ServoLayoutNode<'ln> { + fn get_style_and_layout_data(&self) -> Option { unsafe { - self.get_jsmanaged().get_style_and_layout_data().map(|d| { - let ppld: &AtomicRefCell = &**d.ptr; - let psd: &AtomicRefCell = transmute(ppld); - psd - }) + self.get_jsmanaged().get_style_and_layout_data() } } @@ -325,30 +277,34 @@ impl<'ln> LayoutNode for ServoLayoutNode<'ln> { } } -impl<'ln> GetLayoutData for ServoLayoutNode<'ln> { - fn get_style_and_layout_data(&self) -> Option { - unsafe { - self.get_jsmanaged().get_style_and_layout_data() - } - } -} - impl<'le> GetLayoutData for ServoLayoutElement<'le> { fn get_style_and_layout_data(&self) -> Option { self.as_node().get_style_and_layout_data() } + + fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) { + self.as_node().init_style_and_layout_data(data) + } } impl<'ln> GetLayoutData for ServoThreadSafeLayoutNode<'ln> { fn get_style_and_layout_data(&self) -> Option { self.node.get_style_and_layout_data() } + + fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) { + self.node.init_style_and_layout_data(data) + } } impl<'le> GetLayoutData for ServoThreadSafeLayoutElement<'le> { fn get_style_and_layout_data(&self) -> Option { self.element.as_node().get_style_and_layout_data() } + + fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) { + self.element.as_node().init_style_and_layout_data(data) + } } impl<'ln> ServoLayoutNode<'ln> { @@ -360,14 +316,6 @@ impl<'ln> ServoLayoutNode<'ln> { self.node.set_flag(IS_DIRTY, true) } - fn get_partial_layout_data(&self) -> Option<&AtomicRefCell> { - unsafe { - self.get_jsmanaged().get_style_and_layout_data().map(|d| { - &**d.ptr - }) - } - } - fn dump_indent(self, indent: u32) { let mut s = String::new(); for _ in 0..indent { @@ -400,12 +348,17 @@ impl<'ln> ServoLayoutNode<'ln> { fn debug_str(self) -> String { format!("{:?}: changed={} dirty={} dirty_descendants={}", self.script_type_id(), self.has_changed(), - self.deprecated_dirty_bit_is_set(), - self.has_dirty_descendants()) + self.as_element().map_or(false, |el| el.deprecated_dirty_bit_is_set()), + self.as_element().map_or(false, |el| el.has_dirty_descendants())) } fn debug_style_str(self) -> String { - if let Some(data) = self.borrow_data() { + let maybe_element = self.as_element(); + let maybe_data = match maybe_element { + Some(ref el) => el.borrow_data(), + None => None, + }; + if let Some(data) = maybe_data { format!("{:?}: {:?}", self.script_type_id(), &*data) } else { format!("{:?}: style_data=None", self.script_type_id()) @@ -518,8 +471,7 @@ impl<'le> TElement for ServoLayoutElement<'le> { } fn set_restyle_damage(self, damage: RestyleDamage) { - let node = self.as_node(); - node.get_partial_layout_data().unwrap().borrow_mut().restyle_damage = damage; + self.get_partial_layout_data().unwrap().borrow_mut().restyle_damage = damage; } #[inline] @@ -529,6 +481,53 @@ impl<'le> TElement for ServoLayoutElement<'le> { -> Option<&'a Arc> { current_cv } + + fn deprecated_dirty_bit_is_set(&self) -> bool { + unsafe { self.as_node().node.get_flag(IS_DIRTY) } + } + + fn has_dirty_descendants(&self) -> bool { + unsafe { self.as_node().node.get_flag(HAS_DIRTY_DESCENDANTS) } + } + + unsafe fn set_dirty_descendants(&self) { + self.as_node().node.set_flag(HAS_DIRTY_DESCENDANTS, true) + } + + fn store_children_to_process(&self, n: isize) { + let data = self.get_partial_layout_data().unwrap().borrow(); + data.parallel.children_to_process.store(n, Ordering::Relaxed); + } + + fn did_process_child(&self) -> isize { + let data = self.get_partial_layout_data().unwrap().borrow(); + let old_value = data.parallel.children_to_process.fetch_sub(1, Ordering::Relaxed); + debug_assert!(old_value >= 1); + old_value - 1 + } + + fn begin_styling(&self) -> AtomicRefMut { + let mut data = self.mutate_data().unwrap(); + data.gather_previous_styles(|| None); + data + } + + fn borrow_data(&self) -> Option> { + self.get_style_data().map(|d| d.borrow()) + } + +} + +impl<'le> LayoutElement for ServoLayoutElement<'le> { + fn get_style_data(&self) -> Option<&AtomicRefCell> { + unsafe { + self.get_style_and_layout_data().map(|d| { + let ppld: &AtomicRefCell = &**d.ptr; + let psd: &AtomicRefCell = transmute(ppld); + psd + }) + } + } } impl<'le> PartialEq for ServoLayoutElement<'le> { @@ -551,6 +550,18 @@ impl<'le> ServoLayoutElement<'le> { (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name) } } + + fn mutate_data(&self) -> Option> { + self.get_style_data().map(|d| d.borrow_mut()) + } + + fn get_partial_layout_data(&self) -> Option<&AtomicRefCell> { + unsafe { + self.get_style_and_layout_data().map(|d| { + &**d.ptr + }) + } + } } fn as_element<'le>(node: LayoutJS) -> Option> { @@ -826,7 +837,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { // works, but would be difficult to change. (Text node style is // also not visible to script.) debug_assert!(self.is_text_node()); - let parent = self.node.parent_node().unwrap(); + let parent = self.node.parent_node().unwrap().as_element().unwrap(); let parent_data = parent.get_style_data().unwrap().borrow(); parent_data.current_styles().primary.clone() } @@ -875,17 +886,19 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { if self.node.has_changed() { RestyleDamage::rebuild_and_reflow() } else if self.is_text_node() { - let parent = self.node.parent_node().unwrap(); + let parent = self.node.parent_node().unwrap().as_element().unwrap(); let parent_data = parent.get_partial_layout_data().unwrap().borrow(); parent_data.restyle_damage } else { - self.node.get_partial_layout_data().unwrap().borrow().restyle_damage + let el = self.as_element().unwrap().element; + let damage = el.get_partial_layout_data().unwrap().borrow().restyle_damage.clone(); + damage } } fn clear_restyle_damage(self) { - if self.is_element() { - let mut data = self.node.get_partial_layout_data().unwrap().borrow_mut(); + if let Some(el) = self.as_element() { + let mut data = el.element.get_partial_layout_data().unwrap().borrow_mut(); data.restyle_damage = RestyleDamage::empty(); } } @@ -1078,13 +1091,7 @@ impl<'le> ThreadSafeLayoutElement for ServoThreadSafeLayoutElement<'le> { } fn get_style_data(&self) -> Option<&AtomicRefCell> { - unsafe { - self.element.as_node().get_style_and_layout_data().map(|d| { - let ppld: &AtomicRefCell = &**d.ptr; - let psd: &AtomicRefCell = transmute(ppld); - psd - }) - } + self.element.get_style_data() } } diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index 669a3e331be6..416d04d9522d 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -19,7 +19,7 @@ use style::atomic_refcell::AtomicRefCell; use style::computed_values::display; use style::context::SharedStyleContext; use style::data::NodeData; -use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthetizer, TNode}; +use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthetizer, TElement, TNode}; use style::dom::OpaqueNode; use style::properties::ServoComputedValues; use style::selector_impl::{PseudoElement, PseudoElementCascadeType, ServoSelectorImpl}; @@ -73,6 +73,7 @@ impl PseudoElementType { /// Trait to abstract access to layout data across various data structures. pub trait GetLayoutData { fn get_style_and_layout_data(&self) -> Option; + fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData); } /// A wrapper so that layout can access only the methods that it should have access to. Layout must @@ -88,10 +89,6 @@ pub trait LayoutNode: GetLayoutData + TNode { unsafe fn clear_dirty_bits(&self); - fn get_style_data(&self) -> Option<&AtomicRefCell>; - - fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData); - fn rev_children(self) -> LayoutIterator> { LayoutIterator(ReverseChildrenIterator { current: self.last_child(), @@ -276,6 +273,10 @@ pub trait DangerousThreadSafeLayoutNode: ThreadSafeLayoutNode { unsafe fn dangerous_next_sibling(&self) -> Option; } +pub trait LayoutElement: Clone + Copy + Sized + Debug + GetLayoutData + TElement { + fn get_style_data(&self) -> Option<&AtomicRefCell>; +} + pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug + ::selectors::Element + GetLayoutData + diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 80c03276756c..0aece035e9ee 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -1201,6 +1201,7 @@ dependencies = [ "script 0.0.1", "script_layout_interface 0.0.1", "script_traits 0.0.1", + "selectors 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", diff --git a/components/style/dom.rs b/components/style/dom.rs index 7ead26054164..03eee395f7bf 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -123,7 +123,7 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { /// While doing a reflow, the node at the root has no parent, as far as we're /// concerned. This method returns `None` at the reflow root. - fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option; + fn layout_parent_element(self, reflow_root: OpaqueNode) -> Option; fn debug_id(self) -> usize; @@ -131,6 +131,70 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { fn as_document(&self) -> Option; + fn needs_dirty_on_viewport_size_changed(&self) -> bool; + + unsafe fn set_dirty_on_viewport_size_changed(&self); + + fn can_be_fragmented(&self) -> bool; + + unsafe fn set_can_be_fragmented(&self, value: bool); + + fn parent_node(&self) -> Option; + + fn first_child(&self) -> Option; + + fn last_child(&self) -> Option; + + fn prev_sibling(&self) -> Option; + + fn next_sibling(&self) -> Option; +} + +pub trait TDocument : Sized + Copy + Clone { + type ConcreteNode: TNode; + type ConcreteElement: TElement; + + fn as_node(&self) -> Self::ConcreteNode; + + fn root_node(&self) -> Option; + + 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(&self, hints: &mut V) + where V: Push; +} + +pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer { + type ConcreteNode: TNode; + type ConcreteDocument: TDocument; + type ConcreteRestyleDamage: TRestyleDamage; + + fn as_node(&self) -> Self::ConcreteNode; + + fn style_attribute(&self) -> Option<&Arc>>; + + fn get_state(&self) -> ElementState; + + fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool; + fn attr_equals(&self, namespace: &Namespace, attr: &Atom, value: &Atom) -> bool; + + /// Set the restyle damage field. + fn set_restyle_damage(self, damage: Self::ConcreteRestyleDamage); + + /// XXX: It's a bit unfortunate we need to pass the current computed values + /// as an argument here, but otherwise Servo would crash due to double + /// borrows to return it. + fn existing_style_for_restyle_damage<'a>(&'a self, + current_computed_values: Option<&'a Arc>, + pseudo: Option<&PseudoElement>) + -> Option<&'a ::PreExistingComputedValues>; + /// The concept of a dirty bit doesn't exist in our new restyle algorithm. /// Instead, we associate restyle and change hints with nodes. However, we /// continue to allow the dirty bit to trigger unconditional restyles while @@ -141,14 +205,6 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { unsafe fn set_dirty_descendants(&self); - fn needs_dirty_on_viewport_size_changed(&self) -> bool; - - unsafe fn set_dirty_on_viewport_size_changed(&self); - - fn can_be_fragmented(&self) -> bool; - - unsafe fn set_can_be_fragmented(&self, value: bool); - /// Atomically stores the number of children of this node that we will /// need to process during bottom-up traversal. fn store_children_to_process(&self, n: isize); @@ -157,6 +213,7 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { /// traversal. Returns the number of children left to process. fn did_process_child(&self) -> isize; + /// Returns true if this node has a styled layout frame that owns the style. fn frame_has_style(&self) -> bool { false } @@ -207,69 +264,9 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { /// can be performed. fn begin_styling(&self) -> AtomicRefMut; - /// Set the style directly for a text node. This skips various unnecessary - /// steps from begin_styling like computing the previous style. - fn style_text_node(&self, style: Arc); - /// Immutable borrows the NodeData. fn borrow_data(&self) -> Option>; - fn parent_node(&self) -> Option; - - fn first_child(&self) -> Option; - - fn last_child(&self) -> Option; - - fn prev_sibling(&self) -> Option; - - fn next_sibling(&self) -> Option; -} - -pub trait TDocument : Sized + Copy + Clone { - type ConcreteNode: TNode; - type ConcreteElement: TElement; - - fn as_node(&self) -> Self::ConcreteNode; - - fn root_node(&self) -> Option; - - 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(&self, hints: &mut V) - where V: Push; -} - -pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + PresentationalHintsSynthetizer { - type ConcreteNode: TNode; - type ConcreteDocument: TDocument; - type ConcreteRestyleDamage: TRestyleDamage; - - fn as_node(&self) -> Self::ConcreteNode; - - fn style_attribute(&self) -> Option<&Arc>>; - - fn get_state(&self) -> ElementState; - - fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool; - fn attr_equals(&self, namespace: &Namespace, attr: &Atom, value: &Atom) -> bool; - - /// Set the restyle damage field. - fn set_restyle_damage(self, damage: Self::ConcreteRestyleDamage); - - /// XXX: It's a bit unfortunate we need to pass the current computed values - /// as an argument here, but otherwise Servo would crash due to double - /// borrows to return it. - fn existing_style_for_restyle_damage<'a>(&'a self, - current_computed_values: Option<&'a Arc>, - pseudo: Option<&PseudoElement>) - -> Option<&'a ::PreExistingComputedValues>; - /// Properly marks nodes as dirty in response to restyle hints. fn note_restyle_hint>(&self, hint: RestyleHint) { // Bail early if there's no restyling to do. @@ -279,9 +276,8 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre // If the restyle hint is non-empty, we need to restyle either this element // or one of its siblings. Mark our ancestor chain as having dirty descendants. - let node = self.as_node(); - let mut curr = node; - while let Some(parent) = curr.parent_node() { + let mut curr = *self; + while let Some(parent) = curr.parent_element() { if parent.has_dirty_descendants() { break } unsafe { parent.set_dirty_descendants(); } curr = parent; @@ -289,22 +285,21 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre // Process hints. if hint.contains(RESTYLE_SELF) { - unsafe { C::ensure_node_data(&node).borrow_mut().ensure_restyle_data(); } + unsafe { C::ensure_element_data(self).borrow_mut().ensure_restyle_data(); } // XXX(emilio): For now, dirty implies dirty descendants if found. } else if hint.contains(RESTYLE_DESCENDANTS) { - unsafe { node.set_dirty_descendants(); } - let mut current = node.first_child(); - while let Some(node) = current { - unsafe { C::ensure_node_data(&node).borrow_mut().ensure_restyle_data(); } - current = node.next_sibling(); + unsafe { self.set_dirty_descendants(); } + let mut current = self.first_child_element(); + while let Some(el) = current { + unsafe { C::ensure_element_data(&el).borrow_mut().ensure_restyle_data(); } + current = el.next_sibling_element(); } } if hint.contains(RESTYLE_LATER_SIBLINGS) { let mut next = ::selectors::Element::next_sibling_element(self); while let Some(sib) = next { - let sib_node = sib.as_node(); - unsafe { C::ensure_node_data(&sib_node).borrow_mut().ensure_restyle_data() }; + unsafe { C::ensure_element_data(&sib).borrow_mut().ensure_restyle_data() }; next = ::selectors::Element::next_sibling_element(&sib); } } diff --git a/components/style/gecko/traversal.rs b/components/style/gecko/traversal.rs index c9ddca69d734..e827911006e6 100644 --- a/components/style/gecko/traversal.rs +++ b/components/style/gecko/traversal.rs @@ -5,9 +5,9 @@ use atomic_refcell::AtomicRefCell; use context::{LocalStyleContext, SharedStyleContext, StyleContext}; use data::NodeData; -use dom::{NodeInfo, OpaqueNode, TNode}; +use dom::{NodeInfo, OpaqueNode, StylingMode, TElement, TNode}; use gecko::context::StandaloneStyleContext; -use gecko::wrapper::GeckoNode; +use gecko::wrapper::{GeckoElement, GeckoNode}; use std::mem; use traversal::{DomTraversalContext, recalc_style_at}; use traversal::RestyleResult; @@ -48,8 +48,15 @@ impl<'lc, 'ln> DomTraversalContext> for RecalcStyleOnly<'lc> { /// We don't use the post-order traversal for anything. fn needs_postorder_traversal(&self) -> bool { false } - fn ensure_node_data<'a>(node: &'a GeckoNode<'ln>) -> &'a AtomicRefCell { - node.ensure_data() + fn should_traverse_child(_parent: GeckoElement<'ln>, child: GeckoNode<'ln>) -> bool { + match child.as_element() { + Some(el) => el.styling_mode() != StylingMode::Stop, + None => false, // Gecko restyle doesn't need to traverse text nodes. + } + } + + fn ensure_element_data<'a>(element: &'a GeckoElement<'ln>) -> &'a AtomicRefCell { + element.ensure_data() } fn local_context(&self) -> &LocalStyleContext { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index e1ca9ab6723c..a4fa5d552e19 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -64,51 +64,6 @@ impl<'ln> GeckoNode<'ln> { debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null()); unsafe { &*self.0.mNodeInfo.mRawPtr } } - - fn flags(&self) -> u32 { - (self.0)._base._base_1.mFlags - } - - // FIXME: We can implement this without OOL calls, but we can't easily given - // GeckoNode is a raw reference. - // - // We can use a Cell, but that's a bit of a pain. - fn set_flags(&self, flags: u32) { - unsafe { Gecko_SetNodeFlags(self.0, flags) } - } - - pub fn clear_data(&self) { - let ptr = self.0.mServoData.get(); - if !ptr.is_null() { - let data = unsafe { Box::from_raw(self.0.mServoData.get()) }; - self.0.mServoData.set(ptr::null_mut()); - - // Perform a mutable borrow of the data in debug builds. This - // serves as an assertion that there are no outstanding borrows - // when we destroy the data. - debug_assert!({ let _ = data.borrow_mut(); true }); - } - } - - pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option> { - self.borrow_data().and_then(|data| data.current_styles().pseudos - .get(pseudo).map(|c| c.clone())) - } - - fn get_node_data(&self) -> Option<&AtomicRefCell> { - unsafe { self.0.mServoData.get().as_ref() } - } - - pub fn ensure_data(&self) -> &AtomicRefCell { - match self.get_node_data() { - Some(x) => x, - None => { - let ptr = Box::into_raw(Box::new(AtomicRefCell::new(NodeData::new()))); - self.0.mServoData.set(ptr); - unsafe { &* ptr } - }, - } - } } #[derive(Clone, Copy, Debug, PartialEq)] @@ -192,11 +147,11 @@ impl<'ln> TNode for GeckoNode<'ln> { OpaqueNode(ptr) } - fn layout_parent_node(self, reflow_root: OpaqueNode) -> Option> { + fn layout_parent_element(self, reflow_root: OpaqueNode) -> Option> { if self.opaque() == reflow_root { None } else { - self.parent_node() + self.parent_node().and_then(|x| x.as_element()) } } @@ -216,23 +171,6 @@ impl<'ln> TNode for GeckoNode<'ln> { unimplemented!() } - fn deprecated_dirty_bit_is_set(&self) -> bool { - self.flags() & (NODE_IS_DIRTY_FOR_SERVO as u32) != 0 - } - - fn has_dirty_descendants(&self) -> bool { - // Return true unconditionally if we're not yet styled. This is a hack - // and should go away soon. - if self.get_node_data().is_none() { - return true; - } - self.flags() & (NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0 - } - - unsafe fn set_dirty_descendants(&self) { - self.set_flags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) - } - fn can_be_fragmented(&self) -> bool { // FIXME(SimonSapin): Servo uses this to implement CSS multicol / fragmentation // Maybe this isn’t useful for Gecko? @@ -244,34 +182,6 @@ impl<'ln> TNode for GeckoNode<'ln> { // Maybe this isn’t useful for Gecko? } - fn store_children_to_process(&self, _: isize) { - // This is only used for bottom-up traversal, and is thus a no-op for Gecko. - } - - fn did_process_child(&self) -> isize { - panic!("Atomic child count not implemented in Gecko"); - } - - fn begin_styling(&self) -> AtomicRefMut { - let mut data = self.ensure_data().borrow_mut(); - data.gather_previous_styles(|| self.get_styles_from_frame()); - data - } - - fn style_text_node(&self, style: Arc) { - debug_assert!(self.is_text_node()); - - // FIXME(bholley): Gecko currently relies on the dirty bit being set to - // drive the post-traversal. This will go away soon. - unsafe { self.set_flags(NODE_IS_DIRTY_FOR_SERVO as u32); } - - self.ensure_data().borrow_mut().style_text_node(style); - } - - fn borrow_data(&self) -> Option> { - self.get_node_data().map(|x| x.borrow()) - } - fn parent_node(&self) -> Option> { unsafe { self.0.mParent.as_ref().map(GeckoNode) } } @@ -387,6 +297,55 @@ impl<'le> GeckoElement<'le> { let extra_data = ParserContextExtraData::default(); parse_style_attribute(value, &base_url, Box::new(StdoutErrorReporter), extra_data) } + + fn flags(&self) -> u32 { + self.raw_node()._base._base_1.mFlags + } + + fn raw_node(&self) -> &RawGeckoNode { + &(self.0)._base._base._base + } + + // FIXME: We can implement this without OOL calls, but we can't easily given + // GeckoNode is a raw reference. + // + // We can use a Cell, but that's a bit of a pain. + fn set_flags(&self, flags: u32) { + unsafe { Gecko_SetNodeFlags(self.as_node().0, flags) } + } + + pub fn clear_data(&self) { + let ptr = self.raw_node().mServoData.get(); + if !ptr.is_null() { + let data = unsafe { Box::from_raw(self.raw_node().mServoData.get()) }; + self.raw_node().mServoData.set(ptr::null_mut()); + + // Perform a mutable borrow of the data in debug builds. This + // serves as an assertion that there are no outstanding borrows + // when we destroy the data. + debug_assert!({ let _ = data.borrow_mut(); true }); + } + } + + pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option> { + self.borrow_data().and_then(|data| data.current_styles().pseudos + .get(pseudo).map(|c| c.clone())) + } + + fn get_node_data(&self) -> Option<&AtomicRefCell> { + unsafe { self.raw_node().mServoData.get().as_ref() } + } + + pub fn ensure_data(&self) -> &AtomicRefCell { + match self.get_node_data() { + Some(x) => x, + None => { + let ptr = Box::into_raw(Box::new(AtomicRefCell::new(NodeData::new()))); + self.raw_node().mServoData.set(ptr); + unsafe { &* ptr } + }, + } + } } lazy_static! { @@ -438,7 +397,7 @@ impl<'le> TElement for GeckoElement<'le> { fn set_restyle_damage(self, damage: GeckoRestyleDamage) { // FIXME(bholley): Gecko currently relies on the dirty bit being set to // drive the post-traversal. This will go away soon. - unsafe { self.as_node().set_flags(NODE_IS_DIRTY_FOR_SERVO as u32) } + unsafe { self.set_flags(NODE_IS_DIRTY_FOR_SERVO as u32) } unsafe { Gecko_StoreStyleDifference(self.as_node().0, damage.0) } } @@ -459,6 +418,41 @@ impl<'le> TElement for GeckoElement<'le> { context_ptr.as_ref() } } + + fn deprecated_dirty_bit_is_set(&self) -> bool { + self.flags() & (NODE_IS_DIRTY_FOR_SERVO as u32) != 0 + } + + fn has_dirty_descendants(&self) -> bool { + // Return true unconditionally if we're not yet styled. This is a hack + // and should go away soon. + if self.get_node_data().is_none() { + return true; + } + self.flags() & (NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0 + } + + unsafe fn set_dirty_descendants(&self) { + self.set_flags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) + } + + fn store_children_to_process(&self, _: isize) { + // This is only used for bottom-up traversal, and is thus a no-op for Gecko. + } + + fn did_process_child(&self) -> isize { + panic!("Atomic child count not implemented in Gecko"); + } + + fn begin_styling(&self) -> AtomicRefMut { + let mut data = self.ensure_data().borrow_mut(); + data.gather_previous_styles(|| self.get_styles_from_frame()); + data + } + + fn borrow_data(&self) -> Option> { + self.get_node_data().map(|x| x.borrow()) + } } impl<'le> PartialEq for GeckoElement<'le> { diff --git a/components/style/matching.rs b/components/style/matching.rs index 794e18e2c7b4..07add3148095 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -441,8 +441,7 @@ impl StyleSharingCandidateCache { return; } - let node = element.as_node(); - let data = node.borrow_data().unwrap(); + let data = element.borrow_data().unwrap(); let style = &data.current_styles().primary; let box_style = style.get_box(); @@ -460,7 +459,7 @@ impl StyleSharingCandidateCache { element.as_node().to_unsafe(), parent.as_node().to_unsafe()); self.cache.insert(StyleSharingCandidate { - node: node.to_unsafe(), + node: element.as_node().to_unsafe(), style: style.clone(), common_style_affecting_attributes: None, class_attributes: None, @@ -716,8 +715,7 @@ pub trait MatchMethods : TElement { match sharing_result { Ok(shared_style) => { // Yay, cache hit. Share the style. - let node = self.as_node(); - let mut data = node.begin_styling(); + let mut data = self.begin_styling(); // TODO: add the display: none optimisation here too! Even // better, factor it out/make it a bit more generic so Gecko @@ -863,12 +861,10 @@ pub trait MatchMethods : TElement { where Ctx: StyleContext<'a> { // Get our parent's style. - let parent_as_node = parent.map(|x| x.as_node()); - let parent_data = parent_as_node.as_ref().map(|x| x.borrow_data().unwrap()); + let parent_data = parent.as_ref().map(|x| x.borrow_data().unwrap()); let parent_style = parent_data.as_ref().map(|x| &x.current_styles().primary); - let node = self.as_node(); - let mut data = node.begin_styling(); + let mut data = self.begin_styling(); let mut new_styles; let mut applicable_declarations_cache = diff --git a/components/style/parallel.rs b/components/style/parallel.rs index ad83d3ac475c..c525e0f76802 100644 --- a/components/style/parallel.rs +++ b/components/style/parallel.rs @@ -8,7 +8,7 @@ #![allow(unsafe_code)] -use dom::{OpaqueNode, StylingMode, TNode, UnsafeNode}; +use dom::{OpaqueNode, StylingMode, TElement, TNode, UnsafeNode}; use std::mem; use std::sync::atomic::Ordering; use traversal::{RestyleResult, DomTraversalContext}; @@ -47,7 +47,7 @@ pub fn traverse_dom(root: N, where N: TNode, C: DomTraversalContext { - debug_assert!(root.styling_mode() != StylingMode::Stop); + debug_assert!(root.as_element().unwrap().styling_mode() != StylingMode::Stop); if opts::get().style_sharing_stats { STYLE_SHARING_CACHE_HITS.store(0, Ordering::SeqCst); STYLE_SHARING_CACHE_MISSES.store(0, Ordering::SeqCst); @@ -84,7 +84,7 @@ fn top_down_dom(unsafe_nodes: UnsafeNodeList, // Perform the appropriate traversal. let mut children_to_process = 0isize; if let RestyleResult::Continue = context.process_preorder(node) { - C::traverse_children(node, |kid| { + C::traverse_children(node.as_element().unwrap(), |kid| { children_to_process += 1; discovered_child_nodes.push(kid.to_unsafe()) }); @@ -93,11 +93,13 @@ fn top_down_dom(unsafe_nodes: UnsafeNodeList, // Reset the count of children if we need to do a bottom-up traversal // after the top up. if context.needs_postorder_traversal() { - node.store_children_to_process(children_to_process); - - // If there were no more children, start walking back up. if children_to_process == 0 { + // If there were no more children, start walking back up. bottom_up_dom::(unsafe_nodes.1, unsafe_node, proxy) + } else { + // Otherwise record the number of children to process when the + // time comes. + node.as_element().unwrap().store_children_to_process(children_to_process); } } } @@ -139,7 +141,7 @@ fn bottom_up_dom(root: OpaqueNode, // Perform the appropriate operation. context.process_postorder(node); - let parent = match node.layout_parent_node(root) { + let parent = match node.layout_parent_element(root) { None => break, Some(parent) => parent, }; @@ -151,6 +153,6 @@ fn bottom_up_dom(root: OpaqueNode, } // We were the last child of our parent. Construct flows for our parent. - node = parent; + node = parent.as_node(); } } diff --git a/components/style/sequential.rs b/components/style/sequential.rs index 87925b4d5f7e..2b3f2553798a 100644 --- a/components/style/sequential.rs +++ b/components/style/sequential.rs @@ -4,7 +4,7 @@ //! Implements sequential traversal over the DOM tree. -use dom::{StylingMode, TNode}; +use dom::{StylingMode, TElement, TNode}; use traversal::{RestyleResult, DomTraversalContext}; pub fn traverse_dom(root: N, @@ -17,7 +17,8 @@ pub fn traverse_dom(root: N, C: DomTraversalContext { if let RestyleResult::Continue = context.process_preorder(node) { - C::traverse_children(node, |kid| doit::(context, kid)); + C::traverse_children(node.as_element().unwrap(), + |kid| doit::(context, kid)); } if context.needs_postorder_traversal() { @@ -25,7 +26,7 @@ pub fn traverse_dom(root: N, } } - debug_assert!(root.styling_mode() != StylingMode::Stop); + debug_assert!(root.as_element().unwrap().styling_mode() != StylingMode::Stop); let context = C::new(shared, root.opaque()); doit::(&context, root); diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 48b598ba93ae..80f5cc43dc1a 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -7,7 +7,7 @@ use atomic_refcell::AtomicRefCell; use context::{LocalStyleContext, SharedStyleContext, StyleContext}; use data::NodeData; -use dom::{NodeInfo, OpaqueNode, StylingMode, TElement, TNode, UnsafeNode}; +use dom::{OpaqueNode, StylingMode, TElement, TNode, UnsafeNode}; use matching::{ApplicableDeclarations, MatchMethods, StyleSharingResult}; use selectors::bloom::BloomFilter; use selectors::matching::StyleRelations; @@ -118,7 +118,7 @@ fn insert_ancestors_into_bloom_filter(bf: &mut Box, ancestors += 1; el.insert_into_bloom_filter(&mut **bf); - el = match el.as_node().layout_parent_node(root).and_then(|x| x.as_element()) { + el = match el.as_node().layout_parent_element(root) { None => break, Some(p) => p, }; @@ -142,7 +142,7 @@ pub fn remove_from_bloom_filter<'a, N, C>(context: &C, root: OpaqueNode, node: N assert_eq!(old_node, unsafe_layout_node); assert_eq!(old_generation, context.shared_context().generation); - match node.layout_parent_node(root) { + match node.layout_parent_element(root) { None => { debug!("[{}] - {:X}, and deleting BF.", tid(), unsafe_layout_node.0); // If this is the reflow root, eat the thread-local bloom filter. @@ -150,7 +150,7 @@ pub fn remove_from_bloom_filter<'a, N, C>(context: &C, root: OpaqueNode, node: N Some(parent) => { // Otherwise, put it back, but remove this node. node.as_element().map(|x| x.remove_from_bloom_filter(&mut *bf)); - let unsafe_parent = parent.to_unsafe(); + let unsafe_parent = parent.as_node().to_unsafe(); put_thread_local_bloom_filter(bf, &unsafe_parent, &context.shared_context()); }, }; @@ -175,16 +175,19 @@ pub trait DomTraversalContext { /// If it's false, then process_postorder has no effect at all. fn needs_postorder_traversal(&self) -> bool { true } + /// Returns true if traversal should visit the given child. + fn should_traverse_child(parent: N::ConcreteElement, child: N) -> bool; + /// Helper for the traversal implementations to select the children that /// should be enqueued for processing. - fn traverse_children(parent: N, mut f: F) + fn traverse_children(parent: N::ConcreteElement, mut f: F) { // If we enqueue any children for traversal, we need to set the dirty // descendants bit. Avoid doing it more than once. let mut marked_dirty_descendants = false; - for kid in parent.children() { - if kid.styling_mode() != StylingMode::Stop { + for kid in parent.as_node().children() { + if Self::should_traverse_child(parent, kid) { if !marked_dirty_descendants { unsafe { parent.set_dirty_descendants(); } marked_dirty_descendants = true; @@ -197,7 +200,7 @@ pub trait DomTraversalContext { /// Ensures the existence of the NodeData, and returns it. This can't live /// on TNode because of the trait-based separation between Servo's script /// and layout crates. - fn ensure_node_data(node: &N) -> &AtomicRefCell; + fn ensure_element_data(element: &N::ConcreteElement) -> &AtomicRefCell; fn local_context(&self) -> &LocalStyleContext; } @@ -248,8 +251,7 @@ fn ensure_element_styled_internal<'a, E, C>(element: E, // // We only need to mark whether we have display none, and forget about it, // our style is up to date. - let node = element.as_node(); - if let Some(data) = node.borrow_data() { + if let Some(data) = element.borrow_data() { if let Some(style) = data.get_current_styles().map(|x| &x.primary) { if !*parents_had_display_none { *parents_had_display_none = style.get_box().clone_display() == display::T::none; @@ -290,7 +292,7 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C, let mut bf = take_thread_local_bloom_filter(element.parent_element(), root, context.shared_context()); let mut restyle_result = RestyleResult::Continue; - let mode = element.as_node().styling_mode(); + let mode = element.styling_mode(); debug_assert!(mode != StylingMode::Stop, "Parent should not have enqueued us"); if mode != StylingMode::Traverse { // Check to see whether we can share a style with someone. @@ -358,10 +360,8 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C, // fashion. if mode == StylingMode::Restyle && restyle_result == RestyleResult::Continue { for kid in element.as_node().children() { - let mut data = D::ensure_node_data(&kid).borrow_mut(); - if kid.is_text_node() { - data.ensure_restyle_data(); - } else { + if let Some(kid) = kid.as_element() { + let mut data = D::ensure_element_data(&kid).borrow_mut(); data.gather_previous_styles(|| kid.get_styles_from_frame()); if data.previous_styles().is_some() { data.ensure_restyle_data(); diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 8f929d5f6165..f5e817e527ee 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -1101,6 +1101,7 @@ dependencies = [ "script 0.0.1", "script_layout_interface 0.0.1", "script_traits 0.0.1", + "selectors 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.8.12 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "style 0.0.1", diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 07ea507b5330..4584cf7ab58c 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -73,9 +73,7 @@ pub extern "C" fn Servo_Shutdown() -> () { unsafe { ComputedValues::shutdown(); } } -fn restyle_subtree(node: GeckoNode, raw_data: RawServoStyleSetBorrowed) { - debug_assert!(node.is_element() || node.is_text_node()); - +fn restyle_subtree(element: GeckoElement, raw_data: RawServoStyleSetBorrowed) { // Force the creation of our lazily-constructed initial computed values on // the main thread, since it's not safe to call elsewhere. // @@ -106,16 +104,16 @@ fn restyle_subtree(node: GeckoNode, raw_data: RawServoStyleSetBorrowed) { timer: Timer::new(), }; - if node.styling_mode() == StylingMode::Stop { + if element.styling_mode() == StylingMode::Stop { error!("Unnecessary call to restyle_subtree"); return; } if per_doc_data.num_threads == 1 || per_doc_data.work_queue.is_none() { - sequential::traverse_dom::(node, &shared_style_context); + sequential::traverse_dom::<_, RecalcStyleOnly>(element.as_node(), &shared_style_context); } else { - parallel::traverse_dom::(node, &shared_style_context, - per_doc_data.work_queue.as_mut().unwrap()); + parallel::traverse_dom::<_, RecalcStyleOnly>(element.as_node(), &shared_style_context, + per_doc_data.work_queue.as_mut().unwrap()); } } @@ -123,7 +121,9 @@ fn restyle_subtree(node: GeckoNode, raw_data: RawServoStyleSetBorrowed) { pub extern "C" fn Servo_RestyleSubtree(node: RawGeckoNodeBorrowed, raw_data: RawServoStyleSetBorrowed) -> () { let node = GeckoNode(node); - restyle_subtree(node, raw_data); + if let Some(element) = node.as_element() { + restyle_subtree(element, raw_data); + } } #[no_mangle] @@ -158,8 +158,9 @@ pub extern "C" fn Servo_StyleWorkerThreadCount() -> u32 { #[no_mangle] pub extern "C" fn Servo_Node_ClearNodeData(node: RawGeckoNodeBorrowed) -> () { - let node = GeckoNode(node); - node.clear_data(); + if let Some(element) = GeckoNode(node).as_element() { + element.clear_data(); + } } #[no_mangle] @@ -253,7 +254,20 @@ pub extern "C" fn Servo_StyleSheet_Release(sheet: RawServoStyleSheetBorrowed) -> pub extern "C" fn Servo_ComputedValues_Get(node: RawGeckoNodeBorrowed) -> ServoComputedValuesStrong { let node = GeckoNode(node); - let data = node.borrow_data(); + + // Gecko erroneously calls this function from ServoRestyleManager::RecreateStyleContexts. + // We plan to fix that, but just support it for now until that code gets rewritten. + if node.is_text_node() { + error!("Don't call Servo_ComputedValue_Get() for text nodes"); + let parent = node.parent_node().unwrap().as_element().unwrap(); + let parent_cv = parent.borrow_data().map_or(Arc::new(ComputedValues::initial_values().clone()), + |x| x.get_current_styles().unwrap() + .primary.clone()); + return ComputedValues::inherit_from(&parent_cv).into_strong(); + } + + let element = node.as_element().unwrap(); + let data = element.borrow_data(); let arc_cv = match data.as_ref().and_then(|x| x.get_current_styles()) { Some(styles) => styles.primary.clone(), None => { @@ -315,8 +329,7 @@ pub extern "C" fn Servo_ComputedValues_GetForPseudoElement(parent_style: ServoCo match GeckoSelectorImpl::pseudo_element_cascade_type(&pseudo) { PseudoElementCascadeType::Eager => { - let node = element.as_node(); - let maybe_computed = node.get_pseudo_style(&pseudo); + let maybe_computed = element.get_pseudo_style(&pseudo); maybe_computed.map_or_else(parent_or_null, FFIArcHelpers::into_strong) } PseudoElementCascadeType::Lazy => {