Injecting multiple scripts through executeScript in Google Chrome Injecting multiple scripts through executeScript in Google Chrome google-chrome google-chrome

Injecting multiple scripts through executeScript in Google Chrome


This is my proposed solution:

function executeScripts(tabId, injectDetailsArray){    function createCallback(tabId, injectDetails, innerCallback) {        return function () {            chrome.tabs.executeScript(tabId, injectDetails, innerCallback);        };    }    var callback = null;    for (var i = injectDetailsArray.length - 1; i >= 0; --i)        callback = createCallback(tabId, injectDetailsArray[i], callback);    if (callback !== null)        callback();   // execute outermost function}

Subsequently, the sequence of InjectDetails scripts can be specified as an array:

chrome.browserAction.onClicked.addListener(function (tab) {    executeScripts(null, [         { file: "jquery.js" },         { file: "master.js" },        { file: "helper.js" },        { code: "transformPage();" }    ])});


From Chrome v32, it supports Promise. We should use it for making code clean.

Here is an example:

new ScriptExecution(tab.id)    .executeScripts("js/jquery.js", "js/script.js")    .then(s => s.executeCodes('console.log("executes code...")'))    .then(s => s.injectCss("css/style.css"))    .then(s => console.log('done'));

ScriptExecution source:

(function() {    function ScriptExecution(tabId) {        this.tabId = tabId;    }    ScriptExecution.prototype.executeScripts = function(fileArray) {        fileArray = Array.prototype.slice.call(arguments); // ES6: Array.from(arguments)        return Promise.all(fileArray.map(file => exeScript(this.tabId, file))).then(() => this); // 'this' will be use at next chain    };    ScriptExecution.prototype.executeCodes = function(fileArray) {        fileArray = Array.prototype.slice.call(arguments);        return Promise.all(fileArray.map(code => exeCodes(this.tabId, code))).then(() => this);    };    ScriptExecution.prototype.injectCss = function(fileArray) {        fileArray = Array.prototype.slice.call(arguments);        return Promise.all(fileArray.map(file => exeCss(this.tabId, file))).then(() => this);    };    function promiseTo(fn, tabId, info) {        return new Promise(resolve => {            fn.call(chrome.tabs, tabId, info, x => resolve());        });    }    function exeScript(tabId, path) {        let info = { file : path, runAt: 'document_end' };        return promiseTo(chrome.tabs.executeScript, tabId, info);    }    function exeCodes(tabId, code) {        let info = { code : code, runAt: 'document_end' };        return promiseTo(chrome.tabs.executeScript, tabId, info);    }    function exeCss(tabId, path) {        let info = { file : path, runAt: 'document_end' };        return promiseTo(chrome.tabs.insertCSS, tabId, info);    }    window.ScriptExecution = ScriptExecution;})()

If you would like to use ES5, you can use online compiler to compile above codes to ES5.

Fork me on GitHub: chrome-script-execution


Fun fact, the scripts are injected in order and you don't need to wait for each one to be injected.

chrome.browserAction.onClicked.addListener(tab => {    chrome.tabs.executeScript(tab.id, { file: "jquery.js" });    chrome.tabs.executeScript(tab.id, { file: "master.js" });    chrome.tabs.executeScript(tab.id, { file: "helper.js" });    chrome.tabs.executeScript(tab.id, { code: "transformPage();" }, () => {        // All scripts loaded    });});

This is considerably faster than manually waiting for each one. You can verify that they are loaded in order by loading a huge library first (like d3.js) and then loading a small file after. The order will still be preserved.

Note: errors aren't caught, but this should never happen if all files exist.


If you want to catch the errors, I'd suggest to use the Firefox’ browser.* APIs with their Chrome polyfill

browser.browserAction.onClicked.addListener(tab => {    Promise.all([        browser.tabs.executeScript(tab.id, { file: "jquery.js" }),        browser.tabs.executeScript(tab.id, { file: "master.js" }),        browser.tabs.executeScript(tab.id, { file: "helper.js" }),        browser.tabs.executeScript(tab.id, { code: "transformPage();" })    ]).then(() => {        console.log('All scripts definitely loaded')    }, error => {        console.error(error);    });});