Spec-Zone.ru › Web Extensions

Работа с API вкладок

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

В этой статье пошагового руководства мы рассмотрим:

  • Необходимые разрешения для использования API вкладок.
  • Получение информации о вкладках и их свойствах с помощью tabs.query.
  • Создание, дублирование, перемещение, обновление, перезагрузку и удаление вкладок.
  • Изменение уровня масштабирования вкладки.
  • Изменение CSS вкладки.

Затем мы рассмотрим другие, разнообразные функции, предоставляемые API.

Примечание: Некоторые функции API вкладок описаны в других местах. Это методы, которые можно использовать для управления содержимым вкладки с помощью скриптов (tabs.connect, tabs.sendMessage и tabs.executeScript). Если вам нужна дополнительная информация об этих методах, обратитесь к статье «Концепции» Скрипты содержимого и руководству пошагового руководства Изменение веб-страницы.

Разрешения и API вкладок

Для большинства функций API вкладок разрешения не требуются, однако есть исключения:

  • Разрешение "tabs" необходимо для доступа к свойствам объекта вкладки Tab.url, Tab.title, и Tab.favIconUrl. В Firefox также необходимо разрешение "tabs" для выполнения запроса по URL.
  • Разрешение на хост необходимо для tabs.executeScript() или tabs.insertCSS().

Вот как вы можете запросить разрешение "tabs" в файле manifest.json вашего расширения:

"permissions": [
  "<all_urls>",
  "tabs"
],

Этот запрос предоставляет вам доступ ко всем функциям API вкладок на всех посещаемых пользователем веб-сайтах. Также существует альтернативный подход для запроса разрешений на использование tabs.executeScript() или tabs.insertCSS(), где разрешение на хост не требуется, в виде "activeTab". Это разрешение предоставляет те же права, что и "tabs" с <all_urls>, но с двумя ограничениями:

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

Преимущество этого подхода заключается в том, что пользователь не получит предупреждение о разрешениях, указывающее, что ваше расширение может «доступ к данным для всех веб-сайтов». Это потому, что разрешение <all_urls> дает расширению возможность выполнять скрипты в любой вкладке, когда угодно, тогда как "activeTab" ограничено предоставлением расширению возможности выполнять действие, запрошенное пользователем, в текущей вкладке.

Получение информации о вкладках и их свойствах

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

Для этого используется tabs.query(). Используя его самостоятельно для получения всех вкладок или с объектом queryInfo для указания критериев запроса, таких как активная вкладка, в текущем окне или по одному из 17 критериев, tabs.query() возвращает массив объектов tabs.Tab, содержащих информацию о вкладках.

Если вам нужна информация только о текущей вкладке, вы можете получить объект tabs.Tab для этой вкладки с помощью tabs.getCurrent(). Если у вас есть идентификатор вкладки, вы можете получить ее объект tabs.Tab с помощью tabs.get().

Пример пошагового руководства

Чтобы увидеть, как используются tabs.query() и tabs.Tab, давайте рассмотрим пример tabs-tabs-tabs, который добавляет список «переключения вкладок» в всплывающее окно кнопки на панели инструментов.

The tabs toolbar menu showing the switch to tap area

manifest.json

Вот manifest.json:

{
  "browser_action": {
    "browser_style": true,
    "default_title": "Tabs, tabs, tabs",
    "default_popup": "tabs.html"
  },
  "description": "A list of methods you can perform on a tab.",
  "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/tabs-tabs-tabs",
  "manifest_version": 2,
  "name": "Tabs, tabs, tabs",
  "permissions": [
    "tabs"
  ],
  "version": "1.0"
}

Примечание:

  • tabs.html определяется как default_popup в browser_action. Оно отображается всякий раз, когда пользователь нажимает значок расширения на панели инструментов.
  • Разрешения включают tabs. Это необходимо для поддержки функции списка вкладок, поскольку расширение считывает заголовок вкладок для отображения во всплывающем окне.
tabs.html

