From 218e9bb2aadf44e764400dd96249200f4bca3d32 Mon Sep 17 00:00:00 2001 From: Antoine Guenet Date: Mon, 4 Oct 2021 14:10:39 +0000 Subject: [PATCH] [FIX] web_editor: prevent pasting blocks into paragraph-related elements When pasting HTML, we have to be careful not to introduce invalid HTML. The editor corrected for blocks within inlines but failed to prevent the invalid pasting of blocks within paragraph-related elements (eg., `

text

`). This fixes that issue by unwrapping all blocks from the copied content when trying to paste within an element that doesn't accept paragraph-related elements as children. Notably, this fixes an issue that was caused by that problem, in which pasted HTML content could not be saved because it was invalid. task-2632676 closes odoo/odoo#79070 X-original-commit: 9c77834e03516f831a9600f0ba4131c957b3ba74 Signed-off-by: David Monjoie (dmo) --- .../lib/odoo-editor/src/commands/commands.js | 12 ++++- .../static/lib/odoo-editor/src/utils/utils.js | 49 ++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js b/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js index 0f4b8010c56..ed072c66108 100644 --- a/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js +++ b/addons/web_editor/static/lib/odoo-editor/src/commands/commands.js @@ -33,6 +33,9 @@ import { splitTextNode, startPos, nodeSize, + allowsParagraphRelatedElements, + isUnbreakable, + makeContentsInline, } from '../utils/utils.js'; const TEXT_CLASSES_REGEX = /\btext-[^\s]*\b/g; @@ -71,13 +74,18 @@ function insert(editor, data, isText = true) { let nodeToInsert; const insertedNodes = [...fakeEl.childNodes]; while ((nodeToInsert = fakeEl.childNodes[0])) { - if (isBlock(nodeToInsert) && !isBlock(startNode)) { + if (isBlock(nodeToInsert) && !allowsParagraphRelatedElements(startNode)) { // Split blocks at the edges if inserting new blocks (preventing //

text

scenarios). while ( startNode.parentElement !== editor.editable && - !isBlock(startNode.parentElement) + !allowsParagraphRelatedElements(startNode.parentElement) ) { + if (isUnbreakable(startNode.parentElement)) { + makeContentsInline(fakeEl); + nodeToInsert = fakeEl.childNodes[0]; + break; + } let offset = childNodeIndex(startNode); if (!insertBefore) { offset += 1; diff --git a/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js b/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js index 55ee0a0cf8c..c6ecc28572a 100644 --- a/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js +++ b/addons/web_editor/static/lib/odoo-editor/src/utils/utils.js @@ -800,7 +800,7 @@ export function preserveCursor(document) { * Source: * https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements * - * */ + **/ const blockTagNames = [ 'ADDRESS', 'ARTICLE', @@ -985,6 +985,53 @@ export function getInSelection(document, selector) { ); } +// This is a list of "paragraph-related elements", defined as elements that +// behave like paragraphs. +const paragraphRelatedElements = [ + 'P', + 'H1', + 'H2', + 'H3', + 'H4', + 'H5', + 'H6', +]; + +/** + * Return true if the given node allows "paragraph-related elements". + * + * @see paragraphRelatedElements + * @param {Node} node + * @returns {boolean} + */ +export function allowsParagraphRelatedElements(node) { + return isBlock(node) && !paragraphRelatedElements.includes(node.nodeName); +} + +/** + * Take a node and unwrap all of its block contents recursively. All blocks + * (except for firstChilds) are preceded by a
in order to preserve the line + * breaks. + * + * @param {Node} node + */ +export function makeContentsInline(node) { + let childIndex = 0; + for (const child of node.childNodes) { + if (isBlock(child)) { + if (childIndex && paragraphRelatedElements.includes(child.nodeName)) { + child.before(document.createElement('br')); + } + for (const grandChild of child.childNodes) { + child.before(grandChild); + makeContentsInline(grandChild); + } + child.remove(); + } + childIndex += 1; + } +} + /** * Returns an array of url infos for url matched in the given string. *