[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., `<p><h1>text</h1></p>`). 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) <dmo@odoo.com>
This commit is contained in:
@@ -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
|
||||
// <p><p>text</p></p> 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;
|
||||
|
||||
@@ -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 <br> 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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user