mirror of
https://github.com/servo/servo.git
synced 2025-07-23 07:13:52 +01:00
This change adds the simplest kind of incremental layout. When Servo detects that all style changes only require a repaint, only run stacking context tree and WebRender display list generation. This means that these kind of restyles do not need a re-layout. Instead, the existing box and fragment trees will be used and the styles of damaged nodes will be updated in their box and fragment tree nodes. This requires a new style repair DOM traversal for nodes that have had their style damaged. In addition, careful accounting of all the places where we store style must happen in order ot update those styles. Testing: This is covered by existing WPT tests as it should not change observable behavior. We have created a test case which shows a 50% speedup when run in Servo, even though there still a long way to go to match the speed of other browsers: https://gist.github.com/mrobinson/44ec87d028c0198917a7715a06dd98a0 Co-authored-by: Oriol Brufau <obrufau@igalia.com> Signed-off-by: Martin Robinson <mrobinson@igalia.com> Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
138 lines
3.8 KiB
Rust
138 lines
3.8 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
use script::layout_dom::ServoLayoutNode;
|
|
use script_layout_interface::wrapper_traits::LayoutNode;
|
|
use style::context::{SharedStyleContext, StyleContext};
|
|
use style::data::ElementData;
|
|
use style::dom::{NodeInfo, TElement, TNode};
|
|
use style::selector_parser::RestyleDamage;
|
|
use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
|
|
use style::values::computed::Display;
|
|
|
|
use crate::context::LayoutContext;
|
|
use crate::dom::{DOMLayoutData, NodeExt};
|
|
use crate::dom_traversal::iter_child_nodes;
|
|
|
|
pub struct RecalcStyle<'a> {
|
|
context: &'a LayoutContext<'a>,
|
|
}
|
|
|
|
impl<'a> RecalcStyle<'a> {
|
|
pub fn new(context: &'a LayoutContext<'a>) -> Self {
|
|
RecalcStyle { context }
|
|
}
|
|
|
|
pub fn context(&self) -> &LayoutContext<'a> {
|
|
self.context
|
|
}
|
|
}
|
|
|
|
#[allow(unsafe_code)]
|
|
impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
|
|
where
|
|
E: TElement,
|
|
E::ConcreteNode: 'dom + LayoutNode<'dom>,
|
|
{
|
|
fn process_preorder<F>(
|
|
&self,
|
|
traversal_data: &PerLevelTraversalData,
|
|
context: &mut StyleContext<E>,
|
|
node: E::ConcreteNode,
|
|
note_child: F,
|
|
) where
|
|
F: FnMut(E::ConcreteNode),
|
|
{
|
|
if node.is_text_node() {
|
|
return;
|
|
}
|
|
|
|
let had_style_data = node.style_data().is_some();
|
|
unsafe {
|
|
node.initialize_style_and_layout_data::<DOMLayoutData>();
|
|
}
|
|
|
|
let element = node.as_element().unwrap();
|
|
let mut element_data = element.mutate_data().unwrap();
|
|
|
|
if !had_style_data {
|
|
element_data.damage = RestyleDamage::reconstruct();
|
|
}
|
|
|
|
recalc_style_at(
|
|
self,
|
|
traversal_data,
|
|
context,
|
|
element,
|
|
&mut element_data,
|
|
note_child,
|
|
);
|
|
|
|
unsafe {
|
|
element.unset_dirty_descendants();
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn needs_postorder_traversal() -> bool {
|
|
false
|
|
}
|
|
|
|
fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
|
|
panic!("this should never be called")
|
|
}
|
|
|
|
fn text_node_needs_traversal(node: E::ConcreteNode, parent_data: &ElementData) -> bool {
|
|
node.layout_data().is_none() || !parent_data.damage.is_empty()
|
|
}
|
|
|
|
fn shared_context(&self) -> &SharedStyleContext {
|
|
&self.context.style_context
|
|
}
|
|
}
|
|
|
|
pub(crate) fn compute_damage_and_repair_style(
|
|
context: &SharedStyleContext,
|
|
node: ServoLayoutNode<'_>,
|
|
) -> RestyleDamage {
|
|
compute_damage_and_repair_style_inner(context, node, RestyleDamage::empty())
|
|
}
|
|
|
|
pub(crate) fn compute_damage_and_repair_style_inner(
|
|
context: &SharedStyleContext,
|
|
node: ServoLayoutNode<'_>,
|
|
parent_restyle_damage: RestyleDamage,
|
|
) -> RestyleDamage {
|
|
let original_damage;
|
|
let damage = {
|
|
let mut element_data = node
|
|
.style_data()
|
|
.expect("Should not run `compute_damage` before styling.")
|
|
.element_data
|
|
.borrow_mut();
|
|
|
|
if let Some(ref style) = element_data.styles.primary {
|
|
if style.get_box().display == Display::None {
|
|
return parent_restyle_damage;
|
|
}
|
|
}
|
|
|
|
original_damage = std::mem::take(&mut element_data.damage);
|
|
element_data.damage |= parent_restyle_damage;
|
|
element_data.damage
|
|
};
|
|
|
|
let mut propagated_damage = damage;
|
|
for child in iter_child_nodes(node) {
|
|
if child.is_element() {
|
|
propagated_damage |= compute_damage_and_repair_style_inner(context, child, damage);
|
|
}
|
|
}
|
|
|
|
if propagated_damage == RestyleDamage::REPAINT && original_damage == RestyleDamage::REPAINT {
|
|
node.repair_style(context);
|
|
}
|
|
|
|
propagated_damage
|
|
}
|