Передайте параметр в контент script, введённый с помощью chrome.tabs.executeScript()

Как передать параметр JavaScript в файл содержимого script, который вводится с помощью:

chrome.tabs.executeScript(tab.id, {file: "content.js"});

Ответ 1

Там нет такой вещи, как "передать параметр в файл".

Что вы можете сделать, это либо вставить содержимое script перед выполнением файла, либо отправить сообщение после вставки файла. Ниже приведен пример этих различных методов.

Задайте параметры перед исполнением JS файла

Если вы хотите определить некоторые переменные перед вставкой файла, просто вставьте chrome.tabs.executeScript:

chrome.tabs.executeScript(tab.id, {
    code: 'var config = 1;'
}, function() {
    chrome.tabs.executeScript(tab.id, {file: 'content.js'});
});

Если ваша переменная не так проста, я рекомендую использовать JSON.stringify для преобразования объекта в строку:

var config = {somebigobject: 'complicated value'};
chrome.tabs.executeScript(tab.id, {
    code: 'var config = ' + JSON.stringify(config)
}, function() {
    chrome.tabs.executeScript(tab.id, {file: 'content.js'});
});

В предыдущем методе переменные могут использоваться в content.js следующим образом:

// content.js
alert('Example:' + config);

Установить параметры после выполнения JS файла

Предыдущий метод может использоваться для установки параметров после JS файла. Вместо определения переменных непосредственно в глобальной области вы можете использовать API передачи сообщений для передачи параметров:

chrome.tabs.executeScript(tab.id, {file: 'content.js'}, function() {
    chrome.tabs.sendMessage(tab.id, 'whatever value; String, object, whatever');
});

В содержании script (content.js) вы можете прослушивать эти сообщения с помощью события chrome.runtime.onMessage и обрабатывать сообщение:

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    // Handle message.
    // In this example, message === 'whatever value; String, object, whatever'
});

Ответ 2

Существует пять общих способов передачи данных в контент script с помощью tabs.executeScript() (MDN):

  • Задайте данные до, введя script
    • Используйте chrome.storage.local (MDN) чтобы передать данные (перед вводом вашего script).
    • Введите код перед script, который устанавливает переменную с данными (см. подробное обсуждение возможной проблемы безопасности).
    • Установить файл cookie для домена, в который вводится контент script. Этот метод также можно использовать для передачи данных в сценарии содержания manifest.json, которые вводятся в document_start, без необходимости в содержании script для выполнения асинхронного запроса.
  • Отправить/установить данные после, введя script
    1. Используйте передача сообщений (MDN) для передачи данных после ввода script.
    2. Используйте chrome.storage.onChanged (MDN) в вашем содержании script, чтобы прослушать фон script, чтобы установить значение, используя chrome.storage.local.set() (MDN).

Используйте chrome.storage.local (перед выполнением script)

Используя этот метод, вы поддерживаете парадигму выполнения, которую вы используете, вводя script, который выполняет функцию, а затем завершает работу. Он также не имеет потенциальной проблемы безопасности при использовании динамического значения для создания исполняемого кода, что делается во втором варианте ниже.

Из вашего всплывающего окна script:

var updateTextTo = document.getElementById('comments').value;
chrome.storage.local.set({
    updateTextTo: updateTextTo
}, function () {
    chrome.tabs.executeScript({
        file: "content_script3.js"
    });
});

Из вашего содержимого script:

chrome.storage.local.get('updateTextTo', function (items) {
    assignTextToTextareas(items.updateTextTo);
    chrome.storage.local.remove('updateTextTo');
});
function assignTextToTextareas(newText){
    if (typeof newText === 'string') {
        Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
            el.value = newText;
        });
    }
}

См. Примечания 1 и 2.

Введите код до script, чтобы установить переменную

До выполнения вашего script вы можете ввести код, который устанавливает переменную в контенте script, который может использовать ваш основной script:

Проблема с безопасностью:

