浏览器插件的开发

183 阅读5分钟

什么是浏览器插件?

我们一般以chrome扩展为例子。它是一个可以用web技术开发并增强浏览器功能的软件,实质上是由html、css、js组成的一个.crx后缀的压缩包。

一、重要组成部分

Content Script: 插件植入网页运行的js脚本

Background.js: 插件后台运行的js脚本,有一些浏览器提供的api

Manifest.json: 相当于一个插件的入口或者配置等。包含了插件的名称、描述、版本、权限等等。

Background:

可以理解为一个常驻页面,生命周期是随着浏览器的打开而开启,浏览器的关闭即关闭。一般把需要一直运行的、启动就运行的或者全局的代码放在这里面。

Background的权限很高,可以调用所有的chrome扩展api(除devtools),并且可以无限制跨域,也就是可以跨域访问任何网站而无需要求对方这只cors。其实所有通过chrome-extension://id/xx.html 方式打开的网页都可以无限制跨域。

event-pages
由于background生命周期太长,长时间挂载后台可能会影响性能,与background的区别就是多了一个persistent参数,他的生命周其是在被需要的时候加载,空闲时关闭,比如第一次安装、插件更新、接收到content-script的信息时。

popup
popup是点击browser_action或者page_action图标时打开的一个小窗口网页,焦点离开网页就立即关闭,一般用来做一些临时性的交互。在权限上,它和background非常类似,它们之间最大的不同是生命周期的不同,popup中可以直接通过chrome.extension.getBackgroundPage()获取background的window对象。

manifest

是一个JSON格式的manifest文件,命名为manifest.json

{

// 必须的字段

"name": "my extension",

"version": ".....",

"manifest_version": 2,

// 建议提供的字段

"description": "balabala",

"icons": { //图标要求是png格式;

"16": "icon16.png",

"48": "icon48.png",

"128": "icon128.png"

}, ,

"default_locale": "en", //如果扩展有_locales目录,这个字段是必须的。如果没有_locales目录,这个字段是必须不存在的。

// 多选一,或者都不提供

"browser_action": {...},

"page_action": {...},

"theme": {...},

"app": {...}, //用户点击app的图标后导航到的地方

// 根据需要提供

"background": {...},

"chrome_url_overrides": {...},

"content_scripts": [

{

"matches": ["www.google.com/*"],

"css": ["mystyles.css"],

"js": ["jquery.js", "myscript.js"]

}

],

"content_security_policy": "policyString",

"file_browser_handlers": [...],

"homepage_url": "http://path/to/homepage", //扩展的主页url

"incognito": "spanning" or "split", //指定当扩展在允许隐身模式下运行时如何响应

"intents": {...} //一个字典,用于描述扩展或app所提供的全部intent handler。字典里的每个键指定了一个action verb

"key": "publicKey", //是浏览器辅助生成的,不需要指定的

"minimum_chrome_version": "versionString", //需要的chrome的最小版本

"nacl_modules": [...],

"offline_enabled": true, //指定本扩展或app是否支持脱机运行

"omnibox": { "keyword": "aString" },

"options_page": "aFile.html",

"permissions": [

match_pattern: "http://*.baidu.com",

"tabs",

"bookmarks",

""unlimitedStorage",

"backaground"

], //表示扩展可以使用的权限

"plugins": [...],

"requirements": {...}, //指定本app或扩展所需的特殊技术功能,目前只支持指定“3D”

"update_url": "http://path/to/updateInfo.xml",

"web_accessible_resources": [...]

}

content_scripts 详细介绍

Content scripts是在Web页面内运行的javascript脚本。通过使用标准的DOM,它们可以获取浏览器所访问页面的详细信息,并可以修改这些信息。一个扩展可以向一个页面注入多个content_script脚本;每个content script脚本可以包括多个javascript脚本和css文件。

apis

matches: string[]--定义哪些页面需要注入脚本。

css: string[]-- 样式文件;在页面的dom树创建显示之前按照定义的顺序依次注入;

js:按定义顺序注入;

run_at: 控制脚本注入时机:》document_start, document_end, document_idle; 如果是start, 文件在所有css加载完毕后但是没有创建dom并且没有运行任何脚本时候注入;

include_blobs

all_frames: boolean -- 控制是在匹配页面的所有frame中运行还是只是在最上层frame

exclude_blobs

页面url匹配 matches 模式中任意一项以及 include_globs 中任一项,并且不匹配任何 exclude_matches 或 exclude_globs 模式。

编程式注入:意思是说在用户规定的时机触发脚本,比如点击事件的时候。如果要将代码注入页面,扩展必须具有cross-origin 权限,还必须可以使用chrome.tabs模块。可以通过在manifest文件的permissions字段里声明来取得这些权限。一旦设置好了权限,就可以通过调用executeScript()来注入javascript脚本。如果要注入css,可以调用insertCSS()

example

/* in background.html */
chrome.browserAction.onClicked.addListener(function(tab) {
  chrome.tabs.executeScript(null,{code:"document.body.bgColor='red'"});
});
/* in manifest.json */
"permissions": [
  "tabs", "http://*/*"
],
运行环境

是一个隔离的环境,即它不会访问页面js的变量和函数,可以访问页面的dom。

引用扩展里的文件

通过chrome.extension.getURL()来获取扩展里文件的URL。可以像使用其它url一样使用这些URL

二、通信机制

三、Api部分

action(v3):一般在工具栏控制插件的图标;

alarms:使用chrome.alarms API计划代码定期运行或在将来的指定时间运行

bookmarks: 创建、管理、操作书签相关

browserAction(v2): v2版本的图标相关控制,可以配置popup文件

commands: 可以增加键盘快捷键触发插件的动作;

contextMenus:设置浏览器的右键菜单;可以选择类型例如图片、超链接、页面

cookies: 可以从操作cookies

desktopCapture:捕捉屏幕、窗口或页签

documentScan:从附件中扫描图像;

downloads: 操作管理下载;

events: chrome.events命名空间包含API调度事件所使用的常见类型,以便在发生感兴趣的事情时通知您。

extension: 包括支持在扩展及其内容脚本之间或扩展之间交换消息

history: 交互浏览器的历史记录

nitifications: 通知

pageCapture: 存储页签作为Mhtml

sessions: 查询储存页签和窗口

storage: 可以储存,删除、追踪用户数据改变

tabs: 交互浏览器的页签,可以打开、修改、排序

通讯方法:

content与background

  chrome.runtime.sendMessage({
    type: "open_set_page",
  });
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
  const { type } = request;
  console.log(request);
  if (type === "open_set_page") {
    chrome.runtime.openOptionsPage();
  }
});

background与popup

function fn() {
  return ''
}
var bg = chrome.extension.getBackgroundPage();
const text = bg.fn();

content和popup之间:

content中
chrome.runtime.sendMessage({ info: "我是 content.js" }, res => { // 答复alert(res) })

popup中
chrome.runtime.onMessage.addListener((req,sender, sendResponse) => { sendResponse('我收到了你的来信') console.log('接收了来自 content.js的消息', req.info) })