mirror of
https://github.com/servo/servo.git
synced 2025-07-25 00:00:20 +01:00
Rearrange some data structures in preparation for the new incremental restyle algorithm.
MozReview-Commit-ID: 8iOALQylOuK
This commit is contained in:
parent
6d29bf3f80
commit
adf0fe9b9a
14 changed files with 333 additions and 219 deletions
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>",
|
||||
}
|
||||
],
|
||||
},
|
||||
|
|
|
@ -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>,
|
||||
type PseudoStylesInner = HashMap<PseudoElement, Arc<ComputedValues>,
|
||||
BuildHasherDefault<::fnv::FnvHasher>>;
|
||||
pub struct PersistentStyleData {
|
||||
#[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 {
|
||||
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 {
|
||||
PersistentStyleData {
|
||||
style: None,
|
||||
per_pseudo: HashMap::with_hasher(Default::default()),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 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 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 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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 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)));
|
||||
}
|
||||
}
|
||||
|
||||
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) };
|
||||
pub fn clear_data(&self) {
|
||||
let ptr = self.0.mServoData.get();
|
||||
if !ptr.is_null() {
|
||||
let data = unsafe { Box::from_raw(self.0.mServoData.get()) };
|
||||
self.0.mServoData.set(ptr::null_mut());
|
||||
|
||||
// Perform a mutable borrow of the data in debug builds. This
|
||||
// serves as an assertion that there are no outstanding borrows
|
||||
// when we destroy the data.
|
||||
debug_assert!({ let _ = data.borrow_mut(); true });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pseudo_style(&self, pseudo: &PseudoElement) -> Option<Arc<ComputedValues>> {
|
||||
self.borrow_data().and_then(|data| data.per_pseudo.get(pseudo).map(|c| c.clone()))
|
||||
self.borrow_data().and_then(|data| data.current_styles().pseudos
|
||||
.get(pseudo).map(|c| c.clone()))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn borrow_data(&self) -> Option<AtomicRef<PersistentStyleData>> {
|
||||
self.get_node_data().as_ref().map(|d| d.0.borrow())
|
||||
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 mutate_data(&self) -> Option<AtomicRefMut<PersistentStyleData>> {
|
||||
self.get_node_data().as_ref().map(|d| d.0.borrow_mut())
|
||||
fn get_node_data(&self) -> Option<&AtomicRefCell<NodeData>> {
|
||||
unsafe { self.0.mServoData.get().as_ref() }
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -249,12 +249,14 @@ 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, our style might be out of date. Time to do selector matching
|
||||
// if appropriate and cascade the node.
|
||||
|
|
|
@ -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-
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue