From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../webextensions/native_messaging/index.html | 405 +++++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 files/ja/mozilla/add-ons/webextensions/native_messaging/index.html (limited to 'files/ja/mozilla/add-ons/webextensions/native_messaging') diff --git a/files/ja/mozilla/add-ons/webextensions/native_messaging/index.html b/files/ja/mozilla/add-ons/webextensions/native_messaging/index.html new file mode 100644 index 0000000000..e9a5ca39bc --- /dev/null +++ b/files/ja/mozilla/add-ons/webextensions/native_messaging/index.html @@ -0,0 +1,405 @@ +--- +title: Native messaging +slug: Mozilla/Add-ons/WebExtensions/Native_messaging +tags: + - WebExtensions +translation_of: Mozilla/Add-ons/WebExtensions/Native_messaging +--- +
{{AddonSidebar}}
+ +

Native messaging はユーザーのコンピューターにインストールされたアプリケーションと拡張機能との間のメッセージ交換を可能にします。 Native messaging を利用すれば、ネイティブアプリケーションが Web を介してアクセスできなくても拡張機能にサービスを提供できます。典型的な利用例としてはパスワードマネージャーが挙げられます。ネイティブアプリケーションはパスワードの暗号化と保管を行い、拡張機能と通信して Web フォームに入力を行うといったことが可能です。さらに、Native messaging を用いることで、一部のハードウェア等の WebExtension API ではアクセスできないリソースに対してアドオンからアクセスできるようになります。

+ +

対象となるネイティブアプリケーションは、ブラウザーを使用してインストールや管理を行うわけではありません。OS のインストール機構を使ってインストールします。ネイティブアプリケーションそのものに加えて、「ホストマニフェスト」または「アプリマニフェスト」と呼ばれる JSON ファイルを用意しなければなりません。アプリマニフェストファイルにはブラウザーからネイティブアプリケーションにアクセスするための方法を記述します。

+ +

Native messaging を利用する拡張機能は manifest.json の中で "nativeMessaging" permission を要求する必要があります。反対に、ネイティブアプリケーション側ではアプリマニフェストの "allowed_extensions" フィールドに拡張機能の ID を含めることで permission を認める必要があります。

+ +

それで拡張機能は{{WebExtAPIRef("runtime")}} API の関数セットを用いてネイティブアプリケーションと JSON メッセージを交換することができます。ネイティブアプリケーション側では標準入力 (stdin) を介してメッセージを受信し、標準出力 (stdout) を介してメッセージを送信します。

+ +

+ +

Native messaging のサポートは Chrome とほぼ互換性がありますが、主に 2 つの違いがあります。

+ + + +

GitHub の "webextensions-examples" リポジトリの "native-messaging" ディレクトリーに完全な例があります。この記事におけるサンプルコードの大半は、この例から直接持ち込んでいます。

+ +

セットアップ

+ +

拡張機能の manifest

+ +

もし拡張機能をネイティブアプリケーションと通信させたい場合、

+ + + +

以下に manifest.json の例を示します。

+ +
{
+
+  "description": "Native messaging example add-on",
+  "manifest_version": 2,
+  "name": "Native messaging example",
+  "version": "1.0",
+  "icons": {
+    "48": "icons/message.svg"
+  },
+
+  "applications": {
+    "gecko": {
+      "id": "ping_pong@example.org",
+      "strict_min_version": "50.0"
+    }
+  },
+
+  "background": {
+    "scripts": ["background.js"]
+  },
+
+  "browser_action": {
+    "default_icon": "icons/message.svg"
+  },
+
+  "permissions": ["nativeMessaging"]
+
+}
+ +

App manifest

+ +

アプリマニフェストに、ブラウザーがネイティブアプリケーションに接続する方法を記述します。

+ +

アプリマニフェストファイルはネイティブアプリケーションと一緒にインストールする必要があります。ブラウザーはアプリマニフェストファイルを読み込み、検証を行いますが、インストールや管理は行いません。したがって、app manifest ファイルがインストール・アップデートされた時期や方法についてのセキュリティモデルは、WebExtension を使う拡張機能に対してのものというよりはネイティブアプリケーションに対してのものです。

+ +

native アプリマニフェストの文法と場所については、Native manifests を見てください。

+ +

例として、"ping_pong"ネイティブアプリケーションの manifest を以下に示します。

+ +
{
+  "name": "ping_pong",
+  "description": "Example host for native messaging",
+  "path": "/path/to/native-messaging/app/ping_pong.py",
+  "type": "stdio",
+  "allowed_extensions": [ "ping_pong@example.org" ]
+}
+ +

この設定では、"ping_pong@example.org" という ID の 拡張機能において"ping_pong" という名前を {{WebExtAPIRef("runtime")}} API等に渡すことによる接続が許可されます。 アプリケーション自体は "/path/to/native-messaging/app/ping_pong.py" です。

+ +
+

