LibWeb: Nested editing host focus should propagate to farthest ancestor

Nested editing hosts should act as a single big editing host, as long as
there are no uneditable elements in between.
This commit is contained in:
Jelle Raaijmakers 2025-08-21 15:24:55 +02:00 committed by Jelle Raaijmakers
commit 90f1c8724b
Notes: github-actions[bot] 2025-08-26 08:27:24 +00:00
3 changed files with 20 additions and 0 deletions

View file

@ -550,6 +550,8 @@ void Selection::set_range(GC::Ptr<DOM::Range> range)
// AD-HOC: Focus editing host if the previous selection was outside of it. There seems to be no spec for this. // 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()) { if (range && range->start_container()->is_editable_or_editing_host()) {
GC::Ref new_editing_host = *range->start_container()->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_element() != new_editing_host) { if (document()->focused_element() != new_editing_host) {
// FIXME: Determine and propagate the right focus trigger. // FIXME: Determine and propagate the right focus trigger.
HTML::run_focusing_steps(new_editing_host, nullptr, HTML::FocusTrigger::Other); HTML::run_focusing_steps(new_editing_host, nullptr, HTML::FocusTrigger::Other);

View file

@ -7,3 +7,9 @@ Range: [object Text] 1 [object Text] 1
-- Refocusing on same editing host -- -- Refocusing on same editing host --
Range: [object Text] 0 [object Text] 0 Range: [object Text] 0 [object Text] 0
<DIV id="b"> <DIV id="b">
-- Nested editing host --
Range: [object Text] 0 [object Text] 3
<DIV id="d">
-- Nested editing host with uneditable element as parent --
Range: [object Text] 0 [object Text] 3
<SPAN>

View file

@ -2,6 +2,8 @@
<div id="a" contenteditable>foo</div> <div id="a" contenteditable>foo</div>
<div id="b" contenteditable>foo<span>bar</span></div> <div id="b" contenteditable>foo<span>bar</span></div>
<button id="c">press me</button> <button id="c">press me</button>
<div id="d" contenteditable>foo <span contenteditable>bar</span></div>
<div id="e" contenteditable>foo <span contenteditable="false">bar <span contenteditable>baz</span></span></div>
<script src="include.js"></script> <script src="include.js"></script>
<script> <script>
test(() => { test(() => {
@ -27,5 +29,15 @@ test(() => {
const buttonRect = buttonElm.getBoundingClientRect(); const buttonRect = buttonElm.getBoundingClientRect();
internals.click(buttonRect.left + 5, buttonRect.top + 5); internals.click(buttonRect.left + 5, buttonRect.top + 5);
reportSelectionAndFocus(); reportSelectionAndFocus();
println('-- Nested editing host --');
const spanC = document.querySelector('div#d span');
getSelection().setBaseAndExtent(spanC.childNodes[0], 0, spanC.childNodes[0], 3);
reportSelectionAndFocus();
println('-- Nested editing host with uneditable element as parent --');
const spanD = document.querySelector('div#e span span');
getSelection().setBaseAndExtent(spanD.childNodes[0], 0, spanD.childNodes[0], 3);
reportSelectionAndFocus();
}); });
</script> </script>