Event listener receives null detail if run after document loads

Viewed 403

I've been working on an extension for Chrome.

In order to work, it must pass a global window variable set by the website to the extension's main script using a CustomEvent.
This usually works fine when the page is loaded in an active tab, but whenever it's loaded as a non-focused tab, or when the execution of the extension script is otherwise delayed, the event object received by the listener has a null detail attribute.

Just to be clear, the entire detail object of the event is null, not just the needed variable. I've made a workaround for this by attaching the needed variable to the document as a JSON string, but I'd like to understand what's wrong with the event.

Relevant parts of code are below.

manifest.json:

"content_scripts": [ {
          "js": [ "main.js", "jquery-3.3.1.js"],
          "matches": [ "*://*.site.com/*"],
          "run_at": "document_end"
       } ],
"permissions": [ "declarativeContent", "activeTab", "storage", "*://*.site.com/*" ],
"web_accessible_resources": [ "injectedScript.js" ]

main.js:

$(function() {

        document.addEventListener('globalPasser', function(response) {
            if(response.detail){
                // store variables
            }

        }, true);

        var passGlobals = document.createElement('script');
        passGlobals.src = chrome.extension.getURL('injectedScript.js');
        (document.head||document.documentElement).appendChild(passGlobals);
        passGlobals.onload = function() {
            passGlobals.remove();
        };
    }

injectedScript.js:

var newEvent = new CustomEvent('globalPasser', {detail:{variable: neededGlobalVariable}});
document.dispatchEvent(newEvent);
1 Answers

The workaround/fix is to simply "sanitize" the data via JSON:

document.dispatchEvent(
  new CustomEvent('globalPasser',
    JSON.parse(JSON.stringify({
      detail: {
        variable: neededGlobalVariable
      }
    }))
  )
);

The problem in this case is caused by JS functions being present inside the data - sometimes - probably when the site still processes something. DOM nodes would have caused the same problem.

Intentionally or not, but Chrome aborts serializing the entire detail object and transfers it as null since https://crbug.com/85158 and this behavior weirdly differs from extension messaging, which skips such non-serializable stuff: {foo: functionRef, bar: 123} is transferred as {bar: 123}.

Related