diff --git a/Userland/Libraries/LibWeb/DOM/Range.h b/Userland/Libraries/LibWeb/DOM/Range.h index 697e281de87..d7e84cf1bca 100644 --- a/Userland/Libraries/LibWeb/DOM/Range.h +++ b/Userland/Libraries/LibWeb/DOM/Range.h @@ -84,6 +84,8 @@ public: JS::NonnullGCPtr get_bounding_client_rect() const; + bool contains_node(Node const&) const; + private: explicit Range(Document&); Range(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset); @@ -105,7 +107,6 @@ private: WebIDL::ExceptionOr> clone_the_contents(); WebIDL::ExceptionOr insert(JS::NonnullGCPtr); - bool contains_node(Node const&) const; bool partially_contains_node(Node const&) const; }; diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp index e0b3c365c0d..d25709cf2c1 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -468,53 +469,43 @@ void BrowsingContext::set_cursor_position(DOM::Position position) reset_cursor_blink_cycle(); } -DeprecatedString BrowsingContext::selected_text() const +static DeprecatedString visible_text_in_range(DOM::Range const& range) { + // NOTE: This is an adaption of Range stringification, but we skip over DOM nodes that don't have a corresponding layout node. StringBuilder builder; - if (!active_document()) - return {}; - auto* layout_root = active_document()->layout_node(); - if (!layout_root) - return {}; - if (!layout_root->selection().is_valid()) - return {}; - auto selection = layout_root->selection().normalized(); - - if (selection.start().layout_node.ptr() == selection.end().layout_node) { - if (!is(*selection.start().layout_node)) - return ""; - return verify_cast(*selection.start().layout_node).text_for_rendering().substring(selection.start().index_in_node, selection.end().index_in_node - selection.start().index_in_node); + if (range.start_container() == range.end_container() && is(*range.start_container())) { + if (!range.start_container()->layout_node()) + return ""sv; + return static_cast(*range.start_container()).data().substring(range.start_offset(), range.end_offset() - range.start_offset()); } - // Start node - auto layout_node = selection.start().layout_node; - if (is(*layout_node)) { - auto& text = verify_cast(*layout_node).text_for_rendering(); - builder.append(text.substring(selection.start().index_in_node, text.length() - selection.start().index_in_node)); + if (is(*range.start_container()) && range.start_container()->layout_node()) + builder.append(static_cast(*range.start_container()).data().substring_view(range.start_offset())); + + for (DOM::Node const* node = range.start_container(); node != range.end_container()->next_sibling(); node = node->next_in_pre_order()) { + if (is(*node) && range.contains_node(*node) && node->layout_node()) + builder.append(static_cast(*node).data()); } - // Middle nodes - layout_node = layout_node->next_in_pre_order(); - while (layout_node && layout_node.ptr() != selection.end().layout_node) { - if (is(*layout_node)) - builder.append(verify_cast(*layout_node).text_for_rendering()); - else if (is(*layout_node) || is(*layout_node)) - builder.append('\n'); - - layout_node = layout_node->next_in_pre_order(); - } - - // End node - VERIFY(layout_node.ptr() == selection.end().layout_node); - if (is(*layout_node)) { - auto& text = verify_cast(*layout_node).text_for_rendering(); - builder.append(text.substring(0, selection.end().index_in_node)); - } + if (is(*range.end_container()) && range.end_container()->layout_node()) + builder.append(static_cast(*range.end_container()).data().substring_view(0, range.end_offset())); return builder.to_deprecated_string(); } +DeprecatedString BrowsingContext::selected_text() const +{ + auto* document = active_document(); + if (!document) + return ""sv; + auto selection = const_cast(*document).get_selection(); + auto range = selection->range(); + if (!range) + return ""sv; + return visible_text_in_range(*range); +} + void BrowsingContext::select_all() { if (!active_document())