Как получить доступ к веб-странице DOM, а не к странице расширения DOM?

Я пишу расширение Chrome и пытаюсь наложить <div> на текущую веб-страницу сразу после нажатия кнопки в файле popup.html.

Когда я обращаюсь к методу document.body.insertBefore изнутри popup.html, он накладывает <div> на всплывающее окно, а не на текущую веб-страницу.

Нужно ли использовать обмен сообщениями между background.html и popup.html для доступа к DOM веб-страницы? Я хотел бы сделать все в popup.html и использовать jQuery, если это возможно.

Ответ 1

Как указано в Обзор расширений Chrome: архитектура (который является обязательным для чтения):

Если вашему расширению необходимо взаимодействовать с веб-страницами, ему нужен контент script.. Содержимое script - это некоторый JavaScript, который выполняется в контексте страницы, загруженной в браузер. Подумайте о содержании script как части этой загруженной страницы, а не как части расширения, в которое оно было упаковано (его родительское расширение).

Всплывающее окно (browserAction - это значок на главной панели инструментов Google Chrome справа от адресной строки) - это HTML-страница с URL-адресом chrome-extension://, поэтому, если вы обращаетесь к ее DOM, вы затрагивая его страницу.
То же самое относится к странице фона/опций.

Для доступа/управления веб-страницей DOM у вас есть два способа:

  • Либо объявите контент script (s) в manifest.json и используйте messaging:

    chrome.tabs.sendMessage() с вашего фонового/всплывающего окна до введенного содержимого script chrome.runtime.onMessage, который будет выполнять действия на веб-странице и передавать результаты через обратный вызов sendResponse в соответствии с документацией (обратите внимание: поддерживаются только JSON-объекты, такие как числа, строки, массивы, простые объекты, что означает не элементы DOM, а не классы, а не функции). В случае, если для контента script необходимо инициировать связь на странице расширения, он должен использовать chrome.runtime.sendMessage().

  • Или используйте API вкладок для ввода контента script:
    chrome.tabs.executeScript(tabId, details, callback)

    • Требуется permissions: "tabs", "https://www.example.com/*"
      (или "<all_urls>", "\*://\*/\*", "http://\*/\*", "https://\*/\*")

    • Лучшим выбором в случае явной активации пользователя является использование "activeTab" разрешения вместо "tabs" и "<all_urls>", поскольку он служит альтернативой для многих применений "<all_urls>", но при установке не отображается предупреждающее сообщение.

    • .executeScript() может использоваться с функцией обратного вызова, которая получает массив последних оцененных выражений в вложенном контенте script, по одному элементу на каждый фрейм int, который он вводил внутри вкладки. Chrome использует JSON.parse() и JSON.stringify() по результатам внутри, ограничивая поддерживаемые типы обычными объектами и простыми стробируемыми значениями, такими как number/string или их массивы.
      Таким образом, он не работает для элементов DOM, функций, пользовательских свойств, getters/setters: вам нужно вручную отобразить/извлечь требуемые данные и передать их в простой массив/объект.

Скрипты содержимого выполняются в специальной среде, называемой изолированным миром. Они имеют доступ к DOM страницы, в которую они вставляются, но не к каким-либо переменным JavaScript или функциям, созданным этой страницей. Он выглядит для каждого контента script, как если бы на странице, на которой он запущен, нет другого исполняемого кода JavaScript. То же самое верно в обратном: JavaScript, запущенный на странице, не может вызывать какие-либо функции или обращаться к любым переменным, определенным сценариями контента.

По-прежнему можно перейти на более глубокий уровень и получить доступ к веб-страницам переменных/функций JavaScript.



В качестве примера второго метода покажем, что div при нажатии на действие браузера.
Мы будем использовать chrome.tabs.executeScript() в обработчике кликов browserAction для ввода содержимого script (или буквальной строки кода, если он маленький, см. документацию метода) в DOM этой страницы.

var someVar = {text: 'test', foo: 1, bar: false};
chrome.tabs.executeScript({
    code: '(' + function(params) {
        document.body.insertAdjacentHTML('beforeend',
            '<div style="all:unset; position:fixed; left:0; top:0; right:0; bottom:0;' +
                'background-color:rgba(0,255,0,0.3)">' + params.text + '</div>'
        );
        return {success: true, html: document.body.innerHTML};
    } + ')(' + JSON.stringify(someVar) + ');'
}, function(results) {
    console.log(results[0]);
});

Как вы можете видеть, мы использовали автоматическое преобразование строк кода функции, чтобы иметь возможность писать введенный код как обычный JavaScript с подсветкой синтаксиса и линией. Очевидным недостатком является то, что браузер тратит время на разбор кода, но обычно он менее 1 миллисекунды, таким образом, незначительно.