From 8986e1f1ec646ee5b7ac1f66faaa389877b3fcad Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Wed, 3 Sep 2025 16:53:18 +0200 Subject: [PATCH] LibWeb: Merge nested editing hosts If a node with `contenteditable=true/plaintextonly` is the child of an editable node or an editing host, we should make it editable instead of an editing host. This effectively merges nested editing hosts together, which is how other browsers deal with this as well. Gains us 5 WPT subtest passes in `editing`. --- Libraries/LibWeb/DOM/Node.cpp | 7 ++- Libraries/LibWeb/Selection/Selection.cpp | 2 - .../expected/Editing/execCommand-delete.txt | 3 ++ .../Editing/execCommand-insertParagraph.txt | 6 +++ .../input/Editing/execCommand-delete.html | 20 +++++---- .../Editing/execCommand-insertParagraph.html | 44 ++++++++++++++----- 6 files changed, 59 insertions(+), 23 deletions(-) diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index d9b07a30e65..2f64f353e72 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -1573,9 +1573,14 @@ bool Node::is_editing_host() const // An editing host is either an HTML element with its contenteditable attribute in the true state or // plaintext-only state, + // AD-HOC: Only return true here if this node is not the child of another editable node or an editing host, + // effectively merging this potential editing host with its editing host ancestor. This causes a call to + // `::editing_host()` to automatically traverse to the top-most editing host. auto state = html_element->content_editable_state(); - if (state == HTML::ContentEditableState::True || state == HTML::ContentEditableState::PlaintextOnly) + if ((state == HTML::ContentEditableState::True || state == HTML::ContentEditableState::PlaintextOnly) + && (!parent() || !parent()->is_editable_or_editing_host())) { return true; + } // or a child HTML element of a Document whose design mode enabled is true. return is(parent()) && as(*parent()).design_mode_enabled_state(); diff --git a/Libraries/LibWeb/Selection/Selection.cpp b/Libraries/LibWeb/Selection/Selection.cpp index 013d2c60523..e69ab311ac9 100644 --- a/Libraries/LibWeb/Selection/Selection.cpp +++ b/Libraries/LibWeb/Selection/Selection.cpp @@ -550,8 +550,6 @@ void Selection::set_range(GC::Ptr range) // AD-HOC: Focus editing host if the previous selection was outside of it. There seems to be no spec for this. if (range && range->start_container()->is_editable_or_editing_host()) { GC::Ref new_editing_host = *range->start_container()->editing_host(); - while (new_editing_host->parent() && new_editing_host->parent()->is_editing_host()) - new_editing_host = *new_editing_host->parent(); if (document()->focused_area() != new_editing_host) { // FIXME: Determine and propagate the right focus trigger. HTML::run_focusing_steps(new_editing_host, nullptr, HTML::FocusTrigger::Other); diff --git a/Tests/LibWeb/Text/expected/Editing/execCommand-delete.txt b/Tests/LibWeb/Text/expected/Editing/execCommand-delete.txt index 2ecb7b918bb..b9025465e20 100644 --- a/Tests/LibWeb/Text/expected/Editing/execCommand-delete.txt +++ b/Tests/LibWeb/Text/expected/Editing/execCommand-delete.txt @@ -4,3 +4,6 @@ After: fobar --- b --- Before: foo👩🏼‍❤️‍👨🏻bar After: foobar +--- c --- +Before: foo
bar
+After: foobar diff --git a/Tests/LibWeb/Text/expected/Editing/execCommand-insertParagraph.txt b/Tests/LibWeb/Text/expected/Editing/execCommand-insertParagraph.txt index 79199075692..f7b26a1817d 100644 --- a/Tests/LibWeb/Text/expected/Editing/execCommand-insertParagraph.txt +++ b/Tests/LibWeb/Text/expected/Editing/execCommand-insertParagraph.txt @@ -1,2 +1,8 @@ Before:
  • foobar
After:
  • foo
  • bar
+Before: foo
bar
+After: foo
bar

+Before: foo
bar
+After: foo
bar

+Before: foo
bar
+After: foo
bar

diff --git a/Tests/LibWeb/Text/input/Editing/execCommand-delete.html b/Tests/LibWeb/Text/input/Editing/execCommand-delete.html index ad456e0573b..c9276d67d19 100644 --- a/Tests/LibWeb/Text/input/Editing/execCommand-delete.html +++ b/Tests/LibWeb/Text/input/Editing/execCommand-delete.html @@ -1,25 +1,27 @@ -
foobar
-
foo👩🏼‍❤️‍👨🏻bar
+
foobar
+
foo👩🏼‍❤️‍👨🏻bar
+
foo
bar
diff --git a/Tests/LibWeb/Text/input/Editing/execCommand-insertParagraph.html b/Tests/LibWeb/Text/input/Editing/execCommand-insertParagraph.html index 2c1d9ade6f1..1b339a088e7 100644 --- a/Tests/LibWeb/Text/input/Editing/execCommand-insertParagraph.html +++ b/Tests/LibWeb/Text/input/Editing/execCommand-insertParagraph.html @@ -1,19 +1,41 @@ -
  • foobar
+
  • foobar
+
foo
bar
+
foo
bar
+
foo
bar