diff options
-rw-r--r-- | components/gfx/display_list/mod.rs | 21 | ||||
-rw-r--r-- | components/layout/construct.rs | 33 | ||||
-rw-r--r-- | components/layout/css/matching.rs | 179 | ||||
-rw-r--r-- | components/layout/data.rs | 35 | ||||
-rw-r--r-- | components/layout/incremental.rs | 1 | ||||
-rw-r--r-- | components/layout/layout_task.rs | 33 | ||||
-rw-r--r-- | components/layout/lib.rs | 3 | ||||
-rw-r--r-- | components/layout/parallel.rs | 70 | ||||
-rw-r--r-- | components/layout/query.rs | 6 | ||||
-rw-r--r-- | components/layout/traversal.rs | 10 | ||||
-rw-r--r-- | components/layout/wrapper.rs | 490 | ||||
-rw-r--r-- | components/script/dom/node.rs | 111 | ||||
-rw-r--r-- | components/script/layout_interface.rs | 4 | ||||
-rw-r--r-- | components/style/data.rs | 48 | ||||
-rw-r--r-- | components/style/dom.rs | 274 | ||||
-rw-r--r-- | components/style/lib.rs | 4 | ||||
-rw-r--r-- | components/style/node.rs | 19 | ||||
-rw-r--r-- | components/style/selector_matching.rs | 6 | ||||
-rw-r--r-- | components/util/mem.rs | 2 | ||||
-rw-r--r-- | tests/unit/script/size_of.rs | 14 |
20 files changed, 657 insertions, 706 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index bdb3e2e2d41..3a264d10147 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -22,7 +22,6 @@ use euclid::approxeq::ApproxEq; use euclid::num::Zero; use euclid::{Matrix2D, Matrix4, Point2D, Rect, SideOffsets2D, Size2D}; use gfx_traits::{color, LayerId, LayerKind, ScrollPolicy}; -use libc::uintptr_t; use msg::constellation_msg::PipelineId; use net_traits::image::base::Image; use paint_context::PaintContext; @@ -49,6 +48,8 @@ use util::opts; use util::print_tree::PrintTree; use util::range::Range; +pub use style::dom::OpaqueNode; + // It seems cleaner to have layout code not mention Azure directly, so let's just reexport this for // layout to use. pub use azure::azure_hl::GradientStop; @@ -59,24 +60,6 @@ pub mod optimizer; /// items that involve a blur. This ensures that the display item boundaries include all the ink. pub static BLUR_INFLATION_FACTOR: i32 = 3; -/// An opaque handle to a node. The only safe operation that can be performed on this node is to -/// compare it to another opaque handle or to another node. -/// -/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout -/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for -/// locality reasons. Using `OpaqueNode` enforces this invariant. -#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf, Hash, Eq, Deserialize, Serialize)] -pub struct OpaqueNode(pub uintptr_t); - -impl OpaqueNode { - /// Returns the address of this node, for debugging purposes. - #[inline] - pub fn id(&self) -> uintptr_t { - let OpaqueNode(pointer) = *self; - pointer - } -} - /// LayerInfo is used to store PaintLayer metadata during DisplayList construction. /// It is also used for tracking LayerIds when creating layers to preserve ordering when /// layered DisplayItems should render underneath unlayered DisplayItems. diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 49cc3567d57..6e6ea30332c 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -15,7 +15,7 @@ use block::BlockFlow; use context::LayoutContext; -use data::{HAS_NEWLY_CONSTRUCTED_FLOW, LayoutDataWrapper}; +use data::{HAS_NEWLY_CONSTRUCTED_FLOW, PrivateLayoutData}; use flex::FlexFlow; use floats::FloatKind; use flow::{MutableFlowUtils, MutableOwnedFlowUtils}; @@ -58,7 +58,7 @@ use traversal::PostorderNodeMutTraversal; use url::Url; use util::linked_list; use util::opts; -use wrapper::{PseudoElementType, TextContent, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; +use wrapper::{LayoutNode, PseudoElementType, TextContent, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; /// The results of flow construction for a DOM node. #[derive(Clone)] @@ -1326,10 +1326,9 @@ impl<'a, 'ln, ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>> let mut style = node.style().clone(); - let mut layout_data_ref = node.mutate_layout_data(); - let layout_data = layout_data_ref.as_mut().expect("no layout data"); - let damage = layout_data.data.restyle_damage; - match *node.construction_result_mut(layout_data) { + let mut data = node.mutate_layout_data().unwrap(); + let damage = data.restyle_damage; + match *node.construction_result_mut(&mut *data) { ConstructionResult::None => true, ConstructionResult::Flow(ref mut flow, _) => { // The node's flow is of the same type and has the same set of children and can @@ -1580,7 +1579,7 @@ trait NodeUtils { /// Returns true if this node doesn't render its kids and false otherwise. fn is_replaced_content(&self) -> bool; - fn construction_result_mut(self, layout_data: &mut LayoutDataWrapper) -> &mut ConstructionResult; + fn construction_result_mut(self, layout_data: &mut PrivateLayoutData) -> &mut ConstructionResult; /// Sets the construction result of a flow. fn set_flow_construction_result(self, result: ConstructionResult); @@ -1611,30 +1610,26 @@ impl<'ln, ConcreteThreadSafeLayoutNode> NodeUtils for ConcreteThreadSafeLayoutNo } } - fn construction_result_mut(self, layout_data: &mut LayoutDataWrapper) -> &mut ConstructionResult { + fn construction_result_mut(self, data: &mut PrivateLayoutData) -> &mut ConstructionResult { match self.get_pseudo_element_type() { - PseudoElementType::Before(_) => &mut layout_data.data.before_flow_construction_result, - PseudoElementType::After (_) => &mut layout_data.data.after_flow_construction_result, - PseudoElementType::Normal => &mut layout_data.data.flow_construction_result, + PseudoElementType::Before(_) => &mut data.before_flow_construction_result, + PseudoElementType::After (_) => &mut data.after_flow_construction_result, + PseudoElementType::Normal => &mut data.flow_construction_result, } } #[inline(always)] fn set_flow_construction_result(self, result: ConstructionResult) { - let mut layout_data_ref = self.mutate_layout_data(); - let layout_data = layout_data_ref.as_mut().expect("no layout data"); - - let dst = self.construction_result_mut(layout_data); + let mut layout_data = self.mutate_layout_data().unwrap(); + let dst = self.construction_result_mut(&mut *layout_data); *dst = result; } #[inline(always)] fn swap_out_construction_result(self) -> ConstructionResult { - let mut layout_data_ref = self.mutate_layout_data(); - let layout_data = layout_data_ref.as_mut().expect("no layout data"); - - self.construction_result_mut(layout_data).swap_out() + let mut layout_data = self.mutate_layout_data().unwrap(); + self.construction_result_mut(&mut *layout_data).swap_out() } } diff --git a/components/layout/css/matching.rs b/components/layout/css/matching.rs index ae136aea5ff..a9c07848309 100644 --- a/components/layout/css/matching.rs +++ b/components/layout/css/matching.rs @@ -8,7 +8,7 @@ use animation; use context::SharedLayoutContext; -use data::LayoutDataWrapper; +use data::PrivateLayoutData; use incremental::{self, RestyleDamage}; use msg::ParseErrorReporter; use script::layout_interface::Animation; @@ -20,18 +20,19 @@ use selectors::{Element}; use smallvec::SmallVec; use std::borrow::ToOwned; use std::hash::{Hash, Hasher}; +use std::mem::transmute; use std::slice::Iter; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use string_cache::{Atom, Namespace}; -use style::node::TElementAttributes; +use style::data::PrivateStyleData; +use style::dom::{TElement, TNode}; use style::properties::{ComputedValues, PropertyDeclaration, cascade}; use style::selector_matching::{DeclarationBlock, Stylist}; use util::arc_ptr_eq; use util::cache::{LRUCache, SimpleHashCache}; use util::opts; use util::vec::ForgetfulSink; -use wrapper::{LayoutElement, LayoutNode}; pub struct ApplicableDeclarations { pub normal: SmallVec<[DeclarationBlock; 16]>, @@ -160,7 +161,7 @@ pub struct StyleSharingCandidateCache { cache: LRUCache<StyleSharingCandidate, ()>, } -fn create_common_style_affecting_attributes_from_element<'le, E: LayoutElement<'le>>(element: &E) +fn create_common_style_affecting_attributes_from_element<'le, E: TElement<'le>>(element: &E) -> CommonStyleAffectingAttributes { let mut flags = CommonStyleAffectingAttributes::empty(); for attribute_info in &common_style_affecting_attributes() { @@ -211,17 +212,17 @@ impl StyleSharingCandidate { /// Attempts to create a style sharing candidate from this node. Returns /// the style sharing candidate or `None` if this node is ineligible for /// style sharing. - fn new<'le, E: LayoutElement<'le>>(element: &E) -> Option<StyleSharingCandidate> { + fn new<'le, E: TElement<'le>>(element: &E) -> Option<StyleSharingCandidate> { let parent_element = match element.parent_element() { None => return None, Some(parent_element) => parent_element, }; let style = unsafe { - match *element.as_node().borrow_layout_data_unchecked() { + match element.as_node().borrow_data_unchecked() { None => return None, - Some(ref layout_data_ref) => { - match layout_data_ref.shared_data.style { + Some(data_ref) => { + match (*data_ref).style { None => return None, Some(ref data) => (*data).clone(), } @@ -229,10 +230,10 @@ impl StyleSharingCandidate { } }; let parent_style = unsafe { - match *parent_element.as_node().borrow_layout_data_unchecked() { + match parent_element.as_node().borrow_data_unchecked() { None => return None, - Some(ref parent_layout_data_ref) => { - match parent_layout_data_ref.shared_data.style { + Some(parent_data_ref) => { + match (*parent_data_ref).style { None => return None, Some(ref data) => (*data).clone(), } @@ -257,7 +258,7 @@ impl StyleSharingCandidate { }) } - fn can_share_style_with<'a, E: LayoutElement<'a>>(&self, element: &E) -> bool { + fn can_share_style_with<'a, E: TElement<'a>>(&self, element: &E) -> bool { if *element.get_local_name() != self.local_name { return false } @@ -342,7 +343,7 @@ impl StyleSharingCandidateCache { self.cache.iter() } - pub fn insert_if_possible<'le, E: LayoutElement<'le>>(&mut self, element: &E) { + pub fn insert_if_possible<'le, E: TElement<'le>>(&mut self, element: &E) { match StyleSharingCandidate::new(element) { None => {} Some(candidate) => self.cache.insert(candidate, ()) @@ -363,7 +364,7 @@ pub enum StyleSharingResult { StyleWasShared(usize, RestyleDamage), } -pub trait ElementMatchMethods<'le, ConcreteLayoutElement: LayoutElement<'le>> { +pub trait ElementMatchMethods<'le, ConcreteElement: TElement<'le>> { fn match_element(&self, stylist: &Stylist, parent_bf: Option<&BloomFilter>, @@ -376,11 +377,11 @@ pub trait ElementMatchMethods<'le, ConcreteLayoutElement: LayoutElement<'le>> { unsafe fn share_style_if_possible(&self, style_sharing_candidate_cache: &mut StyleSharingCandidateCache, - parent: Option<ConcreteLayoutElement::ConcreteLayoutNode>) + parent: Option<ConcreteElement::ConcreteNode>) -> StyleSharingResult; } -pub trait MatchMethods<'ln, ConcreteLayoutNode: LayoutNode<'ln>> { +pub trait MatchMethods<'ln, ConcreteNode: TNode<'ln>> { /// Inserts and removes the matching `Descendant` selectors from a bloom /// filter. This is used to speed up CSS selector matching to remove /// unnecessary tree climbs for `Descendant` queries. @@ -396,7 +397,7 @@ pub trait MatchMethods<'ln, ConcreteLayoutNode: LayoutNode<'ln>> { unsafe fn cascade_node(&self, layout_context: &SharedLayoutContext, - parent: Option<ConcreteLayoutNode>, + parent: Option<ConcreteNode>, applicable_declarations: &ApplicableDeclarations, applicable_declarations_cache: &mut ApplicableDeclarationsCache, new_animations_sender: &Mutex<Sender<Animation>>); @@ -420,15 +421,15 @@ trait PrivateMatchMethods { -> bool; } -trait PrivateElementMatchMethods<'le, ConcreteLayoutElement: LayoutElement<'le>> { +trait PrivateElementMatchMethods<'le, ConcreteElement: TElement<'le>> { fn share_style_with_candidate_if_possible(&self, - parent_node: Option<ConcreteLayoutElement::ConcreteLayoutNode>, + parent_node: Option<ConcreteElement::ConcreteNode>, candidate: &StyleSharingCandidate) -> Option<Arc<ComputedValues>>; } -impl<'ln, ConcreteLayoutNode> PrivateMatchMethods for ConcreteLayoutNode - where ConcreteLayoutNode: LayoutNode<'ln> { +impl<'ln, ConcreteNode> PrivateMatchMethods for ConcreteNode + where ConcreteNode: TNode<'ln> { fn cascade_node_pseudo_element(&self, layout_context: &SharedLayoutContext, parent_style: Option<&Arc<ComputedValues>>, @@ -547,11 +548,11 @@ impl<'ln, ConcreteLayoutNode> PrivateMatchMethods for ConcreteLayoutNode } } -impl<'le, ConcreteLayoutElement> PrivateElementMatchMethods<'le, ConcreteLayoutElement> - for ConcreteLayoutElement - where ConcreteLayoutElement: LayoutElement<'le> { +impl<'le, ConcreteElement> PrivateElementMatchMethods<'le, ConcreteElement> + for ConcreteElement + where ConcreteElement: TElement<'le> { fn share_style_with_candidate_if_possible(&self, - parent_node: Option<ConcreteLayoutElement::ConcreteLayoutNode>, + parent_node: Option<ConcreteElement::ConcreteNode>, candidate: &StyleSharingCandidate) -> Option<Arc<ComputedValues>> { let parent_node = match parent_node { @@ -559,13 +560,13 @@ impl<'le, ConcreteLayoutElement> PrivateElementMatchMethods<'le, ConcreteLayoutE Some(_) | None => return None, }; - let parent_layout_data: &Option<LayoutDataWrapper> = unsafe { - &*parent_node.borrow_layout_data_unchecked() + let parent_data: Option<&PrivateStyleData> = unsafe { + parent_node.borrow_data_unchecked().map(|d| &*d) }; - match *parent_layout_data { - Some(ref parent_layout_data_ref) => { + match parent_data { + Some(parent_data_ref) => { // Check parent style. - let parent_style = parent_layout_data_ref.shared_data.style.as_ref().unwrap(); + let parent_style = (*parent_data_ref).style.as_ref().unwrap(); if !arc_ptr_eq(parent_style, &candidate.parent_style) { return None } @@ -584,9 +585,9 @@ impl<'le, ConcreteLayoutElement> PrivateElementMatchMethods<'le, ConcreteLayoutE } } -impl<'le, ConcreteLayoutElement> ElementMatchMethods<'le, ConcreteLayoutElement> - for ConcreteLayoutElement - where ConcreteLayoutElement: LayoutElement<'le> { +impl<'le, ConcreteElement> ElementMatchMethods<'le, ConcreteElement> + for ConcreteElement + where ConcreteElement: TElement<'le> { fn match_element(&self, stylist: &Stylist, parent_bf: Option<&BloomFilter>, @@ -619,7 +620,7 @@ impl<'le, ConcreteLayoutElement> ElementMatchMethods<'le, ConcreteLayoutElement> unsafe fn share_style_if_possible(&self, style_sharing_candidate_cache: &mut StyleSharingCandidateCache, - parent: Option<ConcreteLayoutElement::ConcreteLayoutNode>) + parent: Option<ConcreteElement::ConcreteNode>) -> StyleSharingResult { if opts::get().disable_share_style_cache { return StyleSharingResult::CannotShare @@ -637,9 +638,7 @@ impl<'le, ConcreteLayoutElement> ElementMatchMethods<'le, ConcreteLayoutElement> Some(shared_style) => { // Yay, cache hit. Share the style. let node = self.as_node(); - let mut layout_data_ref = node.mutate_layout_data(); - let shared_data = &mut layout_data_ref.as_mut().unwrap().shared_data; - let style = &mut shared_data.style; + let style = &mut node.mutate_data().unwrap().style; let damage = incremental::compute_damage(style, &*shared_style); *style = Some(shared_style); return StyleSharingResult::StyleWasShared(i, damage) @@ -652,9 +651,9 @@ impl<'le, ConcreteLayoutElement> ElementMatchMethods<'le, ConcreteLayoutElement> } } -impl<'ln, ConcreteLayoutNode> MatchMethods<'ln, ConcreteLayoutNode> - for ConcreteLayoutNode - where ConcreteLayoutNode: LayoutNode<'ln> { +impl<'ln, ConcreteNode> MatchMethods<'ln, ConcreteNode> + for ConcreteNode + where ConcreteNode: TNode<'ln> { // The below two functions are copy+paste because I can't figure out how to // write a function which takes a generic function. I don't think it can // be done. @@ -696,7 +695,7 @@ impl<'ln, ConcreteLayoutNode> MatchMethods<'ln, ConcreteLayoutNode> unsafe fn cascade_node(&self, layout_context: &SharedLayoutContext, - parent: Option<ConcreteLayoutNode>, + parent: Option<ConcreteNode>, applicable_declarations: &ApplicableDeclarations, applicable_declarations_cache: &mut ApplicableDeclarationsCache, new_animations_sender: &Mutex<Sender<Animation>>) { @@ -708,63 +707,57 @@ impl<'ln, ConcreteLayoutNode> MatchMethods<'ln, ConcreteLayoutNode> let parent_style = match parent { None => None, Some(parent_node) => { - let parent_layout_data_ref = parent_node.borrow_layout_data_unchecked(); - let parent_layout_data = (&*parent_layout_data_ref).as_ref() - .expect("no parent data!?"); - let parent_style = parent_layout_data.shared_data - .style - .as_ref() - .expect("parent hasn't been styled yet!"); + let parent_style = (*parent_node.borrow_data_unchecked().unwrap()).style.as_ref().unwrap(); Some(parent_style) } }; - let mut layout_data_ref = self.mutate_layout_data(); - match *layout_data_ref { - None => panic!("no layout data"), - Some(ref mut layout_data) => { - if self.is_text_node() { - // Text nodes get a copy of the parent style. This ensures - // that during fragment construction any non-inherited - // CSS properties (such as vertical-align) are correctly - // set on the fragment(s). - let cloned_parent_style = parent_style.unwrap().clone(); - layout_data.shared_data.style = Some(cloned_parent_style); - } else { - let mut damage = self.cascade_node_pseudo_element( - layout_context, - parent_style, - &applicable_declarations.normal, - &mut layout_data.shared_data.style, - applicable_declarations_cache, - new_animations_sender, - applicable_declarations.normal_shareable, - true); - if !applicable_declarations.before.is_empty() { - damage = damage | self.cascade_node_pseudo_element( - layout_context, - Some(layout_data.shared_data.style.as_ref().unwrap()), - &*applicable_declarations.before, - &mut layout_data.data.before_style, - applicable_declarations_cache, - new_animations_sender, - false, - false); - } - if !applicable_declarations.after.is_empty() { - damage = damage | self.cascade_node_pseudo_element( - layout_context, - Some(layout_data.shared_data.style.as_ref().unwrap()), - &*applicable_declarations.after, - &mut layout_data.data.after_style, - applicable_declarations_cache, - new_animations_sender, - false, - false); - } - layout_data.data.restyle_damage = damage; - } + let mut data_ref = self.mutate_data().unwrap(); + let mut data = &mut *data_ref; + if self.is_text_node() { + // Text nodes get a copy of the parent style. This ensures + // that during fragment construction any non-inherited + // CSS properties (such as vertical-align) are correctly + // set on the fragment(s). + let cloned_parent_style = parent_style.unwrap().clone(); + data.style = Some(cloned_parent_style); + } else { + let mut damage = self.cascade_node_pseudo_element( + layout_context, + parent_style, + &applicable_declarations.normal, + &mut data.style, + applicable_declarations_cache, + new_animations_sender, + applicable_declarations.normal_shareable, + true); + if !applicable_declarations.before.is_empty() { + damage = damage | self.cascade_node_pseudo_element( + layout_context, + Some(data.style.as_ref().unwrap()), + &*applicable_declarations.before, + &mut data.before_style, + applicable_declarations_cache, + new_animations_sender, + false, + false); } + if !applicable_declarations.after.is_empty() { + damage = damage | self.cascade_node_pseudo_element( + layout_context, + Some(data.style.as_ref().unwrap()), + &*applicable_declarations.after, + &mut data.after_style, + applicable_declarations_cache, + new_animations_sender, + false, + false); + } + + // FIXME(bholley): This is the only dependency in this file on non-style + // stuff. + let layout_data: &mut PrivateLayoutData = transmute(data); + layout_data.restyle_damage = damage; } } } diff --git a/components/layout/data.rs b/components/layout/data.rs index 5534773834a..459743cd689 100644 --- a/components/layout/data.rs +++ b/components/layout/data.rs @@ -4,18 +4,15 @@ use construct::ConstructionResult; use incremental::RestyleDamage; -use parallel::DomParallelInfo; -use script::dom::node::SharedLayoutData; -use std::sync::Arc; -use style::properties::ComputedValues; +use style::data::PrivateStyleData; /// Data that layout associates with a node. pub struct PrivateLayoutData { - /// The results of CSS styling for this node's `before` pseudo-element, if any. - pub before_style: Option<Arc<ComputedValues>>, - - /// The results of CSS styling for this node's `after` pseudo-element, if any. - pub after_style: Option<Arc<ComputedValues>>, + /// Data that the style system associates with a node. When the + /// style system is being used standalone, this is all that hangs + /// off the node. This must be first to permit the various + /// transmuations between PrivateStyleData PrivateLayoutData. + pub style_data: PrivateStyleData, /// Description of how to account for recent style changes. pub restyle_damage: RestyleDamage, @@ -28,9 +25,6 @@ pub struct PrivateLayoutData { pub after_flow_construction_result: ConstructionResult, - /// Information needed during parallel traversals. - pub parallel: DomParallelInfo, - /// Various flags. pub flags: LayoutDataFlags, } @@ -39,13 +33,11 @@ impl PrivateLayoutData { /// Creates new layout data. pub fn new() -> PrivateLayoutData { PrivateLayoutData { - before_style: None, - after_style: None, + style_data: PrivateStyleData::new(), restyle_damage: RestyleDamage::empty(), flow_construction_result: ConstructionResult::None, before_flow_construction_result: ConstructionResult::None, after_flow_construction_result: ConstructionResult::None, - parallel: DomParallelInfo::new(), flags: LayoutDataFlags::empty(), } } @@ -57,16 +49,3 @@ bitflags! { const HAS_NEWLY_CONSTRUCTED_FLOW = 0x01 } } - -pub struct LayoutDataWrapper { - pub shared_data: SharedLayoutData, - pub data: Box<PrivateLayoutData>, -} - -#[allow(dead_code, unsafe_code)] -fn static_assertion(x: Option<LayoutDataWrapper>) { - unsafe { - let _: Option<::script::dom::node::LayoutData> = - ::std::intrinsics::transmute(x); - } -} diff --git a/components/layout/incremental.rs b/components/layout/incremental.rs index de89c187120..d69610daa12 100644 --- a/components/layout/incremental.rs +++ b/components/layout/incremental.rs @@ -47,6 +47,7 @@ bitflags! { } } + impl RestyleDamage { /// Supposing a flow has the given `position` property and this damage, returns the damage that /// we should add to the *parent* of this flow. diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 3063690778d..7cec9ed2948 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -13,7 +13,6 @@ use azure::azure::AzColor; use canvas_traits::CanvasMsg; use construct::ConstructionResult; use context::{SharedLayoutContext, StylistWrapper, heap_size_of_local_context}; -use data::LayoutDataWrapper; use display_list_builder::ToGfxColor; use euclid::Matrix4; use euclid::point::Point2D; @@ -45,7 +44,7 @@ use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; use profile_traits::time::{self, TimerMetadata, profile}; use query::{LayoutRPCImpl, process_content_box_request, process_content_boxes_request}; use query::{process_node_geometry_request, process_offset_parent_query, process_resolved_style_request}; -use script::dom::node::LayoutData; +use script::dom::node::OpaqueStyleAndLayoutData; use script::layout_interface::Animation; use script::layout_interface::{LayoutRPC, OffsetParentResponse}; use script::layout_interface::{Msg, NewLayoutTaskInfo, Reflow, ReflowGoal, ReflowQueryType}; @@ -65,6 +64,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::mpsc::{channel, Sender, Receiver}; use std::sync::{Arc, Mutex, MutexGuard, RwLock}; use style::computed_values::{filter, mix_blend_mode}; +use style::dom::{TDocument, TElement, TNode}; use style::media_queries::{Device, MediaType}; use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS}; use style::stylesheets::{CSSRuleIteratorExt, Stylesheet}; @@ -77,8 +77,7 @@ use util::opts; use util::task; use util::task_state; use util::workqueue::WorkQueue; -use wrapper::{LayoutDocument, LayoutElement, LayoutNode}; -use wrapper::{ServoLayoutNode, ThreadSafeLayoutNode}; +use wrapper::{LayoutNode, NonOpaqueStyleAndLayoutData, ServoLayoutNode, ThreadSafeLayoutNode}; /// The number of screens of data we're allowed to generate display lists for in each direction. pub const DISPLAY_PORT_SIZE_FACTOR: i32 = 8; @@ -607,9 +606,9 @@ impl LayoutTask { Msg::SetVisibleRects(new_visible_rects) => { self.set_visible_rects(new_visible_rects, possibly_locked_rw_data); } - Msg::ReapLayoutData(dead_layout_data) => { + Msg::ReapStyleAndLayoutData(dead_data) => { unsafe { - self.handle_reap_layout_data(dead_layout_data) + self.handle_reap_style_and_layout_data(dead_data) } } Msg::CollectReports(reports_chan) => { @@ -707,9 +706,9 @@ impl LayoutTask { response_chan.send(()).unwrap(); loop { match self.port.recv().unwrap() { - Msg::ReapLayoutData(dead_layout_data) => { + Msg::ReapStyleAndLayoutData(dead_data) => { unsafe { - self.handle_reap_layout_data(dead_layout_data) + self.handle_reap_style_and_layout_data(dead_data) } } Msg::ExitNow => { @@ -765,14 +764,11 @@ impl LayoutTask { } fn try_get_layout_root<'ln, N: LayoutNode<'ln>>(&self, node: N) -> Option<FlowRef> { - let mut layout_data_ref = node.mutate_layout_data(); - let layout_data = - match layout_data_ref.as_mut() { - None => return None, - Some(layout_data) => layout_data, - }; - - let result = layout_data.data.flow_construction_result.swap_out(); + let mut data = match node.mutate_layout_data() { + Some(x) => x, + None => return None, + }; + let result = data.flow_construction_result.swap_out(); let mut flow = match result { ConstructionResult::Flow(mut flow, abs_descendants) => { @@ -1312,8 +1308,9 @@ impl LayoutTask { /// Handles a message to destroy layout data. Layout data must be destroyed on *this* task /// because the struct type is transmuted to a different type on the script side. - unsafe fn handle_reap_layout_data(&self, layout_data: LayoutData) { - let _: LayoutDataWrapper = transmute(layout_data); + unsafe fn handle_reap_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData) { + let non_opaque: NonOpaqueStyleAndLayoutData = transmute(data.ptr); + let _ = Box::from_raw(non_opaque); } /// Returns profiling information which is passed to the time profiler. diff --git a/components/layout/lib.rs b/components/layout/lib.rs index fb28d47adac..e1d2f77f32b 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -2,11 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#![feature(as_unsafe_cell)] #![feature(box_syntax)] #![feature(cell_extras)] #![feature(custom_derive)] #![feature(hashmap_hasher)] #![feature(mpsc_select)] +#![feature(nonzero)] #![feature(plugin)] #![feature(raw)] #![feature(step_by)] @@ -22,6 +24,7 @@ extern crate azure; #[macro_use] extern crate bitflags; extern crate canvas_traits; +extern crate core; extern crate cssparser; extern crate encoding; extern crate euclid; diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 4023ed9fbf3..35c9b7f7955 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -15,6 +15,7 @@ use gfx::display_list::OpaqueNode; use profile_traits::time::{self, TimerMetadata, profile}; use std::mem; use std::sync::atomic::{AtomicIsize, Ordering}; +use style::dom::UnsafeNode; use traversal::PostorderNodeMutTraversal; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes, BubbleISizes}; use traversal::{BuildDisplayList, ComputeAbsolutePositions}; @@ -22,17 +23,17 @@ use traversal::{ConstructFlows, RecalcStyleForNode}; use traversal::{PostorderDomTraversal, PreorderDomTraversal}; use util::opts; use util::workqueue::{WorkQueue, WorkUnit, WorkerProxy}; -use wrapper::{LayoutNode, UnsafeLayoutNode}; +use wrapper::LayoutNode; const CHUNK_SIZE: usize = 64; pub struct WorkQueueData(usize, usize); #[allow(dead_code)] -fn static_assertion(node: UnsafeLayoutNode) { +fn static_assertion(node: UnsafeNode) { unsafe { let _: UnsafeFlow = ::std::intrinsics::transmute(node); - let _: UnsafeLayoutNodeList = ::std::intrinsics::transmute(node); + let _: UnsafeNodeList = ::std::intrinsics::transmute(node); } } @@ -55,31 +56,17 @@ pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow { } } -/// Information that we need stored in each DOM node. -pub struct DomParallelInfo { - /// The number of children that still need work done. - pub children_count: AtomicIsize, -} - -impl DomParallelInfo { - pub fn new() -> DomParallelInfo { - DomParallelInfo { - children_count: AtomicIsize::new(0), - } - } -} - -pub type UnsafeLayoutNodeList = (Box<Vec<UnsafeLayoutNode>>, OpaqueNode); +pub type UnsafeNodeList = (Box<Vec<UnsafeNode>>, OpaqueNode); -pub type UnsafeFlowList = (Box<Vec<UnsafeLayoutNode>>, usize); +pub type UnsafeFlowList = (Box<Vec<UnsafeNode>>, usize); pub type ChunkedDomTraversalFunction = - extern "Rust" fn(UnsafeLayoutNodeList, - &mut WorkerProxy<SharedLayoutContext, UnsafeLayoutNodeList>); + extern "Rust" fn(UnsafeNodeList, + &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>); pub type DomTraversalFunction = - extern "Rust" fn(OpaqueNode, UnsafeLayoutNode, - &mut WorkerProxy<SharedLayoutContext, UnsafeLayoutNodeList>); + extern "Rust" fn(OpaqueNode, UnsafeNode, + &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>); pub type ChunkedFlowTraversalFunction = extern "Rust" fn(UnsafeFlowList, &mut WorkerProxy<SharedLayoutContext, UnsafeFlowList>); @@ -91,14 +78,14 @@ pub trait ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode> : PreorderDomTraversal<'ln, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'ln> { fn run_parallel(&self, - nodes: UnsafeLayoutNodeList, - proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeLayoutNodeList>); + nodes: UnsafeNodeList, + proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>); #[inline(always)] fn run_parallel_helper( &self, - unsafe_nodes: UnsafeLayoutNodeList, - proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeLayoutNodeList>, + unsafe_nodes: UnsafeNodeList, + proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>, top_down_func: ChunkedDomTraversalFunction, bottom_up_func: DomTraversalFunction) { let mut discovered_child_nodes = Vec::new(); @@ -113,10 +100,9 @@ pub trait ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode> // Reset the count of children. { - let mut layout_data_ref = node.mutate_layout_data(); - let layout_data = layout_data_ref.as_mut().expect("no layout data"); - layout_data.data.parallel.children_count.store(child_count as isize, - Ordering::Relaxed); + let data = node.mutate_data().unwrap(); + data.parallel.children_count.store(child_count as isize, + Ordering::Relaxed); } // Possibly enqueue the children. @@ -156,7 +142,7 @@ trait ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode> /// /// The only communication between siblings is that they both /// fetch-and-subtract the parent's children count. - fn run_parallel(&self, unsafe_node: UnsafeLayoutNode) { + fn run_parallel(&self, unsafe_node: UnsafeNode) { // Get a real layout node. let mut node = unsafe { ConcreteLayoutNode::from_unsafe(&unsafe_node) }; loop { @@ -168,13 +154,11 @@ trait ParallelPostorderDomTraversal<'ln, ConcreteLayoutNode> Some(parent) => parent, }; - let parent_layout_data = unsafe { - &*parent.borrow_layout_data_unchecked() + let parent_data = unsafe { + &*parent.borrow_data_unchecked().unwrap() }; - let parent_layout_data = parent_layout_data.as_ref().expect("no layout data"); - if parent_layout_data - .data + if parent_data .parallel .children_count .fetch_sub(1, Ordering::Relaxed) != 1 { @@ -360,8 +344,8 @@ impl<'a, 'ln, ConcreteLayoutNode> ParallelPreorderDomTraversal<'ln, ConcreteLayo for RecalcStyleForNode<'a> where ConcreteLayoutNode: LayoutNode<'ln> { fn run_parallel(&self, - unsafe_nodes: UnsafeLayoutNodeList, - proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeLayoutNodeList>) { + unsafe_nodes: UnsafeNodeList, + proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>) { // Not exactly sure why we need UFCS here, but we seem to. <RecalcStyleForNode<'a> as ParallelPreorderDomTraversal<'ln, ConcreteLayoutNode>> ::run_parallel_helper(self, unsafe_nodes, proxy, @@ -370,8 +354,8 @@ impl<'a, 'ln, ConcreteLayoutNode> ParallelPreorderDomTraversal<'ln, ConcreteLayo } } -fn recalc_style<'ln, ConcreteLayoutNode>(unsafe_nodes: UnsafeLayoutNodeList, - proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeLayoutNodeList>) +fn recalc_style<'ln, ConcreteLayoutNode>(unsafe_nodes: UnsafeNodeList, + proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>) where ConcreteLayoutNode: LayoutNode<'ln> { let shared_layout_context = proxy.user_data(); let layout_context = LayoutContext::new(shared_layout_context); @@ -388,8 +372,8 @@ fn recalc_style<'ln, ConcreteLayoutNode>(unsafe_nodes: UnsafeLayoutNodeList, fn construct_flows<'ln, ConcreteLayoutNode: LayoutNode<'ln>>( root: OpaqueNode, - unsafe_node: UnsafeLayoutNode, - proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeLayoutNodeList>) { + unsafe_node: UnsafeNode, + proxy: &mut WorkerProxy<SharedLayoutContext, UnsafeNodeList>) { let shared_layout_context = proxy.user_data(); let layout_context = LayoutContext::new(shared_layout_context); let construct_flows_traversal = ConstructFlows { diff --git a/components/layout/query.rs b/components/layout/query.rs index 83f758b0f50..327bb3aa046 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -479,9 +479,9 @@ pub fn process_resolved_style_request<'ln, N: LayoutNode<'ln>>( layout_root: &mut FlowRef, requested_node: N, property: &Atom) -> Option<String> { - let layout_data = layout_node.borrow_layout_data(); - let position = layout_data.as_ref().map(|layout_data| { - match layout_data.data.flow_construction_result { + let maybe_data = layout_node.borrow_layout_data(); + let position = maybe_data.map(|data| { + match (*data).flow_construction_result { ConstructionResult::Flow(ref flow_ref, _) => flow::base(flow_ref.deref()).stacking_relative_position, // TODO(dzbarsky) search parents until we find node with a flow ref. diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index cf3be1b9d94..007b3a3626d 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -15,10 +15,10 @@ use script::layout_interface::ReflowGoal; use selectors::bloom::BloomFilter; use std::cell::RefCell; use std::mem; +use style::dom::UnsafeNode; use util::opts; use util::tid::tid; -use wrapper::{LayoutNode, UnsafeLayoutNode}; -use wrapper::{ThreadSafeLayoutNode}; +use wrapper::{LayoutNode, ThreadSafeLayoutNode}; /// Every time we do another layout, the old bloom filters are invalid. This is /// detected by ticking a generation number every layout. @@ -45,7 +45,7 @@ type Generation = u32; /// will no longer be the for the parent of the node we're currently on. When /// this happens, the task local bloom filter will be thrown away and rebuilt. thread_local!( - static STYLE_BLOOM: RefCell<Option<(Box<BloomFilter>, UnsafeLayoutNode, Generation)>> = RefCell::new(None)); + static STYLE_BLOOM: RefCell<Option<(Box<BloomFilter>, UnsafeNode, Generation)>> = RefCell::new(None)); /// Returns the task local bloom filter. /// @@ -88,7 +88,7 @@ fn take_task_local_bloom_filter<'ln, N>(parent_node: Option<N>, } fn put_task_local_bloom_filter(bf: Box<BloomFilter>, - unsafe_node: &UnsafeLayoutNode, + unsafe_node: &UnsafeNode, layout_context: &LayoutContext) { STYLE_BLOOM.with(move |style_bloom| { assert!(style_bloom.borrow().is_none(), @@ -153,7 +153,7 @@ impl<'a, 'ln, ConcreteLayoutNode> PreorderDomTraversal<'ln, ConcreteLayoutNode> // // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML // parser. - node.initialize_layout_data(); + node.initialize_data(); // Get the parent node. let parent_opt = node.layout_parent_node(self.root); diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index ddb899b9db5..c2092a81e21 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -30,7 +30,8 @@ #![allow(unsafe_code)] -use data::{LayoutDataFlags, LayoutDataWrapper, PrivateLayoutData}; +use core::nonzero::NonZero; +use data::{LayoutDataFlags, PrivateLayoutData}; use gfx::display_list::OpaqueNode; use gfx::text::glyph::CharIndex; use incremental::RestyleDamage; @@ -49,7 +50,7 @@ use script::dom::htmlimageelement::LayoutHTMLImageElementHelpers; use script::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; use script::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers}; use script::dom::node::{HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; -use script::dom::node::{LayoutNodeHelpers, Node, SharedLayoutData}; +use script::dom::node::{LayoutNodeHelpers, Node, OpaqueStyleAndLayoutData}; use script::dom::text::Text; use script::layout_interface::TrustedNodeAddress; use selectors::matching::DeclarationBlock; @@ -57,204 +58,38 @@ use selectors::parser::{AttrSelector, NamespaceConstraint}; use selectors::states::*; use smallvec::VecLike; use std::borrow::ToOwned; -use std::cell::{Ref, RefMut}; +use std::cell::{Ref, RefCell, RefMut}; use std::marker::PhantomData; -use std::mem; +use std::mem::{transmute, transmute_copy}; use std::sync::Arc; use string_cache::{Atom, Namespace}; use style::computed_values::content::ContentItem; use style::computed_values::{content, display}; -use style::node::TElementAttributes; +use style::data::PrivateStyleData; +use style::dom::{TDocument, TElement, TNode, UnsafeNode}; use style::properties::ComputedValues; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock}; -use style::restyle_hints::{ElementSnapshot, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; +use style::restyle_hints::ElementSnapshot; use url::Url; use util::str::{is_whitespace, search_index}; -/// Opaque type stored in type-unsafe work queues for parallel layout. -/// Must be transmutable to and from LayoutNode. -pub type UnsafeLayoutNode = (usize, usize); +pub type NonOpaqueStyleAndLayoutData = *mut RefCell<PrivateLayoutData>; /// A wrapper so that layout can access only the methods that it should have access to. Layout must /// only ever see these and must never see instances of `LayoutJS`. -pub trait LayoutNode<'ln> : Sized + Copy + Clone { +pub trait LayoutNode<'ln> : TNode<'ln> { type ConcreteThreadSafeLayoutNode: ThreadSafeLayoutNode<'ln>; - type ConcreteLayoutElement: LayoutElement<'ln, ConcreteLayoutNode = Self, - ConcreteLayoutDocument = Self::ConcreteLayoutDocument>; - type ConcreteLayoutDocument: LayoutDocument<'ln, ConcreteLayoutNode = Self, - ConcreteLayoutElement = Self::ConcreteLayoutElement>; - - fn to_unsafe(&self) -> UnsafeLayoutNode; - unsafe fn from_unsafe(&UnsafeLayoutNode) -> Self; - fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode; /// Returns the type ID of this node. fn type_id(&self) -> NodeTypeId; - /// Returns whether this is a text node. It turns out that this is all the style system cares - /// about, and thus obviates the need to compute the full type id, which would be expensive in - /// Gecko. - fn is_text_node(&self) -> bool; - - fn is_element(&self) -> bool; - - fn dump(self); - - fn traverse_preorder(self) -> LayoutTreeIterator<'ln, Self> { - LayoutTreeIterator::new(self) - } - - /// Returns an iterator over this node's children. - fn children(self) -> LayoutNodeChildrenIterator<'ln, Self> { - LayoutNodeChildrenIterator { - current: self.first_child(), - phantom: PhantomData, - } - } - - fn rev_children(self) -> LayoutNodeReverseChildrenIterator<'ln, Self> { - LayoutNodeReverseChildrenIterator { - current: self.last_child(), - phantom: PhantomData, - } - } - - /// Converts self into an `OpaqueNode`. - fn opaque(&self) -> OpaqueNode; - - /// Resets layout data and styles for the node. - /// - /// FIXME(pcwalton): Do this as part of fragment building instead of in a traversal. - fn initialize_layout_data(self); - - /// 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<Self>; - - fn debug_id(self) -> usize; - - fn as_element(&self) -> Option<Self::ConcreteLayoutElement>; - - fn as_document(&self) -> Option<Self::ConcreteLayoutDocument>; - - fn children_count(&self) -> u32; - - fn has_changed(&self) -> bool; - - unsafe fn set_changed(&self, value: bool); - - fn is_dirty(&self) -> bool; - - unsafe fn set_dirty(&self, value: bool); - - fn has_dirty_descendants(&self) -> bool; - - unsafe fn set_dirty_descendants(&self, value: bool); - - fn dirty_self(&self) { - unsafe { - self.set_dirty(true); - self.set_dirty_descendants(true); - } - } - - fn dirty_descendants(&self) { - for ref child in self.children() { - child.dirty_self(); - child.dirty_descendants(); - } - } - - /// Borrows the layout data without checks. - #[inline(always)] - unsafe fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper>; - - /// Borrows the layout data immutably. Fails on a conflicting borrow. - #[inline(always)] - fn borrow_layout_data(&self) -> Ref<Option<LayoutDataWrapper>>; - - /// Borrows the layout data mutably. Fails on a conflicting borrow. - #[inline(always)] - fn mutate_layout_data(&self) -> RefMut<Option<LayoutDataWrapper>>; - - fn parent_node(&self) -> Option<Self>; - - fn first_child(&self) -> Option<Self>; - - fn last_child(&self) -> Option<Self>; - - fn prev_sibling(&self) -> Option<Self>; - - fn next_sibling(&self) -> Option<Self>; -} - -pub trait LayoutDocument<'ld> : Sized + Copy + Clone { - type ConcreteLayoutNode: LayoutNode<'ld, ConcreteLayoutElement = Self::ConcreteLayoutElement, - ConcreteLayoutDocument = Self>; - type ConcreteLayoutElement: LayoutElement<'ld, ConcreteLayoutNode = Self::ConcreteLayoutNode, - ConcreteLayoutDocument = Self>; - - fn as_node(&self) -> Self::ConcreteLayoutNode; - - fn root_node(&self) -> Option<Self::ConcreteLayoutNode>; - - fn drain_modified_elements(&self) -> Vec<(Self::ConcreteLayoutElement, ElementSnapshot)>; -} - -pub trait LayoutElement<'le> : Sized + Copy + Clone + ::selectors::Element + TElementAttributes { - type ConcreteLayoutNode: LayoutNode<'le, ConcreteLayoutElement = Self, - ConcreteLayoutDocument = Self::ConcreteLayoutDocument>; - type ConcreteLayoutDocument: LayoutDocument<'le, ConcreteLayoutNode = Self::ConcreteLayoutNode, - ConcreteLayoutElement = Self>; - - fn as_node(&self) -> Self::ConcreteLayoutNode; - - fn style_attribute(&self) -> &'le Option<PropertyDeclarationBlock>; - - fn get_state(&self) -> ElementState; - - /// Properly marks nodes as dirty in response to restyle hints. - fn note_restyle_hint(&self, mut hint: RestyleHint) { - // Bail early if there's no restyling to do. - if hint.is_empty() { - return; - } - - // 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() { - if parent.has_dirty_descendants() { break } - unsafe { parent.set_dirty_descendants(true); } - curr = parent; - } - - // Process hints. - if hint.contains(RESTYLE_SELF) { - node.dirty_self(); - - // FIXME(bholley, #8438): We currently need to RESTYLE_DESCENDANTS in the - // RESTYLE_SELF case in order to make sure "inherit" style structs propagate - // properly. See the explanation in the github issue. - hint.insert(RESTYLE_DESCENDANTS); - } - if hint.contains(RESTYLE_DESCENDANTS) { - unsafe { node.set_dirty_descendants(true); } - node.dirty_descendants(); - } - 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(); - sib_node.dirty_self(); - sib_node.dirty_descendants(); - next = ::selectors::Element::next_sibling_element(&sib); - } - } - } + /// Similar to borrow_data*, but returns the full PrivateLayoutData rather + /// than only the PrivateStyleData. + unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData>; + fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>>; + fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>>; } #[derive(Copy, Clone)] @@ -294,31 +129,20 @@ impl<'ln> ServoLayoutNode<'ln> { } } -impl<'ln> LayoutNode<'ln> for ServoLayoutNode<'ln> { - type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>; - type ConcreteLayoutElement = ServoLayoutElement<'ln>; - type ConcreteLayoutDocument = ServoLayoutDocument<'ln>; +impl<'ln> TNode<'ln> for ServoLayoutNode<'ln> { + type ConcreteElement = ServoLayoutElement<'ln>; + type ConcreteDocument = ServoLayoutDocument<'ln>; - fn to_unsafe(&self) -> UnsafeLayoutNode { + fn to_unsafe(&self) -> UnsafeNode { unsafe { - let ptr: usize = mem::transmute_copy(self); + let ptr: usize = transmute_copy(self); (ptr, 0) } } - unsafe fn from_unsafe(n: &UnsafeLayoutNode) -> Self { + unsafe fn from_unsafe(n: &UnsafeNode) -> Self { let (node, _) = *n; - mem::transmute(node) - } - - fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode { - ServoThreadSafeLayoutNode::new(self) - } - - fn type_id(&self) -> NodeTypeId { - unsafe { - self.node.type_id_for_layout() - } + transmute(node) } fn is_text_node(&self) -> bool { @@ -339,16 +163,17 @@ impl<'ln> LayoutNode<'ln> for ServoLayoutNode<'ln> { OpaqueNodeMethods::from_jsmanaged(unsafe { self.get_jsmanaged() }) } - fn initialize_layout_data(self) { - let mut layout_data_ref = self.mutate_layout_data(); - match *layout_data_ref { - None => { - *layout_data_ref = Some(LayoutDataWrapper { - shared_data: SharedLayoutData { style: None }, - data: box PrivateLayoutData::new(), - }); + fn initialize_data(self) { + let has_data = unsafe { self.borrow_data_unchecked().is_some() }; + if !has_data { + let ptr: NonOpaqueStyleAndLayoutData = + Box::into_raw(box RefCell::new(PrivateLayoutData::new())); + let opaque = OpaqueStyleAndLayoutData { + ptr: unsafe { NonZero::new(ptr as *mut ()) } + }; + unsafe { + self.node.init_style_and_layout_data(opaque); } - Some(_) => {} } } @@ -400,20 +225,16 @@ impl<'ln> LayoutNode<'ln> for ServoLayoutNode<'ln> { self.node.set_flag(HAS_DIRTY_DESCENDANTS, value) } - unsafe fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper> { - mem::transmute(self.get_jsmanaged().layout_data_unchecked()) + unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData> { + self.borrow_layout_data_unchecked().map(|d| &(*d).style_data as *const PrivateStyleData) } - fn borrow_layout_data(&self) -> Ref<Option<LayoutDataWrapper>> { - unsafe { - mem::transmute(self.get_jsmanaged().layout_data()) - } + fn borrow_data(&self) -> Option<Ref<PrivateStyleData>> { + unsafe { self.borrow_layout_data().map(|d| transmute(d)) } } - fn mutate_layout_data(&self) -> RefMut<Option<LayoutDataWrapper>> { - unsafe { - mem::transmute(self.get_jsmanaged().layout_data_mut()) - } + fn mutate_data(&self) -> Option<RefMut<PrivateStyleData>> { + unsafe { self.mutate_layout_data().map(|d| transmute(d)) } } fn parent_node(&self) -> Option<ServoLayoutNode<'ln>> { @@ -447,6 +268,46 @@ impl<'ln> LayoutNode<'ln> for ServoLayoutNode<'ln> { } } +impl<'ln> LayoutNode<'ln> for ServoLayoutNode<'ln> { + type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>; + + fn to_threadsafe(&self) -> Self::ConcreteThreadSafeLayoutNode { + ServoThreadSafeLayoutNode::new(self) + } + + fn type_id(&self) -> NodeTypeId { + unsafe { + self.node.type_id_for_layout() + } + } + + unsafe fn borrow_layout_data_unchecked(&self) -> Option<*const PrivateLayoutData> { + self.get_jsmanaged().get_style_and_layout_data().map(|opaque| { + let container: NonOpaqueStyleAndLayoutData = transmute(opaque.ptr); + &(*(*container).as_unsafe_cell().get()) as *const PrivateLayoutData + }) + } + + fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>> { + unsafe { + self.get_jsmanaged().get_style_and_layout_data().map(|opaque| { + let container: NonOpaqueStyleAndLayoutData = transmute(opaque.ptr); + (*container).borrow() + }) + } + } + + fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>> { + unsafe { + self.get_jsmanaged().get_style_and_layout_data().map(|opaque| { + let container: NonOpaqueStyleAndLayoutData = transmute(opaque.ptr); + (*container).borrow_mut() + }) + } + } +} + + impl<'ln> ServoLayoutNode<'ln> { fn dump_indent(self, indent: u32) { let mut s = String::new(); @@ -468,11 +329,7 @@ impl<'ln> ServoLayoutNode<'ln> { } pub fn flow_debug_id(self) -> usize { - let layout_data_ref = self.borrow_layout_data(); - match *layout_data_ref { - None => 0, - Some(ref layout_data) => layout_data.data.flow_construction_result.debug_id() - } + self.borrow_layout_data().map_or(0, |d| d.flow_construction_result.debug_id()) } /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to @@ -482,65 +339,6 @@ impl<'ln> ServoLayoutNode<'ln> { } } -pub struct LayoutNodeChildrenIterator<'a, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'a> { - current: Option<ConcreteLayoutNode>, - // Satisfy the compiler about the unused lifetime. - phantom: PhantomData<&'a ()>, -} - -impl<'a, ConcreteLayoutNode> Iterator for LayoutNodeChildrenIterator<'a, ConcreteLayoutNode> - where ConcreteLayoutNode: LayoutNode<'a> { - type Item = ConcreteLayoutNode; - fn next(&mut self) -> Option<ConcreteLayoutNode> { - let node = self.current; - self.current = node.and_then(|node| node.next_sibling()); - node - } -} - -pub struct LayoutNodeReverseChildrenIterator<'a, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'a> { - current: Option<ConcreteLayoutNode>, - // Satisfy the compiler about the unused lifetime. - phantom: PhantomData<&'a ()>, -} - -impl<'a, ConcreteLayoutNode> Iterator for LayoutNodeReverseChildrenIterator<'a, ConcreteLayoutNode> - where ConcreteLayoutNode: LayoutNode<'a> { - type Item = ConcreteLayoutNode; - fn next(&mut self) -> Option<ConcreteLayoutNode> { - let node = self.current; - self.current = node.and_then(|node| node.prev_sibling()); - node - } -} - -pub struct LayoutTreeIterator<'a, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'a> { - stack: Vec<ConcreteLayoutNode>, - // Satisfy the compiler about the unused lifetime. - phantom: PhantomData<&'a ()>, -} - -impl<'a, ConcreteLayoutNode> LayoutTreeIterator<'a, ConcreteLayoutNode> where ConcreteLayoutNode: LayoutNode<'a> { - fn new(root: ConcreteLayoutNode) -> LayoutTreeIterator<'a, ConcreteLayoutNode> { - let mut stack = vec!(); - stack.push(root); - LayoutTreeIterator { - stack: stack, - phantom: PhantomData, - } - } -} - -impl<'a, ConcreteLayoutNode> Iterator for LayoutTreeIterator<'a, ConcreteLayoutNode> - where ConcreteLayoutNode: LayoutNode<'a> { - type Item = ConcreteLayoutNode; - fn next(&mut self) -> Option<ConcreteLayoutNode> { - let ret = self.stack.pop(); - ret.map(|node| self.stack.extend(node.rev_children())); - ret - } -} - // A wrapper around documents that ensures ayout can only ever access safe properties. #[derive(Copy, Clone)] pub struct ServoLayoutDocument<'ld> { @@ -548,9 +346,9 @@ pub struct ServoLayoutDocument<'ld> { chain: PhantomData<&'ld ()>, } -impl<'ld> LayoutDocument<'ld> for ServoLayoutDocument<'ld> { - type ConcreteLayoutNode = ServoLayoutNode<'ld>; - type ConcreteLayoutElement = ServoLayoutElement<'ld>; +impl<'ld> TDocument<'ld> for ServoLayoutDocument<'ld> { + type ConcreteNode = ServoLayoutNode<'ld>; + type ConcreteElement = ServoLayoutElement<'ld>; fn as_node(&self) -> ServoLayoutNode<'ld> { ServoLayoutNode::from_layout_js(self.document.upcast()) @@ -582,9 +380,9 @@ pub struct ServoLayoutElement<'le> { chain: PhantomData<&'le ()>, } -impl<'le> LayoutElement<'le> for ServoLayoutElement<'le> { - type ConcreteLayoutNode = ServoLayoutNode<'le>; - type ConcreteLayoutDocument = ServoLayoutDocument<'le>; +impl<'le> TElement<'le> for ServoLayoutElement<'le> { + type ConcreteNode = ServoLayoutNode<'le>; + type ConcreteDocument = ServoLayoutDocument<'le>; fn as_node(&self) -> ServoLayoutNode<'le> { ServoLayoutNode::from_layout_js(self.element.upcast()) @@ -599,6 +397,28 @@ impl<'le> LayoutElement<'le> for ServoLayoutElement<'le> { fn get_state(&self) -> ElementState { self.element.get_state_for_layout() } + + fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) + where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>> + { + unsafe { + self.element.synthesize_presentational_hints_for_legacy_attributes(hints); + } + } + + #[inline] + fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> { + unsafe { + (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name) + } + } + + #[inline] + fn get_attrs<'a>(&'a self, name: &Atom) -> Vec<&'a str> { + unsafe { + (*self.element.unsafe_get()).get_attr_vals_for_layout(name) + } + } } @@ -783,30 +603,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { } } -impl<'le> TElementAttributes for ServoLayoutElement<'le> { - fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) - where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>> - { - unsafe { - self.element.synthesize_presentational_hints_for_legacy_attributes(hints); - } - } - - #[inline] - fn get_attr<'a>(&'a self, namespace: &Namespace, name: &Atom) -> Option<&'a str> { - unsafe { - (*self.element.unsafe_get()).get_attr_val_for_layout(namespace, name) - } - } - - #[inline] - fn get_attrs<'a>(&'a self, name: &Atom) -> Vec<&'a str> { - unsafe { - (*self.element.unsafe_get()).get_attr_vals_for_layout(name) - } - } -} - #[derive(Copy, PartialEq, Clone)] pub enum PseudoElementType<T> { Normal, @@ -865,18 +661,14 @@ pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized { #[inline] fn get_before_pseudo(&self) -> Option<Self> { - let layout_data_ref = self.borrow_layout_data(); - let node_layout_data_wrapper = layout_data_ref.as_ref().unwrap(); - node_layout_data_wrapper.data.before_style.as_ref().map(|style| { + self.borrow_layout_data().unwrap().style_data.before_style.as_ref().map(|style| { self.with_pseudo(PseudoElementType::Before(style.get_box().display)) }) } #[inline] fn get_after_pseudo(&self) -> Option<Self> { - let layout_data_ref = self.borrow_layout_data(); - let node_layout_data_wrapper = layout_data_ref.as_ref().unwrap(); - node_layout_data_wrapper.data.after_style.as_ref().map(|style| { + self.borrow_layout_data().unwrap().style_data.after_style.as_ref().map(|style| { self.with_pseudo(PseudoElementType::After(style.get_box().display)) }) } @@ -885,24 +677,23 @@ pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized { /// /// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases. #[inline(always)] - fn borrow_layout_data(&self) -> Ref<Option<LayoutDataWrapper>>; + fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>>; /// Borrows the layout data mutably. Fails on a conflicting borrow. /// /// TODO(pcwalton): Make this private. It will let us avoid borrow flag checks in some cases. #[inline(always)] - fn mutate_layout_data(&self) -> RefMut<Option<LayoutDataWrapper>>; + fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>>; /// Returns the style results for the given node. If CSS selector matching /// has not yet been performed, fails. #[inline] fn style(&self) -> Ref<Arc<ComputedValues>> { - Ref::map(self.borrow_layout_data(), |layout_data_ref| { - let layout_data = layout_data_ref.as_ref().expect("no layout data"); + Ref::map(self.borrow_layout_data().unwrap(), |data| { let style = match self.get_pseudo_element_type() { - PseudoElementType::Before(_) => &layout_data.data.before_style, - PseudoElementType::After(_) => &layout_data.data.after_style, - PseudoElementType::Normal => &layout_data.shared_data.style, + PseudoElementType::Before(_) => &data.style_data.before_style, + PseudoElementType::After(_) => &data.style_data.after_style, + PseudoElementType::Normal => &data.style_data.style, }; style.as_ref().unwrap() }) @@ -910,14 +701,12 @@ pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized { /// Removes the style from this node. fn unstyle(self) { - let mut layout_data_ref = self.mutate_layout_data(); - let layout_data = layout_data_ref.as_mut().expect("no layout data"); - + let mut data = self.mutate_layout_data().unwrap(); let style = match self.get_pseudo_element_type() { - PseudoElementType::Before(_) => &mut layout_data.data.before_style, - PseudoElementType::After (_) => &mut layout_data.data.after_style, - PseudoElementType::Normal => &mut layout_data.shared_data.style, + PseudoElementType::Before(_) => &mut data.style_data.before_style, + PseudoElementType::After (_) => &mut data.style_data.after_style, + PseudoElementType::Normal => &mut data.style_data.style, }; *style = None; @@ -928,17 +717,12 @@ pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized { /// Get the description of how to account for recent style changes. /// This is a simple bitfield and fine to copy by value. fn restyle_damage(self) -> RestyleDamage { - let layout_data_ref = self.borrow_layout_data(); - layout_data_ref.as_ref().unwrap().data.restyle_damage + self.borrow_layout_data().unwrap().restyle_damage } /// Set the restyle damage field. fn set_restyle_damage(self, damage: RestyleDamage) { - let mut layout_data_ref = self.mutate_layout_data(); - match *layout_data_ref { - Some(ref mut layout_data) => layout_data.data.restyle_damage = damage, - _ => panic!("no layout data for this node"), - } + self.mutate_layout_data().unwrap().restyle_damage = damage; } /// Returns the layout data flags for this node. @@ -946,20 +730,12 @@ pub trait ThreadSafeLayoutNode<'ln> : Clone + Copy + Sized { /// Adds the given flags to this node. fn insert_flags(self, new_flags: LayoutDataFlags) { - let mut layout_data_ref = self.mutate_layout_data(); - match *layout_data_ref { - Some(ref mut layout_data) => layout_data.data.flags.insert(new_flags), - _ => panic!("no layout data for this node"), - } + self.mutate_layout_data().unwrap().flags.insert(new_flags); } /// Removes the given flags from this node. fn remove_flags(self, flags: LayoutDataFlags) { - let mut layout_data_ref = self.mutate_layout_data(); - match *layout_data_ref { - Some(ref mut layout_data) => layout_data.data.flags.remove(flags), - _ => panic!("no layout data for this node"), - } + self.mutate_layout_data().unwrap().flags.remove(flags); } /// Returns true if this node contributes content. This is used in the implementation of @@ -1049,14 +825,6 @@ impl<'ln> ServoThreadSafeLayoutNode<'ln> { unsafe fn get_jsmanaged(&self) -> &LayoutJS<Node> { self.node.get_jsmanaged() } - - /// Borrows the layout data without checking. - #[inline(always)] - fn borrow_layout_data_unchecked(&self) -> *const Option<LayoutDataWrapper> { - unsafe { - self.node.borrow_layout_data_unchecked() - } - } } impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> { @@ -1112,11 +880,11 @@ impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> { self.pseudo } - fn borrow_layout_data(&self) -> Ref<Option<LayoutDataWrapper>> { + fn borrow_layout_data(&self) -> Option<Ref<PrivateLayoutData>> { self.node.borrow_layout_data() } - fn mutate_layout_data(&self) -> RefMut<Option<LayoutDataWrapper>> { + fn mutate_layout_data(&self) -> Option<RefMut<PrivateLayoutData>> { self.node.mutate_layout_data() } @@ -1143,17 +911,13 @@ impl<'ln> ThreadSafeLayoutNode<'ln> for ServoThreadSafeLayoutNode<'ln> { fn flags(self) -> LayoutDataFlags { unsafe { - match *self.borrow_layout_data_unchecked() { - None => panic!(), - Some(ref layout_data) => layout_data.data.flags, - } + (*self.node.borrow_layout_data_unchecked().unwrap()).flags } } fn text_content(&self) -> TextContent { if self.pseudo != PseudoElementType::Normal { - let layout_data_ref = self.borrow_layout_data(); - let data = &layout_data_ref.as_ref().unwrap().data; + let data = &self.borrow_layout_data().unwrap().style_data; let style = if self.pseudo.is_before() { &data.before_style diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index a2a39979ad2..ee82a25733f 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -57,14 +57,12 @@ use selectors::matching::matches; use selectors::parser::Selector; use selectors::parser::parse_author_origin_selector_list_from_str; use std::borrow::ToOwned; -use std::cell::{Cell, Ref, RefCell, RefMut}; +use std::cell::Cell; use std::cmp::max; use std::default::Default; use std::iter::{self, FilterMap, Peekable}; use std::mem; -use std::sync::Arc; use string_cache::{Atom, Namespace, QualName}; -use style::properties::ComputedValues; use util::str::DOMString; use util::task_state; use uuid::Uuid; @@ -115,11 +113,11 @@ pub struct Node { /// are this node. ranges: WeakRangeVec, - /// Layout information. Only the layout task may touch this data. + /// Style+Layout information. Only the layout task may touch this data. /// /// Must be sent back to the layout task to be destroyed when this /// node is finalized. - layout_data: LayoutDataRef, + style_and_layout_data: Cell<Option<OpaqueStyleAndLayoutData>>, unique_id: DOMRefCell<Option<Box<Uuid>>>, } @@ -164,7 +162,7 @@ impl NodeFlags { impl Drop for Node { #[allow(unsafe_code)] fn drop(&mut self) { - self.layout_data.dispose(self); + self.style_and_layout_data.get().map(|d| d.dispose(self)); } } @@ -177,74 +175,27 @@ enum SuppressObserver { Unsuppressed } -/// Layout data that is shared between the script and layout tasks. -#[derive(HeapSizeOf)] -pub struct SharedLayoutData { - /// The results of CSS styling for this node. - pub style: Option<Arc<ComputedValues>>, -} - -/// Encapsulates the abstract layout data. -#[derive(HeapSizeOf)] -pub struct LayoutData { - _shared_data: SharedLayoutData, - #[ignore_heap_size_of = "TODO(#6910) Box value that should be counted but the type lives in layout"] - _data: NonZero<*const ()>, +#[derive(Copy, Clone, HeapSizeOf)] +pub struct OpaqueStyleAndLayoutData { + #[ignore_heap_size_of = "TODO(#6910) Box value that should be counted but \ + the type lives in layout"] + pub ptr: NonZero<*mut ()> } #[allow(unsafe_code)] -unsafe impl Send for LayoutData {} +unsafe impl Send for OpaqueStyleAndLayoutData {} -#[derive(HeapSizeOf)] -pub struct LayoutDataRef { - data_cell: RefCell<Option<LayoutData>>, -} - -no_jsmanaged_fields!(LayoutDataRef); +no_jsmanaged_fields!(OpaqueStyleAndLayoutData); -impl LayoutDataRef { - pub fn new() -> LayoutDataRef { - LayoutDataRef { - data_cell: RefCell::new(None), - } - } - /// Sends layout data, if any, back to the layout task to be destroyed. - pub fn dispose(&self, node: &Node) { +impl OpaqueStyleAndLayoutData { + /// Sends the style and layout data, if any, back to the layout task to be destroyed. + pub fn dispose(self, node: &Node) { debug_assert!(task_state::get().is_script()); - if let Some(layout_data) = mem::replace(&mut *self.data_cell.borrow_mut(), None) { - let win = window_from_node(node); - let LayoutChan(chan) = win.layout_chan(); - chan.send(Msg::ReapLayoutData(layout_data)).unwrap() - } - } - - /// Borrows the layout data immutably, *assuming that there are no mutators*. Bad things will - /// happen if you try to mutate the layout data while this is held. This is the only thread- - /// safe layout data accessor. - #[inline] - #[allow(unsafe_code)] - pub unsafe fn borrow_unchecked(&self) -> *const Option<LayoutData> { - debug_assert!(task_state::get().is_layout()); - self.data_cell.as_unsafe_cell().get() as *const _ - } - - /// Borrows the layout data immutably. This function is *not* thread-safe. - #[inline] - pub fn borrow(&self) -> Ref<Option<LayoutData>> { - debug_assert!(task_state::get().is_layout()); - self.data_cell.borrow() - } - - /// Borrows the layout data mutably. This function is *not* thread-safe. - /// - /// FIXME(pcwalton): We should really put this behind a `MutLayoutView` phantom type, to - /// prevent CSS selector matching from mutably accessing nodes it's not supposed to and racing - /// on it. This has already resulted in one bug! - #[inline] - pub fn borrow_mut(&self) -> RefMut<Option<LayoutData>> { - debug_assert!(task_state::get().is_layout()); - self.data_cell.borrow_mut() + let win = window_from_node(node); + let LayoutChan(chan) = win.layout_chan(); + node.style_and_layout_data.set(None); + chan.send(Msg::ReapStyleAndLayoutData(self)).unwrap(); } } @@ -334,7 +285,7 @@ impl Node { for node in child.traverse_preorder() { node.set_flag(IS_IN_DOC, false); vtable_for(&&*node).unbind_from_tree(&context); - node.layout_data.dispose(&node); + node.style_and_layout_data.get().map(|d| d.dispose(&node)); } self.owner_doc().content_and_heritage_changed(self, NodeDamage::OtherNodeDamage); @@ -378,7 +329,7 @@ impl<'a> Iterator for QuerySelectorIterator { impl Node { pub fn teardown(&self) { - self.layout_data.dispose(self); + self.style_and_layout_data.get().map(|d| d.dispose(self)); for kid in self.children() { kid.teardown(); } @@ -966,9 +917,8 @@ pub trait LayoutNodeHelpers { unsafe fn children_count(&self) -> u32; - unsafe fn layout_data(&self) -> Ref<Option<LayoutData>>; - unsafe fn layout_data_mut(&self) -> RefMut<Option<LayoutData>>; - unsafe fn layout_data_unchecked(&self) -> *const Option<LayoutData>; + unsafe fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>; + unsafe fn init_style_and_layout_data(&self, OpaqueStyleAndLayoutData); } impl LayoutNodeHelpers for LayoutJS<Node> { @@ -1049,20 +999,15 @@ impl LayoutNodeHelpers for LayoutJS<Node> { #[inline] #[allow(unsafe_code)] - unsafe fn layout_data(&self) -> Ref<Option<LayoutData>> { - (*self.unsafe_get()).layout_data.borrow() - } - - #[inline] - #[allow(unsafe_code)] - unsafe fn layout_data_mut(&self) -> RefMut<Option<LayoutData>> { - (*self.unsafe_get()).layout_data.borrow_mut() + unsafe fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> { + (*self.unsafe_get()).style_and_layout_data.get() } #[inline] #[allow(unsafe_code)] - unsafe fn layout_data_unchecked(&self) -> *const Option<LayoutData> { - (*self.unsafe_get()).layout_data.borrow_unchecked() + unsafe fn init_style_and_layout_data(&self, val: OpaqueStyleAndLayoutData) { + debug_assert!((*self.unsafe_get()).style_and_layout_data.get().is_none()); + (*self.unsafe_get()).style_and_layout_data.set(Some(val)); } } @@ -1322,7 +1267,7 @@ impl Node { inclusive_descendants_version: Cell::new(0), ranges: WeakRangeVec::new(), - layout_data: LayoutDataRef::new(), + style_and_layout_data: Cell::new(None), unique_id: DOMRefCell::new(None), } diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index a533fd6418d..90f83211db6 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -7,7 +7,7 @@ //! the DOM to be placed in a separate crate from layout. use app_units::Au; -use dom::node::LayoutData; +use dom::node::OpaqueStyleAndLayoutData; use euclid::point::Point2D; use euclid::rect::Rect; use gfx_traits::LayerId; @@ -59,7 +59,7 @@ pub enum Msg { /// Destroys layout data associated with a DOM node. /// /// TODO(pcwalton): Maybe think about batching to avoid message traffic. - ReapLayoutData(LayoutData), + ReapStyleAndLayoutData(OpaqueStyleAndLayoutData), /// Requests that the layout task measure its memory usage. The resulting reports are sent back /// via the supplied channel. diff --git a/components/style/data.rs b/components/style/data.rs new file mode 100644 index 00000000000..bfd1cd6113a --- /dev/null +++ b/components/style/data.rs @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use properties::ComputedValues; +use std::sync::Arc; +use std::sync::atomic::AtomicIsize; + +pub struct PrivateStyleData { + /// The results of CSS styling for this node. + pub style: Option<Arc<ComputedValues>>, + + /// The results of CSS styling for this node's `before` pseudo-element, if any. + pub before_style: Option<Arc<ComputedValues>>, + + /// The results of CSS styling for this node's `after` pseudo-element, if any. + pub after_style: Option<Arc<ComputedValues>>, + + /// Information needed during parallel traversals. + pub parallel: DomParallelInfo, +} + +impl PrivateStyleData { + pub fn new() -> PrivateStyleData { + PrivateStyleData { + style: None, + before_style: None, + after_style: None, + parallel: DomParallelInfo::new(), + } + } +} + +/// Information that we need stored in each DOM node. +#[derive(HeapSizeOf)] +pub struct DomParallelInfo { + /// The number of children that still need work done. + pub children_count: AtomicIsize, +} + +impl DomParallelInfo { + pub fn new() -> DomParallelInfo { + DomParallelInfo { + children_count: AtomicIsize::new(0), + } + } +} + diff --git a/components/style/dom.rs b/components/style/dom.rs new file mode 100644 index 00000000000..4567e986bae --- /dev/null +++ b/components/style/dom.rs @@ -0,0 +1,274 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#![allow(unsafe_code)] + +use data::PrivateStyleData; +use properties::{PropertyDeclaration, PropertyDeclarationBlock}; +use restyle_hints::{ElementSnapshot, RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint}; +use selectors::matching::DeclarationBlock; +use selectors::states::ElementState; +use smallvec::VecLike; +use std::cell::{Ref, RefMut}; +use std::marker::PhantomData; +use string_cache::{Atom, Namespace}; + +/// Opaque type stored in type-unsafe work queues for parallel layout. +/// Must be transmutable to and from TNode. +pub type UnsafeNode = (usize, usize); + +/// An opaque handle to a node, which, unlike UnsafeNode, cannot be transformed +/// back into a non-opaque representation. The only safe operation that can be +/// performed on this node is to compare it to another opaque handle or to another +/// OpaqueNode. +/// +/// Layout and Graphics use this to safely represent nodes for comparison purposes. +/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout +/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for +/// locality reasons. Using `OpaqueNode` enforces this invariant. +#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf, Hash, Eq, Deserialize, Serialize)] +pub struct OpaqueNode(pub usize); + +impl OpaqueNode { + /// Returns the address of this node, for debugging purposes. + #[inline] + pub fn id(&self) -> usize { + let OpaqueNode(pointer) = *self; + pointer + } +} + + +pub trait TNode<'ln> : Sized + Copy + Clone { + type ConcreteElement: TElement<'ln, ConcreteNode = Self, ConcreteDocument = Self::ConcreteDocument>; + type ConcreteDocument: TDocument<'ln, ConcreteNode = Self, ConcreteElement = Self::ConcreteElement>; + + fn to_unsafe(&self) -> UnsafeNode; + unsafe fn from_unsafe(n: &UnsafeNode) -> Self; + + /// Returns whether this is a text node. It turns out that this is all the style system cares + /// about, and thus obviates the need to compute the full type id, which would be expensive in + /// Gecko. + fn is_text_node(&self) -> bool; + + fn is_element(&self) -> bool; + + fn dump(self); + + fn traverse_preorder(self) -> TreeIterator<'ln, Self> { + TreeIterator::new(self) + } + + /// Returns an iterator over this node's children. + fn children(self) -> ChildrenIterator<'ln, Self> { + ChildrenIterator { + current: self.first_child(), + phantom: PhantomData, + } + } + + fn rev_children(self) -> ReverseChildrenIterator<'ln, Self> { + ReverseChildrenIterator { + current: self.last_child(), + phantom: PhantomData, + } + } + + /// Converts self into an `OpaqueNode`. + fn opaque(&self) -> OpaqueNode; + + /// Initializes style and layout data for the node. No-op if the data is already + /// initialized. + /// + /// FIXME(pcwalton): Do this as part of fragment building instead of in a traversal. + fn initialize_data(self); + + /// 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<Self>; + + fn debug_id(self) -> usize; + + fn as_element(&self) -> Option<Self::ConcreteElement>; + + fn as_document(&self) -> Option<Self::ConcreteDocument>; + + fn children_count(&self) -> u32; + + fn has_changed(&self) -> bool; + + unsafe fn set_changed(&self, value: bool); + + fn is_dirty(&self) -> bool; + + unsafe fn set_dirty(&self, value: bool); + + fn has_dirty_descendants(&self) -> bool; + + unsafe fn set_dirty_descendants(&self, value: bool); + + fn dirty_self(&self) { + unsafe { + self.set_dirty(true); + self.set_dirty_descendants(true); + } + } + + fn dirty_descendants(&self) { + for ref child in self.children() { + child.dirty_self(); + child.dirty_descendants(); + } + } + + /// Borrows the PrivateStyleData without checks. + #[inline(always)] + unsafe fn borrow_data_unchecked(&self) -> Option<*const PrivateStyleData>; + + /// Borrows the PrivateStyleData immutably. Fails on a conflicting borrow. + #[inline(always)] + fn borrow_data(&self) -> Option<Ref<PrivateStyleData>>; + + /// Borrows the PrivateStyleData mutably. Fails on a conflicting borrow. + #[inline(always)] + fn mutate_data(&self) -> Option<RefMut<PrivateStyleData>>; + + fn parent_node(&self) -> Option<Self>; + + fn first_child(&self) -> Option<Self>; + + fn last_child(&self) -> Option<Self>; + + fn prev_sibling(&self) -> Option<Self>; + + fn next_sibling(&self) -> Option<Self>; +} + +pub trait TDocument<'ld> : Sized + Copy + Clone { + type ConcreteNode: TNode<'ld, ConcreteElement = Self::ConcreteElement, ConcreteDocument = Self>; + type ConcreteElement: TElement<'ld, 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, ElementSnapshot)>; +} + +pub trait TElement<'le> : Sized + Copy + Clone + ::selectors::Element { + type ConcreteNode: TNode<'le, ConcreteElement = Self, ConcreteDocument = Self::ConcreteDocument>; + type ConcreteDocument: TDocument<'le, ConcreteNode = Self::ConcreteNode, ConcreteElement = Self>; + + fn as_node(&self) -> Self::ConcreteNode; + + fn style_attribute(&self) -> &'le Option<PropertyDeclarationBlock>; + + fn get_state(&self) -> ElementState; + + fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, &mut V) + where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>; + + fn get_attr<'a>(&'a self, namespace: &Namespace, attr: &Atom) -> Option<&'a str>; + fn get_attrs<'a>(&'a self, attr: &Atom) -> Vec<&'a str>; + + /// Properly marks nodes as dirty in response to restyle hints. + fn note_restyle_hint(&self, mut hint: RestyleHint) { + // Bail early if there's no restyling to do. + if hint.is_empty() { + return; + } + + // 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() { + if parent.has_dirty_descendants() { break } + unsafe { parent.set_dirty_descendants(true); } + curr = parent; + } + + // Process hints. + if hint.contains(RESTYLE_SELF) { + node.dirty_self(); + + // FIXME(bholley, #8438): We currently need to RESTYLE_DESCENDANTS in the + // RESTYLE_SELF case in order to make sure "inherit" style structs propagate + // properly. See the explanation in the github issue. + hint.insert(RESTYLE_DESCENDANTS); + } + if hint.contains(RESTYLE_DESCENDANTS) { + unsafe { node.set_dirty_descendants(true); } + node.dirty_descendants(); + } + 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(); + sib_node.dirty_self(); + sib_node.dirty_descendants(); + next = ::selectors::Element::next_sibling_element(&sib); + } + } + } +} + +pub struct TreeIterator<'a, ConcreteNode> where ConcreteNode: TNode<'a> { + stack: Vec<ConcreteNode>, + // Satisfy the compiler about the unused lifetime. + phantom: PhantomData<&'a ()>, +} + +impl<'a, ConcreteNode> TreeIterator<'a, ConcreteNode> where ConcreteNode: TNode<'a> { + fn new(root: ConcreteNode) -> TreeIterator<'a, ConcreteNode> { + let mut stack = vec!(); + stack.push(root); + TreeIterator { + stack: stack, + phantom: PhantomData, + } + } +} + +impl<'a, ConcreteNode> Iterator for TreeIterator<'a, ConcreteNode> + where ConcreteNode: TNode<'a> { + type Item = ConcreteNode; + fn next(&mut self) -> Option<ConcreteNode> { + let ret = self.stack.pop(); + ret.map(|node| self.stack.extend(node.rev_children())); + ret + } +} + +pub struct ChildrenIterator<'a, ConcreteNode> where ConcreteNode: TNode<'a> { + current: Option<ConcreteNode>, + // Satisfy the compiler about the unused lifetime. + phantom: PhantomData<&'a ()>, +} + +impl<'a, ConcreteNode> Iterator for ChildrenIterator<'a, ConcreteNode> + where ConcreteNode: TNode<'a> { + type Item = ConcreteNode; + fn next(&mut self) -> Option<ConcreteNode> { + let node = self.current; + self.current = node.and_then(|node| node.next_sibling()); + node + } +} + +pub struct ReverseChildrenIterator<'a, ConcreteNode> where ConcreteNode: TNode<'a> { + current: Option<ConcreteNode>, + // Satisfy the compiler about the unused lifetime. + phantom: PhantomData<&'a ()>, +} + +impl<'a, ConcreteNode> Iterator for ReverseChildrenIterator<'a, ConcreteNode> + where ConcreteNode: TNode<'a> { + type Item = ConcreteNode; + fn next(&mut self) -> Option<ConcreteNode> { + let node = self.current; + self.current = node.and_then(|node| node.prev_sibling()); + node + } +} diff --git a/components/style/lib.rs b/components/style/lib.rs index 4baa68eeca5..81c34b4db5a 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -17,6 +17,7 @@ extern crate app_units; #[macro_use] extern crate bitflags; +extern crate core; #[macro_use] extern crate cssparser; extern crate encoding; @@ -43,9 +44,10 @@ extern crate util; pub mod animation; pub mod attr; mod custom_properties; +pub mod data; +pub mod dom; pub mod font_face; pub mod media_queries; -pub mod node; pub mod parser; pub mod restyle_hints; pub mod selector_matching; diff --git a/components/style/node.rs b/components/style/node.rs deleted file mode 100644 index 302ad48fd80..00000000000 --- a/components/style/node.rs +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and -//! style. - -use properties::PropertyDeclaration; -use selectors::matching::DeclarationBlock; -use smallvec::VecLike; -use string_cache::{Atom, Namespace}; - -pub trait TElementAttributes { - fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, &mut V) - where V: VecLike<DeclarationBlock<Vec<PropertyDeclaration>>>; - - fn get_attr<'a>(&'a self, namespace: &Namespace, attr: &Atom) -> Option<&'a str>; - fn get_attrs<'a>(&'a self, attr: &Atom) -> Vec<&'a str>; -} diff --git a/components/style/selector_matching.rs b/components/style/selector_matching.rs index 4dae08ce9bd..dd99a3f26b4 100644 --- a/components/style/selector_matching.rs +++ b/components/style/selector_matching.rs @@ -3,11 +3,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use cssparser::{Parser, SourcePosition}; +use dom::TElement; use log; use media_queries::{Device, MediaType}; use msg::ParseErrorReporter; use msg::constellation_msg::PipelineId; -use node::TElementAttributes; use properties::{PropertyDeclaration, PropertyDeclarationBlock}; use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet}; use selectors::Element; @@ -270,7 +270,7 @@ impl Stylist { /// The returned boolean indicates whether the style is *shareable*; that is, whether the /// matched selectors are simple enough to allow the matching logic to be reduced to the logic /// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`. - pub fn push_applicable_declarations<E, V>( + pub fn push_applicable_declarations<'le, E, V>( &self, element: &E, parent_bf: Option<&BloomFilter>, @@ -278,7 +278,7 @@ impl Stylist { pseudo_element: Option<PseudoElement>, applicable_declarations: &mut V) -> bool - where E: Element + TElementAttributes, + where E: Element + TElement<'le>, V: VecLike<DeclarationBlock> { assert!(!self.is_device_dirty); assert!(style_attribute.is_none() || pseudo_element.is_none(), diff --git a/components/util/mem.rs b/components/util/mem.rs index 5b976d468e4..6c60186ff0e 100644 --- a/components/util/mem.rs +++ b/components/util/mem.rs @@ -35,6 +35,7 @@ use std::mem::{size_of, transmute}; use std::rc::Rc; use std::result::Result; use std::sync::Arc; +use std::sync::atomic::{AtomicIsize, AtomicUsize}; use str::{DOMString, LengthOrPercentageOrAuto}; use string_cache::atom::Atom; use string_cache::namespace::{QualName, Namespace}; @@ -418,6 +419,7 @@ impl HeapSizeOf for Value { known_heap_size!(0, u8, u16, u32, u64, usize); known_heap_size!(0, i8, i16, i32, i64, isize); known_heap_size!(0, bool, f32, f64); +known_heap_size!(0, AtomicIsize, AtomicUsize); known_heap_size!(0, Rect<T>, Point2D<T>, Size2D<T>, Matrix2D<T>, SideOffsets2D<T>, Range<T>); known_heap_size!(0, Length<T, U>, ScaleFactor<T, U, V>); diff --git a/tests/unit/script/size_of.rs b/tests/unit/script/size_of.rs index 28c7c52449f..b89ca9dd837 100644 --- a/tests/unit/script/size_of.rs +++ b/tests/unit/script/size_of.rs @@ -38,10 +38,10 @@ macro_rules! sizeof_checker ( // Update the sizes here sizeof_checker!(size_event_target, EventTarget, 40); -sizeof_checker!(size_node, Node, 184); -sizeof_checker!(size_element, Element, 328); -sizeof_checker!(size_htmlelement, HTMLElement, 344); -sizeof_checker!(size_div, HTMLDivElement, 344); -sizeof_checker!(size_span, HTMLSpanElement, 344); -sizeof_checker!(size_text, Text, 216); -sizeof_checker!(size_characterdata, CharacterData, 216); +sizeof_checker!(size_node, Node, 168); +sizeof_checker!(size_element, Element, 312); +sizeof_checker!(size_htmlelement, HTMLElement, 328); +sizeof_checker!(size_div, HTMLDivElement, 328); +sizeof_checker!(size_span, HTMLSpanElement, 328); +sizeof_checker!(size_text, Text, 200); +sizeof_checker!(size_characterdata, CharacterData, 200); |