diff --git a/addons/web_editor/static/src/js/backend/html_field.js b/addons/web_editor/static/src/js/backend/html_field.js
index 563d7a69b03..10080d5c9ef 100644
--- a/addons/web_editor/static/src/js/backend/html_field.js
+++ b/addons/web_editor/static/src/js/backend/html_field.js
@@ -420,9 +420,29 @@ export class HtmlField extends Component {
const strippedPropValue = stripHistoryIds(String(this.props.record.data[this.props.name]));
const strippedEditingValue = stripHistoryIds(this.getEditingValue());
const domParser = new DOMParser();
- const parsedPropValue = domParser.parseFromString(strippedPropValue || '
', 'text/html').body;
- const parsedEditingValue = domParser.parseFromString(strippedEditingValue, 'text/html').body;
- return !this.props.readonly && parsedPropValue.innerHTML !== parsedEditingValue.innerHTML;
+ const codeViewEl = this._getCodeViewEl();
+ let parsedPreviousValue;
+ // If the wysiwyg is active, we need to clean the content of the
+ // initialValue as the editingValue will be cleaned.
+ if (!codeViewEl && this.wysiwyg) {
+ const editable = domParser.parseFromString(strippedPropValue || '
', 'text/html').body;
+ // Temporarily append the editable to the DOM because the
+ // wysiwyg.getValue can indirectly call methods that needs to have
+ // access the node.ownerDocument.defaultView.getComputedStyle.
+ // By appending the editable to the dom, the node.ownerDocument will
+ // have a `defaultView`.
+ const div = document.createElement('div');
+ div.style.display = 'none';
+ div.append(editable);
+ document.body.append(div);
+ const editableValue = this.wysiwyg.getValue({ $layout: $(editable) });
+ div.remove();
+ parsedPreviousValue = domParser.parseFromString(editableValue, 'text/html').body;
+ } else {
+ parsedPreviousValue = domParser.parseFromString(strippedPropValue || '
', 'text/html').body;
+ }
+ const parsedNewValue = domParser.parseFromString(strippedEditingValue, 'text/html').body;
+ return !this.props.readonly && parsedPreviousValue.innerHTML !== parsedNewValue.innerHTML;
}
_getCodeViewEl() {
return this.state.showCodeView && this.codeViewRef.el;
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 d96b274477b..f79ec8456fe 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
@@ -4330,7 +4330,7 @@ export class OdooEditor extends EventTarget {
node.replaceChildren();
}
- sanitize(element, this.editable);
+ sanitize(element);
// Remove contenteditable=false on elements
for (const el of element.querySelectorAll('[contenteditable="false"]')) {
diff --git a/addons/web_editor/static/tests/html_field_tests.js b/addons/web_editor/static/tests/html_field_tests.js
index 3b53e8312c2..941d92d7659 100644
--- a/addons/web_editor/static/tests/html_field_tests.js
+++ b/addons/web_editor/static/tests/html_field_tests.js
@@ -941,4 +941,39 @@ QUnit.module("WebEditor.HtmlField", ({ beforeEach }) => {
assert.strictEqual(p.innerText.replaceAll('\u200B', ''), 'New label',
"The link's label should be updated");
});
+
+ QUnit.module("isDirty");
+
+ QUnit.test("isDirty should be false when the content is being transformed by the wysiwyg", async (assert) => {
+ assert.expect(2);
+
+ serverData.models.partner.records.push({
+ id: 1,
+ txt: "abc
",
+ });
+ let htmlField;
+ const wysiwygPromise = makeDeferred();
+ patchWithCleanup(HtmlField.prototype, {
+ async startWysiwyg() {
+ await super.startWysiwyg(...arguments);
+ htmlField = this;
+ wysiwygPromise.resolve();
+ }
+ });
+ await makeView({
+ type: "form",
+ resId: 1,
+ resModel: "partner",
+ serverData,
+ arch: `
+ `,
+ });
+ await wysiwygPromise;
+
+ assert.strictEqual(htmlField.wysiwyg.getValue(), 'abc
', 'the value should be sanitized by the wysiwyg');
+ assert.strictEqual(htmlField._isDirty(), false, 'should not be dirty as the content has not changed');
+
+ });
});