mirror of
https://github.com/servo/servo.git
synced 2025-08-15 18:35:33 +01:00
Replace begin_styling with a centralized mechanism to set a node up for either styling or restyling.
We also make sure that an element never has an ElementData with ElementDataStyles::Uninitialized, and eagerly call prepare_for_styling whenever an ElementData is instantiated. MozReview-Commit-ID: 9YP6eSmdMt0
This commit is contained in:
parent
b98bb241dc
commit
71b9004d86
10 changed files with 129 additions and 104 deletions
|
@ -27,10 +27,10 @@ impl<T> AtomicRefCell<T> {
|
|||
AtomicRefCell(RwLock::new(value))
|
||||
}
|
||||
pub fn borrow(&self) -> AtomicRef<T> {
|
||||
self.0.try_read().unwrap()
|
||||
self.0.try_read().expect("already mutably borrowed")
|
||||
}
|
||||
pub fn borrow_mut(&self) -> AtomicRefMut<T> {
|
||||
self.0.try_write().unwrap()
|
||||
self.0.try_write().expect("already borrowed")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||
use atomic_refcell::{AtomicRef, AtomicRefCell};
|
||||
use data::{ElementStyles, ElementData};
|
||||
use element_state::ElementState;
|
||||
use parking_lot::RwLock;
|
||||
|
@ -243,30 +243,43 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
|||
Stop
|
||||
};
|
||||
|
||||
match self.borrow_data() {
|
||||
// No node data, no style on the frame.
|
||||
let mut mode = match self.borrow_data() {
|
||||
// No element data, no style on the frame.
|
||||
None if !self.frame_has_style() => Initial,
|
||||
// No node data, style on the frame.
|
||||
// No element data, style on the frame.
|
||||
None => mode_for_descendants,
|
||||
// We have element data. Decide below.
|
||||
Some(d) => {
|
||||
if d.restyle_data.is_some() || self.deprecated_dirty_bit_is_set() {
|
||||
Restyle
|
||||
} else {
|
||||
debug_assert!(!self.frame_has_style()); // display:none etc
|
||||
if d.has_current_styles() {
|
||||
// The element has up-to-date style.
|
||||
debug_assert!(!self.frame_has_style());
|
||||
debug_assert!(d.restyle_data.is_none());
|
||||
mode_for_descendants
|
||||
} else {
|
||||
// The element needs processing.
|
||||
if d.previous_styles().is_some() {
|
||||
Restyle
|
||||
} else {
|
||||
Initial
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// 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<ElementData>;
|
||||
// Handle the deprecated dirty bit. This should go away soon.
|
||||
if mode != Initial && self.deprecated_dirty_bit_is_set() {
|
||||
mode = Restyle;
|
||||
}
|
||||
mode
|
||||
|
||||
}
|
||||
|
||||
/// Immutable borrows the ElementData.
|
||||
fn borrow_data(&self) -> Option<AtomicRef<ElementData>>;
|
||||
|
||||
/// Gets a reference to the ElementData container.
|
||||
fn get_data(&self) -> Option<&AtomicRefCell<ElementData>>;
|
||||
|
||||
/// Properly marks nodes as dirty in response to restyle hints.
|
||||
fn note_restyle_hint<C: DomTraversalContext<Self::ConcreteNode>>(&self, hint: RestyleHint) {
|
||||
// Bail early if there's no restyling to do.
|
||||
|
@ -285,13 +298,13 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
|||
|
||||
// Process hints.
|
||||
if hint.contains(RESTYLE_SELF) {
|
||||
unsafe { C::ensure_element_data(self).borrow_mut().ensure_restyle_data(); }
|
||||
unsafe { let _ = C::prepare_for_styling(self); }
|
||||
// XXX(emilio): For now, dirty implies dirty descendants if found.
|
||||
} else if hint.contains(RESTYLE_DESCENDANTS) {
|
||||
unsafe { self.set_dirty_descendants(); }
|
||||
let mut current = self.first_child_element();
|
||||
while let Some(el) = current {
|
||||
unsafe { C::ensure_element_data(&el).borrow_mut().ensure_restyle_data(); }
|
||||
unsafe { let _ = C::prepare_for_styling(&el); }
|
||||
current = el.next_sibling_element();
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +312,7 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
|||
if hint.contains(RESTYLE_LATER_SIBLINGS) {
|
||||
let mut next = ::selectors::Element::next_sibling_element(self);
|
||||
while let Some(sib) = next {
|
||||
unsafe { C::ensure_element_data(&sib).borrow_mut().ensure_restyle_data() };
|
||||
unsafe { let _ = C::prepare_for_styling(&sib); }
|
||||
next = ::selectors::Element::next_sibling_element(&sib);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ impl<'lc, 'ln> DomTraversalContext<GeckoNode<'ln>> for RecalcStyleOnly<'lc> {
|
|||
}
|
||||
}
|
||||
|
||||
fn ensure_element_data<'a>(element: &'a GeckoElement<'ln>) -> &'a AtomicRefCell<ElementData> {
|
||||
unsafe fn ensure_element_data<'a>(element: &'a GeckoElement<'ln>) -> &'a AtomicRefCell<ElementData> {
|
||||
element.ensure_data()
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#![allow(unsafe_code)]
|
||||
|
||||
|
||||
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
||||
use atomic_refcell::{AtomicRef, AtomicRefCell};
|
||||
use data::ElementData;
|
||||
use dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
|
||||
use dom::{OpaqueNode, PresentationalHintsSynthetizer};
|
||||
|
@ -337,12 +337,8 @@ impl<'le> GeckoElement<'le> {
|
|||
.get(pseudo).map(|c| c.clone()))
|
||||
}
|
||||
|
||||
fn get_node_data(&self) -> Option<&AtomicRefCell<ElementData>> {
|
||||
unsafe { self.raw_node().mServoData.get().as_ref() }
|
||||
}
|
||||
|
||||
pub fn ensure_data(&self) -> &AtomicRefCell<ElementData> {
|
||||
match self.get_node_data() {
|
||||
match self.get_data() {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::new())));
|
||||
|
@ -431,7 +427,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
fn has_dirty_descendants(&self) -> bool {
|
||||
// Return true unconditionally if we're not yet styled. This is a hack
|
||||
// and should go away soon.
|
||||
if self.get_node_data().is_none() {
|
||||
if self.get_data().is_none() {
|
||||
return true;
|
||||
}
|
||||
self.flags() & (NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO as u32) != 0
|
||||
|
@ -449,15 +445,14 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
panic!("Atomic child count not implemented in Gecko");
|
||||
}
|
||||
|
||||
fn begin_styling(&self) -> AtomicRefMut<ElementData> {
|
||||
let mut data = self.ensure_data().borrow_mut();
|
||||
data.gather_previous_styles(|| self.get_styles_from_frame());
|
||||
data
|
||||
fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
|
||||
self.get_data().map(|x| x.borrow())
|
||||
}
|
||||
|
||||
fn borrow_data(&self) -> Option<AtomicRef<ElementData>> {
|
||||
self.get_node_data().map(|x| x.borrow())
|
||||
fn get_data(&self) -> Option<&AtomicRefCell<ElementData>> {
|
||||
unsafe { self.raw_node().mServoData.get().as_ref() }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<'le> PartialEq for GeckoElement<'le> {
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
|
||||
use animation;
|
||||
use arc_ptr_eq;
|
||||
use atomic_refcell::AtomicRefMut;
|
||||
use cache::{LRUCache, SimpleHashCache};
|
||||
use cascade_info::CascadeInfo;
|
||||
use context::{SharedStyleContext, StyleContext};
|
||||
use data::{ElementStyles, PseudoStyles};
|
||||
use data::{ElementData, ElementStyles, PseudoStyles};
|
||||
use dom::{TElement, TNode, TRestyleDamage, UnsafeNode};
|
||||
use properties::{CascadeFlags, ComputedValues, SHAREABLE, cascade};
|
||||
use properties::longhands::display::computed_value as display;
|
||||
|
@ -423,6 +424,7 @@ impl StyleSharingCandidateCache {
|
|||
|
||||
pub fn insert_if_possible<E: TElement>(&mut self,
|
||||
element: &E,
|
||||
style: &Arc<ComputedValues>,
|
||||
relations: StyleRelations) {
|
||||
use traversal::relations_are_shareable;
|
||||
|
||||
|
@ -441,9 +443,6 @@ impl StyleSharingCandidateCache {
|
|||
return;
|
||||
}
|
||||
|
||||
let data = element.borrow_data().unwrap();
|
||||
let style = &data.current_styles().primary;
|
||||
|
||||
let box_style = style.get_box();
|
||||
if box_style.transition_property_count() > 0 {
|
||||
debug!("Failing to insert to the cache: transitions");
|
||||
|
@ -695,7 +694,8 @@ pub trait MatchMethods : TElement {
|
|||
unsafe fn share_style_if_possible(&self,
|
||||
style_sharing_candidate_cache:
|
||||
&mut StyleSharingCandidateCache,
|
||||
shared_context: &SharedStyleContext)
|
||||
shared_context: &SharedStyleContext,
|
||||
data: &mut AtomicRefMut<ElementData>)
|
||||
-> StyleSharingResult<Self::ConcreteRestyleDamage> {
|
||||
if opts::get().disable_share_style_cache {
|
||||
return StyleSharingResult::CannotShare
|
||||
|
@ -715,7 +715,6 @@ pub trait MatchMethods : TElement {
|
|||
match sharing_result {
|
||||
Ok(shared_style) => {
|
||||
// Yay, cache hit. Share the style.
|
||||
let mut data = self.begin_styling();
|
||||
|
||||
// TODO: add the display: none optimisation here too! Even
|
||||
// better, factor it out/make it a bit more generic so Gecko
|
||||
|
@ -855,6 +854,7 @@ pub trait MatchMethods : TElement {
|
|||
|
||||
unsafe fn cascade_node<'a, Ctx>(&self,
|
||||
context: &Ctx,
|
||||
mut data: AtomicRefMut<ElementData>,
|
||||
parent: Option<Self>,
|
||||
applicable_declarations: &ApplicableDeclarations)
|
||||
-> RestyleResult
|
||||
|
@ -864,7 +864,6 @@ pub trait MatchMethods : TElement {
|
|||
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);
|
||||
|
||||
let mut data = self.begin_styling();
|
||||
let mut new_styles;
|
||||
|
||||
let mut applicable_declarations_cache =
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
//! Traversing the DOM tree; the bloom filter.
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||
use data::ElementData;
|
||||
use dom::{OpaqueNode, StylingMode, TElement, TNode, UnsafeNode};
|
||||
|
@ -12,6 +12,7 @@ use matching::{ApplicableDeclarations, MatchMethods, StyleSharingResult};
|
|||
use selectors::bloom::BloomFilter;
|
||||
use selectors::matching::StyleRelations;
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
use tid::tid;
|
||||
use util::opts;
|
||||
|
@ -156,6 +157,18 @@ pub fn remove_from_bloom_filter<'a, N, C>(context: &C, root: OpaqueNode, node: N
|
|||
};
|
||||
}
|
||||
|
||||
pub fn prepare_for_styling<E: TElement>(element: E,
|
||||
data: &AtomicRefCell<ElementData>)
|
||||
-> AtomicRefMut<ElementData> {
|
||||
let mut d = data.borrow_mut();
|
||||
d.gather_previous_styles(|| element.get_styles_from_frame());
|
||||
if d.previous_styles().is_some() {
|
||||
d.ensure_restyle_data();
|
||||
}
|
||||
|
||||
d
|
||||
}
|
||||
|
||||
pub trait DomTraversalContext<N: TNode> {
|
||||
type SharedContext: Sync + 'static;
|
||||
|
||||
|
@ -200,7 +213,17 @@ pub trait DomTraversalContext<N: TNode> {
|
|||
/// Ensures the existence of the ElementData, and returns it. This can't live
|
||||
/// on TNode because of the trait-based separation between Servo's script
|
||||
/// and layout crates.
|
||||
fn ensure_element_data(element: &N::ConcreteElement) -> &AtomicRefCell<ElementData>;
|
||||
///
|
||||
/// This is only safe to call in top-down traversal before processing the
|
||||
/// children of |element|.
|
||||
unsafe fn ensure_element_data(element: &N::ConcreteElement) -> &AtomicRefCell<ElementData>;
|
||||
|
||||
/// Sets up the appropriate data structures to style or restyle a node,
|
||||
/// returing a mutable handle to the node data upon which further style
|
||||
/// calculations can be performed.
|
||||
unsafe fn prepare_for_styling(element: &N::ConcreteElement) -> AtomicRefMut<ElementData> {
|
||||
prepare_for_styling(*element, Self::ensure_element_data(element))
|
||||
}
|
||||
|
||||
fn local_context(&self) -> &LocalStyleContext;
|
||||
}
|
||||
|
@ -267,6 +290,7 @@ fn ensure_element_styled_internal<'a, E, C>(element: E,
|
|||
// probably not necessary since we're likely to be matching only a few
|
||||
// nodes, at best.
|
||||
let mut applicable_declarations = ApplicableDeclarations::new();
|
||||
let data = prepare_for_styling(element, element.get_data().unwrap());
|
||||
let stylist = &context.shared_context().stylist;
|
||||
|
||||
element.match_element(&**stylist,
|
||||
|
@ -274,7 +298,7 @@ fn ensure_element_styled_internal<'a, E, C>(element: E,
|
|||
&mut applicable_declarations);
|
||||
|
||||
unsafe {
|
||||
element.cascade_node(context, parent, &applicable_declarations);
|
||||
element.cascade_node(context, data, parent, &applicable_declarations);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,6 +319,8 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
|
|||
let mode = element.styling_mode();
|
||||
debug_assert!(mode != StylingMode::Stop, "Parent should not have enqueued us");
|
||||
if mode != StylingMode::Traverse {
|
||||
let mut data = unsafe { D::prepare_for_styling(&element) };
|
||||
|
||||
// Check to see whether we can share a style with someone.
|
||||
let style_sharing_candidate_cache =
|
||||
&mut context.local_context().style_sharing_candidate_cache.borrow_mut();
|
||||
|
@ -302,7 +328,8 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
|
|||
let sharing_result = if element.parent_element().is_none() {
|
||||
StyleSharingResult::CannotShare
|
||||
} else {
|
||||
unsafe { element.share_style_if_possible(style_sharing_candidate_cache, context.shared_context()) }
|
||||
unsafe { element.share_style_if_possible(style_sharing_candidate_cache,
|
||||
context.shared_context(), &mut data) }
|
||||
};
|
||||
|
||||
// Otherwise, match and cascade selectors.
|
||||
|
@ -334,14 +361,19 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
|
|||
|
||||
// Perform the CSS cascade.
|
||||
unsafe {
|
||||
restyle_result = element.cascade_node(context,
|
||||
restyle_result = element.cascade_node(context, data,
|
||||
element.parent_element(),
|
||||
&applicable_declarations);
|
||||
}
|
||||
|
||||
// Add ourselves to the LRU cache.
|
||||
if let Some(element) = shareable_element {
|
||||
style_sharing_candidate_cache.insert_if_possible(&element, relations);
|
||||
style_sharing_candidate_cache.insert_if_possible(&element,
|
||||
&element.borrow_data()
|
||||
.unwrap()
|
||||
.current_styles()
|
||||
.primary,
|
||||
relations);
|
||||
}
|
||||
}
|
||||
StyleSharingResult::StyleWasShared(index, damage, cached_restyle_result) => {
|
||||
|
@ -350,6 +382,10 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
|
|||
STYLE_SHARING_CACHE_HITS.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
style_sharing_candidate_cache.touch(index);
|
||||
|
||||
// Drop the mutable borrow early, since Servo's set_restyle_damage also borrows.
|
||||
mem::drop(data);
|
||||
|
||||
element.set_restyle_damage(damage);
|
||||
}
|
||||
}
|
||||
|
@ -361,11 +397,7 @@ pub fn recalc_style_at<'a, E, C, D>(context: &'a C,
|
|||
if mode == StylingMode::Restyle && restyle_result == RestyleResult::Continue {
|
||||
for kid in element.as_node().children() {
|
||||
if let Some(kid) = kid.as_element() {
|
||||
let mut data = D::ensure_element_data(&kid).borrow_mut();
|
||||
data.gather_previous_styles(|| kid.get_styles_from_frame());
|
||||
if data.previous_styles().is_some() {
|
||||
data.ensure_restyle_data();
|
||||
}
|
||||
unsafe { let _ = D::prepare_for_styling(&kid); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue