使用标签 API

标签允许用户在浏览器窗口中打开多个网页,然后在这些网页之间切换。使用标签 API,您可以操作这些标签来创建实用程序,为用户提供使用标签的新方法或提供扩展的功能。

在本操作指南中,我们将介绍

  • 使用标签 API 所需的权限。
  • 使用 tabs.query 发现更多关于标签及其属性的信息。
  • 创建、复制、移动、更新、重新加载和删除标签。
  • 操作标签的缩放级别。
  • 操作标签的 CSS。

最后,我们将介绍 API 提供的一些其他杂项功能。

注意: 某些标签 API 功能在其他地方介绍。这些是您可以用来使用脚本操作标签内容的方法(tabs.connecttabs.sendMessagetabs.executeScript)。如果您想了解更多关于这些方法的信息,请参阅概念文章 内容脚本 和操作指南 修改网页

标签 API 的权限

对于大多数标签 API 功能,您不需要任何权限;但是,也有一些例外情况

以下是如何在扩展的 manifest.json 文件中请求 "tabs" 权限

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

此请求使您可以在用户访问的所有网站上使用所有标签 API 功能。还有一种替代方法可以请求使用 tabs.executeScript()tabs.insertCSS() 的权限,而无需主机权限,形式为 "activeTab"。此权限与具有 <all_urls>"tabs" 提供相同的权利,但有两个限制

  • 用户必须通过其浏览器或页面操作、上下文菜单或快捷键与扩展进行交互。
  • 它仅在活动标签中授予权限。

这种方法的好处是用户不会收到权限警告,提示您的扩展可以“访问您在所有网站上的数据”。这是因为 <all_urls> 权限使扩展能够在任何时间、任何标签中执行脚本,而 "activeTab" 仅限于允许扩展在当前标签中执行用户请求的操作。

发现更多关于标签及其属性的信息

在某些情况下,您可能需要获取所有浏览器窗口中所有标签的列表。有时您可能需要找到匹配某些特定条件的标签子集,例如从特定标签打开的标签或显示来自特定域的页面的标签。获得标签列表后,您可能还需要了解它们的属性。

此时,tabs.query() 就派上用场了。单独使用它可以获取所有标签,或者使用 queryInfo 对象(指定查询条件,例如标签是否处于活动状态、在当前窗口中或 17 个条件中的一个或多个),tabs.query() 会返回一个 tabs.Tab 对象数组,其中包含有关标签的信息。

如果要获取有关当前标签的信息,可以使用 tabs.getCurrent() 获取该标签的 tabs.Tab 对象。如果您有标签的 ID,可以使用 tabs.get() 获取其 tabs.Tab 对象。

操作方法示例

要查看如何使用 tabs.query()tabs.Tab,让我们来了解一下 tabs-tabs-tabs 示例是如何将“切换到标签”列表添加到其工具栏按钮弹出窗口的。

The tabs toolbar menu showing the switch to tap area

manifest.json

以下是 manifest.json

