Spec-Zone.ru › Web Extensions

Изменение веб-страницы

Одно из наиболее распространённых применений расширений — это изменение веб-страницы. Например, расширение может изменить стиль страницы, скрыть определённые узлы DOM или добавить новые узлы в документ.

Существует два способа сделать это с помощью API WebExtensions:

  • Декларативно: определите шаблон, который соответствует набору URL-адресов, и загрузите набор скриптов на страницы, чьи URL-адреса соответствуют этому шаблону.
  • Программно: с помощью JavaScript API загрузите скрипт на страницу, размещённую в конкретной вкладке.

В любом случае эти скрипты называются скриптами содержимого и отличаются от других скриптов, составляющих расширение:

  • Они имеют доступ только к небольшой части API WebExtension.
  • Они получают прямой доступ к веб-странице, в которую загружены.
  • Они взаимодействуют с остальной частью расширения с помощью API обмена сообщениями.

В этой статье мы рассмотрим оба метода загрузки скрипта.

Изменение страниц, соответствующих шаблону URL-адреса

Прежде всего, создайте новую директорию с именем "modify-page". В этой директории создайте файл "manifest.json" со следующим содержимым:

{

  "manifest_version": 2,
  "name": "modify-page",
  "version": "1.0",

  "content_scripts": [
    {
      "matches": ["https://developer.mozilla.org/*"],
      "js": ["page-eater.js"]
    }
  ]

}

Ключ content_scripts определяет способ загрузки скриптов на страницы, соответствующие шаблонам URL-адресов. В данном случае content_scripts указывает браузеру загрузить скрипт с именем "page-eater.js" на все страницы по адресу https://developer.mozilla.org/.

Примечание: Поскольку свойство "js" объекта content_scripts представляет собой массив, вы можете использовать его для вставки более одного скрипта на сопоставляемые страницы. При этом страницы будут иметь одинаковый контекст, так же как и несколько скриптов, загруженных на страницу, и они будут загружаться в порядке их перечисления в массиве.

Примечание: Ключ content_scripts также содержит свойство "css", которое можно использовать для вставки таблиц стилей CSS.

Затем создайте файл "page-eater.js" в каталоге "modify-page" и добавьте в него следующее содержимое:

document.body.textContent = "";

let header = document.createElement('h1');
header.textContent = "This page has been eaten";
document.body.appendChild(header);

Теперь установите расширение и перейдите по адресу https://developer.mozilla.org/. Страница должна выглядеть так:

developer.mozilla.org page "eaten" by the script

Изменение страниц программно

Что если вы всё ещё хотите "пожирать" страницы, но только по запросу пользователя? Давайте обновим этот пример, чтобы вставлять скрипт содержимого только при нажатии пункта контекстного меню.

Сначала обновите "manifest.json" следующим содержимым:

{

  "manifest_version": 2,
  "name": "modify-page",
  "version": "1.0",

  "permissions": [
    "activeTab",
    "contextMenus"
  ],

  "background": {
    "scripts": ["background.js"]
  }

}

Здесь мы удалили ключ content_scripts и добавили два новых:

  • permissions: для вставки скриптов на страницы необходимо разрешение на изменение страницы. Разрешение activeTab позволяет временно получить это для текущей активной вкладки. Нам также необходимо разрешение contextMenus для возможности добавления пунктов контекстного меню.
  • background: мы используем его для загрузки постоянного "фонового скрипта" с именем background.js, в котором мы настроим контекстное меню и вставим скрипт содержимого.

Создадим этот файл. Создайте новый файл с именем background.js в каталоге modify-page и добавьте в него следующее содержимое:

browser.contextMenus.create({
  id: "eat-page",
  title: "Eat this page"
});

browser.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "eat-page") {
    browser.tabs.executeScript({
      file: "page-eater.js"
    });
  }
});

В этом скрипте мы создаём пункт контекстного меню с определённым идентификатором и заголовком (текстом, отображаемым в контекстном меню). Затем мы настраиваем обработчик событий, чтобы при нажатии пункта контекстного меню проверить, является ли это наш пункт eat-page. Если да, мы вставляем "page-eater.js" в текущую вкладку с помощью API tabs.executeScript(). Этот API необязательно принимает идентификатор вкладки; мы опустили идентификатор, что означает, что скрипт будет вставлен в текущую активную вкладку.

На этом этапе расширение должно выглядеть так:

modify-page/
    background.js
    manifest.json
    page-eater.js

Теперь перезагрузите расширение, откройте любую страницу, откройте контекстное меню и выберите "Съесть эту страницу":

Option to eat a page on the context menu

Обмен сообщениями

Скрипты содержимого и фоновые скрипты не могут напрямую получить доступ к состоянию друг друга. Однако они могут обмениваться сообщениями. Одна сторона устанавливает обработчик сообщений, а другая может отправлять ей сообщения. В следующей таблице обобщены используемые API на каждой стороне:

В скрипте содержимого В фоновом скрипте
Отправить сообщение browser.runtime.sendMessage() browser.tabs.sendMessage()
Получить сообщение browser.runtime.onMessage browser.runtime.onMessage

Примечание: В дополнение к этому методу обмена разовыми сообщениями, вы также можете использовать подход на основе соединения для обмена сообщениями. Советы по выбору между вариантами см. в Выборе между разовыми сообщениями и обменом сообщениями на основе соединения.

Обновим наш пример, чтобы показать, как отправлять сообщения из фонового скрипта.

Сначала отредактируйте background.js следующим содержимым:

browser.contextMenus.create({
  id: "eat-page",
  title: "Eat this page"
});

function messageTab(tabs) {
  browser.tabs.sendMessage(tabs[0].id, {
    replacement: "Message from the extension!"
  });
}

function onExecuted(result) {
    let querying = browser.tabs.query({
        active: true,
        currentWindow: true
    });
    querying.then(messageTab);
}

browser.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "eat-page") {
    let executing = browser.tabs.executeScript({
      file: "page-eater.js"
    });
    executing.then(onExecuted);
  }
});

Теперь, после вставки page-eater.js, мы используем tabs.query() для получения активной вкладки, а затем используем tabs.sendMessage() для отправки сообщения скриптам содержимого, загруженным в эту вкладку. Сообщение содержит полезную нагрузку {replacement: "Message from the extension!"}.

Далее обновите page-eater.js следующим образом:

function eatPageReceiver(request, sender, sendResponse) {
  document.body.textContent = "";
  let header = document.createElement('h1');
  header.textContent = request.replacement;
  document.body.appendChild(header);
}
browser.runtime.onMessage.addListener(eatPageReceiver);

Теперь скрипт содержимого вместо непосредственного "поедания" страницы слушает сообщения с помощью runtime.onMessage. При получении сообщения скрипт выполняет практически тот же код, что и раньше, за исключением того, что текст замены берётся из request.replacement.

Поскольку tabs.executeScript() — асинхронная функция, и для того, чтобы убедиться, что мы отправляем сообщение только после добавления обработчика в page-eater.js, мы используем onExecuted(), который вызывается после выполнения page-eater.js.

Примечание: Нажмите Ctrl+Shift+J (или Cmd+Shift+J на macOS) ИЛИ web-ext run --bc для открытия Консоли браузера для просмотра console.log в фоновом скрипте.

В качестве альтернативы используйте отладчик расширений, который позволяет установить точки останова. В настоящее время нет способа запустить отладчик расширений напрямую из web-ext.

Если нам нужно отправлять сообщения обратно из скрипта содержимого на фоновую страницу, мы бы использовали runtime.sendMessage() вместо tabs.sendMessage(), например:

browser.runtime.sendMessage({
    title: "from page-eater.js"
});

Примечание: Эти примеры все вставляют JavaScript; вы также можете вставлять CSS программно с помощью функции tabs.insertCSS().

Дополнительная информация

  • Руководство по скриптам содержимого
  • content_scripts ключ манифеста
  • permissions ключ манифеста
  • tabs.executeScript()
  • tabs.insertCSS()
  • tabs.sendMessage()
  • runtime.sendMessage()
  • runtime.onMessage
  • Примеры, использующие content_scripts:
    • borderify
    • emoji-substitution
    • notify-link-clicks-i18n
    • page-to-extension-messaging
  • Примеры, использующие tabs.executeScript():
    • beastify
    • context-menu-copy-link-with-types

© 2005–2023 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Modify_a_web_page

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API