前置知识
Chrome继承了Webkit内核对HTML的高速渲染,同时Google自行研发的V8引擎使得JS在Chrome中的执行效率大幅提升;其Chrome浏览器除了页面渲染速度快,JS执行速度快之外,还有另一个特点就是支持开发者额为其编写各种各样的拓展来扩充其功能;Chrome拓展主要用于对浏览器功能的增强,其更强调与浏览器的结合;
- Google将基于Chrome平台的程序分为两类
- Chrome拓展
- 主要用于对浏览器功能的增强
- 拓展的界面只能限定于浏览器窗口中
- 是一系列文件的集合,这些文件包括HTML、CSS、JS、图片、manifest.json文件等,个别还可以包括其他二进制文件
- Chrome应用
- 更强调的是独立的程序,可以在不打开浏览器的情况下运行这些程序
- Chrome应用可以拥有样式更加自由的独立窗口
- Chrome拓展
Chrome拓展程序
可以理解成:写一个Web应用,然后按照Chrome的额规定将一个快捷方式放在Chrome工具栏上
Chrome插件又称为拓展程序,主要基于前端技术开发(其实是由一个HTML、JS、图片等资源组成的一个
.rtx
后缀的压缩包),用于拓展浏览器功能的额一类软件程序,可以加载在所有以webkit
为内核(浏览器内核
也就是浏览器所采用的渲染引擎
,渲染
引擎决定了浏览器如何显示网页的内容
以及页面的格式信息
)的浏览器中,如Chrome、360浏览器、搜狗浏览器、QQ浏览器等;
拓展程序除了支持基本的前端
WebAPI
和JS-API
外,还拥有一些其他的功能,如浏览器窗口、tab标签页(chrome.tabs)、书签(chrome.bookmarks)、右键菜单、开发者工具、历史记录(chrome.history)、下载请求等操作处理;
Chrome应用商店中的插件分为如下几类,包括应用、游戏、拓展程序、社交通讯、购物、主体背景等
- Chrome拓展程序又两种
- 弹出窗口(popup弹出窗口):点击按钮后有弹出窗口页面,一般用于插件和用户的交互操作
- popup属于Browser Action,当点击浏览器窗口中对应的图标时出现这个窗口,可以进行定制化,这个弹出框是不会被Chrome拦截的
- 后台程序(Background Pages):常驻在后台运行的脚本程序,随浏览器的关闭而关闭,一般用于插件本身做的一些额外的事情
- 不会显示在界面中,是拓展程序的后台服务,会一直保持运行;比如在一下需要数据保存在程序中,当用户关闭popup也需要保存时,就需要通过后台程序进行相应的操作
- 弹出窗口(popup弹出窗口):点击按钮后有弹出窗口页面,一般用于插件和用户的交互操作
- Chrome插件的用户界面
- 浏览器按钮(Browser Action)
- 常驻于浏览器右侧,可以设置「badge」信息
- 页面地址栏按钮(Page Action)
- 不是在每个页面上都有用的,必须在特定的页面中插件才可以使用
- 旧版的浏览器是在地址栏右侧显示或隐藏的,而新版的都统一到浏览器右上角,展示也变成灰置或高亮
- 浏览器按钮(Browser Action)
插件竞品调研浅析
Chrome应用👍🏻浅析
通过构建Chrome应用程序,而不是使用传统的额网络应用程序或本机移动应用程序,可以扩大潜在受众并拓展您的开发能力;
Chrome应用程序的加载方式与网络应用程序不同,两者都加载相同类型的内容:带有CSS和JS的HTML文档;但是Chrome应用程序会加载到应用程序容器中,而不是浏览器的选项卡中,此外应用程序容器必须从本地源加载Chrome应用程序的主文档;其生命周期应该独立于浏览器窗口行为或网络连接
相关能力
- Chrome应用程序可以无缝集成到桌面中,并且看起来比传统的网络应用程序更像桌面应用程序
- Chrome桌面应用程序没有像普通基于浏览器的应用程序那样的多功能框(地址栏)和选项卡条,因为与本机桌面应用程序一样,他们并不存在于浏览器中
- Chrome应用程序启动器使得用户可以轻松找到并启动您的Chrome应用程序
- Chrome桌面应用程序可以访问主机的文件系统并利用硬件功能(USB、蓝牙和连接的人机界面设备)
Chrome插件核心深入
Chrome拓展与浏览器结合的更加紧密,更加强调拓展浏览器的功能,而应用有更加严格的权限限制,无法像拓展一样用户在浏览器中的相关行为,因此应用更强调的是一个独立的与Chrome浏览器关联不大的程序;
主要配置API解析
Chrome插件开发通信
Chrome插件可以通过消息机制来和其他插件或浏览器网页之间进行通信,消息机制一般包括一个监听器和一个发送器;
-
监听器:通过
chrome.extension.onMessage.addListener()
方法设置消息的相应函数来监听其他页面或插件的消息jschrome.extension.onMessage.addListener((request,sender,sendResponse)=>{ console.log(request) })
-
发送器:通过
chrome.extension.sendMessage()
方法发送消息到指定的页面或插件,可以传递json对象jschrome.extension.sendMessage({ action: 'login', param1: 'lbxin', param2: 'lbxin-password' })
manifest.json(必要配置)
是插件的资源和功能配置文件,用于提供插件的基本信息,如版本、图标、标题、入口文件等,其中
manifest_version
、name
、version
是必须的,description
和icons
是推荐的;
调试插件
在Chrome浏览器中打开 chrome://extensions 开启「开发者模式」后,你可以将开发中的插件加载到浏览器中进行测试和调试。在开发过程中,可以使用Chrome插件开发工具包中的调试工具来查找和修复问题。
基本配置解析
-
content_scripts:
定义一系列匹配规则,当URL被匹配时,自动执行JS(哪些脚本何时注入到哪些页面中)- 其实就是Chrome插件向页面注入脚本的一种形式(虽名为script,其实还可以包括CSS等的),借助该API可以实现通过配置的额方式轻松向指定的页面注入JS和CSS(也可以按需注入),常见的功能有广告屏蔽,页面CSS定制等;
content_scripts:
和原始页面共享DOM(自定义属性不会被共享),但是不共享JS,需要访问页面JS(如某个变量),只能通过injected js来实现- 常用配置
matches([Array]):
必须,定义哪些页面需要注入Content scriptcss/js(String):
可选,需要向匹配的页面注入的css/js文件,按定义的顺序依次注入run_at(String):
可以配置对应代码的注入时间,其可选值有:document_end(创建完DOM,没有加载其他如图片、iframe等子资源时注入)
、document_start(CSS加载完毕,DOM没有创建,js没有注入)
、document_idle(页面空闲时:document_end和window.onload事件之间的时机进行注入)-默认
all_frames:
定义脚本是否会注入到嵌入式框架中
json{ // .... "content_scripts": [ { "js": [ "js/companionBubble.js" ], "matches": [ "<all_urls>" ] }, { "js": [ "js/gmail.js" ], "matches": [ "https://mail.google.com/*" ], "css": ["css/custom.css"], "run_at": "document_start" }, { "js": [ "js/linkExpand.js" ], "matches": [ "https://github.com/*", "https://gitlab.com/*", "https://app.intercom.io/*", ] } ], // .... }
-
background
- 是一个常驻页面,其生命周期是插件中所有类型页面中最长的,通常把需要一直运行的、启动就运行的、全局的代码放到background中,background的权限非常高,几乎可以调用所有的Chrome拓展API(除了devtools),且可以无限制跨域;主要用于后台的一些工作,如统计数据,检测网站在线状态等
- 配置中background可以通过page指定一张网页,也可以通过scripts指定一个js,Chrome会自动为这个js生成一个默认的网页,这个界面是看不到的,只能调试它
- 包括三种属性
- scripts:会在拓展启动时自动创建一个包含所有指定脚本的页面
- pages:会将指定的HTML文件作为后台的页面运行
- persistent:定义了常驻后台的方式
- true:表示拓展将一直在后台运行,无论是否在工作
- false:拓展在后台按需运行,即Chrome后来提出的
Event Page
,可以有效的减小拓展对内存的消耗
-
browser_action
- 指定了插件在chrome工具栏中的显示信息
- 指定了点击插件的时候,弹出的页面以及插件的图标
- 可配置项
- default_icon:Object格式,配置图标
- default_title:string格式,配置标题
- default_popup:string格式,配置弹出页
js{ // .... "action": { "default_icon": { "128": "images/icon_128.png", "16": "images/icon_16.png", "32": "images/icon_32.png", "48": "images/icon_64.png" }, "default_title": "weather", "default_popup": "html/popup.html" }, // .... }
Chrome动态更改拓展的图标方式:chrome.browserAction.setIcon(details, callback)
-
permissions权限配置
- activeTab可以配置新标签页打开时的权限,配合chrome_url_overrides的newtab属性可以实现新开页后的默认HTML页面
js"chrome_url_overrides": { "newtab": "main.html" }, "permissions": [ "activeTab", "alarms", "bookmarks", // bookmarks可以用于操作浏览器的书签功能,从而达到拓展浏览器自带的书签功能 "notifications", // notifications用于创建桌面提醒功能的权限开启,桌面提醒的创建依赖于 `webkitNotifications.createNotification`方法,其参数为标题、内容和图片即可,然后通过实例调用对应的show方法即可;[ API Specification](https://www.chromium.org/developers/design-documents/desktop-notifications/api-specification/) "contextMenus", // contextMenus用于设置右键菜单中显示的权限控制,打开该设置后还需要在icons中设置16像素尺寸的图标,这样才可以在右键菜单中显示出拓展的图标 // https://developer.chrome.com/extensions/contextMenus "cookies", "downloads", // downloads接口用于管理浏览器的下载功能,包括暂停、搜索和取消等,当然也是可以通过拓展实现创建下载的功能 "management", // management 用于管理拓展和应用,management接口可以获取用户已安装的拓展和应用信息,同时还可以卸载和禁用他们 // { // id: 扩展id, // name: 扩展名称, // shortName: 扩展短名称, // description: 扩展描述, // version: 扩展版本, // mayDisable: 是否可被用户卸载或禁用, // enabled: 是否已启用, // disabledReason: 扩展被禁用原因, // type: 类型, // appLaunchUrl: 启动url, // homepageUrl: 主页url, // updateUrl: 更新url, // offlineEnabled: 离线是否可用, // optionsUrl: 选项页面url, // icons: [ // { // size: 图片尺寸, // url: 图片URL // } // ], // permissions: 扩展权限, // hostPermissions: 扩展有权限访问的host, // installType: 扩展被安装的方式 // }*/ "desktopCapture", "webRequest", // http://developer.chrome.com/extensions/webRequest "webRequestBlocking", // webRequest可以用于操作网络请求,如更改网络请求,目前主要用于阻断连接,更改header和重定向 // webRequestBlocking用于阻止网络请求 "proxy", // proxy代理设置管理接口,可以通过拓展来做到更加智能的代理设置 //通过chrome.proxy.settings.set方法可以设置代理服务器,该方法需要两个参数,一个是代理设置对象,另一个是回调函数。 "scripting", "storage", "tabCapture", "system.cpu", "system.memory", "system.storage" //通过chrome.system.(cpu/memory/storage).getInfo((info)=>{}) "http://www.sougou/*", // 需要被处理的操作的URL // 如果我们需要向服务器请求数据,就需要在`permissions`中添加请求数据的接口,否则会报跨域请求的限制。但是如果需要向多个接口请求数据,建议直接按我的方式书写匹配规则,这样不管多少接口都适用 ],
js// webRequest阻断所有向bad.example.com的额连接 chrome.webRequest.onBeforeRequest.addListener( function (details) { return { cancel: true }; }, { urls: ["*://bad.example.com/*"], }, ["blocking"] ); // 删除所有连接中的User-Agent chrome.webRequest.onBeforeSendHeaders.addListener( function (details) { for ( var i = 0, headerLen = details.requestHeaders.length; i < headerLen; ++i ) { if (details.requestHeaders[i].name == "User-Agent") { details.requestHeaders.splice(i, 1); break; } } return { requestHeaders: details.requestHeaders }; }, { urls: ["<all_urls>"], }, ["blocking", "requestHeaders"] ); //重定向www.google.com.hk到www.google.com chrome.webRequest.onBeforeRequest.addListener( function (details) { return { redirectUrl: details.url.replace("www.google.com.hk", "www.google.com"), }; }, { urls: ["*://www.google.com.hk/*"], }, ["blocking"] );
js// chrome.proxy设置代理配置 // pacScript指定了使用的pac脚本,可以通过url属性指定脚本位置,也可以直接通过data属性指定脚本内容。 var config = { mode: "fixed_servers", //'direct'(直接连接,即不通过代理)、'auto_detect'(通过WPAD协议自动获取pac脚本)、'pac_script'(使用指定的pac脚本)、'fixed_servers'(固定的代理服务器)和'system'(使用系统的设置) rules: { //rules指定了不同的协议通过不同的代理 proxyForHttp: { scheme: "socks5", host: "1.2.3.4", port: 1080, }, proxyForHttps: { scheme: "socks5", host: "1.2.3.5", port: 1080, }, proxyForFtp: { scheme: "http", host: "1.2.3.6", port: 80, }, bypassList: ["foobar.com"], //不使用任何代理服务器的URL // singleProxy属性(任何协议都使用此代理)和fallbackProxy属性(未匹配到的协议使用此代理) }, }; chrome.proxy.settings.set({ value: config }, function () {});
-
页面、插件间的通信
- 有时候需要让拓展中的多个页面之间,或者不同拓展的额多个页面之间相互传递数据,以获取彼此的状态;
- Chrome提供了4个拓展页面间的相互通信的接口
-
runtime.sendMessage
语法:chrome.runtime.sendMessage(extensionId, message, options, callback)
extensionId:
发送消息的目标拓展,默认为发起此消息的拓展本身message:
要发送的内容,类型不限options:
🔗;对象类型。包含一个值为布尔型的includeTlsChannelId属性,此属性的值决定扩展发起此消息时是否要将TLS通道ID发送给监听此消息的外部扩展callback:
回调函数,用于接收返回的结果
-
runtime.onMessage
语法:chrome.runtime.onMessage.addListener(callback)
jschrome.runtime.onMessage.addListener(function (message, sender, sendResponse) { if (message == "Hello") { sendResponse("Hello from background."); } });
-
runtime.connect
-
runtime.onConnect
-
-
theme主体
- 主体是一种特殊的拓展,可以用来改变整个浏览器的外观;
- 主体中不能包含JS或HTML代码
-
拓展的标题和badge
-
标题设置方式一:
chrome.browserAction.setTitle({title: 'This is a new title'});
-
标题设置方式二:通过
browser_action
的default_title:""
-
badge的设置方式:
jschrome.browserAction.setBadgeBackgroundColor({color: '#0000FF'}); chrome.browserAction.setBadgeText({text: 'Dog'});
-
-
其他
- devtools拓展开发
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!'}); // 直接设置显示某个对象 } );
推荐文献
- 《Chrome插件开发全攻略》
- 《Chrome插件开发指南》
- 《深入理解Chrome插件开发》
Chrome浏览器插件(扩展)开发全攻略👍🏻👍🏻
chrome.extension