Iniezione di più script tramite executeScript in Google Chrome

Ho bisogno di iniettare programmaticamente più file di script (seguiti da uno snippet di codice) nella pagina corrente dalla mia estensione Google Chrome. Il metodo chrome.tabs.executeScript consente un singolo object InjectDetails (che rappresenta un file di script o uno snippet di codice), nonché una funzione di callback da eseguire dopo lo script. Le risposte attuali propongono le chiamate di executeScript :

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

Tuttavia, l’annidamento del callback diventa ingombrante. C’è un modo per astrarre questo?

Questa è la mia soluzione proposta:

 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 } 

Successivamente, la sequenza degli script InjectDetails può essere specificata come matrice:

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

Da Chrome v32, supporta Promise . Dovremmo usarlo per rendere il codice pulito.

Ecco un esempio:

 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 fonte:

 (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; })() 

Se si desidera utilizzare ES5, è ansible utilizzare il compilatore online per compilare i codici sopra a ES5.

Forcela su GitHub: chrome-script-execution

Data la tua risposta, mi aspettavo di iniettare in modo sincrono gli script per causare problemi (ovvero, pensavo che gli script potessero essere caricati nell’ordine sbagliato), ma funziona bene per me.

 var scripts = [ 'first.js', 'middle.js', 'last.js' ]; scripts.forEach(function(script) { chrome.tabs.executeScript(null, { file: script }, function(resp) { if (script!=='last.js') return; // Your callback code here }); }); 

Questo presuppone che tu voglia solo una callback alla fine e non necessiti dei risultati di ogni script eseguito.