json
{
  "browser_action": {
    "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/main/tabs-tabs-tabs",
  "manifest_version": 2,
  "name": "Tabs, tabs, tabs",
  "permissions": ["tabs"],
  "version": "1.0"
}

注意

  • tabs.htmlbrowser_action 中定义为 default_popup 每当用户点击扩展的工具栏图标时,它都会显示。
  • 权限包括 tabs。 这是支持标签列表功能所必需的,因为扩展读取标签的标题并在弹出窗口中显示。
tabs.html

tabs.html 定义了扩展弹出窗口的内容

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. 声明一个带有 ID tabs-list 的空 div,用于包含标签列表。
  3. 调用 tabs.js
tabs.js

tabs.js 中,我们将看到如何构建标签列表并将其添加到弹出窗口。

创建弹出窗口

首先,添加一个事件处理程序,在加载 tabs.html 时执行 listTabs()

js
document.addEventListener("DOMContentLoaded", listTabs);

listTabs() 首先执行的操作是调用 getCurrentWindowTabs()。在此,将使用 tabs.query() 获取当前窗口中标签的 tabs.Tab 对象

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

现在,listTabs() 已准备好创建弹出窗口的内容。

首先

  1. 获取 <div id="tabs-list"> 元素。
  2. 创建一个文档片段(将在其中构建列表)。
  3. 设置计数器。
  4. 清除 <div id="tabs-list"> 元素的内容。
js
function listTabs() {
 getCurrentWindowTabs().then((tabs) => {
    const tabsList = document.getElementById('tabs-list');
    const currentTabs = document.createDocumentFragment();
    const limit = 5;
    let counter = 0;

    tabsList.textContent = '';

接下来,我们将为每个标签创建链接

  1. 循环遍历来自 tabs.Tab 对象的前 5 个项目。
  2. 对于每个项目,将超链接添加到文档片段。
    • 链接的标签(即其文本)使用标签的 title 设置(或者如果它没有 title,则使用 id 设置)。
    • 链接的地址使用标签的 id 设置。
js
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"> 元素

js
    tabsList.appendChild(currentTabs);
  });
}

使用活动标签

另一个相关的示例功能是“提醒活动标签”信息选项,该选项将活动标签的所有 tabs.Tab 对象属性转储到警报中

js
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 对象来查找具有活动设置的项,从而找到活动标签对象。

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

创建、复制、移动、更新、重新加载和删除标签

收集了有关标签的信息后,您很可能希望对它们进行一些操作——无论是为用户提供操作和管理标签的功能,还是在您的扩展程序中实现功能。

以下功能可用

注意:这些函数都需要它们要操作的标签的 ID(或 ID)。

而以下函数将作用于活动标签(如果没有提供标签id

操作方法示例

tabs-tabs-tabs 示例中,练习了所有这些功能,除了更新标签的 URL 之外。这些 API 的使用方法相似,因此我们将研究其中一个更复杂的实现,即“将活动标签移动到窗口列表的开头”选项。

但首先,这里演示了该功能的实际操作。

manifest.json

这些函数都不需要权限来运行,因此 manifest.json 文件中没有需要重点说明的功能。

tabs.html

tabs.html 定义了弹出窗口中显示的“菜单”,其中包括“将活动标签移动到窗口列表的开头”选项,以及由视觉分隔符分组的一系列<a>标签。每个菜单项都被赋予一个id,该idtabs.js中用于确定正在请求的菜单项。

html
<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中点击的监听器。

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

然后,一系列if语句试图匹配被点击的项目的id

这段代码片段是用于“将活动标签移动到窗口列表的开头”选项的。

js
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 对象。然后,它循环遍历该对象以查找并返回活动标签对象。

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

固定标签

标签的一个功能是,用户可以在窗口中固定标签。固定标签放置在标签列表的开头,不能移动。这意味着标签可以移动到的最早位置是任何固定标签后的第一个位置。因此,调用firstUnpinnedTab() 通过循环遍历tabs对象来查找第一个未固定标签的位置。

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

现在我们拥有了移动标签所需的一切:活动标签对象,从中我们可以获取标签id和要移动到的位置。因此,我们可以实现移动操作。

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

复制、重新加载、创建和删除标签的其余函数的实现方式类似。

操作标签的缩放级别

下一组函数使您能够获取 (tabs.getZoom) 和设置 (tabs.setZoom) 标签内的缩放级别。您还可以检索缩放设置 (tabs.getZoomSettings),但在撰写本文时,设置设置的功能 (tabs.setZoomSettings) 在 Firefox 中不可用。

缩放级别可以在 30% 到 500% 之间(表示为小数0.35)。

在 Firefox 中,默认缩放设置为

  • 默认缩放级别 100%.
  • 缩放模式:自动(因此浏览器管理缩放级别的设置方式)。
  • 缩放更改范围:"per-origin",这意味着当您再次访问某个网站时,它将采用您上次访问时设置的缩放级别。

操作方法示例

tabs-tabs-tabs 示例中,包括了三个缩放功能的演示:放大、缩小和重置缩放。以下是该功能的实际操作。

让我们看一下放大的实现方式。

manifest.json

所有缩放功能都不需要权限,因此 manifest.json 文件中没有需要重点说明的功能。

tabs.html

我们已经讨论过 tabs.html 如何定义此扩展程序的选项,在提供缩放选项方面没有新的或独特的内容。

tabs.js

tabs.js 首先定义几个在缩放代码中使用的常量。

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

然后,它使用我们之前讨论过的相同监听器,以便它可以对tabs.html中的点击进行操作。

对于放大功能,这将运行

js
  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

Tabs API 提供的另一个重要功能是能够操作标签内的 CSS——将新 CSS 添加到标签 (tabs.insertCSS()) 或从标签中删除 CSS (tabs.removeCSS()).

例如,如果您想突出显示某些页面元素或更改页面的默认布局,这可能很有用。

操作方法示例

apply-css 示例中,使用这些功能将红色边框添加到活动标签中的网页。以下是该功能的实际操作。

让我们看一下它是如何设置的。

manifest.json

manifest.json 中,请求使用 CSS 功能所需的权限。您需要以下两种权限之一:

  • "tabs" 权限和 主机权限;或者,
  • "activeTab" 权限。

后者最有用,因为它允许扩展程序在从扩展程序的浏览器或页面操作、上下文菜单或快捷方式运行时,在活动标签中使用 tabs.insertCSS()tabs.removeCSS()

json
{
  "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/main/apply-css",

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

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

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

您会注意到,除了"activeTab"之外,还请求了"tabs"权限。需要此附加权限才能使扩展程序的脚本访问标签的 URL,我们将在稍后看到其重要性。

manifest.json 文件中的其他主要功能是定义

  • 后台脚本,该脚本在扩展程序加载后立即开始运行。
  • “页面操作”,它定义了要添加到浏览器的地址栏中的图标。
background.js

启动时,background.js 设置一些常量来定义要应用的 CSS、 “页面操作” 的标题以及扩展程序将在其中运行的协议列表。

js
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()

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

initializePageAction 使用protocolIsApplicable() 确定活动标签的 URL 是否是 CSS 可以应用到的 URL。

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

然后,如果示例可以对标签进行操作,initializePageAction() 将标签的pageAction(导航栏)图标和标题设置为使用“关闭”版本,然后再使pageAction可见。

js
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

js
browser.pageAction.onClicked.addListener(toggleCSS);

toggleCSS() 获取pageAction的标题,然后执行所描述的操作。

  • 对于“应用 CSS”
    • pageAction图标和标题切换为“删除”版本。
    • 使用 tabs.insertCSS() 应用 CSS。
  • 对于“删除 CSS”
    • pageAction图标和标题切换为“应用”版本。
    • 使用 tabs.removeCSS() 删除 CSS。
js
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 可以应用到的协议。

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

一些其他有趣的功能

Tabs API 中还有一些其他功能不适合前面的部分之一。

  • 使用 tabs.captureVisibleTab 捕获可见标签的内容。
  • 使用 tabs.detectLanguage 检测标签中内容的主要语言。例如,这可以用来将扩展程序的 UI 中的语言与它运行的页面的语言匹配。

了解更多

如果您想了解更多有关 Tabs API 的信息,请查看以下内容: