內(nèi)容概要
一、了解chrome擴展與本地應(yīng)用的通信協(xié)議
二、實現(xiàn)簡單的chrome擴展
三足绅、實現(xiàn)簡單的本地的python應(yīng)用
四摩泪、綁定chrome擴展與Python應(yīng)用
五笆焰、應(yīng)用間通信
六、總結(jié)前言
是什么见坑?互為獨立的應(yīng)用通過既定規(guī)則進行通信
為什么嚷掠?在已有工具上進行功能的擴展,滿足多方面的需求
怎么樣荞驴?First不皆,了解protocol,then熊楼,創(chuàng)建crx和native app霹娄,at last, communicate.
一、Native Messaging Protocol
chrome規(guī)定了chrome-extensions與本地應(yīng)用通信的協(xié)議:
簡言之,首先需要將local application與chrome-extension綁定犬耻,然后通過標準的輸入輸出流來交換數(shù)據(jù)踩晶,數(shù)據(jù)的格式約定為header+body,header=sizeof(body), body為JSON格式(UTF8編碼),本地應(yīng)用最多可發(fā)送1M-bit而chrome-extensions最多可發(fā)送4G-bit枕磁。
二渡蜻、Create a simple Chrome extension
- manifest.json
Chrome extension與Native appliaiton信息交互需要獲取權(quán)限nativeMessaging
。
{
"name": "Native sendMessage Sample",
"description": "Native sendMessage",
"version": "0.1",
"permissions": ["nativeMessaging", "*://*/*"],
"background": {
"scripts": ["simple.js"]
},
"browser_action": {},
"manifest_version": 2
}
- background.js
當點擊瀏覽器擴展
時向本地應(yīng)用com.stone
發(fā)送數(shù)據(jù){text: "Hello, stonejianbu."}
计济,發(fā)送數(shù)據(jù)使用接口chrome.runtime.sendNativeMessage(reg_local_app,send-object,callback)
,注意chrome-extension作為發(fā)送端時不需要指明header(32-bit)茸苇,chrome extension內(nèi)部已作處理。
chrome.browserAction.onClicked.addListener(function () {
chrome.runtime.sendNativeMessage("com.stone", {text: "Hello, stonejianbu."}, function (response) {
console.log("Received " + response.response);
});
});
三沦寂、Create a Python Application
- Python應(yīng)用程序源碼
# crx_python.py
"""
chrome extension messaging with local application.
"""
import sys
import struct
import json
import logging
# 主函數(shù)入口
def main():
# 1.對windows平臺的行結(jié)束符處理:\r\n--->\n
if sys.platform == "win32":
import os, msvcrt
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
# 2.日志文件處理對象
logging.basicConfig(filename="ctl.log",
level=logging.INFO,
format="%(levelname)s::%(asctime)s-%(module)s-%(lineno)d: %(message)s",
datefmt="%Y-%d-%m %H:%M:%S")
# 3.與chrome extension進行通信
try:
ctl = CrxToLocal()
ctl.read_message()
logging.info("3.1.正在接收數(shù)據(jù)...")
ctl.handle_message()
logging.info("3.2.正在處理數(shù)據(jù)...")
ctl.send_message()
logging.info("3.3.正在返回數(shù)據(jù)...")
except Exception as e:
logging.error(e)
# Chrome extension to local application:與chrome-extension進行通信的class
class CrxToLocal:
def __init__(self):
self._input_body = None
self._output_body = None
def read_message(self):
# 讀取標準輸入流中的msg-header(first 4 bytes).
text_length_bytes = sys.stdin.buffer.read(4)
# Unpack message length as 4 byte integer, tuple = struct.unpack(fmt, buffer).
text_length = struct.unpack("I", text_length_bytes)[0]
# 讀取標準輸入流中的msg-body bytes
self._input_body = sys.stdin.buffer.read(text_length)
def handle_message(self):
"""更多處理對輸入輸出數(shù)據(jù)的操作..."""
# 將chrome extension發(fā)送過來的數(shù)據(jù)保存到本地
with open("./local_file.json", "a", encoding="utf-8") as f:
json.dump(json.loads(self._input_body, encoding="utf-8"), f, ensure_ascii=False, indent=2)
# _output_body的數(shù)據(jù)格式要求為JSON utf-8 bytes
self._output_body = json.dumps({"response": "Nice to meet you."}).encode("utf-8")
def send_message(self):
# 將msg-header寫到標準輸出流, I表示int類型占4個字節(jié)学密,相關(guān)內(nèi)容查看文檔介紹python-doc/library/struct
sys.stdout.buffer.write(struct.pack("I", len(self._output_body)))
# 將msg-body寫到標準輸出流
sys.stdout.buffer.write(self._output_body)
sys.stdout.flush()
if __name__ == "__main__":
main()
四、Bind chrome-extension with native application
windows下传藏,通過給注冊表添加鍵值以綁定chrome-extension與本地應(yīng)用的關(guān)系:
1.首先創(chuàng)建一個native_manifest.json文件
2.添加Native_applicaiton和chrome-extension的描述信息
3.添加到注冊表
- native_manifest.json
1.path指定本地應(yīng)用的可執(zhí)行程序路徑腻暮,此處的stone.bat內(nèi)部執(zhí)行了python應(yīng)用
2.allowed_origins/chrome-extension指定與本地應(yīng)用綁定的擴展的ID
3.name指定本地應(yīng)用名稱,與注冊表注冊的鍵對應(yīng),native_manifest.json文件將作為注冊表中的值
{
"name": "com.stone",
"description": "My Application",
"path": "C:\\Users\\Administrator\\Desktop\\native_app\\stone.bat",
"type": "stdio",
"allowed_origins": [
"chrome-extension://mnmmaemegkeggpgimaedjkjlmncljibh/"
]
}
- 將com.stone: native_manifest.json鍵值添加到windows注冊表
將以下內(nèi)容寫入*.reg文件中漩氨,點擊運行即注冊成功(注意實際路徑的修改)
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.stone]
@="C:\\Users\\Administrator\\Desktop\\native_app\\native_manifest.json"
五西壮、Communication
- chrome-extension向Native app發(fā)送信息并等待響應(yīng)
chrome.browserAction.onClicked.addListener(function () {
chrome.runtime.sendNativeMessage("com.stone", {text: "Hello, stonejianbu."}, function (response) {
console.log("Received " + response.response);
});
});
- Native app接受chrome-extension發(fā)送的信息
def read_message(self):
# 讀取標準輸入流中的msg-header(first 4 bytes).
text_length_bytes = sys.stdin.buffer.read(4)
# Unpack message length as 4 byte integer, tuple = struct.unpack(fmt, buffer).
text_length = struct.unpack("I", text_length_bytes)[0]
# 讀取標準輸入流中的msg-body bytes
self._input_body = sys.stdin.buffer.read(text_length)
- 3.Native app向chrome-extension發(fā)送信息
def send_message(self):
# 將msg-header寫到標準輸出流, I表示int類型占4個字節(jié),相關(guān)內(nèi)容查看文檔介紹python-doc/library/struct
sys.stdout.buffer.write(struct.pack("I", len(self._output_body)))
# 將msg-body寫到標準輸出流
sys.stdout.buffer.write(self._output_body)
sys.stdout.flush()
- chrome-extension等待接收Native app發(fā)送的數(shù)據(jù)
首先需要將chrome-extension和Native app之間建立長連接叫惊,創(chuàng)建chrome.runtime.Port object款青,注意在上面的應(yīng)用中使用的是單一連接而未創(chuàng)建Port
var port = chrome.runtime.connectNative("com.stone");
port.onMessage.addListener(function(msg){
console.log("Received"+msg);
});
port.onDisconnect.addListener(function(){
console.log("Disconnected.");
});
port.postMessage({greet: "Hello, stonejianbu."});
六、總結(jié)
本文描述的僅是Chrome-extension與Native app通信流程及其簡單使用霍狰,在此基礎(chǔ)上抡草,我們可以做功能上擴展如:
1.給chrome-extension添加pop.html由用戶輸入來控制和本地應(yīng)用進行通信
2.給Native app添加GUI界面以控制多樣化的通信
為了實現(xiàn)與本地應(yīng)用或者遠程服務(wù)器進行通信,我們可以使用更為通用的協(xié)議如http蔗坯、ftp等等康震。如,首先建立本地的http服務(wù)器宾濒,然后管理后臺background.js與服務(wù)器進行交互腿短,示例代碼如下:
"""manifest.json
{
"name": "communication",
"version": "1.0",
"manifest_version": 2,
"browser_action": {},
"permissions": ["http://127.0.0.1/*"],
"background": {
"scripts": ["background.js"]
}
}
"""
-------------------------------------------------------------------------------------------------
"""background.js
// 當點擊擴展圖標時觸發(fā)
chrome.browserAction.onClicked.addListener(function () {
console.log("hello world.");
// 發(fā)送ajax post請求
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
console.log(this.responseText);
console.log("成功發(fā)送到服務(wù)器...");
} else {
console.log("nothing......")
}
};
xhttp.open("POST", "http://127.0.0.1:5000/index", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // post請求時添加
xhttp.send("hello=stone_world&name=stone");
});
"""
附JavaScript、chrome-extension绘梦、python的struct橘忱、logging模塊的使用等請參考相關(guān)官方文檔。