SyntaxHighlighter

2017年8月16日水曜日

Microsoft Edge の Native Messaging を使った拡張機能 その1 (UWP を用いた Native Messaging 拡張機能の概要)

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.sendNativeMessagebrowser.runtime.connectNative における注意点

0 件のコメント:

コメントを投稿