Google ChromeでトレイからCDを排出できるブラウザ拡張をつくりました(Native messaging版)

https://github.com/laiso/crx-eject

これ。

Mac向けです*1。利用にはシステムの管理者権限で独自プログラムのインストールが必要です。とかいって上の動画だけ観て満足して。別にインストールする必要はないです。仕組みが知りたい人は以下の文章やソースコードを参照してください。

Chrome Web Storeにリリースしようとふむ国際化しとくかと思ってやったんだけどやってるうちに我に返ったのでとくに何もやってない。

Windowsユーザーの方はChromeで(☝ ՞ਊ ՞)☝ウイーン - 葉っぱ日記 をご覧ください。

Linux版はよくわからないので誰かお願いします。

Native messaging

上のhasegawayosukeさんのスライドでもちょっと出てくるんだけど、もともとNPAPIのドキュメントを索いたら「非推奨だからNaClやNative messagingを使え」って書いてあってNative messagingが何なのか知らなかったのでちょっと作ってみた。

この拡張でブラウザツールバーのボタンを押した時には以下のスクリプトが実行されてる

chrome.runtime.connectNative("so.lai.crxeject");

これはメッセージホストへの接続を命令している。

実際には決ったシステムのパスにあるマニフェストファイルを参照しにいく。

Mac
/Library/Google/Chrome/NativeMessagingHosts/so.lai.crxeject.json
Linux
/etc/opt/chrome/native-messaging-hosts/so.lai.crxeject.json

マニフェストファイル内にこのホストが実行するプログラムの絶対パスが記述されている

{
  "name": "so.lai.crxeject",
  "description": "Eject command bridge.",
  "path": "/usr/local/bin/crx-eject.sh",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://bbdnjigcilagcnpkibnadpicgmpcjolc/"
  ]
}

上記の場合 /usr/local/bin/crx-eject.sh がそのまま実行される。allowed_origins でどの拡張からの呼び出しを許可するのかを制限できる。

このスクリプトの中でコマンドでejectする

if [ $(uname -s) == 'Darwin' ]; then
  drutil tray eject
else
  eject
fi

というわけで実質2,3行しか書いてない。

が本来Native messagingはホストとの双方向の送受信を意図していて、標準入出力でJSONをやりとりして使うものらしい。

// Chrome拡張
var port = chrome.runtime.connectNative("so.lai.crxeject");
port.onMessage.addListener(fuction(message){
    alert("受信テキスト :"+message.text);
});
// 送信
port .postMessage({"text": "Hello!!!"});

ChromiumリポジトリのサンプルディレクトリにNative messagingを使った実践的なチャットアプリケーションがあって、それを参考にした。

http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/nativeMessaging/

ここではホストプログラムは起動とともにPythonのtkでGUIをつくり裏で標準入力の入ってくるテキストも待ち受けていて

デスクトップのインターフェイスからメッセージが入力されたらそれをまた標準出力でChrome側へ伝えている。

# 受信
# Thread that reads messages from the webapp.
def read_thread_func(queue):
  message_number = 0
  while 1:
    # Read the message type (first 4 bytes).
    text_length_bytes = sys.stdin.read(4)

    if len(text_length_bytes) == 0:
      if queue:
        queue.put(None)
      sys.exit(0)

    # Read the message length (4 bytes).
    text_length = struct.unpack('i', text_length_bytes)[0]

    # Read the text (JSON object) of the message.
    text = sys.stdin.read(text_length).decode('utf-8')

    if queue:
      queue.put(text)
    else:
      # In headless mode just send an echo message back.
      send_message('{"echo": %s}' % text)
#送信
# Helper function that sends a message to the webapp.
def send_message(message):
   # Write message size.
  sys.stdout.write(struct.pack('I', len(message)))
  # Write the message itself.
  sys.stdout.write(message)
  sys.stdout.flush()

Native messagingについて公式ドキュメントは以下に載ってる。くわしくは——というほど詳しくは載ってない。

http://developer.chrome.com/extensions/messaging.html#native-messaging

正直うまい活用方法が思い受かばないけどC++で書かなくてよくて手軽だとは思った。これから便利にしてゆきたい。

*1:Linuxは微妙だったのでMac専用にしました https://twitter.com/laiso/status/412169475392151552