Note for Windows: 上記の例におけるネイティブアプリケーションは Python スクリプトです。Windows においては、この方法で期待通りに Python スクリプトを実行させることは難しいため、代替案として、.bat ファイルを作成してマニフェストからリンクします。

+ +
{
+  "name": "ping_pong",
+  "description": "Example host for native messaging",
+  "path": "c:\\path\\to\\native-messaging\\app\\ping_pong_win.bat",
+  "type": "stdio",
+  "allowed_extensions": [ "ping_pong@example.org" ]
+}
+ +

バッチファイルから Python スクリプトを起動します。

+ +
@echo off
+
+python -u "c:\\path\\to\\native-messaging\\app\\ping_pong.py"
+
+ +

メッセージの交換

+ +

上記のセットアップにより、拡張機能はネイティブアプリケーションと JSON メッセージを交換することができます。

+ +

拡張機能側

+ +

ネイティブメッセージはコンテンツスクリプトで直接使うことはできません; バックグラウンドスクリプトで間接的にやりとりする必要があります。

+ +

これを使うには2つのパターンがあります:ネクションベースのメッセージングとコネクションレスメッセージングです。

+ +

コネクションベースのメッセージング

+ +

このパターンでは、 {{WebExtAPIRef("runtime.connectNative()")}} を呼びだし、その時にアプリケーションの名前(アプリマニフェストの "name" プロパティの値)を渡します。既にアプリケーションが起動済みでなかった場合、これによってアプリケーションが起動し、{{WebExtAPIRef("runtime.Port")}} オブジェクトを拡張機能に返します。

+ +

ネイティブアプリは起動時に次の 2 つの引数を取ります:

+ + + +
+

Chrome  では引数の扱いが異なります:

+ + +
+ +

アプリケーションは 拡張機能が Port.disconnect() を呼び出すか、接続されたページが閉じられるまで実行し続けます。

+ +

Port を使用してメッセージを送信するためには、postMessage() 関数を呼び出し、 送信する JSON メッセージを渡します。Port を使用してメッセージを受信するためには、onMessage.addListener() 関数を使用してリスナーを追加します。

+ +

"ping_pong" アプリケーションとコネクションを確立するバックグラウンドスクリプトの例を示します。アプリケーションからのメッセージを受信し、ユーザーがブラウザーアクションをクリックするたびに "ping" メッセージを送信します。

+ +
/*
+On startup, connect to the "ping_pong" app.
+*/
+var port = browser.runtime.connectNative("ping_pong");
+
+/*
+Listen for messages from the app.
+*/
+port.onMessage.addListener((response) => {
+  console.log("Received: " + response);
+});
+
+/*
+On a click on the browser action, send the app a message.
+*/
+browser.browserAction.onClicked.addListener(() => {
+  console.log("Sending:  ping");
+  port.postMessage("ping");
+});
+ +

コネクションレスメッセージング

+ +

このパターンでは、{{WebExtAPIRef("runtime.sendNativeMessage()")}} を呼び、以下を渡します。

+ + + +

それぞれのメッセージごとに新しいアプリケーションのインスタンスが作成されます。アプリの開始時に次の 2 つの引数が渡されます:

+ + + +

アプリからの最初のメッセージは sendNativeMessage() 呼び出しの応答として扱われ、コールバックに渡されます。

+ +

以下に、先程の例を runtime.sendNativeMessage() を使って書き直したものを示します。

+ +
function onResponse(response) {
+  console.log("Received " + response);
+}
+
+function onError(error) {
+  console.log(`Error: ${error}`);
+}
+
+/*
+On a click on the browser action, send the app a message.
+*/
+browser.browserAction.onClicked.addListener(() => {
+  console.log("Sending:  ping");
+  var sending = browser.runtime.sendNativeMessage(
+    "ping_pong",
+    "ping");
+  sending.then(onResponse, onError);
+});
+
+ +

アプリ側

+ +

アプリケーション側では、標準入力を用いてメッセージを受信し、標準出力を用いてメッセージを送信します。

+ +

各メッセージは JSON でシリアライズされ、UTF-8 でエンコードされ、メッセージ長を表す 32-bit の値がネイティブのバイト順で先頭に付加されます。

+ +

アプリケーションからの一つのメッセージの最大サイズは 1MB です。アプリケーションへの一つのメッセージの最大サイズは 4GB です。

+ +

次の NodeJS コードですぐにメッセージを送受信できます:

+ +
#!/usr/local/bin/node
+
+process.stdin.on('readable', () => {
+  var input = []
+  var chunk
+  while (chunk = process.stdin.read()) {
+    input.push(chunk)
+  }
+  input = Buffer.concat(input)
+
+  var msgLen = input.readUInt32LE(0)
+  var dataLen = msgLen + 4
+
+  if (input.length >= dataLen) {
+    var content = input.slice(4, dataLen)
+    var json = JSON.parse(content.toString())
+    handleMessage(json)
+  }
+})
+
+function sendMessage(msg) {
+  var buffer = Buffer.from(JSON.stringify(msg))
+
+  var header = Buffer.alloc(4)
+  header.writeUInt32LE(buffer.length, 0)
+
+  var data = Buffer.concat([header, buffer])
+  process.stdout.write(data)
+}
+
+process.on('uncaughtException', (err) => {
+  sendMessage({error: err.toString()})
+})
+ +

