Ian McIntyre Silber
Ian McIntyre Silber

Reputation: 5663

Chrome extension: Inject JS before page load

Is it possible to inject JS before page load, or is it necessary to use content scripts and way for the document to finish?

For example, is there a faster way to execute JS that turns the page red as soon as it's opened?

Upvotes: 54

Views: 39662

Answers (2)

Christopher
Christopher

Reputation: 3647

For Manifest V3 there was a new field added to content scripts in 102.0.5005.58 Stable: world

And there are also a lot of chrome bugs related to that topic: #634381, #1054624, #1207006

You have to use "world": "main" at the content script together with "run_at": "document_start" and a CSP to allow the injection from the Extension. Otherwise the injected script gets rejected with:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-*'), or a nonce ('nonce-...') is required to enable inline execution.

"world": "MAIN":

[Extensions] Add main world injections for dynamic content scripts

This CL adds a new field "world" for dynamic content scripts which allows the extension to specify if the script will run in the isolated or main world. By default, scripts which do not specify this field will run in the isolated world.

Valid attributes are "ISOLATED" (default) or "MAIN".

Example files i used:

manifest.json

{
  "name": "script injection",
  "version": "0",
  "manifest_version": 3,
  "minimum_chrome_version": "103.0",
  "content_scripts": [
    {
      "matches": ["*://*/*"],
      "js": ["inject.js"],
      "run_at": "document_start",
      "world": "MAIN"
    }
  ],
  "web_accessible_resources": [{
    "resources": ["injected.js"],
    "matches": ["<all_urls>"]
  }],
  "content_security_policy": {
    "extension_pages": "default-src 'self' 'wasm-unsafe-eval';"
  }
}

inject.js

let el = document.createElement("script");
el.src = chrome.runtime.getURL("injected.js");
document.documentElement.appendChild(el);
console.log("injected");

injected.js

console.log(typeof alert); // "function"
console.log("injected file");
delete window.alert;
console.log(typeof alert); // "undefined"

[enter image description here]

Upvotes: 12

Rob W
Rob W

Reputation: 348972

Declare a content script in the manifest file with "run_at": "document_start" to get it to run as soon as possible, i.e. right after constructing the document root (when <head> does not exist yet).

For your very specific example, it might be better to declare a content style instead, similar to content scripts, but using the "css" key instead of "js".

If you want to dynamically run a script as soon as possible, then call chrome.tabs.executeScript when the chrome.webNavigation.onCommitted event is triggered.

Upvotes: 71

Related Questions