浏览器插件开发必备概念——定制浏览器体验

1,076 阅读6分钟

系列文章可以查看《浏览器扩展程序开发笔记》专栏


Chrome 浏览器提供了很多相关的 API 以定制浏览器体验:

Bookmarks

参考:

可以使用扩展程序定制浏览器的书签管理行为。除了可以使用覆写页面 Override Page 的方式定制书签管理页面,还可以使用 Chrome.bookmarks 一些列 API 操作书签。

如果要使用 Chrome.bookmarks API,需要先在配置清单 manifest.json 的选项 permissions 中进行权限声明注册

{
  // ...
  "permissions": ["bookmarks"]
}

书签数据是以树形结构组织的,每一个节点对象 bookmarks.BookmarkTreeNode 是一个书签或一个文件夹(也称为组),它具有多种属性,以下列出常用的几个:

  • children 子节点列表
  • id 唯一标识符
  • index (必须)在父级文件夹中的索引(从 0 开始)
  • parentId 父级文件夹的唯一标识符(根节点省略该属性)
  • title (必须)节点所展示的名称
  • url 书签的链接,文件夹可以省略该属性

⚠️ 根节点是一个文件夹,不能删除,且只能有一个。 而且不能重命名特殊的节点(文件夹)📁 书签栏 Bookmarks Bar 和 📁 其他书签 Other Bookmarks

使用方法 chrome.bookmarks.create() 创建一个书签节点,接收两个入参,第一个是节点的属性,第二个(可选)参数是回调函数

// 创建一个文件夹,名为 Extension bookmarks
chrome.bookmarks.create(
  {'parentId': bookmarkBar.id, 'title': 'Extension bookmarks'},
  function(newFolder) {
    console.log("added folder: " + newFolder.title);
  },
);

// 创建一个书签,名为 Extensions doc,链接为 https://developer.chrome.com/docs/extensions
chrome.bookmarks.create({
  'parentId': extensionsFolderId,
  'title': 'Extensions doc',
  'url': 'https://developer.chrome.com/docs/extensions',
});

Browsing Data

参考:

扩展程序可以删除特定时间段的用户浏览数据。

如果要使用 Chrome.bookmarks API,需要先在配置清单 manifest.json 的选项 permissions 中进行权限声明注册

{
  // ...
  "permissions": ["browsingData"]
}

使用方法 chrome.browsingData.remove() 删除用户浏览数据。

// 删除一周以来的数据
const millisecondsPerWeek = 1000 * 60 * 60 * 24 * 7;
const oneWeekAgo = (new Date()).getTime() - millisecondsPerWeek;

