LibWeb: Do not modify selection if editing host is already selected

If an editing host receives focus, we would always set a new selection
range. However, we only need to do that if we're not already part of the
active range. This corresponds to behavior shown by Chrome and Firefox.
This commit is contained in:
Jelle Raaijmakers 2025-07-31 14:10:55 +02:00 committed by Jelle Raaijmakers
commit c9d4913bb4
Notes: github-actions[bot] 2025-08-01 08:10:42 +00:00
3 changed files with 39 additions and 0 deletions

View file

@ -18,6 +18,7 @@
#include <LibWeb/DOM/IDLEventListener.h>
#include <LibWeb/DOM/LiveNodeList.h>
#include <LibWeb/DOM/Position.h>
#include <LibWeb/DOM/Range.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/CloseWatcher.h>
@ -41,6 +42,7 @@
#include <LibWeb/Layout/TextNode.h>
#include <LibWeb/Namespace.h>
#include <LibWeb/Painting/PaintableBox.h>
#include <LibWeb/Selection/Selection.h>
#include <LibWeb/UIEvents/EventNames.h>
#include <LibWeb/UIEvents/PointerEvent.h>
#include <LibWeb/WebIDL/DOMException.h>
@ -2039,6 +2041,12 @@ void HTMLElement::did_receive_focus()
auto editing_host = document().editing_host_manager();
editing_host->set_active_contenteditable_element(this);
// Don't update the selection if we're already part of the active range.
if (auto range = document().get_selection()->range()) {
if (is_inclusive_ancestor_of(range->start_container()) || is_inclusive_ancestor_of(range->end_container()))
return;
}
DOM::Text* text = nullptr;
for_each_in_inclusive_subtree_of_type<DOM::Text>([&](auto& node) {
text = &node;

View file

@ -0,0 +1,5 @@
Range: [object Text] 1 [object Text] 2
document.activeElement: [object HTMLDivElement]
Selection must be the same as above:
Range: [object Text] 1 [object Text] 2
document.activeElement: [object HTMLDivElement]

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<div contenteditable>foo</div>
<script src="include.js"></script>
<script>
test(() => {
const divElm = document.querySelector('div[contenteditable]');
// Select the middle 'o'
let range = document.createRange();
range.setStart(divElm.childNodes[0], 1);
range.setEnd(divElm.childNodes[0], 2);
getSelection().addRange(range);
println(`Range: ${range.startContainer} ${range.startOffset} ${range.endContainer} ${range.endOffset}`);
println(`document.activeElement: ${document.activeElement}`);
// Refocus on the editing host
document.body.focus();
divElm.focus();
println('Selection must be the same as above:');
range = getSelection().getRangeAt(0);
println(`Range: ${range.startContainer} ${range.startOffset} ${range.endContainer} ${range.endOffset}`);
println(`document.activeElement: ${document.activeElement}`);
});
</script>