diff --git a/components/layout/data.rs b/components/layout/data.rs index a9875a99876..7052fed7f90 100644 --- a/components/layout/data.rs +++ b/components/layout/data.rs @@ -46,6 +46,8 @@ impl PersistentLayoutData { bitflags! { pub flags LayoutDataFlags: u8 { #[doc = "Whether a flow has been newly constructed."] - const HAS_NEWLY_CONSTRUCTED_FLOW = 0x01 + const HAS_NEWLY_CONSTRUCTED_FLOW = 0x01, + #[doc = "Whether this node has been traversed by layout."] + const HAS_BEEN_TRAVERSED = 0x02, } } diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 69a6fde214d..9ac181c432a 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -53,6 +53,7 @@ use style::values::{self, Either}; use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; use text; use text::TextRunScanner; +use wrapper::ThreadSafeLayoutNodeHelpers; // From gfxFontConstants.h in Firefox. static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index a1109b51443..ed564248d08 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -20,6 +20,7 @@ use style::servo::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, RE use style::traversal::{DomTraversal, recalc_style_at}; use style::traversal::PerLevelTraversalData; use wrapper::{GetRawData, LayoutNodeHelpers, LayoutNodeLayoutData}; +use wrapper::ThreadSafeLayoutNodeHelpers; pub struct RecalcStyleAndConstructFlows { shared: SharedLayoutContext, @@ -134,6 +135,8 @@ fn construct_flows_at<'a, N>(context: &LayoutContext<'a>, tnode.flow_debug_id()); } } + + tnode.mutate_layout_data().unwrap().flags.insert(::data::HAS_BEEN_TRAVERSED); } if let Some(el) = node.as_element() { diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index d4910c40f87..817ff96bdb4 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -37,6 +37,8 @@ use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutD use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use script_layout_interface::wrapper_traits::GetLayoutData; use style::computed_values::content::{self, ContentItem}; +use style::dom::{NodeInfo, TNode}; +use style::selector_parser::RestyleDamage; pub type NonOpaqueStyleAndLayoutData = AtomicRefCell; @@ -120,6 +122,12 @@ pub trait ThreadSafeLayoutNodeHelpers { /// /// FIXME(pcwalton): This might have too much copying and/or allocation. Profile this. fn text_content(&self) -> TextContent; + + /// The RestyleDamage from any restyling, or RestyleDamage::rebuild_and_reflow() if this + /// is the first time layout is visiting this node. We implement this here, rather than + /// with the rest of the wrapper layer, because we need layout code to determine whether + /// layout has visited the node. + fn restyle_damage(self) -> RestyleDamage; } impl ThreadSafeLayoutNodeHelpers for T { @@ -149,6 +157,37 @@ impl ThreadSafeLayoutNodeHelpers for T { return TextContent::Text(self.node_text_content()); } + + fn restyle_damage(self) -> RestyleDamage { + // We need the underlying node to potentially access the parent in the + // case of text nodes. This is safe as long as we don't let the parent + // escape and never access its descendants. + let mut node = unsafe { self.unsafe_get() }; + + // If this is a text node, use the parent element, since that's what + // controls our style. + if node.is_text_node() { + node = node.parent_node().unwrap(); + debug_assert!(node.is_element()); + } + + let data = node.borrow_layout_data().unwrap(); + if let Some(r) = data.base.style_data.as_restyle() { + // We're reflowing a node that just got a restyle, and so the + // damage has been computed and stored in the RestyleData. + r.damage + } else if !data.flags.contains(::data::HAS_BEEN_TRAVERSED) { + // We're reflowing a node that was styled for the first time and + // has never been visited by layout. Return rebuild_and_reflow, + // because that's what the code expects. + RestyleDamage::rebuild_and_reflow() + } else { + // We're reflowing a node whose style data didn't change, but whose + // layout may change due to changes in ancestors or descendants. + RestyleDamage::empty() + } + } + } pub enum TextContent { diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 5576648b84d..15a110e7d51 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -67,7 +67,7 @@ use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, PresentationalHintsSynthe use style::dom::UnsafeNode; use style::element_state::*; use style::properties::{ComputedValues, PropertyDeclarationBlock}; -use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl}; +use style::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl}; use style::sink::Push; use style::str::is_whitespace; use style::stylist::ApplicableDeclarationBlock; @@ -740,6 +740,7 @@ impl<'ln> NodeInfo for ServoThreadSafeLayoutNode<'ln> { } impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { + type ConcreteNode = ServoLayoutNode<'ln>; type ConcreteThreadSafeLayoutElement = ServoThreadSafeLayoutElement<'ln>; type ChildrenIterator = ThreadSafeLayoutNodeChildrenIterator; @@ -815,15 +816,8 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { } } - fn restyle_damage(self) -> RestyleDamage { - let element = if self.is_text_node() { - self.node.parent_node().unwrap().as_element().unwrap() - } else { - self.node.as_element().unwrap() - }; - - let damage = element.borrow_data().unwrap().damage(); - damage + unsafe fn unsafe_get(self) -> Self::ConcreteNode { + self.node } fn can_be_fragmented(&self) -> bool { diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index 6ee40a343c1..36d13750180 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -151,6 +151,7 @@ impl Iterator for TreeIterator /// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout /// node does not allow any parents or siblings of nodes to be accessed, to avoid races. pub trait ThreadSafeLayoutNode: Clone + Copy + Debug + GetLayoutData + NodeInfo + PartialEq + Sized { + type ConcreteNode: LayoutNode; type ConcreteThreadSafeLayoutElement: ThreadSafeLayoutElement + ::selectors::Element; @@ -237,14 +238,20 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + Debug + GetLayoutData + NodeInfo fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool; - fn restyle_damage(self) -> RestyleDamage; - /// Returns true if this node contributes content. This is used in the implementation of /// `empty_cells` per CSS 2.1 ยง 17.6.1.1. fn is_content(&self) -> bool { self.type_id().is_some() } + /// Returns access to the underlying LayoutNode. This is breaks the abstraction + /// barrier of ThreadSafeLayout wrapper layer, and can lead to races if not used + /// carefully. + /// + /// We need this because the implementation of some methods need to access the layout + /// data flags, and we have this annoying trait separation between script and layout :-( + unsafe fn unsafe_get(self) -> Self::ConcreteNode; + fn can_be_fragmented(&self) -> bool; fn node_text_content(&self) -> String;