Tomasz Banasiak
Tomasz Banasiak

Reputation: 1560

Multiple JS files in Chrome Extension - how to load them?

I've wrote a Chrome Extension. My background.js file is quite large, so I want to split it to smaller parts and load specified methods when required (some kind of lazy-loading).

I've done this with Firefox:

// ( call for load specified lib )
var libPath = redExt.basePath + 'content/redExt/lib/' + redExt.browser + '/' + libName + '.js';
var service = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
service.loadSubScript("chrome://" + libPath);
// ( executing loaded file )

Is there any possiblity to do it similar way in Webkit-based browsers? I've found solutions for how to inject multiple JS files into matching pages (using manifest.json) but cannot find way to include JS file just for extension.

Upvotes: 39

Views: 49760

Answers (6)

matt
matt

Reputation: 519

manifest v3 has changed a lot

developer.chrome.com : Extension service worker basics

mdn : importScripts

TL;DR

importScripts('script-1.js','script-2.js');

or more fully

  var err;
  try{      
        importScripts('nmhost/ext-nmhost.js');            
  }
  catch(err2){
        err    = err2;
  }
  if(err){    //handle error        
        console.error(err);            
  }
  

I have a useful example browser extension which makes use of importScripts, the extension allows launching elevated and non-elevated programs from the browser using the native messaging protocol, see github : simple-nativemessaging - example directory

further reading on this topic

chrome developers : Improve extension security

chrome developers : Migrate to a service worker

for those who wish to develop using the old manifest v2, previous versions of chromium can be downloaded from chromium.org : Downloading old builds of Chrome / Chromium, manifest V3 is supported generally in Chrome 88 ( January 19th, 2021 ) or later.

Upvotes: 6

Omn
Omn

Reputation: 3070

UPDATE : This solution works only if you are using manifest V2.

You can also do this the extremely easy way that is described here: https://developer.chrome.com/extensions/background_pages#manifest

{
  "name": "My extension",
  ...
  "background": {
    "scripts": [
      "lib/fileone.js",
      "lib/filetwo.js",
      "background.js"
    ]
  },
  ...
}

You won't be doing lazy loading, but it does allow you to break your code up into multiple files and specify the order in which they are loaded onto the background page.

Upvotes: 49

Ryan Walker
Ryan Walker

Reputation: 844

What I've done isn't lazy loading but I load some files before the others when my chrome extension icon is clicked. In my case, I wanted my util files before the others that utilize them:

chrome.browserAction.onClicked.addListener(function() {
  chrome.tabs.executeScript(null, {file: "src/utils/utilFunction1.js"});
  chrome.tabs.executeScript(null, {file: "src/utils/utilFunction2.js"});
  chrome.tabs.executeScript(null, {file: "src/main.js"});
  chrome.tabs.executeScript(null, {file: "src/otherFeature.js"});
});

Upvotes: 2

rsanchez
rsanchez

Reputation: 14657

If you want to load a javascript file in the context of your background page and want to avoid using eval, you can just add a script tag to your background page's DOM. For instance, this works if your files are present in the lib folder of your extension:

function loadScript(scriptName, callback) {
    var scriptEl = document.createElement('script');
    scriptEl.src = chrome.extension.getURL('lib/' + scriptName + '.js');
    scriptEl.addEventListener('load', callback, false);
    document.head.appendChild(scriptEl);
}

Upvotes: 16

Tomasz Banasiak
Tomasz Banasiak

Reputation: 1560

Found possible solution. Theres a simple implementation of RequireJS main method require which uses JavaScript callback trace to find event for loading main extension file and binds executes it with extension context. https://github.com/salsita/browser-require/blob/master/require.js

It seems to work, but this solution has a few cons:

  • bug reports are reported in "line 1, column 1" because this solution injects code directly to func.call - debugging is very hard
  • Loaded JS files does not appear in console / chromebug
  • If current tab uses HTTPS Chrome will disallow evaling scripts, especially this from local context (file:///), so it sometimes just dont work as expected

Upvotes: 1

mdenton8
mdenton8

Reputation: 616

You could attempt to use web workers, perhaps. In your background.js, include:

var worker = new Worker('new-script.js');

Web workers can spawn new workers, and even have an importScript("new-script.js") method.

Web workers can tend to be very limited, however. Check here for a great article on Web workers.

I don't know if they would work, however. The other option is using XMLHTTPRequest (AJAX) to dynamically retrieve the script, and eval it. Over a non-HTTPS connection however, this is probably blocked due to man-in-the-middle attacks. Alternatively, you can force Chrome to eval script from a non-encrypted connection (BUT DON'T DO IT) by adding this to manifest.json:

"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"permissions": ["http://badpractic.es/insecure.js"]

See Load remote webpage in background page: Chrome Extension for a more in-depth discussion of the AJAX method.

So your options seem to be limited.

This of course loads them all at once:

{
  "name": "My extension",
  ...
  "background": {
    "scripts": ["background.js","background2.js","background3.js"] //and so on
  },
  ...
}

Upvotes: 4

Related Questions