mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:47:36 +00:00
LibWeb: Implement BrowsingContext::selected_text() in terms of Selection
Instead of sifting through the layout tree to extract the selected text, look at the DOM selection instead. Note that we can't just stringify the DOM Range, as that would include non-visible things (like the content of <style> elements, etc.) so we run it through an ad-hoc variant of the range stringification algorithm. This can probably be factored better, but it's a start. :^)
This commit is contained in:
parent
3cabd17f9b
commit
bf69f4370e
2 changed files with 29 additions and 37 deletions
|
@ -84,6 +84,8 @@ public:
|
||||||
|
|
||||||
JS::NonnullGCPtr<Geometry::DOMRect> get_bounding_client_rect() const;
|
JS::NonnullGCPtr<Geometry::DOMRect> get_bounding_client_rect() const;
|
||||||
|
|
||||||
|
bool contains_node(Node const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Range(Document&);
|
explicit Range(Document&);
|
||||||
Range(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset);
|
Range(Node& start_container, u32 start_offset, Node& end_container, u32 end_offset);
|
||||||
|
@ -105,7 +107,6 @@ private:
|
||||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> clone_the_contents();
|
WebIDL::ExceptionOr<JS::NonnullGCPtr<DocumentFragment>> clone_the_contents();
|
||||||
WebIDL::ExceptionOr<void> insert(JS::NonnullGCPtr<Node>);
|
WebIDL::ExceptionOr<void> insert(JS::NonnullGCPtr<Node>);
|
||||||
|
|
||||||
bool contains_node(Node const&) const;
|
|
||||||
bool partially_contains_node(Node const&) const;
|
bool partially_contains_node(Node const&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOM/Event.h>
|
#include <LibWeb/DOM/Event.h>
|
||||||
#include <LibWeb/DOM/HTMLCollection.h>
|
#include <LibWeb/DOM/HTMLCollection.h>
|
||||||
|
#include <LibWeb/DOM/Range.h>
|
||||||
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
|
||||||
#include <LibWeb/HTML/BrowsingContext.h>
|
#include <LibWeb/HTML/BrowsingContext.h>
|
||||||
#include <LibWeb/HTML/BrowsingContextContainer.h>
|
#include <LibWeb/HTML/BrowsingContextContainer.h>
|
||||||
|
@ -468,53 +469,43 @@ void BrowsingContext::set_cursor_position(DOM::Position position)
|
||||||
reset_cursor_blink_cycle();
|
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;
|
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 (range.start_container() == range.end_container() && is<DOM::Text>(*range.start_container())) {
|
||||||
|
if (!range.start_container()->layout_node())
|
||||||
if (selection.start().layout_node.ptr() == selection.end().layout_node) {
|
return ""sv;
|
||||||
if (!is<Layout::TextNode>(*selection.start().layout_node))
|
return static_cast<DOM::Text const&>(*range.start_container()).data().substring(range.start_offset(), range.end_offset() - range.start_offset());
|
||||||
return "";
|
|
||||||
return verify_cast<Layout::TextNode>(*selection.start().layout_node).text_for_rendering().substring(selection.start().index_in_node, selection.end().index_in_node - selection.start().index_in_node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start node
|
if (is<DOM::Text>(*range.start_container()) && range.start_container()->layout_node())
|
||||||
auto layout_node = selection.start().layout_node;
|
builder.append(static_cast<DOM::Text const&>(*range.start_container()).data().substring_view(range.start_offset()));
|
||||||
if (is<Layout::TextNode>(*layout_node)) {
|
|
||||||
auto& text = verify_cast<Layout::TextNode>(*layout_node).text_for_rendering();
|
for (DOM::Node const* node = range.start_container(); node != range.end_container()->next_sibling(); node = node->next_in_pre_order()) {
|
||||||
builder.append(text.substring(selection.start().index_in_node, text.length() - selection.start().index_in_node));
|
if (is<DOM::Text>(*node) && range.contains_node(*node) && node->layout_node())
|
||||||
|
builder.append(static_cast<DOM::Text const&>(*node).data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Middle nodes
|
if (is<DOM::Text>(*range.end_container()) && range.end_container()->layout_node())
|
||||||
layout_node = layout_node->next_in_pre_order();
|
builder.append(static_cast<DOM::Text const&>(*range.end_container()).data().substring_view(0, range.end_offset()));
|
||||||
while (layout_node && layout_node.ptr() != selection.end().layout_node) {
|
|
||||||
if (is<Layout::TextNode>(*layout_node))
|
|
||||||
builder.append(verify_cast<Layout::TextNode>(*layout_node).text_for_rendering());
|
|
||||||
else if (is<Layout::BreakNode>(*layout_node) || is<Layout::BlockContainer>(*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::TextNode>(*layout_node)) {
|
|
||||||
auto& text = verify_cast<Layout::TextNode>(*layout_node).text_for_rendering();
|
|
||||||
builder.append(text.substring(0, selection.end().index_in_node));
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.to_deprecated_string();
|
return builder.to_deprecated_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeprecatedString BrowsingContext::selected_text() const
|
||||||
|
{
|
||||||
|
auto* document = active_document();
|
||||||
|
if (!document)
|
||||||
|
return ""sv;
|
||||||
|
auto selection = const_cast<DOM::Document&>(*document).get_selection();
|
||||||
|
auto range = selection->range();
|
||||||
|
if (!range)
|
||||||
|
return ""sv;
|
||||||
|
return visible_text_in_range(*range);
|
||||||
|
}
|
||||||
|
|
||||||
void BrowsingContext::select_all()
|
void BrowsingContext::select_all()
|
||||||
{
|
{
|
||||||
if (!active_document())
|
if (!active_document())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue