Chrome 扩展crx开发

Chrome扩展提供的入口

  • 左键 crx,popup
  • 右键 crx,homelink + option
  • 右键上下文菜单

Chrome扩展的文件结构

Chrome扩展就是一个文件夹下包括一堆符合规范的文件。首先是清单文件manifest.json,指定了该扩展的整体布局和结构。实例:

    {
// 清单文件的版本,这个必须写,而且必须是2
"manifest_version": 2,
// 插件的名称
"name": "leocrx_demo",
// 插件的版本
"version": "0.1",
// 插件描述
"description": "简单的Chrome扩展demo",
// 图标,一般偷懒全部用一个尺寸的也没问题
"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": "this is browser_action.default_title",
    "default_popup": "popup.html",
    "badge":"这是badge"
},
// 当某些特定页面打开才显示的图标
/*"page_action":
{
    "default_icon": "img/icon.png",
    "default_title": "我是pageAction",
    "default_popup": "page_action_popup.html"
},*/
// 需要直接注入页面的JS
"content_scripts": 
[
    {
        //"matches": ["http://*/*", "https://*/*"],
        // "<all_urls>" 表示匹配所有地址
        "matches": ["<all_urls>"],
        // 多个JS按顺序注入
        "js": ["js/hello_content.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":
[
"nativeMessaging",
    "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?homepage_url",
// 覆盖浏览器默认页面
//"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"

}

content js

manifest.json中的content script可以配置run_at为document_start表示DOM加载之前执行,或者document_end表示页面DOM加载结束后执行,或者document_idle表示页面空闲时加载。content script可以理解为向DOM注入js或者css的入口准备。

popup js是指在popup页面中加载执行的js,逻辑上这个可以看做chrome://crx-id/这个server context下面的一个页面,这个页面chrome://crx-id/popup.html提供独立的操作空间。

background js

在manifest.json中配置background,提供了chrome://crx-id/下面的另一个页面chrome://crx-id/background.html,这个页面是不显示的,只是在后台运行,这是和popup.html页面的唯一区别。我理解chrome提供这两种页面给开发者选择,是因为有的开发者可能不不需要为crx最终客户提供可见的popup页面。从功能上面讲,有了popup.html就可以实现所有的功能。

devtool js

这个主要是修改chrome调试部分的功能。访问chrome api。

下面是总结的表格。

JS种类 可访问的API DOM访问情况 JS访问情况 直接跨域 常见用法
injected script 和普通JS无任何差别,不能访问任何扩展API 可以访问 可以访问 不可以 作为DOM的一部分,操作DOM
content script 只能访问 extension、runtime等部分API 可以访问 不可以 不可以 作为inject js的入口操作
popup js 可访问绝大部分API,除了devtools系列 不可直接访问 不可以 可以 操作popup DOM和chrome的API,扩展本身的数据管理
background js 可访问绝大部分API,除了devtools系列 不可直接访问 不可以 可以 操作bg DOM和chrome API,管理CRX本身的数据
devtools js 只能访问 devtools、extension、runtime等部分API 可以 可以 不可以 devtools部分的修改

调试方法打开popup.html或者background.html所代表的页面,右键审查元素

通信

background和popup通信

前文已经说过,这两个页面是一脉相承的,所以chrome为他们之间的代码层面的访问调用提供了简单的方式。

    // popup.js
var bg = chrome.extension.getBackgroundPage();
bg.test(); // 访问bg的函数
alert(bg.document.body.innerHTML); // 访问bg的DOM

//bg.js
var views = chrome.extension.getViews({type:'popup'});
if(views.length > 0) {
    console.log(views[0].location.href);
}

bg和content之间通信

他们的js中收消息都是通过

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse))

发送的方式略有区别

//content js
chrome.runtime.sendMessage(msgjson, function(response) {
});

//bg js
chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
{
    chrome.tabs.sendMessage(tabs[0].id, message, function(response)
    {
        if(callback) callback(response);
    });
});

content js和inject js, 其他

还有其他相关略去,参考https://www.cnblogs.com/liuxianan/p/chrome-plugin-develop.html#%E6%89%93%E5%8C%85%E4%B8%8E%E5%8F%91%E5%B8%83

chrome扩展自身数据存储