tabs.html определяет содержимое всплывающего окна расширения:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="tabs.css" />
  </head>

  <body>
    <div class="panel">
      <div class="panel-section panel-section-header">
        <div class="text-section-header">Tabs-tabs-tabs</div>
      </div>

      <a href="#" id="tabs-move-beginning">
        Move active tab to the beginning of the window
      </a>
      <br />

      <!-- Define the other menu items -->

      <div class="switch-tabs">
        <p>Switch to tab</p>
        <div id="tabs-list"></div>
      </div>
    </div>

    <script src="tabs.js"></script>
  </body>
</html>

Это делает следующее:

  1. Объявляются пункты меню.
  2. Объявляется пустой div с идентификатором tabs-list для хранения списка вкладок.
  3. Вызывается tabs.js.
tabs.js

В tabs.js мы увидим, как создается список вкладок и добавляется во всплывающее окно.

Создание всплывающего окна

Сначала обработчик событий добавляется для выполнения listTabs() при загрузке tabs.html.

document.addEventListener("DOMContentLoaded", listTabs);

Первое, что делает listTabs() это вызов getCurrentWindowTabs(). Здесь используется tabs.query() для получения объекта tabs.Tab для вкладок в текущем окне:

function getCurrentWindowTabs() {
  return browser.tabs.query({ currentWindow: true });
}

Теперь listTabs() готово создать содержимое всплывающего окна.

Для начала:

  1. Получить элемент <div id="tabs-list">.
  2. Создать фрагмент документа (в который будет построен список).
  3. Установить счетчики.
  4. Очистить содержимое элемента <div id="tabs-list">.
function listTabs() {
 getCurrentWindowTabs().then((tabs) => {
    const tabsList = document.getElementById('tabs-list');
    const currentTabs = document.createDocumentFragment();
    const limit = 5;
    let counter = 0;

    tabsList.textContent = '';

Далее, мы создадим ссылки для каждой вкладки:

  1. Проходит по первым 5 элементам объекта tabs.Tab.
  2. Для каждого элемента добавляется гиперссылка в фрагмент документа.
    • Текст ссылки устанавливается с помощью title вкладки (или id, если вкладка не имеет title).
    • Адрес ссылки устанавливается с помощью id вкладки.
for (const tab of tabs) {
  if (!tab.active && counter <= limit) {
    const tabLink = document.createElement("a");

    tabLink.textContent = tab.title || tab.id;

    tabLink.setAttribute("href", tab.id);
    tabLink.classList.add("switch-tabs");
    currentTabs.appendChild(tabLink);
  }

  counter += 1;
}

Наконец, фрагмент документа записывается в элемент <div id="tabs-list">:

    tabsList.appendChild(currentTabs);
  });
}

Работа с активной вкладкой

Еще одна связанная функция — опция «Вывести информацию об активной вкладке», которая выводит все свойства объекта tabs.Tab для активной вкладки в диалог:

else if (e.target.id === "tabs-alertinfo") {
  callOnActiveTab((tab) => {
    let props = "";
    for (const item in tab) {
      props += `${ item } = ${ tab[item] } \n`;
    }
    alert(props);
  });
}

Где callOnActiveTab() находит объект активной вкладки, перебирая объекты tabs.Tab и ища элемент с активным значением:

