Chrome extension与Native app通信

  • 内容概要
    一、了解chrome扩展与本地应用的通信协议
    二、实现简单的chrome扩展
    三、实现简单的本地的python应用
    四、绑定chrome扩展与Python应用
    五、应用间通信
    六、总结

  • 前言
    是什么?互为独立的应用通过既定规则进行通信
    为什么?在已有工具上进行功能的扩展,满足多方面的需求
    怎么样?First,了解protocol,then,创建crx和native app,at last, communicate.

一、Native Messaging Protocol

chrome规定了chrome-extensions与本地应用通信的协议:
简言之,首先需要将local application与chrome-extension绑定,然后通过标准的输入输出流来交换数据,数据的格式约定为header+body,header=sizeof(body), body为JSON格式(UTF8编码),本地应用最多可发送1M-bit而chrome-extensions最多可发送4G-bit。

Chrome extension与Native app通信_第1张图片
python_app.png

二、Create a simple Chrome extension

Chrome extension与Native app通信_第2张图片
simple_crx.png
  • manifest.json

Chrome extension与Native appliaiton信息交互需要获取权限nativeMessaging

{
  "name": "Native sendMessage Sample",
  "description": "Native sendMessage",
  "version": "0.1",
  "permissions": ["nativeMessaging", "*://*/*"],
  "background": {
    "scripts": ["simple.js"]
  },
  "browser_action": {},
  "manifest_version": 2
}
  • background.js

当点击浏览器扩展时向本地应用com.stone发送数据{text: "Hello, stonejianbu."},发送数据使用接口chrome.runtime.sendNativeMessage(reg_local_app,send-object,callback),注意chrome-extension作为发送端时不需要指明header(32-bit),chrome extension内部已作处理。

chrome.browserAction.onClicked.addListener(function () {
    chrome.runtime.sendNativeMessage("com.stone", {text: "Hello, stonejianbu."}, function (response) {
        console.log("Received " + response.response);
    });
});

三、Create a Python Application

Chrome extension与Native app通信_第3张图片
native_app.png
  • Python应用程序源码
# crx_python.py
"""
chrome extension messaging with local application.
"""
import sys
import struct
import json
import logging


# 主函数入口
def main():
    # 1.对windows平台的行结束符处理:\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.正在接收数据...")
        ctl.handle_message()
        logging.info("3.2.正在处理数据...")
        ctl.send_message()
        logging.info("3.3.正在返回数据...")
    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):
        """更多处理对输入输出数据的操作..."""
        # 将chrome extension发送过来的数据保存到本地
        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的数据格式要求为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个字节,相关内容查看文档介绍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与本地应用的关系:
1.首先创建一个native_manifest.json文件
2.添加Native_applicaiton和chrome-extension的描述信息
3.添加到注册表

  • native_manifest.json

1.path指定本地应用的可执行程序路径,此处的stone.bat内部执行了python应用
2.allowed_origins/chrome-extension指定与本地应用绑定的扩展的ID
3.name指定本地应用名称,与注册表注册的键对应,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注册表

将以下内容写入*.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通信_第4张图片
flow.png
    1. chrome-extension向Native app发送信息并等待响应
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发送的信息
    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发送信息
    def send_message(self):
        # 将msg-header写到标准输出流, I表示int类型占4个字节,相关内容查看文档介绍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发送的数据

首先需要将chrome-extension和Native app之间建立长连接,创建chrome.runtime.Port object,注意在上面的应用中使用的是单一连接而未创建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."});

六、总结

本文描述的仅是Chrome-extension与Native app通信流程及其简单使用,在此基础上,我们可以做功能上扩展如:
1.给chrome-extension添加pop.html由用户输入来控制和本地应用进行通信
2.给Native app添加GUI界面以控制多样化的通信

为了实现与本地应用或者远程服务器进行通信,我们可以使用更为通用的协议如http、ftp等等。如,首先建立本地的http服务器,然后管理后台background.js与服务器进行交互,示例代码如下:

"""manifest.json
{
  "name": "communication",
  "version": "1.0",
  "manifest_version": 2,
  "browser_action": {},
  "permissions": ["http://127.0.0.1/*"],
  "background": {
    "scripts": ["background.js"]
  }
}
"""
-------------------------------------------------------------------------------------------------
"""background.js
// 当点击扩展图标时触发
chrome.browserAction.onClicked.addListener(function () {
    console.log("hello world.");
    // 发送ajax post请求
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState === 4 && this.status === 200) {
            console.log(this.responseText);
            console.log("成功发送到服务器...");
        } 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模块的使用等请参考相关官方文档。

你可能感兴趣的:(Chrome extension与Native app通信)