From 156c1083e9de23a93075ec9e3c55cd5f93c48896 Mon Sep 17 00:00:00 2001 From: Tim Ledbetter Date: Fri, 28 Jun 2024 11:07:58 +0100 Subject: [PATCH] LibWeb: Cache text blocks used by find in page The first step of the find in page algorithm is to walk the layout tree of each document on the page and construct a list of strings against which to search for matches. Previously, this was being done for each new query, even when the page content hadn't been updated. The output of this process is now cached in the viewport node of the associated document. This ensures that this process is no longer repeated unnceessarily. --- Userland/Libraries/LibWeb/DOM/Document.cpp | 56 +---------------- Userland/Libraries/LibWeb/Layout/Viewport.cpp | 63 +++++++++++++++++++ Userland/Libraries/LibWeb/Layout/Viewport.h | 16 +++++ 3 files changed, 81 insertions(+), 54 deletions(-) diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 25b8e7ac229..2320a1ef2b1 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -5111,65 +5111,13 @@ void Document::set_needs_to_refresh_scroll_state(bool b) Vector> Document::find_matching_text(String const& query, CaseSensitivity case_sensitivity) { - if (!document_element() || !document_element()->layout_node()) + if (!layout_node()) return {}; - struct TextPosition { - DOM::Text& dom_node; - size_t start_offset { 0 }; - }; - - struct TextBlock { - String text; - Vector positions; - }; - - auto gather_text_blocks = [&]() -> Vector { - StringBuilder builder; - size_t current_start_position = 0; - Vector text_positions; - Vector text_blocks; - document_element()->layout_node()->for_each_in_inclusive_subtree([&](auto const& layout_node) { - if (layout_node.display().is_none() || !layout_node.paintable() || !layout_node.paintable()->is_visible()) - return TraversalDecision::Continue; - - if (layout_node.is_block_container()) { - if (!builder.is_empty()) { - text_blocks.append({ builder.to_string_without_validation(), text_positions }); - current_start_position = 0; - text_positions.clear_with_capacity(); - builder.clear(); - } - return TraversalDecision::Continue; - } - - if (layout_node.is_text_node()) { - auto const& text_node = verify_cast(layout_node); - auto& dom_node = const_cast(text_node.dom_node()); - if (text_positions.is_empty()) { - text_positions.empend(dom_node); - } else { - text_positions.empend(dom_node, current_start_position); - } - - auto const& current_node_text = text_node.text_for_rendering(); - current_start_position += current_node_text.bytes_as_string_view().length(); - builder.append(move(current_node_text)); - } - - return TraversalDecision::Continue; - }); - - if (!builder.is_empty()) - text_blocks.append({ builder.to_string_without_validation(), text_positions }); - - return text_blocks; - }; - // Ensure the layout tree exists before searching for text matches. update_layout(); - auto text_blocks = gather_text_blocks(); + auto const& text_blocks = layout_node()->text_blocks(); if (text_blocks.is_empty()) return {}; diff --git a/Userland/Libraries/LibWeb/Layout/Viewport.cpp b/Userland/Libraries/LibWeb/Layout/Viewport.cpp index 81d97906c2e..a5e77542cec 100644 --- a/Userland/Libraries/LibWeb/Layout/Viewport.cpp +++ b/Userland/Libraries/LibWeb/Layout/Viewport.cpp @@ -27,4 +27,67 @@ JS::GCPtr Viewport::create_paintable() const return Painting::ViewportPaintable::create(*this); } +void Viewport::visit_edges(Visitor& visitor) +{ + Base::visit_edges(visitor); + if (!m_text_blocks.has_value()) + return; + + for (auto& text_block : *m_text_blocks) { + for (auto& text_position : text_block.positions) + visitor.visit(text_position.dom_node); + } +} + +Vector const& Viewport::text_blocks() +{ + if (!m_text_blocks.has_value()) + update_text_blocks(); + + return *m_text_blocks; +} + +void Viewport::update_text_blocks() +{ + StringBuilder builder; + size_t current_start_position = 0; + Vector text_positions; + Vector text_blocks; + for_each_in_inclusive_subtree([&](auto const& layout_node) { + if (layout_node.display().is_none() || !layout_node.paintable() || !layout_node.paintable()->is_visible()) + return TraversalDecision::Continue; + + if (layout_node.is_box()) { + if (!builder.is_empty()) { + text_blocks.append({ builder.to_string_without_validation(), text_positions }); + current_start_position = 0; + text_positions.clear_with_capacity(); + builder.clear(); + } + return TraversalDecision::Continue; + } + + if (layout_node.is_text_node()) { + auto const& text_node = verify_cast(layout_node); + auto& dom_node = const_cast(text_node.dom_node()); + if (text_positions.is_empty()) { + text_positions.empend(dom_node); + } else { + text_positions.empend(dom_node, current_start_position); + } + + auto const& current_node_text = text_node.text_for_rendering(); + current_start_position += current_node_text.bytes_as_string_view().length(); + builder.append(move(current_node_text)); + } + + return TraversalDecision::Continue; + }); + + if (!builder.is_empty()) + text_blocks.append({ builder.to_string_without_validation(), text_positions }); + + m_text_blocks = move(text_blocks); +} + } diff --git a/Userland/Libraries/LibWeb/Layout/Viewport.h b/Userland/Libraries/LibWeb/Layout/Viewport.h index 0280ba1a4d7..2f446845480 100644 --- a/Userland/Libraries/LibWeb/Layout/Viewport.h +++ b/Userland/Libraries/LibWeb/Layout/Viewport.h @@ -19,12 +19,28 @@ public: explicit Viewport(DOM::Document&, NonnullRefPtr); virtual ~Viewport() override; + struct TextPosition { + JS::NonnullGCPtr dom_node; + size_t start_offset { 0 }; + }; + struct TextBlock { + String text; + Vector positions; + }; + Vector const& text_blocks(); + const DOM::Document& dom_node() const { return static_cast(*Node::dom_node()); } + virtual void visit_edges(Visitor&) override; + private: virtual JS::GCPtr create_paintable() const override; + void update_text_blocks(); + virtual bool is_viewport() const override { return true; } + + Optional> m_text_blocks; }; template<>