Chrome extension與Native app通信

  • 內(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枕磁。

python_app.png

二渡蜻、Create a simple Chrome extension

simple_crx.png
  • 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

native_app.png
  • 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

flow.png
    1. 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);
    });
});
    1. 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()
    1. 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");
});
"""

JavaScriptchrome-extension绘梦、python的struct橘忱、logging模塊的使用等請參考相關(guān)官方文檔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卸奉,一起剝皮案震驚了整個濱河市钝诚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌榄棵,老刑警劉巖凝颇,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件潘拱,死亡現(xiàn)場離奇詭異,居然都是意外死亡拧略,警方通過查閱死者的電腦和手機芦岂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辑鲤,“玉大人盔腔,你說我怎么就攤上這事杠茬≡氯欤” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵瓢喉,是天一觀的道長宁赤。 經(jīng)常有香客問我,道長栓票,這世上最難降的妖魔是什么决左? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮走贪,結(jié)果婚禮上佛猛,老公的妹妹穿的比我還像新娘。我一直安慰自己坠狡,他們只是感情好继找,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逃沿,像睡著了一般婴渡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凯亮,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天边臼,我揣著相機與錄音,去河邊找鬼假消。 笑死柠并,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的富拗。 我是一名探鬼主播臼予,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼媒峡!你這毒婦竟也來了瘟栖?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谅阿,失蹤者是張志新(化名)和其女友劉穎半哟,沒想到半個月后酬滤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡寓涨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年盯串,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片戒良。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡体捏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糯崎,到底是詐尸還是另有隱情几缭,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布沃呢,位于F島的核電站年栓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏薄霜。R本人自食惡果不足惜某抓,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惰瓜。 院中可真熱鬧否副,春花似錦、人聲如沸崎坊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽流强。三九已至痹届,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間打月,已是汗流浹背队腐。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奏篙,地道東北人柴淘。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像秘通,于是被迫代替她去往敵國和親为严。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容