// 删除所有用户操作数据
chrome.browsingData.removeCookies({
  "since": oneWeekAgo
}, callback;

const callback = function () {
  // Do something clever here once data has been removed.
};

💡 删除用户数据可能需要较长的时间(基于用户产生了多少操作历史数据),因此推荐设置回调函数(它在清除完成后执行),以告知用户最新的状态。

⚠️ 方法 chrome.browsingData.remove() 可能在清除完 cookie 后自动设置它们,以便让账户的数据同步功能正常运作,这样才可以让服务器中已同步的相应数据也覆盖清除掉;而使用方法 chrome.browsingData.removeCookies 清除 cookie 时,会停止同步功能。

💡 可以传递一个 JSON 形式的对象,指定需要删除哪几种浏览器数据,也可以调用对应的方法来删除相应类型的用户数据。

// 删除指定的用户操作数据
chrome.browsingData.remove({
  "since": oneWeekAgo
}, {
  "appcache": true,
  "cache": true,
  "cacheStorage": true,
  "cookies": true,
  "downloads": true,
  "fileSystems": true,
  "formData": true,
  "history": true,
  "indexedDB": true,
  "localStorage": true,
  "passwords": true,
  "serviceWorkers": true,
  "webSQL": true
}, callback);

💡 还可以删除在指定网页(或不包含指定网页)下的用户数据(适用于cookies、cache、storage (CacheStorage, FileSystems, IndexedDB, LocalStorage, ServiceWorkers, and WebSQL) 类型的数据),可以在方法 chrome.browsingData.remove() 的第一个参数中设置选项 originsexcludeOrigins

chrome.browsingData.remove({
  "origins": ["https://www.example.com"]
}, {
  "cacheStorage": true,
  "cookies": true,
  "fileSystems": true,
  "indexedDB": true,
  "localStorage": true,
  "serviceWorkers": true,
  "webSQL": true
}, callback);

由于 cookies 是基于域名的,所以删除特定网页的 cookies 时会删除该域名下的所有 cookies,例如删除 https://www.example.com 会删除 .example.com 域名下所有的 cookie。

Downloads

参考:

扩展程序可以对浏览器中的下载功能进行操作,通过 chrome.downloads API 可以启动、监控、操作和搜索下载项。

如果要使用 Chrome.downloads API,需要先在配置清单 manifest.json 的选项 permissions 中进行权限声明注册

{
  // ...
  "permissions": ["downloads"]
}

History

参考:

扩展程序可以操作浏览器的历史记录。除了可以使用覆写页面 Override Page 的方式定制历史记录页面,还可以使用 Chrome.history 一些列 API 操作历史记录。

如果要使用 Chrome.history API,需要先在配置清单 manifest.json 的选项 permissions 中进行权限声明注册

{
  // ...
  "permissions": ["history"]
}

💡 浏览的历史记录是通过不同类型的页面跳转 transition type 所产生的,例如用户从一个网页中点击一个链接跳转到另一个网页,这时候产生的历史记录的类型是 link

Tabs

参考:

扩展程序可以创建、修改、重排标签页。

虽然使用 Chrome.tabs 部分的 API 可以不进行权限声明,但是仍推荐在配置清单 manifest.json 的选项 permissions 中进行权限声明注册,因为需要访问标签的属性 urlpendingUrltitlefavIconUrl 时是需要权限许可的。

{
  // ...
  "permissions": ["tabs"]
}

以下是一些常见的应用场景:

  • 使用方法 chrome.tabs.create() 在新的标签页打开一个网页

    // background.js
    // 可以在安装成功后,打开扩展程序的介绍页面
    chrome.runtime.onInstalled.addListener((reason) => {
      if (reason === chrome.runtime.OnInstalledReason.INSTALL) {
        chrome.tabs.create({
          url: 'onboarding.html'
        });
      }
    });
    

    ⚠️ 内容脚本 content scripts 能使用方法 chrome.tabs.create()

  • 使用方法 chrome.tabs.query({ active: true, currentWindow: true }) 获取符合条件的标签页

    // background.js
    async function getCurrentTab() {
      let queryOptions = { active: true, currentWindow: true };
      // 查询返回的是符合条件的标签页对象列表,当前页面只有一个,可以直接用解构来获取
      let [tab] = await chrome.tabs.query(queryOptions);
      return tab;
    }
    

    ⚠️ 内容脚本 content scripts 能使用方法 chrome.tabs.query()

  • 使用方法 chrome.tabs.update() 更新标签页面的状态

    // background.js
    function toggleMuteState(tabId) {
      chrome.tabs.get(tabId, async (tab) => {
        // 获取标签页的静音状态,并进行切换
        let muted = !tab.mutedInfo.muted;
        // 更新标签页的静音状态
        await chrome.tabs.update(tabId, { muted });
        console.log(`Tab ${tab.id} is ${ muted ? 'muted' : 'unmuted' }`);
      });
    }
    

    ⚠️ 内容脚本 content scripts 能使用方法 chrome.tabs.get()chrome.tabs.update()

  • 使用方法 chrome.tabs.move() 重排标签页

    // background.js
    // 监听标签页激活事件(点击浏览器最顶部一栏进行标签页切换时触发)
    chrome.tabs.onActivated.addListener(activeInfo => move(activeInfo));
    
    // 将激活的标签页移到第一的位置
    async function move(activeInfo) {
      try {
        await chrome.tabs.move(activeInfo.tabId, {index: 0});
        console.log('Success.');
      } catch (error) {
        // 如果用户在拖拽标签页时
        if (error == 'Error: Tabs cannot be edited right now (user may be dragging a tab).') {
          setTimeout(() => move(activeInfo), 50);
        }
      }
    }
    

Windows

参考:

扩展程序可以创建、修改、重排浏览器窗口。

由于使用 Chrome.windows API 时,会获取标签页的相关信息 ,所以需要先在配置清单 manifest.json 的选项 permissions 中进行 tabs 的权限声明。

{
  // ...
  "permissions": ["tabs"],
}

💡 当前窗口是指目前扩展程序执行代码所在的窗口,而不一定是浏览器的置顶窗口或当前聚焦窗口,例如扩展程序在一个窗口中打开了一个 HTML 文档,在文档中调用了 chrome.tabs.query() 方法,则当前窗口是指该 HTML 文档所在的窗口。有些情况下对于后台脚本没有相应的当前窗口。