1. 简介
Chrome插件是一个用Web技术开发、用来增强浏览器功能的软件,Chrome浏览器扩展开发算是相当简单的,基本只要掌握HTML+CSS+Javascript,即可快速开发一个属于你的Chrome插件!它其实就是一个由HTML、CSS、JS、图片等资源组成的一个.crx后缀的压缩包.
2. 学习Chrome插件开发有什么意义?
增强浏览器功能,轻松实现属于自己的“定制版”浏览器,等等。
Chrome插件提供了很多实用API供我们使用,包括但不限于:
- 书签控制;
- 下载控制;
- 窗口控制;
- 标签控制;
- 网络请求控制,
- 各类事件监听;
- 自定义原生菜单;
- 完善的通信机制;
等等;
4. 开发与调试
Chrome插件没有严格的项目结构要求,只要保证本目录有一个manifest.json即可,普通的web开发工具即可。
从右上角菜单->更多工具->扩展程序可以进入 插件管理页面,也可以直接在地址栏输入 chrome://extensions 访问。
5. 核心介绍
manifest.json是一个Chrome插件最重要也是必不可少的文件,用来配置所有和插件相关的配置,必须放在根目录。
下面给出的是一些常见的配置项,均有中文注释,完整的配置文档请戳 这里 。
{
//必选
/*
指定您的应用包要求的清单文件格式的版本。从 Chrome 18 开始,开发人员应该指定 2
*/
"manifest_version": 2,
"name":"我的应用名称",
"version":"我的应用版本",
//推荐
/*
清单文件-默认语言 指定_locales中的子目录,包含该应用默认字符串。
对于含有 _locales 目录的应用来说这一属性是必需的,
在没有 _locales 目录的应用中该属性不能存在
*/
"default_locale":"en",
/*
这个描述在安装应用之后可以看见
*/
"description":"关于应用的描述",
/*一个或多个代表应用、应用或主题背景的图标*/
"icons":{
"16":"icon16.png",
"48":"icon48.png"
},
/*
选择某一个(或者无)
browser_action(浏览器按钮)
page_action(页面按钮)
*/
// 如果有 browser_action, 即在 chrome toolbar 的右边添加了一个 icon
"browser_action": {
"default_icon": "advicedog.jpg",
"default_title": "Google Mail", // tooltip, 光标停留在 icon 上时显示
"default_popup": "popup.html" // 如果有 popup 的页面, 则用户点击图标就会渲染此 HTML 页面
},
// 如果并不是对每个网站页面都需要使用插件, 可以使用 page_action(页面按钮) 而不是 browser_action(浏览器按钮)
// browser_action 应用更加广泛
// 如果 page_action 并不应用在当前页面, 会显示灰色
"page_action":{
"default_icon": { // 可选
"19": "images/icon19.png", // 可选
"38": "images/icon38.png" // 可选
},
"default_title": "Google Mail", // 可选,在工具提示中显示
"default_popup": "popup.html" // 可选
},
//可选
"author":"开发者",
"automation":"",
/*
后台网页
1.应用通常需要有一个长时间运行的脚本来管理一些任务或状态,而后台网页就是为这一目的而设立。
通常情况下,后台页面不需要任何 HTML 标记,这种情况下后台页面可以单独使用 JavaScript文件实现。
后台页面将由应用系统生成,包含 scripts 属性中列出的每一个文件。
2.page:如果您需要在您的后台页面中指定 HTML,您可以改用 page 属性:
3.persistent:应用和应用通常需要长时间运行的脚本来管理某些任务或状态,这就是事件页面的作用。
事件页面只在需要时加载,当事件页面不活动时就会卸载,以便释放内存和其他系统资源。
如何得到事件页面 就是设置一个"persistent"键,如果没有设置,你将得到一个普通的后台页面。
*/
"background":{
"scripts":["background.js"],
"page": "background.html",
"persistent":false
},
/*
内容脚本:其实就是向你想要的网页中插入一个脚本代码,执行你想要做的事情
内容脚本是在网页的上下文中运行的 JavaScript 文件,
它们可以通过标准的文档对象模型(DOM)来获取浏览器访问的网页详情,或者作出更改。
1.run_at 可选。
控制 js 中的 JavaScript 文件何时插入,
可以为 "document_start"、
"document_end" 或 "document_idle",默认为 "document_idle"。
1.1如果是 "document_start",这些文件将在 css 中指定的文件之后,但是在所有其他 DOM 构造或脚本运行之前插入。
1.2.如果是 "document_end",文件将在 DOM 完成之后立即插入,但是在加载子资源(如图像与框架)之前插入。
1.3.如果是 "document_idle",浏览器将在 "document_end" 和刚发生 window.onload 事件这两个时刻之间选择合适的时候插入,
具体的插入时间取决于文档的复杂程度以及加载文档所花的时间,并且浏览器会尽可能地为加快页面加载速度而优化。
2.all_frames 可选。
控制内容脚本运行在匹配页面的所有框架中还是仅在顶层框架中。 默认为 false,意味着仅在顶层框架中运行
content_scripts还有一些其他不是很常用的属性
*/
"content_scripts": [{
"matches": ["https://*.pingan.com.cn/*"], //匹配的地址网页
"exclude_matches":[],
"js": ["jquery.js","ideacome.js"], //插入的js
"css": ["mystyles.css"], //css改变样式
"run_at":"document_idle",
"all_frames": true //该匹配下面的所有窗口
},{
"matches": ["*://*/*.png", "*://*/*.jpg", "*://*/*.gif", "*://*/*.bmp"],
"js": ["js/show-image-content-size.js"] //可以针对不同的规则插入不同的内容
}],
// 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的
"web_accessible_resources": [
"images/*.png",
"style/double-rainbow.css",
"script/double-rainbow.js",
"script/main.js",
"templates/*"
],
/**
如果不是通过 chrome web store 自动更新插件
我们希望扩展能自动升级,理由和让chrome自动升级一样:修改程序bug和安全漏洞 ,增加新功能,提升性能,改善体验。
一个扩展的manifest文件里面必须指定一个"update_url"来执行升级检测。
扩展可以托管在Chrome Web Store,也可以发布到极速浏览器应用开放平台上。
如果托管在Chrome Web Store则update_url应该是:http://clients2.google.com/service/update2/crx
**/
"update_url": "https://clients2.google.com/service/update2/crx",
// 插件主页,这个很重要,不要浪费了这个免费广告位
"homepage_url": "https://www.baidu.com",
/*
扩展或app将使用的一组权限。每个权限是一列已知字符串列表中的一个,
如geolocatioin或者一个匹配模式,来指定可以访问的一个或者多个主机。
权限可以帮助限定危险,如果你的扩展或者app被攻击。
一些权限在安装之前,会告知用户
*/
"permissions":[
"tabs", //Required if the extension uses the chrome.tabs or chrome.windows module.
"bookmarks", //使用chrome.bookmarks模块来创建、组织和管理书签
"http://www.blogger.com/",
"http://*.google.com/",
"unlimitedStorage", //提供了一个无限的HTML5配额来存储客户端数据,如数据库和本地存储文件。没有这个权限,扩展仅限于5 MB的本地存储
"history" //历史记录的使用权限 chrome.history
"notifications",//提示
"cookies",//Required if the extension uses the chrome.cookies module.
],
/**开发时为扩展指定的唯一标识值。
注意:通常您并不需要直接使用这个值,而是在您的代码中使用相对路径或者chrome.extension.getURL()得到的绝对路径。
这个值并不是开发时显式指定的,而是Chrome在安装.crx时辅助生成的。(开发时可以通过上传扩展或者手工打包生成crx文件)。 安装完crx,在Chrome的用户数据目录下的Default/Extensions///manifest.json文件中,您可以看到这个扩展的key。**/
key:'',
"commands": {
// commands API 用来添加快捷键
// 需要在 background page 上添加监听器绑定 handler
"toggle-feature-foo": {
"suggested_key": {
"default": "Ctrl+Shift+Y",
"mac": "Command+Shift+Y"
},
"description": "Toggle feature foo",
"global": true
// 当 chrome 没有 focus 时也可以生效的快捷键
// 仅限 Ctrl+Shift+[0..9]
},
"_execute_browser_action": {
"suggested_key": {
"windows": "Ctrl+Shift+Y",
"mac": "Command+Shift+Y",
"chromeos": "Ctrl+Shift+U",
"linux": "Ctrl+Shift+J"
}
},
"_execute_page_action": {
"suggested_key": {
"default": "Ctrl+Shift+E",
"windows": "Alt+Shift+P",
"mac": "Alt+Shift+P"
}
},
...
},
"content_capabilities": ...,
"optional_permissions": ["tabs"], // 其他需要的 permission, 在使用 chrome.permissions API 时用到, 并非安装插件时需要
"short_name": "Short Name", // 插件名字简写
"storage": {
"managed_schema": "schema.json"
}, // 使用 storage.managed api 的话, 需要一个 schema 文件指定存储字段类型等, 类似定义数据库表的 column
......
//还有很多其他的配置
}
5.1 content-scripts
所谓content-scripts,其实就是Chrome插件中向页面注入脚本的一种形式(虽然名为script,其实还可以包括css的),借助content-scripts我们可以实现通过配置的方式轻松向指定页面注入JS和CSS,最常见的比如:广告屏蔽、页面CSS定制,等等。
content-scripts和原始页面共享DOM,但是不共享JS,如要访问页面JS(例如某个JS变量),只能通过injected js来实现。content-scripts不能访问绝大部分chrome.xxx.api,除了下面这4种:
- chrome.extension(getURL , inIncognitoContext , lastError , onRequest , sendRequest)
- chrome.i18n
- chrome.runtime(connect , getManifest , getURL , id , onConnect , onMessage , sendMessage)
- chrome.storage
这些API绝大部分时候都够用了,非要调用其它API的话,你还可以通过通信来实现让background来帮你调用。
5.2 background
后台y页面,这是一个常驻的页面,它的生命周期是插件中所有类型页面中最长的,它随着浏览器的打开而打开,随着浏览器的关闭而关闭,所以通常把需要一直运行的、启动就运行的、全局的代码放在background里面。
background的权限非常高,几乎可以调用所有的Chrome扩展API(除了devtools),而且它可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方设置CORS。
经过测试,其实不止是background,所有的直接通过chrome-extension://id/xx.html这种方式打开的网页都可以无限制跨域。
配置中,background可以通过page指定一张网页,也可以通过scripts直接指定一个JS,Chrome会自动为这个JS生成一个默认的网页:
{
// 会一直常驻的后台JS或后台页面
"background":
{
// 2种指定方式,如果指定JS,那么会自动生成一个背景页
"page": "background.html"
//"scripts": ["js/background.js"]
},
}
#######5.3 event-pages
这里顺带介绍一下event-pages,它是一个什么东西呢?鉴于background生命周期太长,长时间挂载后台可能会影响性能,所以Google又弄一个event-pages,在配置文件上,它与background的唯一区别就是多了一个persistent参数:
{
"background":
{
"scripts": ["event-page.js"],
"persistent": false
},
}
它的生命周期是:在被需要时加载,在空闲时被关闭,什么叫被需要时呢?比如第一次安装、插件更新、有content-script向它发送消息,等等。
5.4 popup
popup是点击browser_action或者page_action图标时打开的一个小窗口网页,焦点离开网页就立即关闭,一般用来做一些临时性的交互。
popup可以包含任意你想要的HTML内容,并且会自适应大小。可以通过default_popup字段来指定popup页面,也可以调用setPopup()方法。
需要特别注意的是,由于单击图标打开popup,焦点离开又立即关闭,所以popup页面的生命周期一般很短,需要长时间运行的代码千万不要写在popup里面。
在权限上,它和background非常类似,它们之间最大的不同是生命周期的不同,popup中可以直接通过chrome.extension.getBackgroundPage()获取background的window对象。
5.5 homepage_url
开发者或者插件主页设置
5.6 injected-script
指的是通过DOM操作的方式向页面注入的一种JS。为什么需要通过这种方式注入JS呢?
这是因为content-script有一个很大的“缺陷”,也就是无法访问页面中的JS,虽然它可以操作DOM,但是DOM却不能调用它,也就是无法在DOM中通过绑定事件的方式调用content-script中的代码(包括直接写onclick和addEventListener2种方式都不行),但是,“在页面上添加一个按钮并调用插件的扩展API”是一个很常见的需求,那该怎么办呢?其实这就是本小节要讲的。
在content-script中通过DOM方式向页面注入inject-script代码示例:
// 向页面注入JS
function injectCustomJs(jsPath)
{
jsPath = jsPath || 'js/inject.js';
var temp = document.createElement('script');
temp.setAttribute('type', 'text/javascript');
// 获得的地址类似:chrome-extension://ihcokhadfjfchaeagdoclpnjdiokfakg/js/inject.js
temp.src = chrome.extension.getURL(jsPath);
temp.onload = function()
{
// 放在页面不好看,执行完后移除掉
this.parentNode.removeChild(this);
};
document.head.appendChild(temp);
}
manifest.json
{
// 普通页面能够直接访问的插件资源列表,如果不设置是无法直接访问的
"web_accessible_resources": ["js/inject.js"],
}
6. Chrome插件的7种展示形式
6.1 browserAction(浏览器右上角)
通过配置browser_action可以在浏览器的右上角增加一个图标,一个browser_action可以拥有一个图标,一个tooltip(即划过显示title),一个badge(图标上面的文字,有字数限制)和一个popup。
6.2 pageAction(地址栏右侧)
pageAction和普通的browserAction一样也是放在浏览器右上角,也可以说地址栏的右侧更为准确。只不过没有点亮时是灰色的,点亮了才是彩色的,灰色时无论左键还是右键单击都是弹出选项:
// manifest.json
{
"name": "测试",
"description": "........",
"version": "1.0",
"permissions": [
"declarativeContent"
],
"background":{
"scripts":["background.js"]
},
"page_action": {
"default_icon": "icon.png",
"default_title": "我是pageAction",
"default_popup": "popup.html"
},
"manifest_version": 2
}
// 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()]
}
]);
});
});
6.3 右键菜单(api地址)
通过开发Chrome插件可以自定义浏览器的右键菜单,主要是通过chrome.contextMenusAPI实现,右键菜单可以出现在不同的上下文,比如普通页面、选中的文字、图片、链接,等等
//manifest.json
"permissions": [
"declarativeContent",
"contextMenus",
"tabs"
],
"background":{
"scripts":["background.js"]
},
//background.js
//可以右键看看
chrome.contextMenus.create({
title: "测试右键菜单",
onclick: function(){alert('您点击了右键菜单!');}
});
//选择某些文字才出现这个右键菜单
chrome.contextMenus.create({
title: '使用度娘搜索:%s', // %s表示选中的文字
contexts: ['selection'], // 只有当选中文字时才会出现此右键菜单
onclick: function(params)
{
// 注意不能使用location.href,因为location是属于background的window对象
chrome.tabs.create({url: 'https://www.baidu.com/s?ie=utf-8&wd=' + encodeURI(params.selectionText)});
}
});
6.3.2 右键菜单一些基本的api
hrome.contextMenus.create({
type: 'normal', // 类型,可选:["normal", "checkbox", "radio", "separator"],默认 normal
title: '菜单的名字', // 显示的文字,除非为“separator”类型否则此参数必需,如果类型为“selection”,可以使用%s显示选定的文本
contexts: ['page'], // 上下文环境,可选:["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"],默认page
onclick: function(){}, // 单击时触发的方法
parentId: 1, // 右键菜单项的父菜单项ID。指定父菜单项将会使此菜单项成为父菜单项的子菜单
documentUrlPatterns: 'https://*.baidu.com/*' // 只在某些页面显示此右键菜单
});
// 删除某一个菜单项
chrome.contextMenus.remove(menuItemId);
// 删除所有自定义右键菜单
chrome.contextMenus.removeAll();
// 更新某一个菜单项
chrome.contextMenus.update(menuItemId, updateProperties);
6.4. override(覆盖特定页面)
使用override页可以将Chrome默认的一些特定页面替换掉,改为使用扩展提供的页面。
//一个扩展只能替代一个页面;
"chrome_url_overrides":
{
"newtab": "newtab.html"
"history": "history.html",
"bookmarks": "bookmarks.html"
}
6.5 option(选项页)
为了让用户自定义您的应用的行为,您可能会提供一个选项页面。
所谓选项(options)页,就是插件的设置页面,有2个入口,一个是右键图标有一个“选项”菜单,还有一个在插件管理页面:
//manifest.json
//Chrome40以前的插件配置页写法
"options_page":"xx.html",
//Chrome40以后的插件配置页写法,只是样式不一样了,以弹出框的形式显示
"options_ui":
{
"page": "options.html",
// 添加一些默认的样式,推荐使用
"chrome_style": true
},
//为了兼容,建议2种都写,如果都写了,Chrome40以后会默认读取新版的方式;
页面内容看你自己发挥了。这边有一个示例可以看看
6.6 omnibox
omnibox是向用户提供搜索建议的一种方式。具体看api
6.7 桌面通知
Chrome提供了一个chrome.notificationsAPI以便插件推送桌面通知,暂未找到chrome.notifications和HTML5自带的Notification的显著区别及优势。
在后台JS中,无论是使用chrome.notifications还是Notification都不需要申请权限(HTML5方式需要申请权限),直接使用即可。
"permissions": [
"notifications"
],
chrome.contextMenus.create({
title: "测试右键菜单",
onclick: function(){
chrome.notifications.create(null, {
type: 'basic',
iconUrl: 'icon.png',
title: '这是标题',
message: '您刚才点击了自定义右键菜单!'
});
}
});
实战1:(platform项目险企报价)
为了实现险企账号的管理,使得出单人员在各个险企报价时,不需要自己记录管理险企的登录地址,以后手动输入账号密码。
或者在报价页面插入一些已知的报价填写信息。
文件目录
-
manifest.json
清单文件
具体配置:
-
popup.html
:根据上面的描述我们知道,这是点击浏览器按钮会弹出显示。 -
icon.png
:browser_action里面配置的icon显示图标 -
ideacome.js
: 除了本脚本之外,其他的脚本都是依赖。
在这个脚本里面我们可以访问其插入的页面的dom元素,cookie,localStorage,发送ajax请求等等操作。来完成我们想要的效果。
完成上面的工作,我们如何使自己的代码转换成一个可在ChromeL浏览器里面安装扩展程序呢?
接下来我们需要对我们编写的文件进行打包。应用打包为已签名的 ZIP 文件,文件扩展名为“crx”,如:myextension.crx。
当您为应用打包时,应用将获得唯一的密钥对,应用的标识符基于公钥的散列,私有密钥用来为每一个版本的应用签名,必须严格保护,不能由公众访问。注意千万不要将您的私有密钥包含在应用中!
打包步骤:
- 进入以下URL,打开应用管理页面:
chrome://extensions - 确保右上角的开发者模式复选框已选中。
- 单击打包应用按钮,出现一个对话框。
- 在应用根目录字段中,指定应用所在文件夹的路径,例如,C:\myext。(忽略其他字段,您第一次为一个应用打包时不需要指定私有密钥文件。)
- 单击打包应用。打包程序将创建两个文件:一个 .crx 文件,是实际的可安装的应用;另一个是 .pem 文件,包含私有密钥。
安装图片:
不要丢失私有密钥!确保 .pem 文件保密,并存放在安全的地方。如果您今后需要做如下事情,您需要这一文件:
- 更新应用: 更新应用与安装步骤一样。不同的是此时密钥已经存在。
学习参考文件
https://chajian.baidu.com/developer/extensions/api_index.html
http://open.chrome.360.cn/extension_dev/samples.html#a1f7cf79dd555b04fa8d603247a040e644996293
https://developer.chrome.com/extensions/runtime