Skip to content

html2canvas significantly slower in Firefox for Jupyterlab #3191

Description

@nishikantparmariam

Bug report

Context

We write custom React components for Jupyterlab, one of components allows users to download the view of that component as an image that internally uses html2canvas library. But we found that this operation is significantly slower in Firefox as compared to Chrome in Jupyterlab for the same component. This affects our user-experience a lot.

Reproducer

While our setup is bit complex, we managed to create a minimal reproducer.

Go to https://jupyter.org/try-jupyter/lab/, open a new notebook, add a cell with below code and run it. This code downloads html2canvas on the page and runs it against a div of the page:

from IPython.display import Javascript

Javascript("""

function loadScript(url, callback) {
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url; 
    script.onload = callback;    
    document.head.appendChild(script);
}

loadScript('https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js', async () => {

    const refreshIconDiv = document.getElementsByClassName('jp-CommandToolbarButton')[5];

    console.log('Styles:', window.getComputedStyle(refreshIconDiv).length);
    
    console.time('HTMLTOCANVAS TIMING');
    await html2canvas(refreshIconDiv);
    console.timeEnd('HTMLTOCANVAS TIMING');
});
""")

If we check the timing in console then in Chrome it takes just 500-600ms but in Firefox this takes 6-7s.

Root-Cause

Upon lots of debugging, we figured out that

export const copyCSSStyles = <T extends HTMLElement | SVGElement>(style: CSSStyleDeclaration, target: T): T => {

is significantly slower for each call in Firefox than in Chrome.

Firefox (8ms):
firefox-timing

Chrome (2.19ms):
image

Upon debugging further we found out that in Jupyterlab in Firefox this line:

const style = window.getComputedStyle(node);

always includes the custom CSS variables of Jupyterlab for any DOM node. See Styles: count in above snippet in both browsers.

image

Because html2canvas clones entire document and styles of every DOM node, these custom CSS variables are getting copied in Firefox making it very slow for Jupyterlab related use-cases.

Related Chromium Issues:
https://issues.chromium.org/issues/351029427
https://issues.chromium.org/issues/41419198

Proposed Solution

While this happens only in Jupyterlab in Firefox but any webpage having custom CSS variables will get affected and html2canvas performance will be very slow.

For us, these custom variables do not matter to be copied and may be skipped, so we re-wrote the copyCSSStyle function like below that made Firefox as fast as Chrome:

var copyCSSStyles = function (style, target) {
        // Iterate from beginning
        for (var i = 0; i < style.length; i++) {
            var property = style.item(i);
            // Return early if a style like this encountered as styles are sorted
            if(property.startsWith('--')) return target;
            if (ignoredStyleProperties.indexOf(property) === -1) {
                target.style.setProperty(property, style.getPropertyValue(property));
            }
        }
        return target;
};

While this fix is not very generic, an ideal fix could be that html2canvas allows us to override our own copyCSSStyle function, may be in options like this:

html2canvas(element, {customCopyCSSStyle: // My implementation})

and then we can provide our implementation to solve the problem. I'm happy to raise a PR.

Open for other solutions / ideas.

Specifications:

  • html2canvas version tested with: 1.4.1 (Latest)
  • Browser & version: Firefox ( 129.0.1 (64-bit) ) and Chrome ( Version 127.0.6533.120 (Official Build) (64-bit) )
  • Operating system: Windows 10

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions