Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Reconstruct the box tree incrementally in some case
  • Loading branch information
nox committed Jun 4, 2020
1 parent c4ea4b1 commit a30bdc1
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 15 deletions.
140 changes: 139 additions & 1 deletion components/layout_2020/flow/root.rs
Expand Up @@ -12,6 +12,7 @@ use crate::dom_traversal::{iter_child_nodes, Contents, NodeExt};
use crate::element_data::LayoutBox;
use crate::flow::construct::ContainsFloats;
use crate::flow::float::FloatBox;
use crate::flow::inline::InlineLevelBox;
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::Fragment;
Expand All @@ -22,9 +23,11 @@ use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContent;
use crate::sizing::ContentSizesRequest;
use crate::style_ext::ComputedValuesExt;
use crate::style_ext::{Display, DisplayGeneratingBox};
use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
use crate::wrapper::GetStyleAndLayoutData;
use crate::DefiniteContainingBlock;
use app_units::Au;
use atomic_refcell::AtomicRef;
use euclid::default::{Point2D, Rect, Size2D};
use fxhash::FxHashSet;
use gfx_traits::print_tree::PrintTree;
Expand Down Expand Up @@ -88,6 +91,141 @@ impl BoxTree {
canvas_background: CanvasBackground::for_root_element(context, root_element),
}
}

/// This method attempts to incrementally update the box tree from an
/// arbitrary node that is not necessarily the document's root element.
///
/// If the node is not a valid candidate for incremental update, the method
/// loops over its parent. The only valid candidates for now are absolutely
/// positioned boxes which don't change their outside display mode (i.e. it
/// will not attempt to update from an absolutely positioned inline element
/// which became an absolutely positioned block element). The value `true`
/// is returned if an incremental update could be done, and `false`
/// otherwise.
///
/// There are various pain points that need to be taken care of to extend
/// the set of valid candidates:
/// * it is not obvious how to incrementally check whether a block
/// formatting context still contains floats or not;
/// * the propagation of text decorations towards node descendants is
/// hard to do incrementally with our current representation of boxes
/// * how intrinsic content sizes are computed eagerly makes it hard
/// to update those sizes for ancestors of the node from which we
/// made an incremental update.
pub fn update<'dom, Node>(context: &LayoutContext, mut dirty_node: Node) -> bool
where
Node: 'dom + Copy + LayoutNode<'dom> + Send + Sync,
{
enum UpdatePoint {
AbsolutelyPositionedBlockLevelBox(ArcRefCell<BlockLevelBox>),
AbsolutelyPositionedInlineLevelBox(ArcRefCell<InlineLevelBox>),
}

fn update_point<'dom, Node>(
node: Node,
) -> Option<(Arc<ComputedValues>, DisplayInside, UpdatePoint)>
where
Node: NodeExt<'dom>,
{
if !node.is_element() {
return None;
}

if node.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLBodyElement) {
// This can require changes to the canvas background.
return None;
}

// Don't update unstyled nodes.
let data = node.get_style_and_layout_data()?;

// Don't update nodes that have pseudo-elements.
let element_data = data.style_data.element_data.borrow();
if !element_data.styles.pseudos.is_empty() {
return None;
}

let layout_data = data.layout_data.borrow();
if layout_data.pseudo_before_box.borrow().is_some() {
return None;
}
if layout_data.pseudo_after_box.borrow().is_some() {
return None;
}

let primary_style = element_data.styles.primary();
let box_style = primary_style.get_box();

if !box_style.position.is_absolutely_positioned() {
return None;
}

let display_inside = match Display::from(box_style.display) {
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => {
inside
},
_ => return None,
};

let update_point =
match &*AtomicRef::filter_map(layout_data.self_box.borrow(), Option::as_ref)? {
LayoutBox::DisplayContents => return None,
LayoutBox::BlockLevel(block_level_box) => match &*block_level_box.borrow() {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_)
if box_style.position.is_absolutely_positioned() =>
{
UpdatePoint::AbsolutelyPositionedBlockLevelBox(block_level_box.clone())
},
_ => return None,
},
LayoutBox::InlineLevel(inline_level_box) => match &*inline_level_box.borrow() {
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_)
if box_style.position.is_absolutely_positioned() =>
{
UpdatePoint::AbsolutelyPositionedInlineLevelBox(
inline_level_box.clone(),
)
},
_ => return None,
},
};
Some((primary_style.clone(), display_inside, update_point))
}

loop {
if let Some((primary_style, display_inside, update_point)) = update_point(dirty_node) {
let contents = ReplacedContent::for_element(dirty_node)
.map_or(Contents::OfElement, Contents::Replaced);
let out_of_flow_absolutely_positioned_box =
Arc::new(AbsolutelyPositionedBox::construct(
context,
dirty_node,
primary_style,
display_inside,
contents,
));
match update_point {
UpdatePoint::AbsolutelyPositionedBlockLevelBox(block_level_box) => {
*block_level_box.borrow_mut() =
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
out_of_flow_absolutely_positioned_box,
);
},
UpdatePoint::AbsolutelyPositionedInlineLevelBox(inline_level_box) => {
*inline_level_box.borrow_mut() =
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(
out_of_flow_absolutely_positioned_box,
);
},
}
return true;
}
dirty_node = match dirty_node.parent_node() {
Some(parent) => parent,
None => return false,
};
}
}
}

fn construct_for_root_element<'dom>(
Expand Down
32 changes: 18 additions & 14 deletions components/layout_thread_2020/lib.rs
Expand Up @@ -1081,39 +1081,43 @@ impl LayoutThread {
let rayon_pool = STYLE_THREAD_POOL.pool();
let rayon_pool = rayon_pool.as_ref();

let box_tree = if token.should_traverse() {
driver::traverse_dom(&traversal, token, rayon_pool);
if token.should_traverse() {
let dirty_root = driver::traverse_dom(&traversal, token, rayon_pool).as_node();

let root_node = root_element.as_node();
let build_box_tree = || BoxTree::construct(traversal.context(), root_node);
let box_tree = if let Some(pool) = rayon_pool {
let mut box_tree = self.box_tree.borrow_mut();
let box_tree = &mut *box_tree;
let mut build_box_tree = || {
if !BoxTree::update(traversal.context(), dirty_root) {
*box_tree = Some(Arc::new(BoxTree::construct(traversal.context(), root_node)));
}
};
if let Some(pool) = rayon_pool {
pool.install(build_box_tree)
} else {
build_box_tree()
};

Some(Arc::new(box_tree))
} else {
None
};

layout_context = traversal.destroy();

if let Some(box_tree) = box_tree {
let viewport_size = Size2D::new(
self.viewport_size.width.to_f32_px(),
self.viewport_size.height.to_f32_px(),
);
let run_layout = || box_tree.layout(&layout_context, viewport_size);
let run_layout = || {
box_tree
.as_ref()
.unwrap()
.layout(traversal.context(), viewport_size)
};
let fragment_tree = Arc::new(if let Some(pool) = rayon_pool {
pool.install(run_layout)
} else {
run_layout()
});
*self.box_tree.borrow_mut() = Some(box_tree);
*self.fragment_tree.borrow_mut() = Some(fragment_tree);
}

layout_context = traversal.destroy();

for element in elements_with_snapshot {
unsafe { element.unset_snapshot_flags() }
}
Expand Down

0 comments on commit a30bdc1

Please sign in to comment.