作者:小影前端团队—— 祥帅
1.前言
1.1.什么是Chrome插件
首先要明确一个观念,这种扩展程序,实际上不是一个exe、app之类的程序,下载了本地打开运行安装,本质上,它就是一个网页,写的用的都是前端的语言,高档点说是一个程序,通俗来讲, 就是运行在浏览器上的一个网站,网页。
严格来讲,我们正在说的东西应该叫Chrome扩展(Chrome Extension),真正意义上的Chrome插件是更底层的浏览器功能扩展,可能需要对浏览器源码有一定掌握才有能力去开发。
Chrome插件是一个用Web技术开发、用来增强浏览器功能的软件,它其实就是一个由HTML、CSS、JS、图片等资源组成的一个.crx后缀的压缩包.
个人猜测crx可能是Chrome Extension这3个字母的简写:
2.开发与调试
对于一个Chrome插件而言,其项目结构没有严格的要求,只要其目录有一个manifest.json即可, 也不需要专门的IDE,普通的web开发工具即可。
对于开发完成的插件只需要从右上角菜单->更多工具->扩展程序可以进入 插件管理页面,也可以直接在地址栏输入 chrome://extensions 进行访问。
勾选
开发者模式即可以文件夹的形式直接加载插件,否则只能安装.crx格式的文件。Chrome要求插件必须从它的Chrome应用商店安装,其它任何网站下载的都无法直接安装,所以,其实我们可以把crx文件解压,然后通过开发者模式直接加载。
开发中,代码有任何改动都必须重新加载插件,只需要在插件管理页按下Ctrl+R即可,以防万一最好还把页面刷新一下。
3.manifest.json里是什么
这是一个Chrome插件最重要也是必不可少的文件,用来配置所有和插件相关的配置,必须放在根目录。其中,manifest_version、name、version3个是必不可少的,description和icons是推荐的。
// 清单文件的版本,这个必须写,而且必须是2:其他会报错(未能成功加载扩展程序)
"manifest_version": 2,
// 插件的名称
"name": "demo",
// 插件的版本
"version": "1.0.0", // 随意
// 插件描述
"description": "简单的Chrome扩展demo",
// 图标(非必填) 在不同的分辨率下,chrome会根据不同情况采用不同大小的图标。
"icons":
{
"16": "img/icon.png",
"48": "img/icon.png",
"128": "img/icon.png"
},
// 会一直常驻的后台JS或后台页面
"background":
{
// 2种指定方式,如果指定JS,那么会自动生成一个背景页
"page": "background.html"
//"scripts": ["js/background.js"]
},
// 浏览器右上角图标设置,browser_action、page_action、app必须三选一
"browser_action":
{
"default_icon": "img/icon.png",
// 图标悬停时的标题,可选
"default_title": "这是一个示例Chrome插件",
"default_popup": "popup.html"
},
// 当某些特定页面打开才显示的图标
/*"page_action":
{
"default_icon": "img/icon.png",
"default_title": "我是pageAction",
"default_popup": "popup.html"
},*/
// 需要直接注入页面的JS
"content_scripts":
[
{
//"matches": ["http://*/*", "https://*/*"],
// "<all_urls>" 表示匹配所有地址
"matches": ["<all_urls>"],
// 多个JS按顺序注入
"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
// JS的注入可以随便一点,但是CSS的注意就要千万小心了,因为一不小心就可能影响全局样式
"css": ["css/custom.css"],
// 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle
"run_at": "document_start"
},
// 这里仅仅是为了演示content-script可以配置多个规则
{
"matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"],
"js": ["js/show-image-content-size.js"]
}
],
// 权限申请
"permissions":
[
"contextMenus", // 右键菜单
"tabs", // 标签
"notifications", // 通知
"webRequest", // web请求
"webRequestBlocking",
"storage", // 插件本地存储
"http://*/*", // 可以通过executeScript或者insertCSS访问的网站
"https://*/*" // 可以通过executeScript或者insertCSS访问的网站
],
// 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的
"web_accessible_resources": ["js/inject.js"],
// 插件主页,这个很重要,不要浪费了这个免费广告位
"homepage_url": "https://www.baidu.com",
// 覆盖浏览器默认页面
"chrome_url_overrides":
{
// 覆盖浏览器默认的新标签页
"newtab": "newtab.html"
},
// Chrome40以前的插件配置页写法
"options_page": "options.html",
// Chrome40以后的插件配置页写法,如果2个都写,新版Chrome只认后面这一个
"options_ui":
{
"page": "options.html",
// 添加一些默认的样式,推荐使用
"chrome_style": true
},
// 向地址栏注册一个关键字以提供搜索建议,只能设置一个关键字
"omnibox": { "keyword" : "go" },
// 默认语言
"default_locale": "zh_CN",
// devtools页面入口,注意只能指向一个HTML文件,不能是JS文件
"devtools_page": "devtools.html"
完整的配置文档请戳这里。
4.核心介绍
4.1.content-scripts
所谓content-scripts,其实就是Chrome插件中向页面注入脚本的一种形式(虽然名为script,其实还可以包括css的),借助content-scripts我们可以实现通过配置的方式轻松向指定页面注入JS和CSS(如果需要动态注入,可以参考下文),最常见的比如:广告屏蔽、页面CSS定制,等等。
{
// 需要直接注入页面的JS
"content_scripts":
[
{
//"matches": ["http://*/*", "https://*/*"],
// "<all_urls>" 表示匹配所有地址
"matches": ["<all_urls>"],
// 多个JS按顺序注入
"js": ["js/jquery-1.8.3.js", "js/content-script.js"],
// JS的注入可以随便一点,但是CSS的注意就要千万小心了,因为一不小心就可能影响全局样式
"css": ["css/custom.css"],
// 代码注入的时间,可选值: "document_start", "document_end", or "document_idle",最后一个表示页面空闲时,默认document_idle
"run_at": "document_start"
}
],
}
4.2.background
后台(姑且这么翻译吧),是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。
background的权限非常高,几乎可以调用所有的Chrome扩展API(除了devtools),而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置CORS。
配置中,background可以通过page指定一张网页,也可以通过scripts直接指定一个JS,Chrome会自动为这个JS生成一个默认的网页:
{
// 会一直常驻的后台JS或后台页面
"background":
{
// 2种指定方式,如果指定JS,那么会自动生成一个背景页
"page": "background.html"
//"scripts": ["js/background.js"]
},
}
需要特别说明的是,虽然你可以通过chrome-extension://xxx/background.html
直接打开后台页,但是你打开的后台页和真正一直在后台运行的那个页面不是同一个,换句话说,你可以打开无数个background.html,但是真正在后台常驻的只有一个,而且这个你永远看不到它的界面,只能调试它的代码。
比如,我们在background.js中对某个站点发送ajax请求,当站点有信息更新时,background.js也会更新工具栏右侧插件图标的状态,进而通知到用户!
4.3.popup
popup是点击图标时打开的一个小窗口网页,焦点离开网页就立即关闭,一般用来做一些临时性的交互。
popup可以包含任意你想要的HTML内容,并且会自适应大小。可以通过default_popup字段来指定popup页面,也可以调用setPopup()方法。
{
"browser_action":
{
"default_icon": "img/icon.png",
// 图标悬停时的标题,可选
"default_title": "这是一个示例Chrome插件",
"default_popup": "popup.html"
}
}
4.4 pageAction(地址栏右侧)
所谓pageAction,指的是只有当某些特定页面打开才显示的图标,它和browserAction最大的区别是一个始终都显示,一个只在特定情况才显示。
通俗的举个例子,一些扩展任何页面可用,就都会显示在工具栏上为可用状态,一些扩展只适用于某些页面,如大家很熟悉的vue tools调试器,在检测到页面用的是vue时,就会在工具栏显示出来并可用(非灰色)。
需要特别说明的是早些版本的Chrome是将pageAction放在地址栏的最右边,左键单击弹出popup,右键单击则弹出相关默认的选项菜单:
而新版的Chrome更改了这一策略,pageAction和普通的browserAction一样也是放在浏览器右上角,只不过没有点亮时是灰色的,点亮了才是彩色的,灰色时无论左键还是右键单击都是弹出选项:
- chrome.pageAction.show(tabId) 显示图标;
- chrome.pageAction.hide(tabId) 隐藏图标;
// manifest.json
{
"page_action":
{
"default_icon": "img/icon.png",
"default_title": "我是pageAction",
"default_popup": "popup.html"
},
"permissions": ["declarativeContent"]
}
// background.js
chrome.runtime.onInstalled.addListener(function(){
chrome.declarativeContent.onPageChanged.removeRules(undefined, function(){
chrome.declarativeContent.onPageChanged.addRules([
{
conditions: [
// 只有打开百度才显示pageAction
new chrome.declarativeContent.PageStateMatcher({pageUrl: {urlContains: 'baidu.com'}})
],
actions: [new chrome.declarativeContent.ShowPageAction()]
}
]);
});
});
4.5.override(覆盖特定页面)
使用override页可以将Chrome默认的一些特定页面替换掉,改为使用扩展提供的页面。
扩展可以替代如下页面:
- 历史记录:从工具菜单上点击历史记录时访问的页面,或者从地址栏直接输入 chrome://history
- 新标签页:当创建新标签的时候访问的页面,或者从地址栏直接输入 chrome://newtab
- 书签:浏览器的书签,或者直接输入 chrome://bookmarks
注意:
- 一个扩展只能替代一个页面;
- 不能替代隐身窗口的新标签页;
- 网页必须设置title,否则用户可能会看到网页的URL,造成困扰;
"chrome_url_overrides":
{
"newtab": "newtab.html", // 新标签页
"history": "history.html", // 历史记录页
"bookmarks": "bookmarks.html" // 书签页
}
5.devtools(开发者工具)
是的,Chrome允许插件在开发者工具(devtools)上动手脚,主要表现在:
- 自定义一个和多个和
Elements、Console、Sources等同级别的面板;
- 自定义侧边栏(sidebar),目前只能自定义
Elements面板的侧边栏;
其实每打开一个开发者工具窗口,都会创建devtools页面的实例,F12窗口关闭,页面也随着关闭,所以devtools页面的生命周期和devtools窗口是一致的。devtools页面可以访问一组特有的DevTools API以及有限的扩展API,这组特有的DevTools API只有devtools页面才可以访问,background都无权访问,这些API包括:
chrome.devtools.panels:面板相关;
chrome.devtools.inspectedWindow:获取被审查窗口的有关信息;
chrome.devtools.network:获取有关网络请求的信息;
大部分扩展API都无法直接被DevTools页面调用,但它可以像content-script一样直接调用chrome.extension和chrome.runtimeAPI,同时它也可以像content-script一样使用Message交互的方式与background页面进行通信。
尝试创建一个devtools扩展:
首先,要针对开发者工具开发插件,需要在清单文件声明如下:
{
// 只能指向一个HTML文件,不能是JS文件
"devtools_page": "devtools.html"
}
这个devtools.html里面一般什么都没有,就引入一个js:
<!DOCTYPE html>
<html>
<head></head>
<body>
<script type="text/javascript" src="js/devtools.js"></script>
</body>
</html>
再来看看这个js,感觉是不是有点多此一举。
// 创建自定义面板,同一个插件可以创建多个自定义面板
// 几个参数依次为:panel标题、图标(其实设置了也没地方显示)、要加载的页面、加载成功后的回调
chrome.devtools.panels.create('MyPanel', 'img/icon.png', 'mypanel.html', function(panel)
{
console.log('自定义面板创建成功!'); // 注意这个log一般看不到
});
// 创建自定义侧边栏
chrome.devtools.panels.elements.createSidebarPane("Images", function(sidebar)
{
// sidebar.setPage('../sidebar.html'); // 指定加载某个页面
sidebar.setExpression('document.querySelectorAll("img")', 'All Images');
// 通过表达式来指定
//sidebar.setObject({aaa: 111, bbb: 'Hello World!'}); // 直接设置显示某个对象
});
6. 其他补充
虽然在background和popup无法直接访问页面dom元素,但是可以通过chrome.tabs.executeScript 来执行脚本。
示例
manifest.json配置:
{
"name": "动态JS注入演示",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
}
// 动态执行JS代码
chrome.tabs.executeScript(tabId, {code: 'document.body.style.backgroundColor="red"'});
// 动态执行JS文件
chrome.tabs.executeScript(tabId, {file: 'example.js'});
6.1 . 查看已安装插件路径
在地址栏输入chrome://version 找到chrome安装路径:
然后打开Extensions文件夹
每一个插件被放在以插件ID为名的文件夹里面,想要学习某个插件的某个功能是如何实现的,看人家的源码是最好的方法了。
总结
- Chrome插件本身有机制控制,不会无限制的开放很多权限给你。
- 页面的DOM元素时可以操作的,Chrome插件是和宿主页面之间是共享DOM对象,但是不共享Javascript上下文容器,也就是说在插件中你不能也无权执行宿主页面中的JS脚本。这个和跨域限制同理。
- 既然可以操作DOM元素,那么可以通过插入
- 基于第3点,不要随意的使用别人提供的插件,尽量从官方市场下载经过安全审核的。除非你确认这个插件是可信的或仅限于内部使用的。
- 除了操作dom和js,再补充点吧,cookie也是能够被插件所操作的(有很多cookie工具插件),而且估计最大的危险就是cookie被操作了。
相关资料参考
推荐查看官方文档,虽然是英文,但是全且新,国内的中文资料都比较旧:
招贤纳士
小影前端团队,是一个年轻、活动且富有创造力的团队,隶属于小影科技开发中心,Base 在风景如画的杭州。团队在日常的业务对接之外,还在互动技术、图像渲染、跨端技术、工程化平台、性能体验、数据分析及可视化等方向进行技术探索和实战,推动并落地了一系列的内部技术产品,并一直保持好奇,持续探索前端技术。 如果你想大胆自信的表达你的想法;如果你想有个机会实现自己的想法;如果你希望落地的想法有一个团队来支撑;如果你愿意跟一群积极向上的小伙伴干一些持续迭代自己,持续提升技术能力的事;如果你相信相信的力量;那就加入我们吧!快戳链接>>>quvideo.jobs.feishu.cn/s/eV8jfyf