diff options
-rw-r--r-- | components/layout/wrapper.rs | 2 | ||||
-rw-r--r-- | components/script/layout_wrapper.rs | 47 | ||||
-rw-r--r-- | components/script_layout_interface/lib.rs | 8 | ||||
-rw-r--r-- | components/script_layout_interface/wrapper_traits.rs | 51 | ||||
-rwxr-xr-x | components/style/binding_tools/regen.py | 6 | ||||
-rw-r--r-- | components/style/data.rs | 188 | ||||
-rw-r--r-- | components/style/dom.rs | 32 | ||||
-rw-r--r-- | components/style/gecko/traversal.rs | 4 | ||||
-rw-r--r-- | components/style/gecko/wrapper.rs | 99 | ||||
-rw-r--r-- | components/style/gecko_bindings/structs_debug.rs | 5 | ||||
-rw-r--r-- | components/style/gecko_bindings/structs_release.rs | 5 | ||||
-rw-r--r-- | components/style/matching.rs | 84 | ||||
-rw-r--r-- | components/style/traversal.rs | 10 | ||||
-rw-r--r-- | ports/geckolib/glue.rs | 5 |
14 files changed, 330 insertions, 216 deletions
diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 2c8b3a175c3..99f9a4cc54d 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -41,7 +41,7 @@ pub type NonOpaqueStyleAndLayoutData = *mut AtomicRefCell<PersistentLayoutData>; pub trait LayoutNodeLayoutData { /// Similar to borrow_data*, but returns the full PersistentLayoutData rather - /// than only the PersistentStyleData. + /// than only the style::data::NodeData. fn borrow_layout_data(&self) -> Option<AtomicRef<PersistentLayoutData>>; fn mutate_layout_data(&self) -> Option<AtomicRefMut<PersistentLayoutData>>; fn initialize_data(self); diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 0be87e63db6..653bcae478b 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -52,7 +52,7 @@ use selectors::matching::ElementFlags; use selectors::parser::{AttrSelector, NamespaceConstraint}; use std::fmt; use std::marker::PhantomData; -use std::mem::{replace, transmute}; +use std::mem::transmute; use std::sync::Arc; use std::sync::atomic::Ordering; use string_cache::{Atom, Namespace}; @@ -60,7 +60,7 @@ use style::atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use style::attr::AttrValue; use style::computed_values::display; use style::context::SharedStyleContext; -use style::data::{PersistentStyleData, PseudoStyles}; +use style::data::NodeData; use style::dom::{LayoutIterator, NodeInfo, OpaqueNode, PresentationalHintsSynthetizer, TDocument, TElement, TNode}; use style::dom::{TRestyleDamage, UnsafeNode}; use style::element_state::*; @@ -107,11 +107,7 @@ impl<'ln> ServoLayoutNode<'ln> { } } - pub fn borrow_data(&self) -> Option<AtomicRef<PersistentStyleData>> { - self.get_style_data().map(|d| d.borrow()) - } - - pub fn mutate_data(&self) -> Option<AtomicRefMut<PersistentStyleData>> { + pub fn mutate_data(&self) -> Option<AtomicRefMut<NodeData>> { self.get_style_data().map(|d| d.borrow_mut()) } @@ -234,32 +230,25 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { old_value - 1 } - fn get_existing_style(&self) -> Option<Arc<ComputedValues>> { - self.borrow_data().and_then(|x| x.style.clone()) - } - - fn set_style(&self, style: Arc<ComputedValues>) { - self.mutate_data().unwrap().style = Some(style); - } - - fn take_pseudo_styles(&self) -> PseudoStyles { - replace(&mut self.mutate_data().unwrap().per_pseudo, PseudoStyles::default()) - } - - fn set_pseudo_styles(&self, styles: PseudoStyles) { - debug_assert!(self.borrow_data().unwrap().per_pseudo.is_empty()); - self.mutate_data().unwrap().per_pseudo = styles; + fn begin_styling(&self) -> AtomicRefMut<NodeData> { + let mut data = self.mutate_data().unwrap(); + data.gather_previous_styles(|| None); + data } fn style_text_node(&self, style: Arc<ComputedValues>) { debug_assert!(self.is_text_node()); let mut data = self.get_partial_layout_data().unwrap().borrow_mut(); - data.style_data.style = Some(style); + data.style_data.style_text_node(style); if self.has_changed() { data.restyle_damage = RestyleDamage::rebuild_and_reflow(); } } + fn borrow_data(&self) -> Option<AtomicRef<NodeData>> { + self.get_style_data().map(|d| d.borrow()) + } + fn restyle_damage(self) -> RestyleDamage { self.get_partial_layout_data().unwrap().borrow().restyle_damage } @@ -345,11 +334,11 @@ impl<'ln> LayoutNode for ServoLayoutNode<'ln> { self.node.set_flag(HAS_DIRTY_DESCENDANTS, false); } - fn get_style_data(&self) -> Option<&AtomicRefCell<PersistentStyleData>> { + fn get_style_data(&self) -> Option<&AtomicRefCell<NodeData>> { unsafe { self.get_jsmanaged().get_style_and_layout_data().map(|d| { let ppld: &AtomicRefCell<PartialPersistentLayoutData> = &**d.ptr; - let psd: &AtomicRefCell<PersistentStyleData> = transmute(ppld); + let psd: &AtomicRefCell<NodeData> = transmute(ppld); psd }) } @@ -413,11 +402,7 @@ impl<'ln> ServoLayoutNode<'ln> { fn debug_style_str(self) -> String { if let Some(data) = self.borrow_data() { - if let Some(data) = data.style.as_ref() { - format!("{:?}: {:?}", self.script_type_id(), data) - } else { - format!("{:?}: style=None", self.script_type_id()) - } + format!("{:?}: {:?}", self.script_type_id(), &*data) } else { format!("{:?}: style_data=None", self.script_type_id()) } @@ -922,7 +907,7 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> { } } - fn get_style_data(&self) -> Option<&AtomicRefCell<PersistentStyleData>> { + fn get_style_data(&self) -> Option<&AtomicRefCell<NodeData>> { self.node.get_style_data() } } diff --git a/components/script_layout_interface/lib.rs b/components/script_layout_interface/lib.rs index 31d4334cb6d..120f794c249 100644 --- a/components/script_layout_interface/lib.rs +++ b/components/script_layout_interface/lib.rs @@ -53,14 +53,14 @@ use libc::c_void; use restyle_damage::RestyleDamage; use std::sync::atomic::AtomicIsize; use style::atomic_refcell::AtomicRefCell; -use style::data::PersistentStyleData; +use style::data::NodeData; pub struct PartialPersistentLayoutData { /// 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 - /// transmutations between PersistentStyleData and PersistentLayoutData. - pub style_data: PersistentStyleData, + /// transmutations between NodeData and PersistentLayoutData. + pub style_data: NodeData, /// Description of how to account for recent style changes. pub restyle_damage: RestyleDamage, @@ -72,7 +72,7 @@ pub struct PartialPersistentLayoutData { impl PartialPersistentLayoutData { pub fn new() -> Self { PartialPersistentLayoutData { - style_data: PersistentStyleData::new(), + style_data: NodeData::new(), restyle_damage: RestyleDamage::empty(), parallel: DomParallelInfo::new(), } diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index 842a4082f73..5526c449247 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -18,7 +18,7 @@ use string_cache::{Atom, Namespace}; use style::atomic_refcell::AtomicRefCell; use style::computed_values::display; use style::context::SharedStyleContext; -use style::data::PersistentStyleData; +use style::data::NodeData; use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthetizer, TNode}; use style::dom::OpaqueNode; use style::properties::ServoComputedValues; @@ -83,7 +83,7 @@ pub trait LayoutNode: TNode { unsafe fn clear_dirty_bits(&self); - fn get_style_data(&self) -> Option<&AtomicRefCell<PersistentStyleData>>; + fn get_style_data(&self) -> Option<&AtomicRefCell<NodeData>>; fn init_style_and_layout_data(&self, data: OpaqueStyleAndLayoutData); fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>; @@ -190,7 +190,7 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized { if self.get_style_data() .unwrap() .borrow() - .per_pseudo + .current_styles().pseudos .contains_key(&PseudoElement::Before) { Some(self.with_pseudo(PseudoElementType::Before(None))) } else { @@ -203,7 +203,7 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized { if self.get_style_data() .unwrap() .borrow() - .per_pseudo + .current_styles().pseudos .contains_key(&PseudoElement::After) { Some(self.with_pseudo(PseudoElementType::After(None))) } else { @@ -249,7 +249,7 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized { match self.get_pseudo_element_type() { PseudoElementType::Normal => { self.get_style_data().unwrap().borrow() - .style.as_ref().unwrap().clone() + .current_styles().primary.clone() }, other => { // Precompute non-eagerly-cascaded pseudo-element styles if not @@ -262,13 +262,13 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized { if !self.get_style_data() .unwrap() .borrow() - .per_pseudo.contains_key(&style_pseudo) { + .current_styles().pseudos.contains_key(&style_pseudo) { let mut data = self.get_style_data().unwrap().borrow_mut(); let new_style = context.stylist .precomputed_values_for_pseudo(&style_pseudo, - data.style.as_ref()); - data.per_pseudo + Some(&data.current_styles().primary)); + data.current_pseudos_mut() .insert(style_pseudo.clone(), new_style.unwrap()); } } @@ -277,22 +277,22 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized { if !self.get_style_data() .unwrap() .borrow() - .per_pseudo.contains_key(&style_pseudo) { + .current_styles().pseudos.contains_key(&style_pseudo) { let mut data = self.get_style_data().unwrap().borrow_mut(); let new_style = context.stylist .lazily_compute_pseudo_element_style( &self.as_element(), &style_pseudo, - data.style.as_ref().unwrap()); - data.per_pseudo + &data.current_styles().primary); + data.current_pseudos_mut() .insert(style_pseudo.clone(), new_style.unwrap()); } } } self.get_style_data().unwrap().borrow() - .per_pseudo.get(&style_pseudo) + .current_styles().pseudos.get(&style_pseudo) .unwrap().clone() } } @@ -310,34 +310,19 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized { let data = self.get_style_data().unwrap().borrow(); match self.get_pseudo_element_type() { PseudoElementType::Normal - => data.style.as_ref().unwrap().clone(), + => data.current_styles().primary.clone(), other - => data.per_pseudo.get(&other.style_pseudo_element()).unwrap().clone(), + => data.current_styles().pseudos.get(&other.style_pseudo_element()).unwrap().clone(), } } #[inline] fn selected_style(&self, _context: &SharedStyleContext) -> Arc<ServoComputedValues> { let data = self.get_style_data().unwrap().borrow(); - data.per_pseudo + data.current_styles().pseudos .get(&PseudoElement::Selection) - .unwrap_or(data.style.as_ref().unwrap()).clone() - } - - /// Removes the style from this node. - /// - /// Unlike the version on TNode, this handles pseudo-elements. - fn unstyle(self) { - let mut data = self.get_style_data().unwrap().borrow_mut(); - - match self.get_pseudo_element_type() { - PseudoElementType::Normal => { - data.style = None; - } - other => { - data.per_pseudo.remove(&other.style_pseudo_element()); - } - }; + .unwrap_or(&data.current_styles().primary) + .clone() } fn is_ignorable_whitespace(&self, context: &SharedStyleContext) -> bool; @@ -377,7 +362,7 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + NodeInfo + PartialEq + Sized { fn get_colspan(&self) -> u32; - fn get_style_data(&self) -> Option<&AtomicRefCell<PersistentStyleData>>; + fn get_style_data(&self) -> Option<&AtomicRefCell<NodeData>>; } // This trait is only public so that it can be implemented by the gecko wrapper. diff --git a/components/style/binding_tools/regen.py b/components/style/binding_tools/regen.py index 48fe2fe23fc..822f6766419 100755 --- a/components/style/binding_tools/regen.py +++ b/components/style/binding_tools/regen.py @@ -66,8 +66,8 @@ COMPILATION_TARGETS = { } }, "raw_lines": [ - # We can get rid of this when the bindings move into the style crate. - "pub enum OpaqueStyleData {}", + "use atomic_refcell::AtomicRefCell;", + "use data::NodeData;", "pub use nsstring::nsStringRepr as nsString;" ], "blacklist_types": ["nsString"], @@ -229,7 +229,7 @@ COMPILATION_TARGETS = { }, { "generic": False, "gecko": "ServoNodeData", - "servo": "OpaqueStyleData" + "servo": "AtomicRefCell<NodeData>", } ], }, diff --git a/components/style/data.rs b/components/style/data.rs index c0b07ac340f..511b1e174c3 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -8,24 +8,192 @@ use properties::ComputedValues; use selector_impl::PseudoElement; use std::collections::HashMap; use std::hash::BuildHasherDefault; +use std::mem; +use std::ops::{Deref, DerefMut}; use std::sync::Arc; -pub type PseudoStyles = HashMap<PseudoElement, Arc<ComputedValues>, - BuildHasherDefault<::fnv::FnvHasher>>; -pub struct PersistentStyleData { +type PseudoStylesInner = HashMap<PseudoElement, Arc<ComputedValues>, + BuildHasherDefault<::fnv::FnvHasher>>; +#[derive(Clone, Debug)] +pub struct PseudoStyles(PseudoStylesInner); + +impl PseudoStyles { + pub fn empty() -> Self { + PseudoStyles(HashMap::with_hasher(Default::default())) + } +} + +impl Deref for PseudoStyles { + type Target = PseudoStylesInner; + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl DerefMut for PseudoStyles { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } +} + +/// The styles associated with a node, including the styles for any +/// pseudo-elements. +#[derive(Clone, Debug)] +pub struct NodeStyles { /// The results of CSS styling for this node. - pub style: Option<Arc<ComputedValues>>, + pub primary: Arc<ComputedValues>, /// The results of CSS styling for each pseudo-element (if any). - pub per_pseudo: PseudoStyles, + pub pseudos: PseudoStyles, } -impl PersistentStyleData { - pub fn new() -> Self { - PersistentStyleData { - style: None, - per_pseudo: HashMap::with_hasher(Default::default()), +impl NodeStyles { + pub fn new(primary: Arc<ComputedValues>) -> Self { + NodeStyles { + primary: primary, + pseudos: PseudoStyles::empty(), + } + } +} + +#[derive(Debug)] +enum NodeDataStyles { + /// The field has not been initialized. + Uninitialized, + + /// The field holds the previous style of the node. If this is None, the + /// node has not been previously styled. + /// + /// This is the input to the styling algorithm. It would ideally be + /// immutable, but for now we need to mutate it a bit before styling to + /// handle animations. + /// + /// Note that since NodeStyles contains an Arc, the null pointer + /// optimization prevents the Option<> here from consuming an extra word. + Previous(Option<NodeStyles>), + + /// The field holds the current, up-to-date style. + /// + /// This is the output of the styling algorithm. + Current(NodeStyles), +} + +impl NodeDataStyles { + fn is_previous(&self) -> bool { + use self::NodeDataStyles::*; + match *self { + Previous(_) => true, + _ => false, + } + } +} + +/// Transient data used by the restyle algorithm. This structure is instantiated +/// either before or during restyle traversal, and is cleared at the end of node +/// processing. +#[derive(Debug)] +pub struct RestyleData { + // FIXME(bholley): Start adding the fields from the algorithm doc. + pub _dummy: u64, +} + +impl RestyleData { + fn new() -> Self { + RestyleData { + _dummy: 42, } } } +/// Style system data associated with a node. +/// +/// In Gecko, this hangs directly off a node, but is dropped when the frame takes +/// ownership of the computed style data. +/// +/// In Servo, this is embedded inside of layout data, which itself hangs directly +/// off the node. Servo does not currently implement ownership transfer of the +/// computed style data to the frame. +/// +/// In both cases, it is wrapped inside an AtomicRefCell to ensure thread +/// safety. +#[derive(Debug)] +pub struct NodeData { + styles: NodeDataStyles, + pub restyle: Option<RestyleData>, +} + +impl NodeData { + pub fn new() -> Self { + NodeData { + styles: NodeDataStyles::Uninitialized, + restyle: None, + } + } + + pub fn has_current_styles(&self) -> bool { + match self.styles { + NodeDataStyles::Current(_) => true, + _ => false, + } + } + + pub fn get_current_styles(&self) -> Option<&NodeStyles> { + match self.styles { + NodeDataStyles::Current(ref s) => Some(s), + _ => None, + } + } + + pub fn current_styles(&self) -> &NodeStyles { + self.get_current_styles().expect("Calling current_styles before or during styling") + } + + // Servo does lazy pseudo computation in layout and needs mutable access + // to the current styles + #[cfg(not(feature = "gecko"))] + pub fn current_pseudos_mut(&mut self) -> &mut PseudoStyles { + match self.styles { + NodeDataStyles::Current(ref mut s) => &mut s.pseudos, + _ => panic!("Calling current_pseudos_mut before or during styling"), + } + } + + pub fn previous_styles(&self) -> Option<&NodeStyles> { + match self.styles { + NodeDataStyles::Previous(ref s) => s.as_ref(), + _ => panic!("Calling previous_styles without having gathered it"), + } + } + + pub fn previous_styles_mut(&mut self) -> Option<&mut NodeStyles> { + match self.styles { + NodeDataStyles::Previous(ref mut s) => s.as_mut(), + _ => panic!("Calling previous_styles without having gathered it"), + } + } + + pub fn gather_previous_styles<F>(&mut self, f: F) + where F: FnOnce() -> Option<NodeStyles> + { + use self::NodeDataStyles::*; + self.styles = match mem::replace(&mut self.styles, Uninitialized) { + Uninitialized => Previous(f()), + Current(x) => Previous(Some(x)), + _ => panic!("Already have previous styles"), + }; + } + + // FIXME(bholley): Called in future patches. + pub fn ensure_restyle_data(&mut self) { + if self.restyle.is_none() { + self.restyle = Some(RestyleData::new()); + } + } + + pub fn style_text_node(&mut self, style: Arc<ComputedValues>) { + debug_assert!(self.restyle.is_none()); + self.styles = NodeDataStyles::Current(NodeStyles::new(style)); + } + + pub fn finish_styling(&mut self, styles: NodeStyles) { + debug_assert!(self.styles.is_previous()); + self.styles = NodeDataStyles::Current(styles); + self.restyle = None; + } +} diff --git a/components/style/dom.rs b/components/style/dom.rs index 1988b57e7e7..d8b82d94d04 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -6,7 +6,8 @@ #![allow(unsafe_code)] -use data::PseudoStyles; +use atomic_refcell::{AtomicRef, AtomicRefMut}; +use data::NodeData; use element_state::ElementState; use parking_lot::RwLock; use properties::{ComputedValues, PropertyDeclarationBlock}; @@ -140,29 +141,18 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { /// traversal. Returns the number of children left to process. fn did_process_child(&self) -> isize; - /// Returns the computed style values corresponding to the existing style - /// for this node, if any. - /// - /// This returns an cloned Arc (rather than a borrow) to abstract over the - /// multitude of ways these values may be stored under the hood. By - /// returning an enum with various OwningRef/OwningHandle entries, we could - /// avoid the refcounting traffic here, but it's probably not worth the - /// complexity. - fn get_existing_style(&self) -> Option<Arc<ComputedValues>>; - - /// Sets the computed style for this node. - fn set_style(&self, style: Arc<ComputedValues>); - - /// Transfers ownership of the existing pseudo styles, if any, to the - /// caller. The stored pseudo styles are replaced with an empty map. - fn take_pseudo_styles(&self) -> PseudoStyles; + /// Sets up the appropriate data structures to style a node, returing a + /// mutable handle to the node data upon which further style calculations + /// can be performed. + fn begin_styling(&self) -> AtomicRefMut<NodeData>; - /// Sets the pseudo styles on the element, replacing any existing styles. - fn set_pseudo_styles(&self, styles: PseudoStyles); - - /// Set the style for a text node. + /// 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<ComputedValues>); + /// Immutable borrows the NodeData. + fn borrow_data(&self) -> Option<AtomicRef<NodeData>>; + /// Get the description of how to account for recent style changes. fn restyle_damage(self) -> Self::ConcreteRestyleDamage; diff --git a/components/style/gecko/traversal.rs b/components/style/gecko/traversal.rs index 2fffa043499..2325bd1d303 100644 --- a/components/style/gecko/traversal.rs +++ b/components/style/gecko/traversal.rs @@ -29,10 +29,6 @@ impl<'lc, 'ln> DomTraversalContext<GeckoNode<'ln>> for RecalcStyleOnly<'lc> { } fn process_preorder(&self, node: GeckoNode<'ln>) -> RestyleResult { - // FIXME(pcwalton): Stop allocating here. Ideally this should just be done by the HTML - // parser. - node.initialize_data(); - recalc_style_at(&self.context, self.root, node) } diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 134012e89ac..7c78ef908f4 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -6,7 +6,7 @@ use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; -use data::{PersistentStyleData, PseudoStyles}; +use data::{NodeData, NodeStyles}; use dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, TRestyleDamage, UnsafeNode}; use dom::{OpaqueNode, PresentationalHintsSynthetizer}; use element_state::ElementState; @@ -29,7 +29,6 @@ use gecko_bindings::structs; use gecko_bindings::structs::{NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO, NODE_IS_DIRTY_FOR_SERVO}; use gecko_bindings::structs::{RawGeckoDocument, RawGeckoElement, RawGeckoNode}; use gecko_bindings::structs::{nsChangeHint, nsIAtom, nsIContent, nsStyleContext}; -use gecko_bindings::structs::OpaqueStyleData; use gecko_bindings::sugar::ownership::FFIArcHelpers; use libc::uintptr_t; use parking_lot::RwLock; @@ -48,22 +47,6 @@ use std::sync::Arc; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use url::Url; -pub struct NonOpaqueStyleData(AtomicRefCell<PersistentStyleData>); - -impl NonOpaqueStyleData { - pub fn new() -> Self { - NonOpaqueStyleData(AtomicRefCell::new(PersistentStyleData::new())) - } -} - -// We can eliminate OpaqueStyleData when the bindings move into the style crate. -fn to_opaque_style_data(d: *mut NonOpaqueStyleData) -> *mut OpaqueStyleData { - d as *mut OpaqueStyleData -} -fn from_opaque_style_data(d: *mut OpaqueStyleData) -> *mut NonOpaqueStyleData { - d as *mut NonOpaqueStyleData -} - // Important: We don't currently refcount the DOM, because the wrapper lifetime // magic guarantees that our LayoutFoo references won't outlive the root, and // we don't mutate any of the references on the Gecko side during restyle. We @@ -94,40 +77,44 @@ impl<'ln> GeckoNode<'ln> { unsafe { Gecko_SetNodeFlags(self.0, flags) } } - fn get_node_data(&self) -> Option<&NonOpaqueStyleData> { - unsafe { - from_opaque_style_data(self.0.mServoData.get()).as_ref() - } - } + 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()); - pub fn initialize_data(self) { - if self.get_node_data().is_none() { - let ptr = Box::new(NonOpaqueStyleData::new()); - debug_assert!(self.0.mServoData.get().is_null()); - self.0.mServoData.set(to_opaque_style_data(Box::into_raw(ptr))); + // 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 clear_data(self) { - if !self.get_node_data().is_none() { - let d = from_opaque_style_data(self.0.mServoData.get()); - let _ = unsafe { Box::from_raw(d) }; - self.0.mServoData.set(ptr::null_mut()); - } + pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option<Arc<ComputedValues>> { + self.borrow_data().and_then(|data| data.current_styles().pseudos + .get(pseudo).map(|c| c.clone())) } - pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option<Arc<ComputedValues>> { - self.borrow_data().and_then(|data| data.per_pseudo.get(pseudo).map(|c| c.clone())) + fn styles_from_frame(&self) -> Option<NodeStyles> { + // FIXME(bholley): Once we start dropping NodeData from nodes when + // creating frames, we'll want to teach this method to actually get + // style data from the frame. + None } - #[inline(always)] - fn borrow_data(&self) -> Option<AtomicRef<PersistentStyleData>> { - self.get_node_data().as_ref().map(|d| d.0.borrow()) + fn get_node_data(&self) -> Option<&AtomicRefCell<NodeData>> { + unsafe { self.0.mServoData.get().as_ref() } } - #[inline(always)] - fn mutate_data(&self) -> Option<AtomicRefMut<PersistentStyleData>> { - self.get_node_data().as_ref().map(|d| d.0.borrow_mut()) + fn ensure_node_data(&self) -> &AtomicRefCell<NodeData> { + 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 } + }, + } } } @@ -283,29 +270,19 @@ impl<'ln> TNode for GeckoNode<'ln> { panic!("Atomic child count not implemented in Gecko"); } - fn get_existing_style(&self) -> Option<Arc<ComputedValues>> { - self.borrow_data().and_then(|x| x.style.clone()) - } - - fn set_style(&self, style: Arc<ComputedValues>) { - self.mutate_data().unwrap().style = Some(style); - } - - fn take_pseudo_styles(&self) -> PseudoStyles { - use std::mem; - let mut tmp = PseudoStyles::default(); - mem::swap(&mut tmp, &mut self.mutate_data().unwrap().per_pseudo); - tmp - } - - fn set_pseudo_styles(&self, styles: PseudoStyles) { - debug_assert!(self.borrow_data().unwrap().per_pseudo.is_empty()); - self.mutate_data().unwrap().per_pseudo = styles; + fn begin_styling(&self) -> AtomicRefMut<NodeData> { + let mut data = self.ensure_node_data().borrow_mut(); + data.gather_previous_styles(|| self.styles_from_frame()); + data } fn style_text_node(&self, style: Arc<ComputedValues>) { debug_assert!(self.is_text_node()); - self.mutate_data().unwrap().style = Some(style); + self.ensure_node_data().borrow_mut().style_text_node(style); + } + + fn borrow_data(&self) -> Option<AtomicRef<NodeData>> { + self.get_node_data().map(|x| x.borrow()) } fn restyle_damage(self) -> Self::ConcreteRestyleDamage { diff --git a/components/style/gecko_bindings/structs_debug.rs b/components/style/gecko_bindings/structs_debug.rs index d447674ea1a..1997c09f806 100644 --- a/components/style/gecko_bindings/structs_debug.rs +++ b/components/style/gecko_bindings/structs_debug.rs @@ -1,10 +1,11 @@ /* automatically generated by rust-bindgen */ -pub enum OpaqueStyleData {} +use atomic_refcell::AtomicRefCell; +use data::NodeData; pub use nsstring::nsStringRepr as nsString; pub type ServoUnsafeCell<T> = ::std::cell::UnsafeCell<T>; pub type ServoCell<T> = ::std::cell::Cell<T>; -pub type ServoNodeData = OpaqueStyleData; +pub type ServoNodeData = AtomicRefCell<NodeData>; #[derive(Debug)] #[repr(C)] diff --git a/components/style/gecko_bindings/structs_release.rs b/components/style/gecko_bindings/structs_release.rs index 91d155aea95..eb66f803531 100644 --- a/components/style/gecko_bindings/structs_release.rs +++ b/components/style/gecko_bindings/structs_release.rs @@ -1,10 +1,11 @@ /* automatically generated by rust-bindgen */ -pub enum OpaqueStyleData {} +use atomic_refcell::AtomicRefCell; +use data::NodeData; pub use nsstring::nsStringRepr as nsString; pub type ServoUnsafeCell<T> = ::std::cell::UnsafeCell<T>; pub type ServoCell<T> = ::std::cell::Cell<T>; -pub type ServoNodeData = OpaqueStyleData; +pub type ServoNodeData = AtomicRefCell<NodeData>; #[derive(Debug)] #[repr(C)] diff --git a/components/style/matching.rs b/components/style/matching.rs index bc69e158f79..6f92e3e9144 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -11,6 +11,7 @@ use arc_ptr_eq; use cache::{LRUCache, SimpleHashCache}; use cascade_info::CascadeInfo; use context::{SharedStyleContext, StyleContext}; +use data::{NodeStyles, PseudoStyles}; use dom::{NodeInfo, TElement, TNode, TRestyleDamage, UnsafeNode}; use properties::{ComputedValues, cascade}; use properties::longhands::display::computed_value as display; @@ -23,6 +24,7 @@ use sink::ForgetfulSink; use smallvec::SmallVec; use std::collections::HashMap; use std::hash::{BuildHasherDefault, Hash, Hasher}; +use std::mem; use std::ops::Deref; use std::slice::IterMut; use std::sync::Arc; @@ -440,7 +442,8 @@ impl StyleSharingCandidateCache { } let node = element.as_node(); - let style = node.get_existing_style().unwrap(); + let data = node.borrow_data().unwrap(); + let style = &data.current_styles().primary; let box_style = style.get_box(); if box_style.transition_property_count() > 0 { @@ -722,13 +725,14 @@ pub trait ElementMatchMethods : TElement { Ok(shared_style) => { // Yay, cache hit. Share the style. let node = self.as_node(); + let mut data = node.begin_styling(); // TODO: add the display: none optimisation here too! Even // better, factor it out/make it a bit more generic so Gecko // can decide more easily if it knows that it's a child of // replaced content, or similar stuff! let damage = - match node.existing_style_for_restyle_damage(node.get_existing_style().as_ref(), None) { + match node.existing_style_for_restyle_damage(data.previous_styles().map(|x| &x.primary), None) { Some(ref source) => { <<Self as TElement>::ConcreteNode as TNode> ::ConcreteRestyleDamage::compute(source, &shared_style) @@ -745,7 +749,7 @@ pub trait ElementMatchMethods : TElement { RestyleResult::Continue }; - node.set_style(shared_style); + data.finish_styling(NodeStyles::new(shared_style)); return StyleSharingResult::StyleWasShared(i, damage, restyle_result) } @@ -879,7 +883,8 @@ pub trait MatchMethods : TNode { where Ctx: StyleContext<'a> { // Get our parent's style. - let parent_style = parent.as_ref().map(|x| x.get_existing_style().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); // In the case we're styling a text node, we don't need to compute the // restyle damage, since it's a subset of the restyle damage of the @@ -890,63 +895,69 @@ pub trait MatchMethods : TNode { // In Servo, this is also true, since text nodes generate UnscannedText // fragments, which aren't repairable by incremental layout. if self.is_text_node() { - self.style_text_node(ComputedValues::style_for_child_text_node(parent_style.as_ref().unwrap())); - + self.style_text_node(ComputedValues::style_for_child_text_node(parent_style.clone().unwrap())); return RestyleResult::Continue; } + let mut data = self.begin_styling(); + let mut new_styles; + let mut applicable_declarations_cache = context.local_context().applicable_declarations_cache.borrow_mut(); let (damage, restyle_result) = { - // Compute the parameters for the cascade. - let mut old_style = self.get_existing_style(); - let cacheable = match old_style { - None => true, - Some(ref mut old) => { - // Update animations before the cascade. This may modify - // the value of old_style. - !self.update_animations_for_cascade(context.shared_context(), old) - }, - }; + // Update animations before the cascade. This may modify the value of the old primary + // style. + let cacheable = data.previous_styles_mut().map_or(true, + |x| !self.update_animations_for_cascade(context.shared_context(), &mut x.primary)); let shareable = applicable_declarations.normal_shareable; + let (old_primary, old_pseudos) = match data.previous_styles_mut() { + None => (None, None), + Some(x) => (Some(&x.primary), Some(&mut x.pseudos)), + }; - let new_style = + new_styles = NodeStyles::new( self.cascade_node_pseudo_element(context, - parent_style.as_ref(), - old_style.as_ref(), + parent_style.clone(), + old_primary, &applicable_declarations.normal, &mut applicable_declarations_cache, CascadeBooleans { shareable: shareable, cacheable: cacheable, animate: true, - }); + })); let (damage, restyle_result) = - self.compute_damage_and_cascade_pseudos(&new_style, old_style.as_ref(), + self.compute_damage_and_cascade_pseudos(old_primary, + old_pseudos, + &new_styles.primary, + &mut new_styles.pseudos, context, applicable_declarations, &mut applicable_declarations_cache); - self.set_style(new_style); - self.set_can_be_fragmented(parent.map_or(false, |p| { p.can_be_fragmented() || - parent_style.as_ref().unwrap().is_multicol() + parent_style.unwrap().is_multicol() })); (damage, restyle_result) }; + data.finish_styling(new_styles); + // Drop the mutable borrow early, since Servo's set_restyle_damage also borrows. + mem::drop(data); self.set_restyle_damage(damage); restyle_result } fn compute_damage_and_cascade_pseudos<'a, Ctx>(&self, - new_style: &Arc<ComputedValues>, - old_style: Option<&Arc<ComputedValues>>, + old_primary: Option<&Arc<ComputedValues>>, + mut old_pseudos: Option<&mut PseudoStyles>, + new_primary: &Arc<ComputedValues>, + new_pseudos: &mut PseudoStyles, context: &Ctx, applicable_declarations: &ApplicableDeclarations, mut applicable_declarations_cache: &mut ApplicableDeclarationsCache) @@ -957,10 +968,10 @@ pub trait MatchMethods : TNode { // previous and the new styles having display: none. In this // case, we can always optimize the traversal, regardless of the // restyle hint. - let this_display = new_style.get_box().clone_display(); + let this_display = new_primary.get_box().clone_display(); if this_display == display::T::none { - let old_display = old_style.map(|old_style| { - old_style.get_box().clone_display() + let old_display = old_primary.map(|old| { + old.get_box().clone_display() }); // If display passed from none to something, then we need to reflow, @@ -981,13 +992,13 @@ pub trait MatchMethods : TNode { // Otherwise, we just compute the damage normally, and sum up the damage // related to pseudo-elements. let mut damage = - self.compute_restyle_damage(old_style, new_style, None); + self.compute_restyle_damage(old_primary, new_primary, None); let rebuild_and_reflow = Self::ConcreteRestyleDamage::rebuild_and_reflow(); let no_damage = Self::ConcreteRestyleDamage::empty(); - let mut pseudo_styles = self.take_pseudo_styles(); + debug_assert!(new_pseudos.is_empty()); <Self::ConcreteElement as MatchAttr>::Impl::each_eagerly_cascaded_pseudo_element(|pseudo| { let applicable_declarations_for_this_pseudo = applicable_declarations.per_pseudo.get(&pseudo).unwrap(); @@ -995,9 +1006,8 @@ pub trait MatchMethods : TNode { let has_declarations = !applicable_declarations_for_this_pseudo.is_empty(); - // The old entry will be replaced. Remove it from the map but keep - // it for analysis. - let mut old_pseudo_style = pseudo_styles.remove(&pseudo); + // Grab the old pseudo style for analysis. + let mut old_pseudo_style = old_pseudos.as_mut().and_then(|x| x.remove(&pseudo)); if has_declarations { // We have declarations, so we need to cascade. Compute parameters. @@ -1013,7 +1023,7 @@ pub trait MatchMethods : TNode { }; let new_pseudo_style = - self.cascade_node_pseudo_element(context, Some(new_style), + self.cascade_node_pseudo_element(context, Some(new_primary), old_pseudo_style.as_ref(), &*applicable_declarations_for_this_pseudo, &mut applicable_declarations_cache, @@ -1033,7 +1043,7 @@ pub trait MatchMethods : TNode { } // Insert the new entry into the map. - let existing = pseudo_styles.insert(pseudo, new_pseudo_style); + let existing = new_pseudos.insert(pseudo, new_pseudo_style); debug_assert!(existing.is_none()); } else { damage = damage | match old_pseudo_style { @@ -1043,8 +1053,6 @@ pub trait MatchMethods : TNode { } }); - self.set_pseudo_styles(pseudo_styles); - (damage, RestyleResult::Continue) } } diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 05033730753..0405c2f90bd 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -249,10 +249,12 @@ fn ensure_node_styled_internal<'a, N, C>(node: N, // // We only need to mark whether we have display none, and forget about it, // our style is up to date. - if let Some(ref style) = node.get_existing_style() { - if !*parents_had_display_none { - *parents_had_display_none = style.get_box().clone_display() == display::T::none; - return; + if let Some(data) = node.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; + return; + } } } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index c540e21684f..679295aa040 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -253,8 +253,9 @@ pub extern "C" fn Servo_StyleSheet_Release(sheet: RawServoStyleSheetBorrowed) -> pub extern "C" fn Servo_ComputedValues_Get(node: RawGeckoNodeBorrowed) -> ServoComputedValuesStrong { let node = GeckoNode(node); - let arc_cv = match node.get_existing_style() { - Some(style) => style, + let data = node.borrow_data(); + let arc_cv = match data.as_ref().and_then(|x| x.get_current_styles()) { + Some(styles) => styles.primary.clone(), None => { // FIXME(bholley): This case subverts the intended semantics of this // function, and exists only to make stylo builds more robust corner- |