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是指在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_MACHINE\SOFTWARE\WOW6432Node\Google\Chrome\NativeMessagingHosts\
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,這種方式也可以,但是基本不是很現實的場景。