aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout/wrapper.rs2
-rw-r--r--components/script/layout_wrapper.rs47
-rw-r--r--components/script_layout_interface/lib.rs8
-rw-r--r--components/script_layout_interface/wrapper_traits.rs51
-rwxr-xr-xcomponents/style/binding_tools/regen.py6
-rw-r--r--components/style/data.rs188
-rw-r--r--components/style/dom.rs32
-rw-r--r--components/style/gecko/traversal.rs4
-rw-r--r--components/style/gecko/wrapper.rs99
-rw-r--r--components/style/gecko_bindings/structs_debug.rs5
-rw-r--r--components/style/gecko_bindings/structs_release.rs5
-rw-r--r--components/style/matching.rs84
-rw-r--r--components/style/traversal.rs10
-rw-r--r--ports/geckolib/glue.rs5
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-