浏览器插件(extentions)是那种听起来就让我感觉像《windows秘籍》一样的东西,充斥着大量看不懂的高深底层代码。然而当我被一些页面上不堪入目🙈的图片搞得眼睛都要瞎了时,我想起来好像写个插件可以帮助我解决这个问题。
放下内心的成见,我开始尝试编写一个浏览器插件,发现它跟底层基本没有关系,而且现在浏览器都提供了很好理解的API给开发者调用,有些简单功能的插件几十行代码就能搞定。话不多说,先介绍一下插件的基础知识。
作为一个前端人员,我是没想到插件其实跟前端项目是一样的,就是由压缩后的HTML、JS、CSS以及其他可以在web平台使用的文件组成。插件就是通过web技术编写的,并且可以使用浏览器提供给开放web相同的API。
插件的功能非常广泛,它可以修改用户看到的网页内容,提供交互,扩展或改变浏览器原本的行为,通过插件可以打造非常个性化的浏览器。
插件包含的内容
插件中包含的文件和目录可能各不相同,但是插件必备的一个文件为manifest.json,在这个文件中包含了插件的基本信息。如下为一个简单的示例,其中name、description、version和manifest_version是必须包含的字段。version字段代表开发的插件版本,manifest_version则是浏览器支持的manifest版本,不同的版本配置文件和支持的功能可能有差异,目前chrome已经支持3版本了,在官网提供了从2版本迁移到3版本的指南。icons是插件的图标。
{
"name": "My Extension",
"description": "A nice little demo extension.",
"version": "2.1",
"manifest_version": 3,
"icons": {
"16": "icon_16.png",
"32": "icon_32.png",
"48": "icon_48.png",
"128": "icon_128.png"
},
"background": {
"service_worker": "background.js"
},
"permissions": ["activeTab"],
"host_permissions": ["*://*.example.com/*"],
"action": {
"default_icon": "icon_16.png",
"default_popup": "popup.html"
}
}
插件的结构
插件的结构取决于它的功能,大部分的插件都会包含以下几个部分
- Manifest
- Background Script
- UI Elements
- Content Script
- Options Page
Background Script
Background Scipt是插件中的事件处理脚本,它可以监听对于插件有用的浏览器事件,当监听的事件触发时,它就会执行相应的处理代码。常见的浏览器事件有网络请求、存储等。
UI Elements
插件的用户界面应该尽量保持简洁,大多数的插件都会包含browser action或者page action。browser action代表这个插件在每个页面都可以使用,page action代表插件只在部分页面可用。插件的用户交互页面,比如为popup,可以包含任意的HTML和JS。插件甚至可以创建新的Tab页或者打开新的窗口来展示额外的页面。
Content Script
插件可以利用Content Script读取或修改网页内容。Content Script中使用JS来读取和修改加载到浏览器中的页面的DOM结构。
以上的三个部分都可以互相传递信息,如下图所示。不同的HTML页面可以通过chrome.extention方法找到对方,比如getViews()和getBackgroundPage()。此外,插件中的所有部分都可以通过storage API来读取或写入值。
Options Page
就像插件允许用户自定义浏览器,Options Page允许自定义插件的功能。可以使用选项来启用插件的功能,并允许用户选择与他们的需求相关的功能。
实战开发
由于chrome浏览器的资料大都需要fq,所以我写的是基于firefox浏览器的插件
mdn上的插件开发教程中已经包含很多示例代码了,本文开发的屏蔽图片插件也是参考示例完成的。在访问某些网站时,我只想看文字,而不想看图片,在仔细研究了这些网页的结构后,我发现这些网页除了加载图片外,还会额外加载一些js,这些js运行后,会在页面的各种空隙中插入对话框,因此我只需要拦截浏览器发出的图片和js请求,就可以获得一个干净的页面了。
在上面的介绍部分中说过插件的background部分可以监听浏览器的各种事件,其中就包括网络请求。因此该插件主要代码都集中在background中。
该插件包含的内容如下所示
manifest.json文件
在这部分除了定义插件的基本信息,还需要声明插件需要的权限permissions字段。拦截网络请求需要声明webRequest和webRequestBlocking权限。该插件可以选择性屏蔽网站图片,因此需要存储屏蔽网站的域名,使用到了storage权限。为了提供更好的用户体验,在每次点击屏蔽或不屏蔽时,需要重新加载网页才能生效,因此直接reload tab页面,使用到了tabs权限。由于需要匹配所有的域名,再声明<all_urls>权限。
background字段定义了插件的background部分,这里只需要说明使用到的js文件。
browser_action字段表明该插件在浏览器的所有页面都生效,给它提供一个默认的icon。
{
"manifest_version": 2,
"name": "Text only",
"version": "1.0",
"description": "Only display text",
"icons": {
"48": "icons/red.png"
},
"permissions": [
"webRequest", "webRequestBlocking", "storage", "tabs", "<all_urls>"
],
"background": {
"scripts": ["background.js"]
},
"browser_action":{
"default_icon": "icons/red.png",
"default_title": "Block Images!"
}
}
background.js
在这部分中主要监听浏览器的发出网络请求事件以及插件图标的点击事件。
在发出网络请求事件中,查看当前的url的域名是否在屏蔽名单中,并且请求的文件类型是否是image和script,如果满足条件,则拦截该网络请求。
在插件图标的点击事件中,如果该域名已经在屏蔽名单中,则将该域名从名单中移除,并且更换成红色icon;如果该域名不在屏蔽名单中,则将该域名加入名单,并且更换成绿色icon。最后重新加载当前tab页,使得更改立即生效。
具体的代码实现可以参照firefox插件示例http-proxy和bookmark-it。