mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibWeb: Do not update selection on uneditable contents without Shift key
If selection navigation happens through an editing host, we should enforce that for collapsed navigations (i.e. moving the caret) it can only happen if the focus node of the selection is editable.
This commit is contained in:
parent
09645875ea
commit
60a501d824
|
|
@ -78,39 +78,58 @@ void EditingHostManager::set_selection_focus(GC::Ref<DOM::Node> focus_node, size
|
|||
m_document->reset_cursor_blink_cycle();
|
||||
}
|
||||
|
||||
GC::Ptr<Selection::Selection> EditingHostManager::get_selection_for_navigation(CollapseSelection collapse) const
|
||||
{
|
||||
// In order for navigation to happen inside an editing host, the document must have a selection,
|
||||
auto selection = m_document->get_selection();
|
||||
if (!selection)
|
||||
return {};
|
||||
|
||||
// and the focus node must be inside a text node,
|
||||
auto focus_node = selection->focus_node();
|
||||
if (!is<Text>(focus_node.ptr()))
|
||||
return {};
|
||||
|
||||
// and if we're performing collapsed navigation (i.e. moving the caret), the focus node must be editable.
|
||||
if (collapse == CollapseSelection::Yes && !focus_node->is_editable())
|
||||
return {};
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
void EditingHostManager::move_cursor_to_start(CollapseSelection collapse)
|
||||
{
|
||||
auto selection = m_document->get_selection();
|
||||
auto node = selection->anchor_node();
|
||||
if (!node || !is<DOM::Text>(*node))
|
||||
auto selection = get_selection_for_navigation(collapse);
|
||||
if (!selection)
|
||||
return;
|
||||
auto node = selection->focus_node();
|
||||
|
||||
if (collapse == CollapseSelection::Yes) {
|
||||
MUST(selection->collapse(node, 0));
|
||||
m_document->reset_cursor_blink_cycle();
|
||||
return;
|
||||
}
|
||||
MUST(selection->set_base_and_extent(*node, selection->anchor_offset(), *node, 0));
|
||||
MUST(selection->set_base_and_extent(*selection->anchor_node(), selection->anchor_offset(), *node, 0));
|
||||
}
|
||||
|
||||
void EditingHostManager::move_cursor_to_end(CollapseSelection collapse)
|
||||
{
|
||||
auto selection = m_document->get_selection();
|
||||
auto node = selection->anchor_node();
|
||||
if (!node || !is<DOM::Text>(*node))
|
||||
auto selection = get_selection_for_navigation(collapse);
|
||||
if (!selection)
|
||||
return;
|
||||
auto node = selection->focus_node();
|
||||
|
||||
if (collapse == CollapseSelection::Yes) {
|
||||
m_document->reset_cursor_blink_cycle();
|
||||
MUST(selection->collapse(node, node->length()));
|
||||
return;
|
||||
}
|
||||
MUST(selection->set_base_and_extent(*node, selection->anchor_offset(), *node, node->length()));
|
||||
MUST(selection->set_base_and_extent(*selection->anchor_node(), selection->anchor_offset(), *node, node->length()));
|
||||
}
|
||||
|
||||
void EditingHostManager::increment_cursor_position_offset(CollapseSelection collapse)
|
||||
{
|
||||
auto selection = m_document->get_selection();
|
||||
auto selection = get_selection_for_navigation(collapse);
|
||||
if (!selection)
|
||||
return;
|
||||
selection->move_offset_to_next_character(collapse == CollapseSelection::Yes);
|
||||
|
|
@ -118,7 +137,7 @@ void EditingHostManager::increment_cursor_position_offset(CollapseSelection coll
|
|||
|
||||
void EditingHostManager::decrement_cursor_position_offset(CollapseSelection collapse)
|
||||
{
|
||||
auto selection = m_document->get_selection();
|
||||
auto selection = get_selection_for_navigation(collapse);
|
||||
if (!selection)
|
||||
return;
|
||||
selection->move_offset_to_previous_character(collapse == CollapseSelection::Yes);
|
||||
|
|
@ -126,7 +145,7 @@ void EditingHostManager::decrement_cursor_position_offset(CollapseSelection coll
|
|||
|
||||
void EditingHostManager::increment_cursor_position_to_next_word(CollapseSelection collapse)
|
||||
{
|
||||
auto selection = m_document->get_selection();
|
||||
auto selection = get_selection_for_navigation(collapse);
|
||||
if (!selection)
|
||||
return;
|
||||
selection->move_offset_to_next_word(collapse == CollapseSelection::Yes);
|
||||
|
|
@ -134,7 +153,7 @@ void EditingHostManager::increment_cursor_position_to_next_word(CollapseSelectio
|
|||
|
||||
void EditingHostManager::decrement_cursor_position_to_previous_word(CollapseSelection collapse)
|
||||
{
|
||||
auto selection = m_document->get_selection();
|
||||
auto selection = get_selection_for_navigation(collapse);
|
||||
if (!selection)
|
||||
return;
|
||||
selection->move_offset_to_previous_word(collapse == CollapseSelection::Yes);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ private:
|
|||
|
||||
virtual GC::Ref<JS::Cell> as_cell() override { return *this; }
|
||||
|
||||
GC::Ptr<Selection::Selection> get_selection_for_navigation(CollapseSelection) const;
|
||||
|
||||
GC::Ref<Document> m_document;
|
||||
GC::Ptr<DOM::Node> m_active_contenteditable_element;
|
||||
};
|
||||
|
|
|
|||
14
Tests/LibWeb/Text/expected/selection-keyboard-navigation.txt
Normal file
14
Tests/LibWeb/Text/expected/selection-keyboard-navigation.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#text 0 - #text 3
|
||||
--- Left/Right key should not modify a selection on uneditable contents ---
|
||||
#text 0 - #text 3
|
||||
#text 0 - #text 3
|
||||
--- Neither should WordJump + Left/Right ---
|
||||
#text 0 - #text 3
|
||||
#text 0 - #text 3
|
||||
--- Shift + Left/Right key however, should modify the selection ---
|
||||
#text 0 - #text 4
|
||||
#text 0 - #text 3
|
||||
--- Shift + WordJump + Left/Right as well ---
|
||||
#text 0 - #text 7
|
||||
#text 0 - #text 11
|
||||
#text 0 - #text 8
|
||||
46
Tests/LibWeb/Text/input/selection-keyboard-navigation.html
Normal file
46
Tests/LibWeb/Text/input/selection-keyboard-navigation.html
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<div>foo bar baz</div>
|
||||
<script src="include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
function reportSelection() {
|
||||
const range = getSelection().getRangeAt(0);
|
||||
println(`${range.startContainer.nodeName} ${range.startOffset} - ${range.endContainer.nodeName} ${range.endOffset}`);
|
||||
}
|
||||
|
||||
const divElm = document.querySelector('div');
|
||||
getSelection().setBaseAndExtent(divElm.childNodes[0], 0, divElm.childNodes[0], 3);
|
||||
reportSelection();
|
||||
|
||||
const modAlt = 1 << 0;
|
||||
const modControl = 1 << 1;
|
||||
const modShift = 1 << 2;
|
||||
const modWordJump = modAlt | modControl; // macOS uses Mod_Alt, we should probably select just one modifier here.
|
||||
|
||||
println('--- Left/Right key should not modify a selection on uneditable contents ---');
|
||||
internals.sendKey(divElm, 'Right');
|
||||
reportSelection();
|
||||
internals.sendKey(divElm, 'Left');
|
||||
reportSelection();
|
||||
|
||||
println('--- Neither should WordJump + Left/Right ---');
|
||||
internals.sendKey(divElm, 'Right', modWordJump);
|
||||
reportSelection();
|
||||
internals.sendKey(divElm, 'Left', modWordJump);
|
||||
reportSelection();
|
||||
|
||||
println('--- Shift + Left/Right key however, should modify the selection ---');
|
||||
internals.sendKey(divElm, 'Right', modShift);
|
||||
reportSelection();
|
||||
internals.sendKey(divElm, 'Left', modShift);
|
||||
reportSelection();
|
||||
|
||||
println('--- Shift + WordJump + Left/Right as well ---');
|
||||
internals.sendKey(divElm, 'Right', modWordJump | modShift);
|
||||
reportSelection();
|
||||
internals.sendKey(divElm, 'Right', modWordJump | modShift);
|
||||
reportSelection();
|
||||
internals.sendKey(divElm, 'Left', modWordJump | modShift);
|
||||
reportSelection();
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in New Issue
Block a user