/src/serenity/Userland/Libraries/LibWeb/Layout/Viewport.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <LibWeb/DOM/Range.h> |
8 | | #include <LibWeb/Dump.h> |
9 | | #include <LibWeb/Layout/Viewport.h> |
10 | | #include <LibWeb/Painting/PaintableBox.h> |
11 | | #include <LibWeb/Painting/StackingContext.h> |
12 | | #include <LibWeb/Painting/ViewportPaintable.h> |
13 | | |
14 | | namespace Web::Layout { |
15 | | |
16 | | JS_DEFINE_ALLOCATOR(Viewport); |
17 | | |
18 | | Viewport::Viewport(DOM::Document& document, NonnullRefPtr<CSS::StyleProperties> style) |
19 | 0 | : BlockContainer(document, &document, move(style)) |
20 | 0 | { |
21 | 0 | } |
22 | | |
23 | 0 | Viewport::~Viewport() = default; |
24 | | |
25 | | JS::GCPtr<Painting::Paintable> Viewport::create_paintable() const |
26 | 0 | { |
27 | 0 | return Painting::ViewportPaintable::create(*this); |
28 | 0 | } |
29 | | |
30 | | void Viewport::visit_edges(Visitor& visitor) |
31 | 0 | { |
32 | 0 | Base::visit_edges(visitor); |
33 | 0 | if (!m_text_blocks.has_value()) |
34 | 0 | return; |
35 | | |
36 | 0 | for (auto& text_block : *m_text_blocks) { |
37 | 0 | for (auto& text_position : text_block.positions) |
38 | 0 | visitor.visit(text_position.dom_node); |
39 | 0 | } |
40 | 0 | } |
41 | | |
42 | | Vector<Viewport::TextBlock> const& Viewport::text_blocks() |
43 | 0 | { |
44 | 0 | if (!m_text_blocks.has_value()) |
45 | 0 | update_text_blocks(); |
46 | |
|
47 | 0 | return *m_text_blocks; |
48 | 0 | } |
49 | | |
50 | | void Viewport::update_text_blocks() |
51 | 0 | { |
52 | 0 | StringBuilder builder; |
53 | 0 | size_t current_start_position = 0; |
54 | 0 | Vector<TextPosition> text_positions; |
55 | 0 | Vector<TextBlock> text_blocks; |
56 | 0 | for_each_in_inclusive_subtree([&](auto const& layout_node) { |
57 | 0 | if (layout_node.display().is_none() || !layout_node.paintable() || !layout_node.paintable()->is_visible()) |
58 | 0 | return TraversalDecision::Continue; |
59 | | |
60 | 0 | if (layout_node.is_box() || layout_node.is_generated()) { |
61 | 0 | if (!builder.is_empty()) { |
62 | 0 | text_blocks.append({ builder.to_string_without_validation(), text_positions }); |
63 | 0 | current_start_position = 0; |
64 | 0 | text_positions.clear_with_capacity(); |
65 | 0 | builder.clear(); |
66 | 0 | } |
67 | 0 | return TraversalDecision::Continue; |
68 | 0 | } |
69 | | |
70 | 0 | if (layout_node.is_text_node()) { |
71 | 0 | auto const& text_node = verify_cast<Layout::TextNode>(layout_node); |
72 | 0 | auto& dom_node = const_cast<DOM::Text&>(text_node.dom_node()); |
73 | 0 | if (text_positions.is_empty()) { |
74 | 0 | text_positions.empend(dom_node); |
75 | 0 | } else { |
76 | 0 | text_positions.empend(dom_node, current_start_position); |
77 | 0 | } |
78 | |
|
79 | 0 | auto const& current_node_text = text_node.text_for_rendering(); |
80 | 0 | current_start_position += current_node_text.bytes_as_string_view().length(); |
81 | 0 | builder.append(move(current_node_text)); |
82 | 0 | } |
83 | |
|
84 | 0 | return TraversalDecision::Continue; |
85 | 0 | }); |
86 | |
|
87 | 0 | if (!builder.is_empty()) |
88 | 0 | text_blocks.append({ builder.to_string_without_validation(), text_positions }); |
89 | |
|
90 | 0 | m_text_blocks = move(text_blocks); |
91 | 0 | } |
92 | | |
93 | | } |