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.
This commit is contained in:
Tim Ledbetter 2024-06-28 11:07:58 +01:00 committed by Alexander Kalenik
commit 156c1083e9
Notes: sideshowbarker 2024-07-17 10:16:43 +09:00
3 changed files with 81 additions and 54 deletions

View file

@ -27,4 +27,67 @@ JS::GCPtr<Painting::Paintable> 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<Viewport::TextBlock> 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<TextPosition> text_positions;
Vector<TextBlock> 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::TextNode>(layout_node);
auto& dom_node = const_cast<DOM::Text&>(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);
}
}