[FIX] web_editor: make to_inline with html2canvas fast

In mass_mailing, when there is an element with a background image, the
method html2canvas is used.
On a Mac M1, rendering that snippet takes ±700ms per element that goes
into html2canvas.
The reason is that html2canvas clone all the document of the node that
is passed to html2canvas. This is the document of the mass mailing
iframe (for instance including the snippet menu).

As a rendering is called at each input change in the editable
(eg. editor blur, adding a snipet, ...), the main thread will be blocked
more than 700ms each time the input change.

The reason it is slow is because html2canvas is cloning the whole
document, in order to properly get the style of the targeted element.
Because the element style is already inlined by the to_inline, this is
not necessary to clone the whole document.

In order to make html2canvas instantaneous without changing the lib
source code, the solution is to render from another iframe that
contain only the element to render.

task-3100165

X-original-commit: 8ea22ea04e4d7108ba6920fed3e1898df28a53d8
Part-of: odoo/odoo#112908
This commit is contained in:
Nicolas Bayet
2023-02-16 17:57:19 +01:00
parent 1987279f2a
commit 0c9f5147b3
@@ -640,7 +640,18 @@ async function toInline($editable, cssRules, $iframe) {
async function flattenBackgroundImages(editable) {
for (const backgroundImage of editable.querySelectorAll('*[style*=background-image]')) {
if (backgroundImage.parentElement) { // If the image was nested, we removed it already.
const canvas = await html2canvas(backgroundImage, { scale: 1 });
const iframe = document.createElement('iframe');
const style = getComputedStyle(backgroundImage);
const clonedBackground = backgroundImage.cloneNode(true);
clonedBackground.style.height = style['height'];
clonedBackground.style.width = style['width'];
iframe.style.height = style['height'];
iframe.style.width = style['width'];
backgroundImage.after(iframe);
iframe.contentDocument.body.append(clonedBackground);
iframe.contentDocument.body.style.margin = 0;
const canvas = await html2canvas(clonedBackground, { scale: 1 });
const image = document.createElement('img');
image.setAttribute('src', canvas.toDataURL('png'));
image.setAttribute('width', canvas.getAttribute('width'));
@@ -648,12 +659,11 @@ async function flattenBackgroundImages(editable) {
image.style.setProperty('margin', 0);
image.style.setProperty('display', 'block'); // Ensure no added vertical space.
// Clean up the original element.
for (const child of [...backgroundImage.childNodes]) {
child.remove();
}
backgroundImage.replaceChildren();
backgroundImage.style.setProperty('padding', 0);
backgroundImage.style.removeProperty('background-image');
backgroundImage.prepend(image); // Add the image to the original element.
iframe.remove();
}
}
}