From f60e8faacae55b72ba5562d443d5ea3236248393 Mon Sep 17 00:00:00 2001 From: "Rodopho Cammarosano de Lima (rcdl)" Date: Wed, 21 Dec 2022 17:47:26 +0000 Subject: [PATCH] [FIX] web_editor: fix issues caused by textContent Issue #1: reading textContent When reading the textContent property of an element, its does not take into account line breaks introduced by
and block elements, whilst innerHtml does. This commit solves the issue of updating a link's href to a non-sense url upon label change. Before this commit, "www.abc.com
www.xyz.com" was being incorrectely parsed as "www.abc.comwww.xyz.com". Issue #2: setting textContent When setting the textContent property of an element, all of its child nodes are removed and replaced by a single text node. While this is fine when the element contains a single text node, it might not be the desired result when the element contains other children. This commit solves the issue obtained when pasting html such as "link.com
link.be" inside an empty inline element such as STRONG or A, in which cleanForSave was changing its content (removing the line-break) due to misuse of textContent, when the intended effected was simply removing ZeroWidthSpace characters. A safer alternative for removing ZeroWidthSpace is thus provided. task-3081254 closes odoo/odoo#112579 X-original-commit: 9ff7c2ccff5f959f2a1cd7334c42b7bf0134f5d2 Signed-off-by: David Monjoie (dmo) --- .../src/js/editor/odoo-editor/src/OdooEditor.js | 12 +++++++----- .../src/js/editor/odoo-editor/src/utils/sanitize.js | 12 ++++++------ .../src/js/editor/odoo-editor/src/utils/utils.js | 11 +++++++++++ .../src/js/editor/odoo-editor/test/spec/link.test.js | 8 ++++++++ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js index fbcdc382ac4..309e9700477 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/OdooEditor.js @@ -13,7 +13,6 @@ import './commands/align.js'; import { sanitize } from './utils/sanitize.js'; import { serializeNode, unserializeNode, serializeSelection } from './utils/serialize.js'; import { - isMacOS, closestBlock, commonParentGet, containsUnremovable, @@ -70,7 +69,10 @@ import { parseHTML, splitTextNode, isEditorTab, - isVoidElement + isMacOS, + isVoidElement, + cleanZWS, + isZWS, } from './utils/utils.js'; import { editorCommands } from './commands/commands.js'; import { Powerbox } from './powerbox/Powerbox.js'; @@ -3762,14 +3764,14 @@ export class OdooEditor extends EventTarget { // we only remove the attribute to ensure we don't break some style. // Otherwise we remove the entire inline element. for (const emptyElement of element.querySelectorAll('[data-oe-zws-empty-inline]')) { - if (emptyElement.textContent.length === 1 && emptyElement.textContent.includes('\u200B')) { + if (isZWS(emptyElement)) { if (emptyElement.classList.length > 0) { emptyElement.removeAttribute('data-oe-zws-empty-inline'); } else { emptyElement.remove(); } } else { - emptyElement.textContent = emptyElement.textContent.replace('\u200B', ''); + cleanZWS(emptyElement); emptyElement.removeAttribute('data-oe-zws-empty-inline'); } } @@ -3796,7 +3798,7 @@ export class OdooEditor extends EventTarget { // Remove Zero Width Spaces on Font awesome elements const faSelector = 'i.fa,span.fa,i.fab,span.fab,i.fad,span.fad,i.far,span.far'; for (const el of element.querySelectorAll(faSelector)) { - el.textContent = el.textContent.replace('\u200B', ''); + cleanZWS(el); } // Clean custom selections diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/sanitize.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/sanitize.js index 2b7e64d2a20..acfd0760836 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/sanitize.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/sanitize.js @@ -16,7 +16,6 @@ import { isEditorTab, isZWS, getUrlsInfosInString, - URL_REGEX, isVoidElement, } from './utils.js'; @@ -240,16 +239,17 @@ class Sanitize { ) { node.setAttribute('contenteditable', 'false'); } + if (node.firstChild) { this._parse(node.firstChild); } + // Update link URL if label is a new valid link. if (node.nodeName === 'A' && anchorEl === node) { - const linkLabel = node.textContent; - const match = linkLabel.match(URL_REGEX); - if (match && match[0] === node.textContent && !node.href.startsWith('mailto:')) { - const urlInfo = getUrlsInfosInString(linkLabel)[0]; - node.setAttribute('href', urlInfo.url); + const linkLabel = node.innerText; + const urlInfo = getUrlsInfosInString(linkLabel); + if (urlInfo.length && urlInfo[0].label === linkLabel && !node.href.startsWith('mailto:')) { + node.setAttribute('href', urlInfo[0].url); } } node = node.nextSibling; diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js index f68c62c6974..62dc4064aef 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/src/utils/utils.js @@ -2574,3 +2574,14 @@ export function peek(arr) { export function isMacOS() { return window.navigator.userAgent.includes('Mac'); } + +/** + * Remove zero-width spaces from the provided node and its descendants. + * + * @param {Node} node + */ +export function cleanZWS(node) { + [node, ...descendants(node)] + .filter(node => node.nodeType === Node.TEXT_NODE) + .forEach(node => node.nodeValue = node.nodeValue.replace(/\u200B/g, '')); +} diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js index 3418ac272a3..03bce980a8d 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/link.test.js @@ -239,6 +239,14 @@ describe('Link', () => { }, contentAfter: '

agoogle.comu[]b

', }); + await testEditor(BasicEditor, { + contentBefore: '

agoogle.com[]

', + stepFunction: async editor => { + await insertLineBreak(editor); + await insertText(editor, 'odoo.com') + }, + contentAfter: '

agoogle.com
odoo.com[]

', + }); }); }); describe('range not collapsed', () => {