diff --git a/Tests/LibWeb/Text/expected/DOM/Text-wholeText.txt b/Tests/LibWeb/Text/expected/DOM/Text-wholeText.txt new file mode 100644 index 00000000000..7a5faa33fe8 --- /dev/null +++ b/Tests/LibWeb/Text/expected/DOM/Text-wholeText.txt @@ -0,0 +1,6 @@ +Text node A with no siblings: A +Text node B with previous sibling A: AB +Text node B with previous sibling A and next sibling C: ABC +CDATA section D with no siblings: D +CDATA section E with previous sibling D: DE +CDATA section E with previous sibling D and next sibling F: DEF \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/DOM/Text-wholeText.html b/Tests/LibWeb/Text/input/DOM/Text-wholeText.html new file mode 100644 index 00000000000..88362152ec3 --- /dev/null +++ b/Tests/LibWeb/Text/input/DOM/Text-wholeText.html @@ -0,0 +1,36 @@ + + + diff --git a/Userland/Libraries/LibWeb/DOM/Text.cpp b/Userland/Libraries/LibWeb/DOM/Text.cpp index d78d65dec32..58413a15f92 100644 --- a/Userland/Libraries/LibWeb/DOM/Text.cpp +++ b/Userland/Libraries/LibWeb/DOM/Text.cpp @@ -117,4 +117,36 @@ WebIDL::ExceptionOr> Text::split_text(size_t offset) return new_node; } +// https://dom.spec.whatwg.org/#dom-text-wholetext +String Text::whole_text() +{ + // https://dom.spec.whatwg.org/#contiguous-text-nodes + // The contiguous Text nodes of a node node are node, node’s previous sibling Text node, if any, and its contiguous + // Text nodes, and node’s next sibling Text node, if any, and its contiguous Text nodes, avoiding any duplicates. + Vector nodes; + + nodes.append(this); + + auto* current_node = previous_sibling(); + while (current_node && (current_node->is_text() || current_node->is_cdata_section())) { + nodes.append(static_cast(current_node)); + current_node = current_node->previous_sibling(); + } + + // Reverse nodes so they are in tree order + nodes.reverse(); + + current_node = next_sibling(); + while (current_node && (current_node->is_text() || current_node->is_cdata_section())) { + nodes.append(static_cast(current_node)); + current_node = current_node->next_sibling(); + } + + StringBuilder builder; + for (auto const& text_node : nodes) + builder.append(text_node->data()); + + return MUST(builder.to_string()); +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Text.h b/Userland/Libraries/LibWeb/DOM/Text.h index 9d531dd5e82..4549f18f57c 100644 --- a/Userland/Libraries/LibWeb/DOM/Text.h +++ b/Userland/Libraries/LibWeb/DOM/Text.h @@ -43,6 +43,7 @@ public: EditableTextNodeOwner* editable_text_node_owner(); WebIDL::ExceptionOr> split_text(size_t offset); + String whole_text(); bool is_password_input() const { return m_is_password_input; } void set_is_password_input(Badge, bool b) { m_is_password_input = b; } diff --git a/Userland/Libraries/LibWeb/DOM/Text.idl b/Userland/Libraries/LibWeb/DOM/Text.idl index 4dc15d10227..059dcdf7fe7 100644 --- a/Userland/Libraries/LibWeb/DOM/Text.idl +++ b/Userland/Libraries/LibWeb/DOM/Text.idl @@ -8,7 +8,7 @@ interface Text : CharacterData { constructor(optional DOMString data = ""); [NewObject] Text splitText(unsigned long offset); - [FIXME] readonly attribute DOMString wholeText; + readonly attribute DOMString wholeText; }; Text includes Slottable;