skip to Main Content

I am working on a Chrome Extension. This extension has to set a few window variables before the website JS loads. To set these window variables, I am injecting a script from the content-script into the document.

The issue I am facing is that the injected script is running after the website’s JS loads.
I need the injected script to run before the website JS so that the values are properly initialized.

Current Execution Order:

  • Content Script -> Website JS -> Injected JS

Desired Execution Order:

  • Content Script -> Website JS -> Injected JS

Is there any way that the injected script can be forced to run before the application JS?
I have no control over the Website JS, so the configuration will have to be on the extension.

manifest.json

{
  "name": "script-injector",
  "manifest_version": 3,
  "version": "0.0.1",
  "content_scripts": [
    {
      "matches": ["*://localhost:*/*", "*://127.0.0.1:*/*"],
      "js": ["content.js"],
      "run_at": "document_start"
    }
  ],
  "web_accessible_resources": [
    {
      "matches": ["<all_urls>"],
      "resources": ["injected.js"]
    }
  ]
}

content.js

console.log("CE: Content Script Loaded");

function injectScript() {
  const script = document.createElement("script");
  script.src = chrome.runtime.getURL("injected.js");
  (document.head || document.documentElement).appendChild(script);
}

injectScript();

injected.js

console.log("CE: Injected Script Loaded");
window.someVar = true;

2

Answers


  1. You need to listen for change in dom that adds the script, and remove it. I use this snippet to replace the script from game.js with my script.

    // prevent default script
    let patcher = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        mutation.addedNodes.forEach(async function(node) {
          if (node.tagName == "SCRIPT" && node.src.includes("game.js")) {
            // block game.js
            console.log("replacing game script!");
            node.type = "javascript/blocked"; // block for chrome
            node.addEventListener("beforescriptexecute", e => e.preventDefault(), {
              once: true
            });
    
            var urlScript = "yourscript.com"
            var script = document.createElement("script");
            script.src = urlScript;
            document.head.appendChild(script);
            patcher.disconnect();
    
          }
        });
      });
    });
    patcher.observe(document, {
      attributes: false,
      childList: true,
      subtree: true
    });
    Login or Signup to reply.
  2. You can run the code in the context of the page (aka MAIN world) directly since Chrome 111.

    1. Specify "world": "MAIN" for your content script
    2. Remove web_accessible_resources and injected.js

    Your new content.js:

    window.someVar = true;
    

    Your new manifest.json:

    {
      "name": "script-injector",
      "manifest_version": 3,
      "version": "0.0.1",
      "content_scripts": [{
        "matches": ["*://localhost:*/*", "*://127.0.0.1:*/*"],
        "js": ["content.js"],
        "run_at": "document_start",
        "world": "MAIN"
      }]
    }
    

    Note that in this MAIN world your script can’t access chrome API of your extension, so if you need that you’ll need a second content script (without specifying world) and then use CustomEvent messaging, example.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search