Подтвердить что ты не робот

Внедрение нескольких сценариев через executeScript в Google Chrome

Мне нужно программно вставить несколько файлов script (с последующим фрагментом кода) на текущую страницу из моего расширения Google Chrome. Метод chrome.tabs.executeScript позволяет использовать один InjectDetails (представляющий script файл или фрагмент кода), а также функцию обратного вызова, которая будет выполнена после script. Текущие ответы предлагают вложенные вызовы 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();" })
            })
        })
    })
});

Однако разворот обратного вызова становится громоздким. Есть ли способ абстрагировать это?

4b9b3361

Ответ 1

Это мое предлагаемое решение:

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
}

Впоследствии последовательность скриптов InjectDetails может быть задана как массив:

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

Ответ 2

Из Chrome v32 он поддерживает Promise. Мы должны использовать его для очистки кода.

Вот пример:

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 источник:

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

Если вы хотите использовать ES5, вы можете использовать онлайн-компилятор для компиляции над кодами ES5.

Направьте меня на GitHub: chrome-script-execution

Ответ 3

Учитывая ваш ответ, я ожидал синхронного ввода сценариев, чтобы вызвать проблемы (а именно, я думал, что скрипты могут быть загружены в неправильном порядке), но он работает хорошо для меня.

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

Предполагается, что вы хотите только один обратный вызов в конце и не нуждаетесь в результатах каждого выполненного script.