From 59afdb959ff20ac8516ceb180c68084997538b70 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 21 Mar 2022 10:56:02 +0100 Subject: [PATCH] LibWeb: Build stacking context tree lazily There's no actual need to build the stacking context tree before performing layout. Instead, make it lazy and build the tree when it's actually needed for something. This avoids a bunch of work in situations where multiple synchronous layouts are forced (typically by JavaScript) without painting or hit testing taking place in between. It also opens up for style invalidations that only target the stacking context tree. --- Userland/Libraries/LibWeb/DOM/Document.cpp | 2 -- .../LibWeb/Layout/InitialContainingBlock.cpp | 13 ++++++++++--- .../LibWeb/Layout/InitialContainingBlock.h | 4 ++-- Userland/Libraries/LibWeb/Painting/PaintableBox.cpp | 10 +++++++++- Userland/Libraries/LibWeb/Painting/PaintableBox.h | 2 ++ 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index a08584cf67..b7f357c958 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -585,8 +585,6 @@ void Document::update_layout() root_formatting_context.run(*m_layout_root, Layout::LayoutMode::Normal); formatting_state.commit(); - m_layout_root->build_stacking_context_tree(); - browsing_context()->set_needs_display(); if (browsing_context()->is_top_level()) { diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.cpp b/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.cpp index 724352db54..a077dfcdcf 100644 --- a/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.cpp +++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.cpp @@ -18,13 +18,19 @@ InitialContainingBlock::InitialContainingBlock(DOM::Document& document, NonnullR InitialContainingBlock::~InitialContainingBlock() = default; +void InitialContainingBlock::build_stacking_context_tree_if_needed() +{ + if (paint_box()->stacking_context()) + return; + build_stacking_context_tree(); +} + void InitialContainingBlock::build_stacking_context_tree() { const_cast(paint_box())->set_stacking_context(make(*this, nullptr)); - for_each_in_inclusive_subtree_of_type([&](Box& box) { - if (&box == this) - return IterationDecision::Continue; + for_each_in_subtree_of_type([&](Box& box) { + const_cast(box.paint_box())->invalidate_stacking_context(); if (!box.establishes_stacking_context()) { VERIFY(!box.paint_box()->stacking_context()); return IterationDecision::Continue; @@ -40,6 +46,7 @@ void InitialContainingBlock::build_stacking_context_tree() void InitialContainingBlock::paint_all_phases(PaintContext& context) { + build_stacking_context_tree_if_needed(); context.painter().fill_rect(enclosing_int_rect(paint_box()->absolute_rect()), context.palette().base()); context.painter().translate(-context.viewport_rect().location()); paint_box()->stacking_context()->paint(context); diff --git a/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.h b/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.h index db360f656d..2c35b82f1c 100644 --- a/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.h +++ b/Userland/Libraries/LibWeb/Layout/InitialContainingBlock.h @@ -24,11 +24,11 @@ public: void set_selection(const LayoutRange&); void set_selection_end(const LayoutPosition&); - void build_stacking_context_tree(); - + void build_stacking_context_tree_if_needed(); void recompute_selection_states(); private: + void build_stacking_context_tree(); virtual bool is_initial_containing_block_box() const override { return true; } LayoutRange m_selection; diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 2cdfbbbf22..670aae965f 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,11 @@ PaintableBox::~PaintableBox() { } +void PaintableBox::invalidate_stacking_context() +{ + m_stacking_context = nullptr; +} + PaintableWithLines::PaintableWithLines(Layout::BlockContainer const& layout_box) : PaintableBox(layout_box) { @@ -524,8 +530,10 @@ void PaintableBox::for_each_child_in_paint_order(Callback callback) const HitTestResult PaintableBox::hit_test(Gfx::FloatPoint const& position, HitTestType type) const { - if (layout_box().is_initial_containing_block_box()) + if (layout_box().is_initial_containing_block_box()) { + const_cast(static_cast(layout_box())).build_stacking_context_tree_if_needed(); return stacking_context()->hit_test(position, type); + } HitTestResult result { absolute_border_box_rect().contains(position.x(), position.y()) ? this : nullptr }; for_each_child_in_paint_order([&](auto& child) { diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 649586d9ac..a4673f1201 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -117,6 +117,8 @@ public: virtual HitTestResult hit_test(Gfx::FloatPoint const&, HitTestType) const override; + void invalidate_stacking_context(); + protected: explicit PaintableBox(Layout::Box const&);