From 6f9110bfb0a4496205c220960bfa432c079756d8 Mon Sep 17 00:00:00 2001 From: Nicolas Bayet Date: Mon, 14 Mar 2022 12:19:07 +0000 Subject: [PATCH] [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 `

` with inline content, the inline content moved to the parent and the `

` 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) --- .../src/commands/deleteBackward.js | 26 ++++++++++++------ .../lib/odoo-editor/test/spec/editor.test.js | 27 +++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/addons/web_editor/static/lib/odoo-editor/src/commands/deleteBackward.js b/addons/web_editor/static/lib/odoo-editor/src/commands/deleteBackward.js index 0b8a01e2185..a0c24356be0 100644 --- a/addons/web_editor/static/lib/odoo-editor/src/commands/deleteBackward.js +++ b/addons/web_editor/static/lib/odoo-editor/src/commands/deleteBackward.js @@ -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; diff --git a/addons/web_editor/static/lib/odoo-editor/test/spec/editor.test.js b/addons/web_editor/static/lib/odoo-editor/test/spec/editor.test.js index fe799300391..b7ea65210f6 100644 --- a/addons/web_editor/static/lib/odoo-editor/test/spec/editor.test.js +++ b/addons/web_editor/static/lib/odoo-editor/test/spec/editor.test.js @@ -1358,6 +1358,33 @@ X[] contentAfter: `

[]def
`, }); }); + it('should remove contenteditable="false" at the beggining of a P', async () => { + await testEditor(BasicEditor, { + contentBefore: `

abc

def

[]ghi

`, + stepFunction: async editor => { + await deleteBackward(editor); + }, + contentAfter: `

abc

[]ghi

`, + }); + }); + it('should remove a fontawesome', async () => { + await testEditor(BasicEditor, { + contentBefore: `

abc

[]def

`, + stepFunction: async editor => { + await deleteBackward(editor); + }, + contentAfter: `

abc

[]def

`, + }); + }); + it('should remove a media element', async () => { + await testEditor(BasicEditor, { + contentBefore: `

abc

[]def

`, + stepFunction: async editor => { + await deleteBackward(editor); + }, + contentAfter: `

abc

[]def

`, + }); + }); }); describe('Line breaks', () => { describe('Single', () => {