Microsoft Edge の Native Messaging を使った拡張機能 その1 (UWP を用いた Native Messaging 拡張機能の概要)
Firefox, Chrome に続いて、 "Open TortoiseSVN" を Microsoft Edge にも移植してみたので、そのまとめです。
もう少し記事を分けるかもしれませんが、以下のような順番で書いていこうと思います。
- その1: UWP を用いた Native Messaging 拡張機能の概要
- その2: パッケージングの方法と、 Win32 アプリケーションの起動
なお、最終的なソースコードは GitHubの Open TortoiseSVN for Microsoft Edge にあります。
Edge の拡張機能のサポート
元々 Google Chrome の拡張機能で導入された拡張機能の形式ですが、 Mozilla Firefox にも WebExtensions として導入され、 Edge でも同様の機能がサポートされ始めていました。
具体的には、2016年の Anniversary update 以降、 Microsoft Edge も Chrome や Firefox とほぼ同様の拡張機能をサポートしています。
そして、 2017年の Creators update で、 Native Messaging のサポートも開始されました。
詳しい内容は Native messaging in Microsoft Edge のページに載っているので、基本的な作成方法などはこちらを参照するのがよいでしょう。
この記事では実際に上記のページに従って拡張機能を作成した際に、気になったところを中心に書こうと思います。
また、 Microsoft によるサンプルは SecureInput として GitHub で公開されています。
Firefox, Chrome との違い
Edge での Native Messaging サポートは、 Firefox, Chrome とは大きく違います。
Native messaging in Microsoft Edge のページに書かれているように、下記が特徴的です。
- JavaScript で書かれた Extension は、 UWP 形式のアプリケーションとのみ通信できる。 Win32 形式のアプリケーションとは通信できない。
Firefox, Chrome の場合は、 (Windows であれば) Win32 形式のアプリケーションと通信できる。 - UWP アプリケーションとは、 AppService の仕組みを用いて通信を行う。
Firefox, Chrome の場合は、標準入出力で JSON 形式のデータを所定のフォーマットでやり取りすることで通信を行う。
つまり、外部アプリケーションと通信する拡張機能の JavaScript 側のインターフェースは Firefox, Chrome と (ほぼ) 同じですが、それを受ける側であるアプリケーションの仕様は、まったく異なることになります。
なお、 Edge の Native Messaging 拡張機能の特徴として二つ書きましたが、実際には「外部のアプリケーションとは AppService の仕組みで通信を行う」という部分が重要です。
これは、 AppService は UWP アプリでしか作成できないので、必然的に UWP 形式のアプリケーションが必要条件となるためです。
UWP アプリとの通信
作成する AppService は、通常のものとまったく同じものです。なので、 UWP アプリで AppService を作成する方法については、他のページを探してみてください。
ここでは、 Edge との通信部分について書きます。
例として、仮に、 com.example.uwp
という名前の AppService を作成するとします。
JavaScript 側のコード
その場合、 JavaScript 側から UWP アプリを呼び出すのは以下のようになります。
このように、 browser.runtime.sendNativeMessage
もしくは browser.runtime.connectNative
の第一引数で指定する application の名前は、 AppService の名前そのものになります。
それ以外は、 Firefox, Chrome の場合と何も違いはありません。
UWP 側のコード
AppService を実装する方法としては、 in-process な実装と、 out-of-process な実装の二種類があります。
詳しくは Microsoft の説明などを見るとよいでしょう。
Open TortoiseSVN では in-process な方法で実装したので、下記を実装する必要があります。
OnBackgroundActivated
- 他のアプリケーション (今回の場合は Edge) から AppService への接続があった場合、この関数が呼ばれます。
通常の AppService と同じように、この中で Connection に対してイベントハンドラを登録します。 OnAppServiceRequestReceived
- 他のアプリケーション (今回の場合は Edge) からメッセージが送られてきた際にそのメッセージを処理するためのイベントハンドラです。
Native Messaging のメッセージ処理の手順に合わせて実装します。
OnBackgroundActivated
OnBackgroundActivated
の概要としては下記のような実装になりました。
こちらに関しては、特に通常の AppService と違いはないはずです。
OnAppServiceRequestReceived
この関数では、 Edge からのメッセージを受け取り、それに応じて処理を行います。
概要としては、以下のようなことをすればよいです。
ここでは、上で書いた例のように JavaScript 側から {"action": "action1", "args": ["arg1", "arg2", "arg3"]}
という引数で呼ぶことを考えてみます。
いくつか重要な点を挙げておきます。
- Edge からのメッセージは
ValueSet
の一つ目のValue
として渡される。 ValueSet
は辞書のように Key, Value の組を保存できるものですが、 AppService ではこのValueSet
を用いてデータをやり取りすることになっています。そのため、 Edge の Native Messaging 拡張機能も、 ValueSet でデータをやり取りすることになります。Edge から送られてきたデータは、 JSON 形式の文字列となって、渡された
ValueSet
オブジェクトの最初の (唯一の) 要素のValue
に格納されてきます。Key
は "Message" になっているようですが、これを仮定しないのがよいと思います。- UWP アプリからのメッセージも同様に
ValueSet
として返す。 逆方向もまったく同じです。
Key は適当な名前でよいので、 Value に JSON 文字列を指定し、SendResponseAsync
でメッセージを送ります。JSON 形式にシリアライズするためには
JavaScriptSerializer
と匿名型を用いるのが楽でよいと思います。
なお、本筋とは関係ないですが、やや複雑な JSON 文字列をパースする場合、以下のようにすればよいです。
例えば、 {"action": "action1", "args": ["arg1", "arg2", "arg3"]}
の args
をパースする場合は、例えば以下のようにすればよいです。
このようにすることで、 "args" の中身を配列で取り出すことができます。
あとは UWP アプリ内で好きな処理をすればよいわけです。
注意点
Microsoft EdgeHTML 15.15063 の時点では、 UWP アプリからの戻り値が (私の予想と) やや異なった形式で返ってきます。
具体的には、下記のようになります。
このように、 Firefox, Chrome と違ってわざわざ JSON.parse
する必要があります。
私はこの挙動はバグのように思いましたので、 issue として登録しましたが、まだ有効な回答はない状態です。
続いて、作成した UWP アプリを JavaScript のコードとまとめてパッケージングし、 Edge の拡張機能として登録することになります。
次回以降は、下記について書く予定です。
- Appx パッケージの作成
- Win32 アプリケーションの起動
browser.runtime.sendNativeMessage
とbrowser.runtime.connectNative
における注意点