document.addEventListener("click", (e) => {
  function callOnActiveTab(callback) {
    getCurrentWindowTabs().then((tabs) => {
      for (const tab of tabs) {
        if (tab.active) {
          callback(tab, tabs);
        }
      }
    });
  }
}

Создание, дублирование, перемещение, обновление, перезагрузка и удаление вкладок

После сбора информации о вкладках, вы, скорее всего, захотите что-то с ними сделать — предоставить пользователю возможности для управления вкладками или реализовать функциональность в вашем расширении.

Доступны следующие функции:

  • создание новой вкладки (tabs.create()).
  • дублирование вкладки (tabs.duplicate()).
  • удаление вкладки (tabs.remove()).
  • перемещение вкладки (tabs.move()).
  • обновление URL вкладки — переход на новую страницу — (tabs.update()).
  • перезагрузка страницы вкладки (tabs.reload()).

Примечание: Для всех этих функций требуется идентификатор (или идентификаторы) вкладки, над которой осуществляется манипулирование:

  • tabs.duplicate()
  • tabs.remove()
  • tabs.move()

В то время как следующие функции будут действовать на активную вкладку (если не указана вкладка):

  • tabs.update()
  • tabs.reload()
END_OF_DOCUMENT_MARKER

Пример использования

Пример tabs-tabs-tabs демонстрирует все эти возможности, за исключением обновления URL-адреса вкладки. Способ использования этих API похож, поэтому мы рассмотрим более сложную реализацию — опцию "Переместить активную вкладку в начало списка окон".

Но сначала вот демонстрация работы функции:

manifest.json

Ни одна из функций не требует разрешений, поэтому в файле manifest.json нет никаких особенностей, требующих особого внимания.

tabs.html

tabs.html определяет "меню", отображаемое в всплывающем окне, которое включает опцию "Переместить активную вкладку в начало списка окон", с рядом <a> тегов, сгруппированных визуальным разделителем. Каждый пункт меню получает id, который используется в tabs.js для определения, какой элемент меню запрошен.

<a href="#" id="tabs-move-beginning">
  Move active tab to the beginning of the window
</a>
<br />
<a href="#" id="tabs-move-end">Move active tab to the end of the window</a>
<br />

<div class="panel-section-separator"></div>

<a href="#" id="tabs-duplicate">Duplicate active tab</a><br />
<a href="#" id="tabs-reload">Reload active tab</a><br />
<a href="#" id="tabs-alertinfo">Alert active tab info</a><br />
tabs.js

Для реализации "меню", определённого в tabs.html, tabs.js включает обработчик кликов в tabs.html:

document.addEventListener("click", (e) => {
  function callOnActiveTab(callback) {
    getCurrentWindowTabs().then((tabs) => {
      for (const tab of tabs) {
        if (tab.active) {
          callback(tab, tabs);
        }
      }
    });
  }
});

Затем ряд if операторов пытаются сопоставить id нажатого элемента.

Этот фрагмент кода предназначен для опции "Переместить активную вкладку в начало списка окон":

if (e.target.id === "tabs-move-beginning") {
  callOnActiveTab((tab, tabs) => {
    let index = 0;
    if (!tab.pinned) {
      index = firstUnpinnedTab(tabs);
    }
    console.log(`moving ${tab.id} to ${index}`);
    browser.tabs.move([tab.id], { index });
  });
}

Обратите внимание на использование console.log(). Это позволяет выводить информацию в консоль отладчика отладчика, что может быть полезно при устранении проблем, возникших во время разработки.

Example of the console.log output, from the move tabs feature, in the debugging console

Код перемещения сначала вызывает callOnActiveTab(), который в свою очередь вызывает getCurrentWindowTabs(), чтобы получить объект tabs.Tab, содержащий вкладки активного окна. Затем он перебирает объект, чтобы найти и вернуть объект активной вкладки:

function callOnActiveTab(callback) {
  getCurrentWindowTabs().then((tabs) => {
    for (const tab of tabs) {
      if (tab.active) {
        callback(tab, tabs);
      }
    }
  });
}

Закреплённые вкладки

Особенность вкладок заключается в том, что пользователь может закрепить вкладки в окне. Закреплённые вкладки помещаются в начало списка вкладок и не могут быть перемещены. Это означает, что наиболее раннее положение, куда можно переместить вкладку, — это первое положение после любых закреплённых вкладок. Следовательно, firstUnpinnedTab() вызывается для определения позиции первой незакреплённой вкладки, перебирая объект tabs:

function firstUnpinnedTab(tabs) {
  for (const tab of tabs) {
    if (!tab.pinned) {
      return tab.index;
    }
  }
}

Теперь у нас есть всё необходимое для перемещения вкладки: объект активной вкладки, из которого можно получить вкладку id, и позицию, куда нужно переместить вкладку. Таким образом, мы можем реализовать перемещение:

browser.tabs.move([tab.id], { index });

Остальные функции для дублирования, перезагрузки, создания и удаления вкладок реализуются аналогично.

Изменение уровня масштабирования вкладки

Следующий набор функций позволяет получить (tabs.getZoom) и установить (tabs.setZoom) уровень масштабирования в вкладке. Вы также можете получить параметры масштабирования (tabs.getZoomSettings), но на момент написания утилиты возможности установки параметров (tabs.setZoomSettings) в Firefox не было.

Уровень масштабирования может быть от 30% до 500% (представлен в виде десятичных значений 0.3 до 5).

В Firefox параметры масштабирования по умолчанию:

  • Уровень масштабирования по умолчанию: 100%.
  • Режим масштабирования: автоматический (браузер управляет установкой уровней масштабирования).
  • Область действия изменений масштабирования: "per-origin", что означает, что при повторном посещении сайта используется уровень масштабирования, установленный при последнем посещении.

Пример использования

Пример tabs-tabs-tabs содержит три демонстрации функции масштабирования: увеличение, уменьшение и сброс масштабирования. Вот демонстрация работы функции:

Давайте посмотрим, как реализовано увеличение масштаба.

manifest.json

Ни одна из функций масштабирования не требует разрешений, поэтому в файле manifest.json нет никаких особенностей, требующих особого внимания.

tabs.html

Мы уже обсуждали, как tabs.html определяет опции для этой расширения, никаких новых или уникальных действий для предоставления опций масштабирования не выполняется.

tabs.js

tabs.js начинает с определения нескольких констант, используемых в коде масштабирования:

const ZOOM_INCREMENT = 0.2;
const MAX_ZOOM = 5;
const MIN_ZOOM = 0.3;
const DEFAULT_ZOOM = 1;

Затем он использует тот же обработчик кликов, о котором мы говорили ранее, чтобы реагировать на клики в tabs.html.

Для функции увеличения масштаба выполняется следующее:

  else if (e.target.id === "tabs-add-zoom") {
    callOnActiveTab((tab) => {
      browser.tabs.getZoom(tab.id).then((zoomFactor) => {
        //the maximum zoomFactor is 5, it can't go higher
        if (zoomFactor >= MAX_ZOOM) {
          alert("Tab zoom factor is already at max!");
        } else {
          let newZoomFactor = zoomFactor + ZOOM_INCREMENT;
          //if the newZoomFactor is set to higher than the max accepted
          //it won't change, and will never alert that it's at maximum
          newZoomFactor = newZoomFactor > MAX_ZOOM ? MAX_ZOOM : newZoomFactor;
          browser.tabs.setZoom(tab.id, newZoomFactor);
        }
      });
    });
  }

Этот код использует callOnActiveTab() для получения данных активной вкладки, затем tabs.getZoom получает текущий коэффициент масштабирования вкладки. Текущий коэффициент масштабирования сравнивается с определённым максимальным значением (MAX_ZOOM), и выводится сообщение об ошибке, если вкладка уже имеет максимальное значение масштабирования. В противном случае уровень масштабирования увеличивается, но ограничивается максимальным значением масштабирования, затем масштабирование устанавливается с помощью tabs.getZoom.

Изменение CSS вкладки

Ещё одна важная возможность, предоставляемая API вкладок, — изменение CSS внутри вкладки: добавление нового CSS к вкладке (tabs.insertCSS()) или удаление CSS из вкладки (tabs.removeCSS()).

Это может быть полезно, например, для выделения определённых элементов страницы или изменения стандартного макета страницы.

END_OF_DOCUMENT_MARKER

Пример использования

Пример apply-css использует эти возможности для добавления красной рамки к веб-странице в активной вкладке. Вот пример в действии:

Давайте разберем его настройку.

manifest.json

Файл manifest.json запрашивает разрешения, необходимые для использования возможностей CSS. Вам необходимо:

  • "tabs" разрешение и разрешение на доступ к хосту; или,
  • "activeTab" разрешение.

Второй вариант наиболее полезен, так как он позволяет расширению использовать tabs.insertCSS() и tabs.removeCSS() в активной вкладке, когда оно запущено из действия расширения, контекстного меню или ярлыка.

{
  "description": "Adds a page action to toggle applying CSS to pages.",

  "manifest_version": 2,
  "name": "apply-css",
  "version": "1.0",
  "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/apply-css",

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

  "page_action": {
    "default_icon": "icons/off.svg",
    "browser_style": true
  },

  "permissions": ["activeTab", "tabs"]
}

Обратите внимание, что "tabs" разрешение запрашивается дополнительно к "activeTab". Это дополнительное разрешение необходимо, чтобы скрипт расширения мог получить доступ к URL вкладки, что мы увидим в ближайшее время.

Другие основные функции в файле manifest.json определяют:

  • скрипт фонового процесса, который начинает выполняться сразу после загрузки расширения.
  • "действие страницы", которое определяет значок, который будет добавлен в адресную строку браузера.
background.js

При запуске background.js устанавливает некоторые константы для определения применяемого CSS, заголовки для "действия страницы" и список протоколов, в которых будет работать расширение:

const CSS = "body { border: 20px solid red; }";
const TITLE_APPLY = "Apply CSS";
const TITLE_REMOVE = "Remove CSS";
const APPLICABLE_PROTOCOLS = ["http:", "https:"];

При первой загрузке расширение использует tabs.query() для получения списка всех вкладок в текущем окне браузера. Затем оно перебирает вкладки, вызывая initializePageAction().

browser.tabs.query({}).then((tabs) => {
  for (const tab of tabs) {
    initializePageAction(tab);
  }
});

initializePageAction использует protocolIsApplicable() для определения, относится ли URL активной вкладки к тем, к которым можно применить CSS:

function protocolIsApplicable(url) {
  const anchor = document.createElement("a");
  anchor.href = url;
  return APPLICABLE_PROTOCOLS.includes(anchor.protocol);
}

Затем, если пример может воздействовать на вкладку, initializePageAction() устанавливает значок и заголовок вкладки (панель навигации) на "выкл" перед отображением pageAction:

function initializePageAction(tab) {
  if (protocolIsApplicable(tab.url)) {
    browser.pageAction.setIcon({ tabId: tab.id, path: "icons/off.svg" });
    browser.pageAction.setTitle({ tabId: tab.id, title: TITLE_APPLY });
    browser.pageAction.show(tab.id);
  }
}

Далее, обработчик события pageAction.onClicked ждет нажатия значка pageAction и вызывает toggleCSS при нажатии.

browser.pageAction.onClicked.addListener(toggleCSS);

toggleCSS() получает заголовок pageAction и выполняет указанные действия:

  • Для "Применить CSS":
    • переключает значок и заголовок на "удалить".
    • применяет CSS с помощью tabs.insertCSS().
  • Для "Удалить CSS":
    • переключает значок и заголовок на "применить".
    • удаляет CSS с помощью tabs.removeCSS().
function toggleCSS(tab) {
  function gotTitle(title) {
    if (title === TITLE_APPLY) {
      browser.pageAction.setIcon({ tabId: tab.id, path: "icons/on.svg" });
      browser.pageAction.setTitle({ tabId: tab.id, title: TITLE_REMOVE });
      browser.tabs.insertCSS({ code: CSS });
    } else {
      browser.pageAction.setIcon({ tabId: tab.id, path: "icons/off.svg" });
      browser.pageAction.setTitle({ tabId: tab.id, title: TITLE_APPLY });
      browser.tabs.removeCSS({ code: CSS });
    }
  }

  browser.pageAction.getTitle({ tabId: tab.id }).then(gotTitle);
}

Наконец, чтобы убедиться, что pageAction действителен после каждого обновления вкладки, обработчик события tabs.onUpdated вызывает initializePageAction() каждый раз при обновлении вкладки, чтобы проверить, использует ли вкладка протокол, к которому можно применить CSS.

browser.tabs.onUpdated.addListener((id, changeInfo, tab) => {
  initializePageAction(tab);
});

Другие интересные возможности

Есть еще несколько функций API вкладок, которые не подходят к предыдущим разделам:

  • Захват видимого содержимого вкладки с помощью tabs.captureVisibleTab.
  • Определение основного языка содержимого вкладки с помощью tabs.detectLanguage. Это может быть использовано, например, для соответствия языка интерфейса расширения языку страницы, в которой он выполняется.

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

Если вы хотите узнать больше о Tabs API, ознакомьтесь с:

  • Справочник по Tabs API
  • Примеры расширений (многие из которых используют Tabs API)

© 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/Working_with_the_Tabs_API

Spec-Zone.ru

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