浏览器扩展的基本元素
与大多数软件平台一样,浏览器扩展从概念上可以分为几个独立的组成部分。对于初涉浏览器扩展开发领域的新手来说,这些组成部分的特殊性可能很难内化,因为其中一些行为是冗余的或不直观的。了解浏览器扩展的各个组成部分是如何组合在一起的,对于成为浏览器扩展开发专家至关重要。
注意:本章将高层次地分别介绍浏览器扩展的每个组成部分,而不会深入太多代码细节。后续章节将对这些组成部分进行深入研究。
浏览器模型
在探索浏览器扩展的各个元素之前,让我们首先回顾一下浏览器的默认行为。请参考以下打开了一个标签页的浏览器示意图(图2-1)。
注意:了解网页是如何渲染的、JavaScript是如何执行的以及浏览器是如何执行网络请求的,是开发浏览器扩展的先决条件。如果对这些主题不熟悉,可以在Mozilla开发者网络(MDN)上找到一份优秀的资源列表,网址为:https://developer.mozilla.org/en-US/docs/Web/Guide/Introduction_to_Web_development。
此图展示了当网页浏览器的一个标签页指向某个URL时所涉及的高级概念。网页在浏览器的主要用户界面中进行渲染,并拥有自己的JavaScript运行时环境。在初始HTML加载之后,网页本身及其内部的脚本会发起后续的网络请求:页面的头部会加载CSS、图像和其他脚本等资源,而脚本可以执行额外的请求来加载资源以及发送和接收数据。
浏览器标签页
让我们扩展前面的示意图,以展示当浏览器中打开多个标签页时的情况(图2-2)。
浏览器有效地将不同的标签页隔离在不同的沙箱中,为每个标签页提供自己的文档对象、JavaScript运行时环境、内存,以及(取决于浏览器)自己的系统线程和进程。当然,在匹配来源时,有无数重要的考虑因素。尽管每个标签页都有单独的运行时环境,但两个来源匹配的独立标签页能够共享资源。这些资源包括但不限于:
- Cookies
- LocalStorage
- IndexedDB
- Shared Workers
- Service Workers
- HTTP 缓存
同源策略
在应用同源策略(SOP)时,来源也是一个需要考虑的因素。简而言之,同源策略是由所有浏览器实现的一组安全策略,用于控制Web应用程序之间的数据访问。当两个Web应用程序的来源不同时,浏览器会自动应用一组限制,以保护可能敏感的信息免受不受信任的来源的访问。Web开发人员通常在以下三种情况下遇到同源策略:向不同来源发送网络请求时、执行来自不同来源的脚本时以及从不同来源访问存储API时。
浏览器扩展模型
从网页的角度来看,浏览器扩展可以被视为一个无形的补充实体。它们在自己的运行时环境中执行JavaScript,在自己的沙箱上下文中渲染页面,并且可以访问自己独立的一组API。网页将无法知道是否安装了浏览器扩展以及它们正在做什么。
注意:内容脚本(Content Scripts)是这一规则的一个重要例外,因为它们允许网页和扩展程序访问DOM和一些共享资源,但正如您将在内容脚本章节中看到的那样,它们也受到了一些特定于扩展的沙箱限制。
请考虑如图2-3所示的安装了浏览器扩展的浏览器示意图。
让我们逐一审视该示意图中展示的重要概念。
独立的JavaScript页面和运行时
浏览器扩展一旦安装,便可在多个位置提供多个自定义HTML用户界面,包括弹出页面、选项页面和开发者工具页面。这些页面在几乎所有方面都表现得像普通网页一样:
- 每个页面都以与普通网页相同的方式渲染,具有加载事件和传统的文档对象。
- 每个页面都被分配了一个专用的原生浏览器容器,并与其他所有网页完全隔离。
- 每个界面都有自己的JavaScript运行时环境。背景脚本和内容脚本也有自己的运行时环境,但它们并不作为独立的用户界面存在。
注意:有关弹出页面、选项页面、开发者工具页面、背景脚本和内容脚本的详细信息,将在本章后续部分介绍。
原生API和用户界面
浏览器扩展可以访问一组原生浏览器API,这些API允许它们控制浏览器的行为、外观和数据。以下是一些示例:
- 标签页API(Tabs API)
- 书签API(Bookmarks API)
- 下载API(Downloads API)
- 浏览器历史API(Browser History API)
- 浏览数据API(Browsing Data API)
注意:这些API将在“扩展和浏览器API”章节中详细介绍。
此外,浏览器扩展有多种方式与浏览器用户界面本身进行集成。这包括管理工具栏图标的外观和行为、指定URL栏(地址栏)Omnibox界面的行为,以及定义在打开扩展上下文菜单时应显示的内容。
标签页和域访问
浏览器扩展并不局限于单个域或标签页——它们存在于浏览器本身。当获得适当的权限时,浏览器扩展能够与多个域上的多个标签页进行交互。这意味着浏览器扩展能够检查和管理浏览器打开页面的部分或全部内容。
浏览器扩展也完全可以不使用任何网页。扩展作为仅存在于浏览器本身的独立软件组件,已经足够完备。当然,浏览器扩展的某些方面,如内容脚本和开发者工具接口,只有在操作网页时才有用。
观察和拦截网络请求
通常,从网页发出的网络请求会直接交给浏览器处理。浏览器扩展可以获得权限,作为网络请求的代理(shim),在请求被传递给浏览器之前,由扩展对请求进行预处理。这意味着扩展可以添加规则来有条件地修改请求,甚至直接阻止它们。这也意味着扩展可以实时看到进出页面的流量流。
注意:在manifest v2和v3之间,修改网络请求的机制发生了显著变化。这将在“网络”章节中详细介绍。
浏览器扩展组件
前一节将浏览器扩展作为一个整体概念进行了介绍。这对于从整体上理解这一概念非常有用。然而,正如本章引言中所述,浏览器扩展是由一系列半独立组件组成的。本节将高层次地分析这些组件。
扩展清单(Manifest)
清单是浏览器扩展的规则手册。每个扩展都需要在manifest.json文件中以JSON数据的形式提供这个清单。清单中包含的内容示例如下:
- 公共信息,如扩展名称、描述、语义版本、图标和作者
- 背景脚本、弹出页面、选项页面、开发工具页面和内容脚本的入口文件指针
- 扩展正常运行所需的要求和配置,如权限、内容安全策略、跨域策略和最低浏览器版本
- 用于管理网络请求、启用域和扩展希望通过内容脚本注入到页面中的资源的模式匹配规则集
- 其他特定于扩展的选项,如启用离线模式和隐身模式以及键盘快捷键
注意:关于如何编写manifest.json文件的具体细节,可以在“扩展清单(Extension Manifests)”章节中找到。
清单v2和v3
浏览器扩展的世界目前正处于manifest.json文件的两个版本——版本2(v2)和版本3(v3)——之间的过渡期。新的清单v3极大地改变了扩展的执行方式,并更改了它们可以访问的API。谷歌Chrome浏览器正引领向清单v3的过渡;由于其市场主导地位,其他浏览器供应商大多也在紧随其后。
向清单v3的过渡将是本书的一个重要主题。它在浏览器扩展开发者社区中引发了高度争议,并广泛影响了许多极为流行的浏览器扩展。
背景脚本
背景脚本的主要目的是处理浏览器事件。这些事件可能是扩展生命周期事件,如安装或卸载,也可能是浏览器事件,如导航到网页或添加新书签。背景脚本可以在JavaScript文件中定义,也可以作为单个HTML文件,该HTML文件在一个无头页面中通过一个或多个<script>
标签运行背景脚本。
提示:大多数简单的浏览器扩展通过一个背景脚本就足够满足需求了。
尽管背景脚本拥有自己独立的JavaScript运行时环境,但它们并非孤立存在。背景脚本可以访问WebExtensions API,因此能够执行诸如与其他扩展部分交换消息、与其他扩展交换消息或程序化地将内容脚本注入到页面中等操作。
在清单v2和清单v3之间,背景脚本经历了重大变革。在清单v2中,背景脚本可以选择作为“持久性”脚本,这意味着背景脚本只初始化一次并一直驻留在内存中,直到浏览器关闭;或者作为非持久性脚本,即作为“事件页面”存在,每当发生相关浏览器事件时按需初始化。在清单v3中,背景脚本以service workers的形式存在,这在很大程度上复制了清单v2中非持久性背景脚本的行为。
注意:有关背景脚本的更多详细信息,请参阅“背景脚本”章节。
弹出页面(Popup Page)
弹出页面是浏览器扩展用来显示自定义用户界面的原生浏览器容器。当用户点击扩展工具栏按钮时,弹出页面会作为一个“弹出”在网页上方的对话框出现。弹出页面总是直接显示在工具栏下方。由于其易于访问且可以覆盖在任何网页上,弹出页面通常包含用户需要快速访问的内容。图2-4展示了一个弹出页面的示例。
弹出页面的渲染方式与常规网页相同,但其对话框式的特性意味着它们是一次性的:每次打开弹出页面时都会重新渲染,而在关闭弹出页面时则会卸载。与背景脚本一样,弹出页面也可以访问WebExtensions API,这意味着它们具有相同的功能集。
注意:弹出页面不能通过编程方式打开,它必须由工具栏点击或类似的浏览器特权操作触发。这是为了防止扩展滥用此功能,频繁或随意地打开其弹出页面,从而给用户体验带来问题。
由于弹出页面的这种限制,它们通常用于显示简短的信息、选项或让用户执行快速操作。用户可以通过点击工具栏按钮来手动打开弹出页面,从而获得所需的信息或执行所需的操作,而无需在网页上查找或导航到扩展的其他部分。这种交互方式使得弹出页面成为浏览器扩展中一种非常直观和高效的用户界面元素。
选项页面(Options Page)
选项页面是浏览器扩展使用的原生浏览器容器,用于显示自定义用户界面。当用户点击扩展工具栏上下文菜单中的“选项”时,选项页面会作为一个独立的网页打开。打开选项页面的示例如图2-5和图2-6所示。
“选项”这个名字有些误导:这个页面并不局限于只显示扩展的选项。与弹出页面一样,这也是一个功能齐全的网页,可以访问WebExtensions API,这意味着你可以将其用作一个完整的Web应用程序。
注意:有关这些页面如何工作以及如何使用它们的更多详细信息,请参阅“弹出页面和选项页面”章节。
内容脚本(Content Scripts)
内容脚本这一术语广义上指的是被注入到网页中的任何内容。JavaScript可以通过在清单中声明式注入,或者通过WebExtensions API从扩展页面或背景脚本中编程式注入。这些内容可以是JavaScript、CSS,或者两者兼有。JavaScript在其独立的运行时环境中被沙盒化,这意味着它无法读取主网页运行时环境中的JavaScript变量或属性,但它仍然可以访问与网页本身相同的DOM。这意味着内容脚本完全能够读取和写入页面,从而实现诸如页面内小部件或与网页的完全集成等功能。
内容脚本对WebExtensions API的访问权限有限,因此它们无法执行在弹出页面、选项页面或背景脚本中可能执行的多项操作。然而,它们可以与其他扩展元素(如背景脚本)交换消息。因此,内容脚本仍然可以通过将操作委托给背景脚本来间接使用WebExtensions API。
注意:有关内容脚本如何工作以及如何使用它们的更多详细信息,请参阅“内容脚本”章节。
开发者工具面板和侧边栏(Devtools Panels and Sidebars)
开发者工具面板和侧边栏是浏览器扩展可借以展示自定义用户界面的原生浏览器容器。当用户在开发者工具界面中选定对应的标签页时,这些开发者工具界面会表现为嵌套在原生浏览器开发者工具中的面板。如图2-7和图2-8所示。
与弹出页面相似,开发者工具面板和侧边栏也是一个功能齐全的网页,但与内容脚本一样,它对WebExtensions API的访问权限有限。然而,它可以使用额外的Devtools API,这些API可用于执行诸如检查页面和观察页面网络流量等操作。
注意:有关这些页面如何工作以及如何使用它们的更多详细信息,请参阅“开发者工具页面(Devtools Pages)”章节。
扩展元素在实际中的应用
为了帮助我们理解所有这些元素是如何组合在一起的,让我们来看看这些组件是如何融入一些流行的浏览器扩展中的。基于我们对浏览器扩展各种元素行为方式的了解,即使无法访问代码库,我们也能理解扩展是如何实现这些用户界面的。
Honey
Honey是一款浏览器扩展,它能够在用户在电子商务网站结账时检测到这一点,并自动应用优惠券代码(如图2-9所示)。
在这里,Honey通过内容脚本在页面上渲染了一个小部件。当用户处于结账流程中时,它还使用内容脚本来程序化地提交优惠券代码。
此外,请注意,工具栏图标上显示了一个包含“15”的徽章,这是Honey在告诉我们,在这个域名上有15个可能的优惠券代码可以尝试。Honey之所以知道这一点,是因为它能够评估用户正在访问的域名,并将其与Honey数据库中匹配的优惠券代码进行比对。然后,该扩展能够利用其动态设置工具栏图标显示内容的能力,并在Honey徽标上渲染出包含“15”的徽章(如图2-10所示)。
上面的截图显示了Honey的弹出视图。然而,Honey并不主要依赖这个弹出窗口作为用户界面,因为无法以编程方式打开弹出窗口。当检测到可以提交优惠券代码时,扩展需要自动向用户显示内容,而它依赖的是由内容脚本提供支持的小部件来实现这一点。
LastPass
LastPass是一款浏览器扩展,它可以安全地存储并自动填充用户的密码,同时还可以在用户提交新密码时记录这些密码(如图2-11所示)。
LastPass使用内容脚本来查找并填充身份验证表单字段,同时还在表单字段内渲染用于管理密码的小部件。此外,LastPass还监视浏览器内外的流量,以查找设置或更新密码的请求,并将请求的有效载荷记录在您加密的帐户凭据列表中(如图2-12所示)。
当然,表单检测永远不可能做到完美,因此LastPass在弹出窗口中提供了所有相同的密码实用程序,以应对内容脚本小部件未呈现的情况(如图2-13所示)。
除了管理密码外,LastPass还提供了一套广泛的安全工具,并允许用户对其行为进行复杂的自定义。浏览器扩展在扩展选项页面中提供了这些设置,如上所示。
Grammarly
Grammarly是一款浏览器扩展,它观察用户在浏览器中输入的文本,并自动提供如何修改或改进的建议(如图2-14和2-15所示)。
与LastPass检测身份验证表单字段的方式类似,Grammarly使用内容脚本来智能地检测用户何时在页面上输入内容,解析这些内容,并在这些内容之上呈现建议。此外,该扩展还能够将所有活跃的建议整合到统一的内容脚本小部件中(如图2-16所示)。
Grammarly还在弹出页面中提供了扩展设置的切换选项。
React Developer Tools
React Developer Tools 是一个浏览器扩展程序,它使用户能够理解 React 应用程序在页面上是如何进行渲染的(如图2-17所示)。
该浏览器扩展可以分析网页,以检测它是否是一个React应用程序。如果是,工具栏图标将会显示相关信息,并且弹出页面会告知用户存在哪种类型的React应用程序(如图2-18所示)。
该扩展使用Devtools API在浏览器的开发者工具界面中创建一个自定义面板。它解析页面内容,并将其解包为面板页面内可浏览的组件树。
总结
顾名思义,浏览器扩展更多的是浏览器的扩展,而非页面的扩展。WebExtensions API允许扩展执行网页通常无法完成的操作。
浏览器扩展由一系列元素组成。其中,唯一必需的元素是清单文件(manifest)。可选元素包括后台脚本、弹出页面、选项页面、开发者工具页面和内容脚本。
下一章将为您带来速成课程,介绍如何构建和安装基本的浏览器扩展。