もうひとつ、Python による例を示します。このアプリケーションはアドオンからのメッセージを受信します。Linuxでは、このファイルを実行可能にしてください。メッセージが "ping" であった場合、"pong" というメッセージを返します。これはPython 2のバージョンです:

+ +
#!/usr/bin/python -u
+
+# Note that running python with the `-u` flag is required on Windows,
+# in order to ensure that stdin and stdout are opened in binary, rather
+# than text, mode.
+
+import json
+import sys
+import struct
+
+
+# Read a message from stdin and decode it.
+def get_message():
+    raw_length = sys.stdin.read(4)
+    if not raw_length:
+        sys.exit(0)
+    message_length = struct.unpack('=I', raw_length)[0]
+    message = sys.stdin.read(message_length)
+    return json.loads(message)
+
+
+# Encode a message for transmission, given its content.
+def encode_message(message_content):
+    encoded_content = json.dumps(message_content)
+    encoded_length = struct.pack('=I', len(encoded_content))
+    return {'length': encoded_length, 'content': encoded_content}
+
+
+# Send an encoded message to stdout.
+def send_message(encoded_message):
+    sys.stdout.write(encoded_message['length'])
+    sys.stdout.write(encoded_message['content'])
+    sys.stdout.flush()
+
+
+while True:
+    message = get_message()
+    if message == "ping":
+        send_message(encode_message("pong"))
+ +

Python 3では、受信したバイナリーデータを文字列にデコードしないといけません。アドオンに送り返されるコンテンツは構造体を使ってバイナリーデータにエンコードする必要があります:

+ +
#!/usr/bin/python -u
+
+# Note that running python with the `-u` flag is required on Windows,
+# in order to ensure that stdin and stdout are opened in binary, rather
+# than text, mode.
+
+import json
+import sys
+import struct
+
+
+# Read a message from stdin and decode it.
+def get_message():
+    raw_length = sys.stdin.buffer.read(4)
+
+    if not raw_length:
+        sys.exit(0)
+    message_length = struct.unpack('=I', raw_length)[0]
+    message = sys.stdin.buffer.read(message_length).decode("utf-8")
+    return json.loads(message)
+
+
+# Encode a message for transmission, given its content.
+def encode_message(message_content):
+    encoded_content = json.dumps(message_content).encode("utf-8")
+    encoded_length = struct.pack('=I', len(encoded_content))
+    # use struct.pack("10s", bytes), to pack a string of the length of 10 characters
+    return {'length': encoded_length, 'content': struct.pack(str(len(encoded_content))+"s",encoded_content)}
+
+
+# Send an encoded message to stdout.
+def send_message(encoded_message):
+    sys.stdout.buffer.write(encoded_message['length'])
+    sys.stdout.buffer.write(encoded_message['content'])
+    sys.stdout.buffer.flush()
+
+
+while True:
+    message = get_message()
+    if message == "ping":
+        send_message(encode_message("pong"))
+ +

ネイティブアプリを閉じる

+ +

runtime.connectNative() を使用してネイティブアプリケーションに接続した場合、アプリケーションは拡張機能が Port.disconnect() を呼び出すか接続したページが閉じられるまで実行されます。runtime.sendNativeMessage() を使用してネイティブアプリケーションの実行を開始した場合、アプリケーションはメッセージを受信してレスポンスを送信した後閉じられます。

+ +

ネイティブアプリケーションを閉じるために

+ + + +

トラブルシューティング

+ +

もしうまくいかない場合、ブラウザーコンソールをチェックしてください。ネイティブアプリケーションが何かしらの出力を stderr に送っていた場合、ブラウザーはそれをブラウザーのコンソールにリダイレクトします。そのため、ネイティブアプリケーションが起動できている限り、出力されたエラーメッセージを確認することができます。

+ +

アプリケーションが起動できていなかった場合、問題の手がかりとなるエラーメッセージを確認してください。

+ +
"No such native application <name>"
+ + + +
"Error: Invalid application <name>"
+ + + +
"'python' is not recognized as an internal or external command, ..."
+ + + +
"File at path <path> does not exist, or is not executable"
+ + + +
"This extension does not have permission to use native application <name>"
+ + + +
"TypeError: browser.runtime.connectNative is not a function"
+ + + +
"[object Object]       NativeMessaging.jsm:218"
+ + + +

Chrome での非互換性

+ +

{{Page("Mozilla/Add-ons/WebExtensions/Chrome_incompatibilities", "Native_messaging")}}

-- cgit v1.2.3-54-g00ecf