Skip to content

Simulating pseudo-class selectors hover/active/focus/target broken on newer Chromes if executeJs set to true #219

@cburgmer

Description

@cburgmer

Simulating pseudo-class selectors hover/active/focus/target (see https://github.com/cburgmer/rasterizeHTML.js/wiki/API#optional-parameters) will result in the following error on Chrome, if the option executeJs is set to true:

Uncaught (in promise) TypeError: Cannot read properties of null (reading 'cssRules')
    at documentUtil.js:89:60
    at Array.forEach (<anonymous>)
    at replaceSimpleSelectorsBy (documentUtil.js:80:52)
    at Object.module.lowercaseCssTypeSelectors (documentUtil.js:131:9)
    at Object.module.rewriteTagNameSelectorsToLowerCase (documentHelper.js:68:22)
    at Object.module.getSvgForDocument (document2svg.js:107:24)
    at document2svg.js:122:31

Example of problematic invocation:

rasterizeHTML.drawHTML(
  html,
  {
    executeJs: true,
    active: ".bgimage", // remove this to circumvent the error
  }
);

Analysis:

  1. When executeJs is set to true, we load the document to be rendered into a sandboxed iframe, so that the JavaScript is executed automatically by the browser.
  2. We then extract the new document from the iframe and pass this on.
  3. The iframe has been added to the current window's document, to force the browser to evaluate the code. We now clean it up once the previous step is done.
  4. Eventually when we get to the pseudo-class handling code, we inspect all cssRules of style elements (styleElement.sheet.cssRules), and rewrite the selectors to fake a hover/active/focus/target event.
  5. Then we serialise the changed cssRules back into text/css and update the style's textContent (styleElement.textContent), as we need to serialize the whole (X)HTML document at the end before embedding in the SVG.

In newer Chromes now step 5 seems to reset the sheet property of the changed style elements. As we will access this property repeatedly (minimum once later to handle XHTML's lowercase tag names, and hence the need for tag name selectors to be lowercase), we fail and run into the above problem. (If we apply multiple fake pseudo-class activations, the stack trace looks slightly different.)

When exectuteJs is set to false, we do not use an iframe and this does not seem to trigger the issue.

We can circumvent the problem by not immediately removing the temporary iframe from step 3, but keeping it until we are done with the document exposed by it. It seems that removing the iframe from an active parent document, will not update the sheet property if a style's textContent is changed.

Here is a minimal test case to reproduce the browser issue: https://jsfiddle.net/cburgmer/zyLeu8w5/12/. Whether this is standard conformant or not is not clear to me.

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