[FIX] web_editor: prevent deleteBackspace to move nodes

Before this commit, whenever the element that must be deleted by
`deleteBackspace` matches `isMediaElement` or `isNotEditableNode` and
the cursor was in the beginning of a `<p>` with inline content, the
inline content moved to the parent and the `<p>` was deleted.
Which is wrong.

Now, whenever the element that must be deleted by `deleteBackspace`
matches `isMediaElement` or `isNotEditableNode`, no inline content
moves.

Task-2781328

closes odoo/odoo#91233

X-original-commit: 34d06958d053b070b1e07541214899649602873a
Signed-off-by: David Monjoie (dmo) <dmo@odoo.com>
This commit is contained in:
Nicolas Bayet
2022-05-12 17:35:00 +02:00
parent 9699cb4d55
commit 6f9110bfb0
2 changed files with 45 additions and 8 deletions
@@ -15,6 +15,7 @@ import {
isVisible,
isVisibleStr,
leftPos,
rightPos,
moveNodes,
nodeSize,
prepareUpdate,
@@ -22,6 +23,8 @@ import {
splitTextNode,
isMediaElement,
isVisibleEmpty,
isNotEditableNode,
createDOMPathGenerator,
} from '../utils/utils.js';
Text.prototype.oDeleteBackward = function (offset, alreadyMoved = false) {
@@ -65,6 +68,10 @@ Text.prototype.oDeleteBackward = function (offset, alreadyMoved = false) {
setSelection(parentNode, firstSplitOffset);
};
const isDeletable = (node) => {
return isMediaElement(node) || isNotEditableNode(node);
}
HTMLElement.prototype.oDeleteBackward = function (offset, alreadyMoved = false, offsetLimit) {
const contentIsZWS = this.textContent === '\u200B';
let moveDest;
@@ -74,18 +81,11 @@ HTMLElement.prototype.oDeleteBackward = function (offset, alreadyMoved = false,
throw UNREMOVABLE_ROLLBACK_CODE;
}
if (
isMediaElement(leftNode) ||
(leftNode.getAttribute &&
typeof leftNode.getAttribute('contenteditable') === 'string' &&
leftNode.getAttribute('contenteditable').toLowerCase() === 'false')
isDeletable(leftNode)
) {
leftNode.remove();
return;
}
if (leftNode.getAttribute && leftNode.getAttribute('contenteditable') === 'false') {
leftNode.remove();
return;
}
if (!isBlock(leftNode) || isVisibleEmpty(leftNode)) {
/**
* Backspace just after an inline node, convert to backspace at the
@@ -162,6 +162,16 @@ HTMLElement.prototype.oDeleteBackward = function (offset, alreadyMoved = false,
moveDest = leftPos(this);
}
const domPathGenerator = createDOMPathGenerator(DIRECTIONS.LEFT, {
leafOnly: true,
stopTraverseFunction: isDeletable,
});
const domPath = domPathGenerator(this, offset)
const leftNode = domPath.next().value;
if (leftNode && isDeletable(leftNode)) {
const [parent, offset] = rightPos(leftNode);
return parent.oDeleteBackward(offset, alreadyMoved);
}
let node = this.childNodes[offset];
const nextSibling = this.nextSibling;
let currentNodeIndex = offset;
@@ -1358,6 +1358,33 @@ X[]
contentAfter: `<div>[]def</div>`,
});
});
it('should remove contenteditable="false" at the beggining of a P', async () => {
await testEditor(BasicEditor, {
contentBefore: `<p>abc</p><div contenteditable="false">def</div><p>[]ghi</p>`,
stepFunction: async editor => {
await deleteBackward(editor);
},
contentAfter: `<p>abc</p><p>[]ghi</p>`,
});
});
it('should remove a fontawesome', async () => {
await testEditor(BasicEditor, {
contentBefore: `<p>abc</p><span class="fa"></span><p>[]def</p>`,
stepFunction: async editor => {
await deleteBackward(editor);
},
contentAfter: `<p>abc</p><p>[]def</p>`,
});
});
it('should remove a media element', async () => {
await testEditor(BasicEditor, {
contentBefore: `<p>abc</p><div class="o_image"></div><p>[]def</p>`,
stepFunction: async editor => {
await deleteBackward(editor);
},
contentAfter: `<p>abc</p><p>[]def</p>`,
});
});
});
describe('Line breaks', () => {
describe('Single', () => {