通过HTML5的localStorage完成,chrome也有特殊的api,chrome.storage

API总结

  • chrome.tabs
  • chrome.runtime
  • chrome.webRequest
  • chrome.window
  • chrome.storage
  • chrome.contextMenu
  • chrome.devtools
  • chrome.extension

不支持内联js

在popup.html和backgrou.html中不支持嵌入的js代码,甚至定义标签的onclick也不可以,只能在js中显式绑定。

Chrome扩展与本地进程通信

chrome扩展crx提供的能力是:

本地程序向chrome注册可以被那些crx调用和通信,注册的方式是通过配置清单文件customizenamed.json:

{
    "allowed_origins" : [ "chrome-extension://fakmdcpkljckjjmemeninkejdibeiobe/" ],
    "description" : "Leo Test Native fire Native Message Host",
    "name" : "com.leo.test",
    //"path" : "C:\ProgramFiles\Python27\python.exe",
    //"path" : "C:\Users\leo\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\ConsoleApp1.exe",
     "path" : "C:\D\dist\leonative.exe",
     //必须是stdio
    "type" : "stdio"
}

其中的kv含义显而易见,主要是定义了path指定的可执行文件可以被那些crx调用和通信。
chrome如何找到这个json文件并load? chrome约定了两种实现

  • windows平台,在注册表中添加key,HKEY_LOCAL_MACHINESOFTWAREWOW6432NodeGoogleChromeNativeMessagingHosts com.leo.test,value为json的路径
  • POSIX平台,把json文件放置到特定路径/etc/opt/chrome/native-messaging-hosts/下,文件名为com.leo.test.json

这样chrome通过load文件customizenamed.json,可以找到需要启动的本地可执行程序。

与本地进程通信的协议

本地程序需要是专用的程序。
因为chrome启动本地程序的时候,传入的启动args是固定的:

[arg 0]  chrome-extension://fakmdcpkljckjjmemeninkejdibeiobe/
[arg 1] --parent-window=1057900

真正的启动命令可能是yourexecutable chrome-extension://fakmdcpkljckjjmemeninkejdibeiobe/ --parent-window=1057900

启动后,chrome(实际上应该是crx对应的进程)向本地进程yourexecutable的stdin写入消息,从其stdout读取消息,完成crx和本地进程之间的通信。

本地进程通信消息协议

crx与本地通信的消息协议参见 https://developer.chrome.com/apps/nativeMessaging#native-messaging-host-protocol

要点是,消息的头部插入4byte标记消息字节数,意味着一条消息不可能超过4GB,而从native进程发送到crx的消息则规定不超过1MB。
本地进程通过args知道自己是本crx启动的,开始从stdin读取4byte,转为int,读取后续消息。

//bg.js
//connect to native host and get the communicatetion port
function connectToNativeHost()
{
    var msg = 'hello msg'
    var nativeHostName = "com.leo.test";
    console.log(nativeHostName);
    port = chrome.runtime.connectNative(nativeHostName);
    port.onMessage.addListener(onNativeMessage);
    port.onDisconnect.addListener(onDisconnected);
    port.postMessage(msg);
 }

可执行文件

customizenamed.json中指定的可执行文件不可以是.py脚本源文件,而必须是一个exe文件,因为实际上执行.py文件的cmd是 python xx.p。而chrome应该是通过win32的CreateProcess启动本地进程,构造cmd作为参数传递给该API,如果是一个.py源文件,不符合win32可执行文件格式。

workaround方案:通过pyinstaller把py程序打包成一个win32的可执行exe文件。

由于linux上ld对脚本第一行#!的直接支持,可能在Linux上面是可以指定.py文件的。
实验验证:

  • Linux可以通过#!识别脚本这是一个可执行文件,这是系统层ld-*.so加载链接器提供的支持。

使用场景

本地App需要提供crx支持,那么安装App的时候需要部署com.leo.test.json文件(Linux平台)或者添加注册表Key-Value指定json文件路径(Windows平台)。
客户安装crx即可按预定方案调用App。

如果crx需要本地App支持,稍微复杂,需要倒逼客户安装App,这种方式也可以,但是基本不是很现实的场景。

原文地址:https://www.cnblogs.com/linlei2099/p/9154676.html