From 996f3228a2e518fb5562a4b2f91743ba435df9c3 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 21 Mar 2022 12:12:50 +0100 Subject: [PATCH] LibWeb: Fix O(n^2) traversal in hit testing We already walk the entire paint tree within each stacking context in the main hit testing function (StackingContext::hit_test()), so there's no need for each individual paintable to walk its own children again. By not doing that, we remove a source of O(n^2) traversal which made hit testing on deeply nested web pages unbearably slow. --- .../Libraries/LibWeb/Painting/PaintableBox.cpp | 11 ++--------- .../Libraries/LibWeb/Painting/StackingContext.cpp | 15 ++++++--------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 8e7f59d797..4dbcf2349a 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -535,16 +535,9 @@ Optional PaintableBox::hit_test(Gfx::FloatPoint const& position, return stacking_context()->hit_test(position, type); } - Optional result; if (absolute_border_box_rect().contains(position.x(), position.y())) - result = HitTestResult { *this }; - for_each_child_in_paint_order([&](auto& child) { - if (child.paintable()) { - if (auto child_result = child.paintable()->hit_test(position, type); child_result.has_value()) - result = move(child_result); - } - }); - return result; + return HitTestResult { *this }; + return {}; } Optional PaintableWithLines::hit_test(const Gfx::FloatPoint& position, HitTestType type) const diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp index 2cf8aa97e3..c8349e4fd3 100644 --- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp @@ -297,9 +297,8 @@ Optional StackingContext::hit_test(Gfx::FloatPoint const& positio // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0. m_box.for_each_in_subtree_of_type([&](Layout::Box const& box) { if (box.is_positioned() && !box.paint_box()->stacking_context()) { - result = box.paint_box()->hit_test(transformed_position, type); - if (result.has_value()) - return IterationDecision::Break; + if (auto candidate = box.paint_box()->hit_test(transformed_position, type); candidate.has_value()) + result = move(candidate); } return IterationDecision::Continue; }); @@ -316,9 +315,8 @@ Optional StackingContext::hit_test(Gfx::FloatPoint const& positio // 4. the non-positioned floats. m_box.for_each_in_subtree_of_type([&](Layout::Box const& box) { if (box.is_floating()) { - result = box.paint_box()->hit_test(transformed_position, type); - if (result.has_value()) - return IterationDecision::Break; + if (auto candidate = box.paint_box()->hit_test(transformed_position, type); candidate.has_value()) + result = move(candidate); } return IterationDecision::Continue; }); @@ -329,9 +327,8 @@ Optional StackingContext::hit_test(Gfx::FloatPoint const& positio if (!m_box.children_are_inline()) { m_box.for_each_in_subtree_of_type([&](Layout::Box const& box) { if (!box.is_absolutely_positioned() && !box.is_floating()) { - result = box.paint_box()->hit_test(transformed_position, type); - if (result.has_value()) - return IterationDecision::Break; + if (auto candidate = box.paint_box()->hit_test(transformed_position, type); candidate.has_value()) + result = move(candidate); } return IterationDecision::Continue; });