2020-01-18 05:38:21 -03:00
|
|
|
/*
|
2024-10-04 08:19:50 -03:00
|
|
|
* Copyright (c) 2018-2023, Andreas Kling <andreas@ladybird.org>
|
2020-01-18 05:38:21 -03:00
|
|
|
*
|
2021-04-22 05:24:48 -03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 05:38:21 -03:00
|
|
|
*/
|
|
|
|
|
|
2026-05-30 13:53:00 -03:00
|
|
|
#include <AK/CharacterTypes.h>
|
2026-06-22 08:30:00 -03:00
|
|
|
#include <AK/Utf16StringBuilder.h>
|
2026-02-08 13:19:13 -03:00
|
|
|
#include <LibWeb/DOM/Document.h>
|
2023-01-11 15:48:53 -03:00
|
|
|
#include <LibWeb/DOM/Range.h>
|
2020-03-07 06:32:51 -03:00
|
|
|
#include <LibWeb/Dump.h>
|
2024-10-14 11:07:56 -03:00
|
|
|
#include <LibWeb/Layout/TextNode.h>
|
2023-02-25 07:04:29 -03:00
|
|
|
#include <LibWeb/Layout/Viewport.h>
|
2022-03-10 19:13:37 -03:00
|
|
|
#include <LibWeb/Painting/PaintableBox.h>
|
2020-06-18 16:39:27 -03:00
|
|
|
#include <LibWeb/Painting/StackingContext.h>
|
2023-08-18 10:52:40 -03:00
|
|
|
#include <LibWeb/Painting/ViewportPaintable.h>
|
2019-06-15 17:49:44 -03:00
|
|
|
|
2020-11-22 11:53:01 -03:00
|
|
|
namespace Web::Layout {
|
2020-03-07 06:27:02 -03:00
|
|
|
|
2026-06-06 09:03:41 -03:00
|
|
|
Viewport::Viewport(DOM::Document& document, CSS::ComputedProperties const& style)
|
|
|
|
|
: BlockContainer(document, &document, style)
|
2019-06-15 17:49:44 -03:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-25 07:04:29 -03:00
|
|
|
Viewport::~Viewport() = default;
|
2019-06-16 16:35:03 -03:00
|
|
|
|
2026-02-08 13:19:13 -03:00
|
|
|
DOM::Document const& Viewport::dom_node() const
|
|
|
|
|
{
|
|
|
|
|
return static_cast<DOM::Document const&>(*Node::dom_node());
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 08:38:05 -03:00
|
|
|
RefPtr<Painting::Paintable> Viewport::create_paintable() const
|
2023-08-18 10:52:40 -03:00
|
|
|
{
|
|
|
|
|
return Painting::ViewportPaintable::create(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-28 07:07:58 -03:00
|
|
|
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()
|
|
|
|
|
{
|
2026-06-22 08:30:00 -03:00
|
|
|
Utf16StringBuilder builder;
|
2024-06-28 07:07:58 -03:00
|
|
|
Vector<TextPosition> text_positions;
|
|
|
|
|
Vector<TextBlock> text_blocks;
|
2025-07-09 15:13:38 -03:00
|
|
|
|
2026-05-30 13:53:00 -03:00
|
|
|
DOM::Text* pending_space_dom_node = nullptr;
|
|
|
|
|
DOM::Text const* current_dom_node = nullptr;
|
|
|
|
|
size_t pending_space_dom_offset = 0;
|
|
|
|
|
size_t expected_dom_offset = 0;
|
|
|
|
|
size_t builder_length_in_code_units = 0;
|
|
|
|
|
|
|
|
|
|
auto flush_block = [&] {
|
|
|
|
|
if (!builder.is_empty())
|
2026-06-22 08:30:00 -03:00
|
|
|
text_blocks.append({ builder.to_string(), text_positions });
|
2026-05-30 13:53:00 -03:00
|
|
|
text_positions.clear_with_capacity();
|
|
|
|
|
builder.clear();
|
|
|
|
|
builder_length_in_code_units = 0;
|
|
|
|
|
pending_space_dom_node = nullptr;
|
|
|
|
|
current_dom_node = nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto append_code_unit = [&](DOM::Text& dom_node, char16_t code_unit, size_t dom_offset) {
|
|
|
|
|
if (current_dom_node != &dom_node || dom_offset != expected_dom_offset)
|
|
|
|
|
text_positions.empend(dom_node, builder_length_in_code_units, dom_offset);
|
|
|
|
|
builder.append_code_unit(code_unit);
|
|
|
|
|
builder_length_in_code_units++;
|
|
|
|
|
current_dom_node = &dom_node;
|
|
|
|
|
expected_dom_offset = dom_offset + 1;
|
|
|
|
|
};
|
|
|
|
|
|
2024-06-28 07:07:58 -03:00
|
|
|
for_each_in_inclusive_subtree([&](auto const& layout_node) {
|
2024-10-16 10:19:32 -03:00
|
|
|
if (layout_node.display().is_none() || !layout_node.first_paintable() || !layout_node.first_paintable()->is_visible())
|
2024-06-28 07:07:58 -03:00
|
|
|
return TraversalDecision::Continue;
|
|
|
|
|
|
2026-04-12 14:27:36 -03:00
|
|
|
auto const pseudo = layout_node.generated_for_pseudo_element();
|
|
|
|
|
auto const wraps_dom_text = pseudo == CSS::PseudoElement::FirstLetter;
|
|
|
|
|
|
|
|
|
|
if (layout_node.is_box() || (pseudo.has_value() && !wraps_dom_text)) {
|
2026-05-30 13:53:00 -03:00
|
|
|
flush_block();
|
2024-06-28 07:07:58 -03:00
|
|
|
return TraversalDecision::Continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-05 20:57:14 -03:00
|
|
|
if (auto* text_node = as_if<Layout::TextNode>(layout_node)) {
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/interaction.html#inert-subtrees
|
|
|
|
|
// When a node is inert:
|
|
|
|
|
// - The user agent should ignore the node for the purposes of find-in-page.
|
2026-06-07 07:44:31 -03:00
|
|
|
auto* dom_text = text_node->dom_text();
|
|
|
|
|
if (!dom_text)
|
|
|
|
|
return TraversalDecision::Continue;
|
|
|
|
|
|
|
|
|
|
auto& dom_node = const_cast<DOM::Text&>(*dom_text);
|
2026-05-30 13:53:00 -03:00
|
|
|
if (dom_node.is_inert())
|
|
|
|
|
return TraversalDecision::Continue;
|
|
|
|
|
|
|
|
|
|
auto white_space_collapse = text_node->computed_values().white_space_collapse();
|
|
|
|
|
auto const should_collapse = first_is_one_of(white_space_collapse,
|
|
|
|
|
CSS::WhiteSpaceCollapse::Collapse,
|
|
|
|
|
CSS::WhiteSpaceCollapse::PreserveBreaks);
|
|
|
|
|
auto const dom_start_offset = text_node->dom_start_offset();
|
|
|
|
|
auto const& text = text_node->text_for_rendering();
|
|
|
|
|
auto const text_view = text.utf16_view();
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < text_view.length_in_code_units(); ++i) {
|
|
|
|
|
auto const code_unit = text_view.code_unit_at(i);
|
|
|
|
|
auto const dom_offset = dom_start_offset + i;
|
|
|
|
|
|
|
|
|
|
if (should_collapse && is_ascii_space(code_unit) && code_unit != '\n') {
|
|
|
|
|
if (!pending_space_dom_node) {
|
|
|
|
|
pending_space_dom_node = &dom_node;
|
|
|
|
|
pending_space_dom_offset = dom_offset;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pending_space_dom_node) {
|
|
|
|
|
if (!text_positions.is_empty())
|
|
|
|
|
append_code_unit(*pending_space_dom_node, ' ', pending_space_dom_offset);
|
|
|
|
|
pending_space_dom_node = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
append_code_unit(dom_node, code_unit, dom_offset);
|
2024-06-28 07:07:58 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TraversalDecision::Continue;
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-30 13:53:00 -03:00
|
|
|
flush_block();
|
2024-06-28 07:07:58 -03:00
|
|
|
|
|
|
|
|
m_text_blocks = move(text_blocks);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-07 06:27:02 -03:00
|
|
|
}
|