В следующем случае "'" + JSON.stringify().replace(/\\/g,'\\\\').replace(/'/g,"\\'") + "'" следует кодировать данные в текст, который будет правильным JSON, если он интерпретируется как код, перед тем как положить его в строка code. Методы .replace() необходимы для A) имеют текст, который правильно интерпретируется как строка при использовании в качестве кода, и B) цитирует любой ', который существует в данных. Затем он использует JSON.parse(), чтобы вернуть данные в строку в вашем контенте script. Хотя это кодирование строго не требуется, это хорошая идея, так как вы не знаете содержание значения, которое вы собираетесь отправить в контент script. Это значение может быть легко может привести к повреждению кода, который вы вводите (т.е. Пользователь может использовать ' и/или " в тексте, который они ввели). Если вы этого не сделаете, каким-то образом избегайте значения, есть отверстие безопасности, которое может привести к выполнению произвольного кода.

Из вашего всплывающего окна script:

  • Внедрить простой фрагмент кода, который устанавливает переменную для хранения данных.
  • В обратном вызове chrome.tabs.executeScript() (MDN)20 > , вызовите tabs.executeScript(), чтобы ввести script (Примечание: tabs.executeScript() будет выполнять скрипты в том порядке, в котором вы вызываете tabs.executeScript(), как долго так как они имеют одинаковое значение для runAt. Таким образом, ожидание обратного вызова маленького code строго не требуется).
var updateTextTo = document.getElementById('comments').value;
chrome.tabs.executeScript({
    code: "var newText = JSON.parse('" + encodeToPassToContentScript(updateTextTo) + "');"
}, function () {
    chrome.tabs.executeScript({
        file: "content_script3.js"
    });
});

function encodeToPassToContentScript(obj){
    //Encodes into JSON and quotes \ characters so they will not break
    //  when re-interpreted as a string literal. Failing to do so could
    //  result in the injection of arbitrary code and/or JSON.parse() failing.
    return JSON.stringify(obj).replace(/\\/g,'\\\\').replace(/'/g,"\\'")
}

Из вашего содержимого script:

  • Внесите изменения в DOM, используя данные, хранящиеся в переменной
if (typeof newText === 'string') {
    Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
        el.value = newText;
    });
}

См. Примечания 1, 2 и 3.

Использовать сообщение (MDN) (отправить данные после содержимого script)

Для этого требуется, чтобы ваш контент script для установки прослушивателя для сообщения, посланного всплывающим окном, или, возможно, фона script (если взаимодействие с пользовательским интерфейсом заставляет всплывающее окно закрываться). Это немного сложнее.

Из вашего всплывающего окна script:

  • Определите активную вкладку, используя tabs.query() (MDN).
  • Вызов tabs.executeScript() (MDN)
  • В обратном вызове для tabs.executeScript() используйте tabs.sendMessage() (MDN) (который требует знания tabId), чтобы отправить данные в виде сообщения.
var updateTextTo = document.getElementById('comments').value;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
    chrome.tabs.executeScript(tabs[0].id, {
        file: "content_script3.js"
    }, function(){
        chrome.tabs.sendMessage(tabs[0].id,{
            updateTextTo: updateTextTo
        });
    });
});

Из вашего содержимого script:

  • Добавить слушателя с помощью chrome.runtime.onMessage.addListener() (MDN).
  • Выйдите из основного кода, оставив слушателя активным. Вы можете вернуть индикатор успеха, если вы выберете.
  • После получения сообщения с данными:
    • Внесите изменения в DOM.
    • Удалите прослушиватель runtime.onMessage

# 3.2 не является обязательным. Вы могли бы сохранить свой код активным, ожидая другого сообщения, но это изменит парадигму, которую вы используете, на ту, где вы загружаете свой код, и она остается резидентом, ожидая сообщения для инициирования действий.

chrome.runtime.onMessage.addListener(assignTextToTextareas);
function assignTextToTextareas(message){
    newText = message.updateTextTo;
    if (typeof newText === 'string') {
        Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
            el.value = newText;
        });
    }
    chrome.runtime.onMessage.removeListener(assignTextToTextareas);  //optional
}

См. Примечания 1 и 2.


Примечание 1: Использование Array.from() прекрасно, если вы не делаете это много раз и используете версия браузера, которая имеет его (Chrome >= версия 45, Firefox >= 32). В Chrome и Firefox Array.from() медленнее по сравнению с другими методами получения массива из NodeList. Для более быстрого и совместимого преобразования в Array вы можете использовать код asArray() в этом ответе. Вторая версия asArray(), предоставленная в этом ответе, также более надежна.

Примечание 2: Если вы хотите ограничить свой код версией Chrome >= 51 или версией Firefox >= 50, у Chrome есть forEach() для NodeLists с версии v51. Таким образом, преобразование в массив не требуется. Очевидно, что вам не нужно преобразовывать в массив, если вы используете другой тип цикла.

Примечание 3: Хотя я ранее использовал этот метод (вводя script с переменным значением) в свой собственный код, мне напомнили, что я должен был включить его здесь при чтении этот ответ.