[FIX] web_editor: prevent traceback on arrow keypress at editor edge

Steps to reproduce:
1. Type a backtick
2. Type "A"
3. Type a backtick
4. Hit Left Arrow key
5. Hit Right Arrow key 2x

This led to a traceback ("Cannot read properties of null (reading
'isContentEditable') at OdooEditor._onKeyDown") because using `nextLeaf`
without specifying the editable bounds returned a node that was not
within these bounds, and a subsequent call to `closestElement` on this
node returned `null` as a result.

task-3749506

closes odoo/odoo#154484

X-original-commit: ec1fe7b05e0d91605f78dd1844c7d2222671eab3
Signed-off-by: Geelen Sébastien (sge) <sge@odoo.com>
Signed-off-by: Antoine Guenet (age) <age@odoo.com>
This commit is contained in:
Antoine Guenet
2024-02-19 07:42:48 +00:00
parent 1cf48deef9
commit 951ea9ea3d
@@ -4070,20 +4070,22 @@ export class OdooEditor extends EventTarget {
// Find previous character.
let previousCharacter = focusOffset > 0 && focusNode.textContent[focusOffset - 1];
if (!previousCharacter) {
focusNode = previousLeaf(focusNode);
focusOffset = nodeSize(focusNode);
previousCharacter = focusNode.textContent[focusOffset - 1];
focusNode = previousLeaf(focusNode, this.editable);
focusOffset = focusNode && nodeSize(focusNode);
previousCharacter = focusNode && focusNode.textContent[focusOffset - 1];
}
// Move selection if previous character is zero-width space
if (previousCharacter === '\u200B' && !focusNode.parentElement.hasAttribute('data-o-link-zws')) {
if (focusNode && previousCharacter === '\u200B' && !focusNode.parentElement.hasAttribute('data-o-link-zws')) {
focusOffset -= 1;
while (focusNode && (focusOffset < 0 || !focusNode.textContent[focusOffset])) {
focusNode = nextLeaf(focusNode);
focusNode = nextLeaf(focusNode, this.editable);
focusOffset = focusNode && nodeSize(focusNode);
}
const startContainer = ev.shiftKey ? anchorNode : focusNode;
const startOffset = ev.shiftKey ? anchorOffset : focusOffset;
setSelection(startContainer, startOffset, focusNode, focusOffset);
if (focusNode) {
const startContainer = ev.shiftKey ? anchorNode : focusNode;
const startOffset = ev.shiftKey ? anchorOffset : focusOffset;
setSelection(startContainer, startOffset, focusNode, focusOffset);
}
}
}
} else if (IS_KEYBOARD_EVENT_RIGHT_ARROW(ev)) {
@@ -4111,25 +4113,27 @@ export class OdooEditor extends EventTarget {
// Find next character.
let nextCharacter = focusNode.textContent[focusOffset];
if (!nextCharacter) {
focusNode = nextLeaf(focusNode);
focusNode = nextLeaf(focusNode, this.editable);
focusOffset = 0;
nextCharacter = focusNode.textContent[focusOffset];
nextCharacter = focusNode && focusNode.textContent[focusOffset];
}
// Move selection if next character is zero-width space
if (nextCharacter === '\u200B' && !focusNode.parentElement.hasAttribute('data-o-link-zws')) {
focusOffset += 1;
let newFocusNode = focusNode;
while (newFocusNode && (!newFocusNode.textContent[focusOffset] || !closestElement(newFocusNode).isContentEditable)) {
newFocusNode = nextLeaf(newFocusNode);
newFocusNode = nextLeaf(newFocusNode, this.editable);
focusOffset = 0;
}
if (!focusOffset && closestBlock(focusNode) !== closestBlock(newFocusNode)) {
if (newFocusNode && !focusOffset && closestBlock(focusNode) !== closestBlock(newFocusNode)) {
newFocusNode = focusNode; // Do not move selection to next block.
focusOffset = nodeSize(focusNode);
focusOffset = focusNode && nodeSize(focusNode);
}
if (newFocusNode) {
const startContainer = ev.shiftKey ? anchorNode : newFocusNode;
const startOffset = ev.shiftKey ? anchorOffset : focusOffset;
setSelection(startContainer, startOffset, newFocusNode, focusOffset);
}
const startContainer = ev.shiftKey ? anchorNode : newFocusNode;
const startOffset = ev.shiftKey ? anchorOffset : focusOffset;
setSelection(startContainer, startOffset, newFocusNode, focusOffset);
}
}
}