mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Introduce StylingMode and deprecate explicit dirtiness.
MozReview-Commit-ID: 5tF075EJKBa
This commit is contained in:
parent
8bd7978980
commit
05c1f1e016
11 changed files with 204 additions and 116 deletions
|
@ -13,8 +13,10 @@ use gfx::display_list::OpaqueNode;
|
||||||
use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
|
use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage};
|
||||||
use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
|
use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use style::atomic_refcell::AtomicRefCell;
|
||||||
use style::context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
use style::context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||||
use style::dom::TNode;
|
use style::data::NodeData;
|
||||||
|
use style::dom::{TNode, TRestyleDamage};
|
||||||
use style::selector_impl::ServoSelectorImpl;
|
use style::selector_impl::ServoSelectorImpl;
|
||||||
use style::traversal::{DomTraversalContext, recalc_style_at, remove_from_bloom_filter};
|
use style::traversal::{DomTraversalContext, recalc_style_at, remove_from_bloom_filter};
|
||||||
use style::traversal::RestyleResult;
|
use style::traversal::RestyleResult;
|
||||||
|
@ -75,13 +77,18 @@ impl<'lc, N> DomTraversalContext<N> for RecalcStyleAndConstructFlows<'lc>
|
||||||
// done by the HTML parser.
|
// done by the HTML parser.
|
||||||
node.initialize_data();
|
node.initialize_data();
|
||||||
|
|
||||||
recalc_style_at(&self.context, self.root, node)
|
recalc_style_at::<_, _, Self>(&self.context, self.root, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_postorder(&self, node: N) {
|
fn process_postorder(&self, node: N) {
|
||||||
construct_flows_at(&self.context, self.root, node);
|
construct_flows_at(&self.context, self.root, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_node_data(node: &N) -> &AtomicRefCell<NodeData> {
|
||||||
|
node.initialize_data();
|
||||||
|
node.get_style_data().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn local_context(&self) -> &LocalStyleContext {
|
fn local_context(&self) -> &LocalStyleContext {
|
||||||
self.context.local_context()
|
self.context.local_context()
|
||||||
}
|
}
|
||||||
|
@ -103,7 +110,8 @@ fn construct_flows_at<'a, N: LayoutNode>(context: &'a LayoutContext<'a>, root: O
|
||||||
|
|
||||||
// Always reconstruct if incremental layout is turned off.
|
// Always reconstruct if incremental layout is turned off.
|
||||||
let nonincremental_layout = opts::get().nonincremental_layout;
|
let nonincremental_layout = opts::get().nonincremental_layout;
|
||||||
if nonincremental_layout || node.is_dirty() || node.has_dirty_descendants() {
|
if nonincremental_layout || node.has_dirty_descendants() ||
|
||||||
|
node.restyle_damage() != N::ConcreteRestyleDamage::empty() {
|
||||||
let mut flow_constructor = FlowConstructor::new(context);
|
let mut flow_constructor = FlowConstructor::new(context);
|
||||||
if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) {
|
if nonincremental_layout || !flow_constructor.repair_if_possible(&tnode) {
|
||||||
flow_constructor.process(&tnode);
|
flow_constructor.process(&tnode);
|
||||||
|
|
|
@ -1146,7 +1146,7 @@ impl LayoutThread {
|
||||||
if !needs_dirtying {
|
if !needs_dirtying {
|
||||||
for (el, snapshot) in modified_elements {
|
for (el, snapshot) in modified_elements {
|
||||||
let hint = rw_data.stylist.compute_restyle_hint(&el, &snapshot, el.get_state());
|
let hint = rw_data.stylist.compute_restyle_hint(&el, &snapshot, el.get_state());
|
||||||
el.note_restyle_hint(hint);
|
el.note_restyle_hint::<RecalcStyleAndConstructFlows>(hint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,14 +186,10 @@ impl<'ln> TNode for ServoLayoutNode<'ln> {
|
||||||
self.node.downcast().map(ServoLayoutDocument::from_layout_js)
|
self.node.downcast().map(ServoLayoutDocument::from_layout_js)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_dirty(&self) -> bool {
|
fn deprecated_dirty_bit_is_set(&self) -> bool {
|
||||||
unsafe { self.node.get_flag(IS_DIRTY) }
|
unsafe { self.node.get_flag(IS_DIRTY) }
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn set_dirty(&self) {
|
|
||||||
self.node.set_flag(IS_DIRTY, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_dirty_descendants(&self) -> bool {
|
fn has_dirty_descendants(&self) -> bool {
|
||||||
unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) }
|
unsafe { self.node.get_flag(HAS_DIRTY_DESCENDANTS) }
|
||||||
}
|
}
|
||||||
|
@ -242,6 +238,20 @@ impl<'ln> TNode for ServoLayoutNode<'ln> {
|
||||||
data.style_data.style_text_node(style);
|
data.style_data.style_text_node(style);
|
||||||
if self.has_changed() {
|
if self.has_changed() {
|
||||||
data.restyle_damage = RestyleDamage::rebuild_and_reflow();
|
data.restyle_damage = RestyleDamage::rebuild_and_reflow();
|
||||||
|
} else {
|
||||||
|
// FIXME(bholley): This is necessary to make it correct to use restyle
|
||||||
|
// damage in construct_flows_at to determine whether to reconstruct
|
||||||
|
// text nodes. Without it, we fail cascade-import-dynamic-002.htm.
|
||||||
|
//
|
||||||
|
// Long-term, We should teach layout how to correctly propagate
|
||||||
|
// style changes from elements to child text nodes so that we don't
|
||||||
|
// need to do this explicitly here. This will likely all be rolled
|
||||||
|
// into a patch where we stop styling text nodes from the style
|
||||||
|
// system and instead generate the styles on the fly during frame
|
||||||
|
// construction / repair.
|
||||||
|
let parent = self.parent_node().unwrap();
|
||||||
|
let parent_data = parent.get_partial_layout_data().unwrap().borrow();
|
||||||
|
data.restyle_damage = parent_data.restyle_damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,6 +368,14 @@ impl<'ln> LayoutNode for ServoLayoutNode<'ln> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ln> ServoLayoutNode<'ln> {
|
impl<'ln> ServoLayoutNode<'ln> {
|
||||||
|
pub fn is_dirty(&self) -> bool {
|
||||||
|
unsafe { self.node.get_flag(IS_DIRTY) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set_dirty(&self) {
|
||||||
|
self.node.set_flag(IS_DIRTY, true)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_partial_layout_data(&self) -> Option<&AtomicRefCell<PartialPersistentLayoutData>> {
|
fn get_partial_layout_data(&self) -> Option<&AtomicRefCell<PartialPersistentLayoutData>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_jsmanaged().get_style_and_layout_data().map(|d| {
|
self.get_jsmanaged().get_style_and_layout_data().map(|d| {
|
||||||
|
@ -397,7 +415,9 @@ impl<'ln> ServoLayoutNode<'ln> {
|
||||||
|
|
||||||
fn debug_str(self) -> String {
|
fn debug_str(self) -> String {
|
||||||
format!("{:?}: changed={} dirty={} dirty_descendants={}",
|
format!("{:?}: changed={} dirty={} dirty_descendants={}",
|
||||||
self.script_type_id(), self.has_changed(), self.is_dirty(), self.has_dirty_descendants())
|
self.script_type_id(), self.has_changed(),
|
||||||
|
self.deprecated_dirty_bit_is_set(),
|
||||||
|
self.has_dirty_descendants())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_style_str(self) -> String {
|
fn debug_style_str(self) -> String {
|
||||||
|
|
|
@ -115,14 +115,14 @@ impl RestyleData {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NodeData {
|
pub struct NodeData {
|
||||||
styles: NodeDataStyles,
|
styles: NodeDataStyles,
|
||||||
pub restyle: Option<RestyleData>,
|
pub restyle_data: Option<RestyleData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeData {
|
impl NodeData {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
NodeData {
|
NodeData {
|
||||||
styles: NodeDataStyles::Uninitialized,
|
styles: NodeDataStyles::Uninitialized,
|
||||||
restyle: None,
|
restyle_data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,25 +175,24 @@ impl NodeData {
|
||||||
self.styles = match mem::replace(&mut self.styles, Uninitialized) {
|
self.styles = match mem::replace(&mut self.styles, Uninitialized) {
|
||||||
Uninitialized => Previous(f()),
|
Uninitialized => Previous(f()),
|
||||||
Current(x) => Previous(Some(x)),
|
Current(x) => Previous(Some(x)),
|
||||||
_ => panic!("Already have previous styles"),
|
Previous(x) => Previous(x),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(bholley): Called in future patches.
|
|
||||||
pub fn ensure_restyle_data(&mut self) {
|
pub fn ensure_restyle_data(&mut self) {
|
||||||
if self.restyle.is_none() {
|
if self.restyle_data.is_none() {
|
||||||
self.restyle = Some(RestyleData::new());
|
self.restyle_data = Some(RestyleData::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn style_text_node(&mut self, style: Arc<ComputedValues>) {
|
pub fn style_text_node(&mut self, style: Arc<ComputedValues>) {
|
||||||
debug_assert!(self.restyle.is_none());
|
|
||||||
self.styles = NodeDataStyles::Current(NodeStyles::new(style));
|
self.styles = NodeDataStyles::Current(NodeStyles::new(style));
|
||||||
|
self.restyle_data = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_styling(&mut self, styles: NodeStyles) {
|
pub fn finish_styling(&mut self, styles: NodeStyles) {
|
||||||
debug_assert!(self.styles.is_previous());
|
debug_assert!(self.styles.is_previous());
|
||||||
self.styles = NodeDataStyles::Current(styles);
|
self.styles = NodeDataStyles::Current(styles);
|
||||||
self.restyle = None;
|
self.restyle_data = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||||
use data::NodeData;
|
use data::{NodeStyles, NodeData};
|
||||||
use element_state::ElementState;
|
use element_state::ElementState;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use properties::{ComputedValues, PropertyDeclarationBlock};
|
use properties::{ComputedValues, PropertyDeclarationBlock};
|
||||||
|
@ -19,6 +19,8 @@ use std::fmt::Debug;
|
||||||
use std::ops::BitOr;
|
use std::ops::BitOr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use string_cache::{Atom, Namespace};
|
use string_cache::{Atom, Namespace};
|
||||||
|
use traversal::DomTraversalContext;
|
||||||
|
use util::opts;
|
||||||
|
|
||||||
pub use style_traits::UnsafeNode;
|
pub use style_traits::UnsafeNode;
|
||||||
|
|
||||||
|
@ -43,6 +45,19 @@ impl OpaqueNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum StylingMode {
|
||||||
|
/// The node has never been styled before, and needs a full style computation.
|
||||||
|
Initial,
|
||||||
|
/// The node has been styled before, but needs some amount of recomputation.
|
||||||
|
Restyle,
|
||||||
|
/// The node does not need any style processing, but one or more of its
|
||||||
|
/// descendants do.
|
||||||
|
Traverse,
|
||||||
|
/// No nodes in this subtree require style processing.
|
||||||
|
Stop,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait TRestyleDamage : Debug + PartialEq + BitOr<Output=Self> + Copy {
|
pub trait TRestyleDamage : Debug + PartialEq + BitOr<Output=Self> + Copy {
|
||||||
/// The source for our current computed values in the cascade. This is a
|
/// The source for our current computed values in the cascade. This is a
|
||||||
/// ComputedValues in Servo and a StyleContext in Gecko.
|
/// ComputedValues in Servo and a StyleContext in Gecko.
|
||||||
|
@ -117,9 +132,11 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo {
|
||||||
|
|
||||||
fn as_document(&self) -> Option<Self::ConcreteDocument>;
|
fn as_document(&self) -> Option<Self::ConcreteDocument>;
|
||||||
|
|
||||||
fn is_dirty(&self) -> bool;
|
/// The concept of a dirty bit doesn't exist in our new restyle algorithm.
|
||||||
|
/// Instead, we associate restyle and change hints with nodes. However, we
|
||||||
unsafe fn set_dirty(&self);
|
/// continue to allow the dirty bit to trigger unconditional restyles while
|
||||||
|
/// we transition both Servo and Stylo to the new architecture.
|
||||||
|
fn deprecated_dirty_bit_is_set(&self) -> bool;
|
||||||
|
|
||||||
fn has_dirty_descendants(&self) -> bool;
|
fn has_dirty_descendants(&self) -> bool;
|
||||||
|
|
||||||
|
@ -141,6 +158,51 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo {
|
||||||
/// traversal. Returns the number of children left to process.
|
/// traversal. Returns the number of children left to process.
|
||||||
fn did_process_child(&self) -> isize;
|
fn did_process_child(&self) -> isize;
|
||||||
|
|
||||||
|
/// Returns true if this node has a styled layout frame that owns the style.
|
||||||
|
fn frame_has_style(&self) -> bool { false }
|
||||||
|
|
||||||
|
/// Returns the styles from the layout frame that owns them, if any.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
fn get_styles_from_frame(&self) -> Option<NodeStyles> { None }
|
||||||
|
|
||||||
|
/// Returns the styling mode for this node. This is only valid to call before
|
||||||
|
/// and during restyling, before finish_styling is invoked.
|
||||||
|
///
|
||||||
|
/// See the comments around StylingMode.
|
||||||
|
fn styling_mode(&self) -> StylingMode {
|
||||||
|
use self::StylingMode::*;
|
||||||
|
|
||||||
|
// Non-incremental layout impersonates Initial.
|
||||||
|
if opts::get().nonincremental_layout {
|
||||||
|
return Initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the default result if this node doesn't require processing.
|
||||||
|
let mode_for_descendants = if self.has_dirty_descendants() {
|
||||||
|
Traverse
|
||||||
|
} else {
|
||||||
|
Stop
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.borrow_data() {
|
||||||
|
// No node data, no style on the frame.
|
||||||
|
None if !self.frame_has_style() => Initial,
|
||||||
|
// No node data, style on the frame.
|
||||||
|
None => mode_for_descendants,
|
||||||
|
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
|
||||||
|
mode_for_descendants
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets up the appropriate data structures to style a node, returing a
|
/// Sets up the appropriate data structures to style a node, returing a
|
||||||
/// mutable handle to the node data upon which further style calculations
|
/// mutable handle to the node data upon which further style calculations
|
||||||
/// can be performed.
|
/// can be performed.
|
||||||
|
@ -212,7 +274,7 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
||||||
fn attr_equals(&self, namespace: &Namespace, attr: &Atom, value: &Atom) -> bool;
|
fn attr_equals(&self, namespace: &Namespace, attr: &Atom, value: &Atom) -> bool;
|
||||||
|
|
||||||
/// Properly marks nodes as dirty in response to restyle hints.
|
/// Properly marks nodes as dirty in response to restyle hints.
|
||||||
fn note_restyle_hint(&self, hint: RestyleHint) {
|
fn note_restyle_hint<C: DomTraversalContext<Self::ConcreteNode>>(&self, hint: RestyleHint) {
|
||||||
// Bail early if there's no restyling to do.
|
// Bail early if there's no restyling to do.
|
||||||
if hint.is_empty() {
|
if hint.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
@ -230,13 +292,13 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
||||||
|
|
||||||
// Process hints.
|
// Process hints.
|
||||||
if hint.contains(RESTYLE_SELF) {
|
if hint.contains(RESTYLE_SELF) {
|
||||||
unsafe { node.set_dirty(); }
|
unsafe { C::ensure_node_data(&node).borrow_mut().ensure_restyle_data(); }
|
||||||
// XXX(emilio): For now, dirty implies dirty descendants if found.
|
// XXX(emilio): For now, dirty implies dirty descendants if found.
|
||||||
} else if hint.contains(RESTYLE_DESCENDANTS) {
|
} else if hint.contains(RESTYLE_DESCENDANTS) {
|
||||||
unsafe { node.set_dirty_descendants(); }
|
unsafe { node.set_dirty_descendants(); }
|
||||||
let mut current = node.first_child();
|
let mut current = node.first_child();
|
||||||
while let Some(node) = current {
|
while let Some(node) = current {
|
||||||
unsafe { node.set_dirty(); }
|
unsafe { C::ensure_node_data(&node).borrow_mut().ensure_restyle_data(); }
|
||||||
current = node.next_sibling();
|
current = node.next_sibling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +307,7 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre
|
||||||
let mut next = ::selectors::Element::next_sibling_element(self);
|
let mut next = ::selectors::Element::next_sibling_element(self);
|
||||||
while let Some(sib) = next {
|
while let Some(sib) = next {
|
||||||
let sib_node = sib.as_node();
|
let sib_node = sib.as_node();
|
||||||
unsafe { sib_node.set_dirty() };
|
unsafe { C::ensure_node_data(&sib_node).borrow_mut().ensure_restyle_data() };
|
||||||
next = ::selectors::Element::next_sibling_element(&sib);
|
next = ::selectors::Element::next_sibling_element(&sib);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use atomic_refcell::AtomicRefCell;
|
||||||
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||||
|
use data::NodeData;
|
||||||
use dom::OpaqueNode;
|
use dom::OpaqueNode;
|
||||||
use gecko::context::StandaloneStyleContext;
|
use gecko::context::StandaloneStyleContext;
|
||||||
use gecko::wrapper::GeckoNode;
|
use gecko::wrapper::GeckoNode;
|
||||||
|
@ -29,7 +31,7 @@ impl<'lc, 'ln> DomTraversalContext<GeckoNode<'ln>> for RecalcStyleOnly<'lc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_preorder(&self, node: GeckoNode<'ln>) -> RestyleResult {
|
fn process_preorder(&self, node: GeckoNode<'ln>) -> RestyleResult {
|
||||||
recalc_style_at(&self.context, self.root, node)
|
recalc_style_at::<_, _, Self>(&self.context, self.root, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_postorder(&self, _: GeckoNode<'ln>) {
|
fn process_postorder(&self, _: GeckoNode<'ln>) {
|
||||||
|
@ -39,6 +41,10 @@ impl<'lc, 'ln> DomTraversalContext<GeckoNode<'ln>> for RecalcStyleOnly<'lc> {
|
||||||
/// We don't use the post-order traversal for anything.
|
/// We don't use the post-order traversal for anything.
|
||||||
fn needs_postorder_traversal(&self) -> bool { false }
|
fn needs_postorder_traversal(&self) -> bool { false }
|
||||||
|
|
||||||
|
fn ensure_node_data<'a>(node: &'a GeckoNode<'ln>) -> &'a AtomicRefCell<NodeData> {
|
||||||
|
node.ensure_data()
|
||||||
|
}
|
||||||
|
|
||||||
fn local_context(&self) -> &LocalStyleContext {
|
fn local_context(&self) -> &LocalStyleContext {
|
||||||
self.context.local_context()
|
self.context.local_context()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
|
|
||||||
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
||||||
use data::{NodeData, NodeStyles};
|
use data::NodeData;
|
||||||
use dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
|
use dom::{LayoutIterator, NodeInfo, TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
|
||||||
use dom::{OpaqueNode, PresentationalHintsSynthetizer};
|
use dom::{OpaqueNode, PresentationalHintsSynthetizer};
|
||||||
use element_state::ElementState;
|
use element_state::ElementState;
|
||||||
|
@ -95,18 +95,11 @@ impl<'ln> GeckoNode<'ln> {
|
||||||
.get(pseudo).map(|c| c.clone()))
|
.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
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_node_data(&self) -> Option<&AtomicRefCell<NodeData>> {
|
fn get_node_data(&self) -> Option<&AtomicRefCell<NodeData>> {
|
||||||
unsafe { self.0.mServoData.get().as_ref() }
|
unsafe { self.0.mServoData.get().as_ref() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_node_data(&self) -> &AtomicRefCell<NodeData> {
|
pub fn ensure_data(&self) -> &AtomicRefCell<NodeData> {
|
||||||
match self.get_node_data() {
|
match self.get_node_data() {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => {
|
None => {
|
||||||
|
@ -224,20 +217,10 @@ impl<'ln> TNode for GeckoNode<'ln> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_dirty(&self) -> bool {
|
fn deprecated_dirty_bit_is_set(&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() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.flags() & (NODE_IS_DIRTY_FOR_SERVO as u32) != 0
|
self.flags() & (NODE_IS_DIRTY_FOR_SERVO as u32) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn set_dirty(&self) {
|
|
||||||
self.set_flags(NODE_IS_DIRTY_FOR_SERVO as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_dirty_descendants(&self) -> bool {
|
fn has_dirty_descendants(&self) -> bool {
|
||||||
// Return true unconditionally if we're not yet styled. This is a hack
|
// Return true unconditionally if we're not yet styled. This is a hack
|
||||||
// and should go away soon.
|
// and should go away soon.
|
||||||
|
@ -271,14 +254,19 @@ impl<'ln> TNode for GeckoNode<'ln> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_styling(&self) -> AtomicRefMut<NodeData> {
|
fn begin_styling(&self) -> AtomicRefMut<NodeData> {
|
||||||
let mut data = self.ensure_node_data().borrow_mut();
|
let mut data = self.ensure_data().borrow_mut();
|
||||||
data.gather_previous_styles(|| self.styles_from_frame());
|
data.gather_previous_styles(|| self.get_styles_from_frame());
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style_text_node(&self, style: Arc<ComputedValues>) {
|
fn style_text_node(&self, style: Arc<ComputedValues>) {
|
||||||
debug_assert!(self.is_text_node());
|
debug_assert!(self.is_text_node());
|
||||||
self.ensure_node_data().borrow_mut().style_text_node(style);
|
|
||||||
|
// FIXME(bholley): Gecko currently relies on the dirty bit being set to
|
||||||
|
// drive the post-traversal. This will go away soon.
|
||||||
|
unsafe { self.set_flags(NODE_IS_DIRTY_FOR_SERVO as u32); }
|
||||||
|
|
||||||
|
self.ensure_data().borrow_mut().style_text_node(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn borrow_data(&self) -> Option<AtomicRef<NodeData>> {
|
fn borrow_data(&self) -> Option<AtomicRef<NodeData>> {
|
||||||
|
@ -291,6 +279,10 @@ impl<'ln> TNode for GeckoNode<'ln> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_restyle_damage(self, damage: Self::ConcreteRestyleDamage) {
|
fn set_restyle_damage(self, damage: Self::ConcreteRestyleDamage) {
|
||||||
|
// FIXME(bholley): Gecko currently relies on the dirty bit being set to
|
||||||
|
// drive the post-traversal. This will go away soon.
|
||||||
|
unsafe { self.set_flags(NODE_IS_DIRTY_FOR_SERVO as u32) }
|
||||||
|
|
||||||
unsafe { Gecko_StoreStyleDifference(self.0, damage.0) }
|
unsafe { Gecko_StoreStyleDifference(self.0, damage.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#![allow(unsafe_code)]
|
#![allow(unsafe_code)]
|
||||||
|
|
||||||
use dom::{OpaqueNode, TNode, UnsafeNode};
|
use dom::{OpaqueNode, StylingMode, TNode, UnsafeNode};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use traversal::{RestyleResult, DomTraversalContext};
|
use traversal::{RestyleResult, DomTraversalContext};
|
||||||
|
@ -47,6 +47,7 @@ pub fn traverse_dom<N, C>(root: N,
|
||||||
where N: TNode,
|
where N: TNode,
|
||||||
C: DomTraversalContext<N>
|
C: DomTraversalContext<N>
|
||||||
{
|
{
|
||||||
|
debug_assert!(root.styling_mode() != StylingMode::Stop);
|
||||||
if opts::get().style_sharing_stats {
|
if opts::get().style_sharing_stats {
|
||||||
STYLE_SHARING_CACHE_HITS.store(0, Ordering::SeqCst);
|
STYLE_SHARING_CACHE_HITS.store(0, Ordering::SeqCst);
|
||||||
STYLE_SHARING_CACHE_MISSES.store(0, Ordering::SeqCst);
|
STYLE_SHARING_CACHE_MISSES.store(0, Ordering::SeqCst);
|
||||||
|
@ -80,28 +81,13 @@ fn top_down_dom<N, C>(unsafe_nodes: UnsafeNodeList,
|
||||||
// Get a real layout node.
|
// Get a real layout node.
|
||||||
let node = unsafe { N::from_unsafe(&unsafe_node) };
|
let node = unsafe { N::from_unsafe(&unsafe_node) };
|
||||||
|
|
||||||
if !context.should_process(node) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Possibly enqueue the children.
|
|
||||||
let mut children_to_process = 0isize;
|
|
||||||
// Perform the appropriate traversal.
|
// Perform the appropriate traversal.
|
||||||
|
let mut children_to_process = 0isize;
|
||||||
if let RestyleResult::Continue = context.process_preorder(node) {
|
if let RestyleResult::Continue = context.process_preorder(node) {
|
||||||
for kid in node.children() {
|
C::traverse_children(node, |kid| {
|
||||||
// Trigger the hook pre-adding the kid to the list. This can
|
children_to_process += 1;
|
||||||
// (and in fact uses to) change the result of the should_process
|
discovered_child_nodes.push(kid.to_unsafe())
|
||||||
// operation.
|
});
|
||||||
//
|
|
||||||
// As of right now, this hook takes care of propagating the
|
|
||||||
// restyle flag down the tree. In the future, more accurate
|
|
||||||
// behavior is probably going to be needed.
|
|
||||||
context.pre_process_child_hook(node, kid);
|
|
||||||
if context.should_process(kid) {
|
|
||||||
children_to_process += 1;
|
|
||||||
discovered_child_nodes.push(kid.to_unsafe())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the count of children if we need to do a bottom-up traversal
|
// Reset the count of children if we need to do a bottom-up traversal
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
//! Implements sequential traversal over the DOM tree.
|
//! Implements sequential traversal over the DOM tree.
|
||||||
|
|
||||||
use dom::TNode;
|
use dom::{StylingMode, TNode};
|
||||||
use traversal::{RestyleResult, DomTraversalContext};
|
use traversal::{RestyleResult, DomTraversalContext};
|
||||||
|
|
||||||
pub fn traverse_dom<N, C>(root: N,
|
pub fn traverse_dom<N, C>(root: N,
|
||||||
|
@ -16,14 +16,8 @@ pub fn traverse_dom<N, C>(root: N,
|
||||||
where N: TNode,
|
where N: TNode,
|
||||||
C: DomTraversalContext<N>
|
C: DomTraversalContext<N>
|
||||||
{
|
{
|
||||||
debug_assert!(context.should_process(node));
|
|
||||||
if let RestyleResult::Continue = context.process_preorder(node) {
|
if let RestyleResult::Continue = context.process_preorder(node) {
|
||||||
for kid in node.children() {
|
C::traverse_children(node, |kid| doit::<N, C>(context, kid));
|
||||||
context.pre_process_child_hook(node, kid);
|
|
||||||
if context.should_process(kid) {
|
|
||||||
doit::<N, C>(context, kid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.needs_postorder_traversal() {
|
if context.needs_postorder_traversal() {
|
||||||
|
@ -31,10 +25,10 @@ pub fn traverse_dom<N, C>(root: N,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug_assert!(root.styling_mode() != StylingMode::Stop);
|
||||||
let context = C::new(shared, root.opaque());
|
let context = C::new(shared, root.opaque());
|
||||||
if context.should_process(root) {
|
doit::<N, C>(&context, root);
|
||||||
doit::<N, C>(&context, root);
|
|
||||||
}
|
|
||||||
// Clear the local LRU cache since we store stateful elements inside.
|
// Clear the local LRU cache since we store stateful elements inside.
|
||||||
context.local_context().style_sharing_candidate_cache.borrow_mut().clear();
|
context.local_context().style_sharing_candidate_cache.borrow_mut().clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
|
|
||||||
//! Traversing the DOM tree; the bloom filter.
|
//! Traversing the DOM tree; the bloom filter.
|
||||||
|
|
||||||
|
use atomic_refcell::AtomicRefCell;
|
||||||
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
use context::{LocalStyleContext, SharedStyleContext, StyleContext};
|
||||||
use dom::{OpaqueNode, TNode, UnsafeNode};
|
use data::NodeData;
|
||||||
|
use dom::{OpaqueNode, StylingMode, TNode, UnsafeNode};
|
||||||
use matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult};
|
use matching::{ApplicableDeclarations, ElementMatchMethods, MatchMethods, StyleSharingResult};
|
||||||
use selectors::bloom::BloomFilter;
|
use selectors::bloom::BloomFilter;
|
||||||
use selectors::matching::StyleRelations;
|
use selectors::matching::StyleRelations;
|
||||||
|
@ -22,6 +24,7 @@ pub type Generation = u32;
|
||||||
/// an element.
|
/// an element.
|
||||||
///
|
///
|
||||||
/// So far this only happens where a display: none node is found.
|
/// So far this only happens where a display: none node is found.
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum RestyleResult {
|
pub enum RestyleResult {
|
||||||
Continue,
|
Continue,
|
||||||
Stop,
|
Stop,
|
||||||
|
@ -172,30 +175,30 @@ pub trait DomTraversalContext<N: TNode> {
|
||||||
/// If it's false, then process_postorder has no effect at all.
|
/// If it's false, then process_postorder has no effect at all.
|
||||||
fn needs_postorder_traversal(&self) -> bool { true }
|
fn needs_postorder_traversal(&self) -> bool { true }
|
||||||
|
|
||||||
/// Returns if the node should be processed by the preorder traversal (and
|
/// Helper for the traversal implementations to select the children that
|
||||||
/// then by the post-order one).
|
/// should be enqueued for processing.
|
||||||
///
|
fn traverse_children<F: FnMut(N)>(parent: N, mut f: F)
|
||||||
/// Note that this is true unconditionally for servo, since it requires to
|
{
|
||||||
/// bubble the widths bottom-up for all the DOM.
|
// If we enqueue any children for traversal, we need to set the dirty
|
||||||
fn should_process(&self, node: N) -> bool {
|
// descendants bit. Avoid doing it more than once.
|
||||||
opts::get().nonincremental_layout || node.is_dirty() || node.has_dirty_descendants()
|
let mut marked_dirty_descendants = false;
|
||||||
}
|
|
||||||
|
|
||||||
/// Do an action over the child before pushing him to the work queue.
|
for kid in parent.children() {
|
||||||
///
|
if kid.styling_mode() != StylingMode::Stop {
|
||||||
/// By default, propagate the IS_DIRTY flag down the tree.
|
if !marked_dirty_descendants {
|
||||||
#[allow(unsafe_code)]
|
unsafe { parent.set_dirty_descendants(); }
|
||||||
fn pre_process_child_hook(&self, parent: N, kid: N) {
|
marked_dirty_descendants = true;
|
||||||
// NOTE: At this point is completely safe to modify either the parent or
|
}
|
||||||
// the child, since we have exclusive access to both of them.
|
f(kid);
|
||||||
if parent.is_dirty() {
|
|
||||||
unsafe {
|
|
||||||
kid.set_dirty();
|
|
||||||
parent.set_dirty_descendants();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensures the existence of the NodeData, and returns it. This can't live
|
||||||
|
/// on TNode because of the trait-based separation between Servo's script
|
||||||
|
/// and layout crates.
|
||||||
|
fn ensure_node_data(node: &N) -> &AtomicRefCell<NodeData>;
|
||||||
|
|
||||||
fn local_context(&self) -> &LocalStyleContext;
|
fn local_context(&self) -> &LocalStyleContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,11 +284,12 @@ fn ensure_node_styled_internal<'a, N, C>(node: N,
|
||||||
/// Calculates the style for a single node.
|
/// Calculates the style for a single node.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub fn recalc_style_at<'a, N, C>(context: &'a C,
|
pub fn recalc_style_at<'a, N, C, D>(context: &'a C,
|
||||||
root: OpaqueNode,
|
root: OpaqueNode,
|
||||||
node: N) -> RestyleResult
|
node: N) -> RestyleResult
|
||||||
where N: TNode,
|
where N: TNode,
|
||||||
C: StyleContext<'a>
|
C: StyleContext<'a>,
|
||||||
|
D: DomTraversalContext<N>
|
||||||
{
|
{
|
||||||
// Get the parent node.
|
// Get the parent node.
|
||||||
let parent_opt = match node.parent_node() {
|
let parent_opt = match node.parent_node() {
|
||||||
|
@ -296,9 +300,10 @@ pub fn recalc_style_at<'a, N, C>(context: &'a C,
|
||||||
// Get the style bloom filter.
|
// Get the style bloom filter.
|
||||||
let mut bf = take_thread_local_bloom_filter(parent_opt, root, context.shared_context());
|
let mut bf = take_thread_local_bloom_filter(parent_opt, root, context.shared_context());
|
||||||
|
|
||||||
let nonincremental_layout = opts::get().nonincremental_layout;
|
|
||||||
let mut restyle_result = RestyleResult::Continue;
|
let mut restyle_result = RestyleResult::Continue;
|
||||||
if nonincremental_layout || node.is_dirty() {
|
let mode = node.styling_mode();
|
||||||
|
debug_assert!(mode != StylingMode::Stop, "Parent should not have enqueued us");
|
||||||
|
if mode != StylingMode::Traverse {
|
||||||
// Check to see whether we can share a style with someone.
|
// Check to see whether we can share a style with someone.
|
||||||
let style_sharing_candidate_cache =
|
let style_sharing_candidate_cache =
|
||||||
&mut context.local_context().style_sharing_candidate_cache.borrow_mut();
|
&mut context.local_context().style_sharing_candidate_cache.borrow_mut();
|
||||||
|
@ -370,6 +375,23 @@ pub fn recalc_style_at<'a, N, C>(context: &'a C,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we restyled this node, conservatively mark all our children as needing
|
||||||
|
// processing. The eventual algorithm we're designing does this in a more granular
|
||||||
|
// fashion.
|
||||||
|
if mode == StylingMode::Restyle && restyle_result == RestyleResult::Continue {
|
||||||
|
for kid in node.children() {
|
||||||
|
let mut data = D::ensure_node_data(&kid).borrow_mut();
|
||||||
|
if kid.is_text_node() {
|
||||||
|
data.ensure_restyle_data();
|
||||||
|
} else {
|
||||||
|
data.gather_previous_styles(|| kid.get_styles_from_frame());
|
||||||
|
if data.previous_styles().is_some() {
|
||||||
|
data.ensure_restyle_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let unsafe_layout_node = node.to_unsafe();
|
let unsafe_layout_node = node.to_unsafe();
|
||||||
|
|
||||||
// Before running the children, we need to insert our nodes into the bloom
|
// Before running the children, we need to insert our nodes into the bloom
|
||||||
|
@ -380,9 +402,5 @@ pub fn recalc_style_at<'a, N, C>(context: &'a C,
|
||||||
// NB: flow construction updates the bloom filter on the way up.
|
// NB: flow construction updates the bloom filter on the way up.
|
||||||
put_thread_local_bloom_filter(bf, &unsafe_layout_node, context.shared_context());
|
put_thread_local_bloom_filter(bf, &unsafe_layout_node, context.shared_context());
|
||||||
|
|
||||||
if nonincremental_layout {
|
restyle_result
|
||||||
RestyleResult::Continue
|
|
||||||
} else {
|
|
||||||
restyle_result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use std::str::from_utf8_unchecked;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use style::arc_ptr_eq;
|
use style::arc_ptr_eq;
|
||||||
use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext};
|
use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext};
|
||||||
use style::dom::{NodeInfo, TElement, TNode};
|
use style::dom::{NodeInfo, StylingMode, TElement, TNode};
|
||||||
use style::error_reporting::StdoutErrorReporter;
|
use style::error_reporting::StdoutErrorReporter;
|
||||||
use style::gecko::data::{NUM_THREADS, PerDocumentStyleData};
|
use style::gecko::data::{NUM_THREADS, PerDocumentStyleData};
|
||||||
use style::gecko::selector_impl::{GeckoSelectorImpl, PseudoElement};
|
use style::gecko::selector_impl::{GeckoSelectorImpl, PseudoElement};
|
||||||
|
@ -107,8 +107,11 @@ fn restyle_subtree(node: GeckoNode, raw_data: RawServoStyleSetBorrowed) {
|
||||||
timer: Timer::new(),
|
timer: Timer::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// We ensure this is true before calling Servo_RestyleSubtree()
|
if node.styling_mode() == StylingMode::Stop {
|
||||||
debug_assert!(node.is_dirty() || node.has_dirty_descendants());
|
error!("Unnecessary call to restyle_subtree");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if per_doc_data.num_threads == 1 || per_doc_data.work_queue.is_none() {
|
if per_doc_data.num_threads == 1 || per_doc_data.work_queue.is_none() {
|
||||||
sequential::traverse_dom::<GeckoNode, RecalcStyleOnly>(node, &shared_style_context);
|
sequential::traverse_dom::<GeckoNode, RecalcStyleOnly>(node, &shared_style_context);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue