0

MIDIデバイスを演奏したらパスワードが入力されるChrome拡張を作った

最近のChromeはWeb MIDI APIといってMIDIキーボード等の楽器を接続し、その入力が取れる。

Chrome拡張でもそれは使える。
inputタグにフォーカスしている時に楽器からの入力をキャプチャして、パスワードを生成&入力するChrome拡張を作ってみた。

https://github.com/shokai/webmidi-password-generator

パスワードは覚えるのが難しいけど曲のフレーズとかなら覚えやすいだろ、多分・・という気持ちを形にした。

といっても見た目に派手な動きがあるわけじゃなくてUSBコネクタにMIDIデバイス刺して演奏したらいきなりinputタグにパスワードが入力されてる状態になるので、blogで説明は難しい。

こんな感じでMIDIメッセージを受信してパスワードっぽい文字列が生成される。2回演奏して、ちゃんと同じパスワードが出てる。

これの問題はあらかじめパスワードを記憶させておいて取り出せるわけじゃないので、28c813172158みたいな到底覚えられない文字列になってしまう事で、作りながらうすうす気づいてたけどまあ自分でも使う気になれない。ビミョーすぎる。
パスワード覚えなくていいソリューションを作ってもパスワード覚えること前提のサービスやデバイスが世の中にたくさんあるから利便性の面でどうしても無理がある。使うなら完全移行しないとならないのが面倒くさい。

MIDIのメッセージは標準化されているものだから、別のMIDIキーボードと別のPCで同じフレーズを演奏したらちゃんと同じパスワードが入力できる、という点は良いとは思う。


久しぶりにChrome拡張を作ったら簡単だった

なんだか以前作った時より簡単になった気がする。
あまりしっかり差を把握していないんだけど、以前はもっとゴチャゴチャ色々なファイルを作らなければならなかったような。

Chrome拡張が簡単になったというより、browserifyでnpmのライブラリやnode中でpure jsで書かれているライブラリをそのままChrome拡張に持っていけるのですっきりしたのかも。

MIDIデバイスを開いて入力読んで現在ブラウザで開いているHTMLを少し書きなおすぐらいなら、
簡単なmanifest.jsonを書いて、その中で指定したjavascript1つに全部やらせればいい。

ようするにgreasemonkeyでやってたような事はmanifest.jsonとmain.js(と必要であればアイコン画像)さえ配置すればさくっとできる。browserifyのおかげでグローバル汚染せずにjQuery等を持ってくる事もできる。

こんな感じで書いてる。
npmでインストールしたjqueryと、Web MIDI APIからパスワード作って”password”イベントを発火してくれるライブラリをbrowserifyで1ファイルに固める。

main.es6
"use strict";
import PasswordGenerator from "./password_generator";
import $ from "jquery";

var target = null;
$("input")
.on("focus", function(e){
target = e.target;
})
.on("focusout", function(e){
target = null;
});

var passgen = new PasswordGenerator(8);
passgen.on("password", function(pass){
console.log(pass.raw);
console.log(`password => ${pass.string}`);
if(target){
target.value = pass.string;
}
});

Web MIDI APIはインタフェースにPromise、iterator、TypedArrayなどのES6の機能が使われている。ChromeでもES6が動くのでES6でいきなり書けばいいかなと思ったけど、Arrow Function等いくつかのES6の機能が動いたり動かなかったりして混乱したのでbrowserify+babelifyでES5に変換した。


Chrome拡張のインストール

拡張は昔は1ファイルに固めないと動かなかった気がするけど、最近は[ウィンドウ]→[拡張機能]→[パッケージ化されていない拡張機能を読み込む]でディレクトリを指定して読み込んでくれるので手軽。

0

chrome拡張の開発

1年ぶりぐらいにchrome拡張を作っているのだが、開発しやすくなっていた。ymrlとmyatsumotoに教わった。


chrome://extensionschrome://settings/extensionSettingsを開く。
chrome extension


右上の「デベロッパーモード」を押して、「パッケージ化されていない拡張機能を読み込む」ボタンを押す。フォルダを拡張として読み込める。

拡張の名前に(開発中)とつく。再読み込みを押すとリロードできる。

アイコンの左側の三角をクリックすると、background.htmlへのリンクがでる。ここでjsコンソールが出せるので、console.logを使ったりしてdebugできる。

0

chrome extensionの作り方+クロスドメインXMLHTTPリクエストのやり方

こないだ箱根合宿で学んだ。

単にchrome拡張を作ってみたかったというのと、chromeではgreasemonkeyが実行できるけど、Firefoxと違ってGM_xmlhttpRequestが無いのでクロスドメイン通信ができないのでなんとなくやってみたかった。
自分の見ているページの履歴を全部自分のサーバーに送って保存したい。

まずos0xさんのChrome拡張入門のスライドが勉強になる。
とくに

拡張コンテキスト、コンテントコンテキスト、ページコンテキストの3つのコンテキストが存在し、それぞれは完全に分かれているので、お互いが干渉してしまうことはない。さらに、拡張同士も独立したコンテキストで実行される。

拡張コンテキストはタブ操作やクロスドメイン通信などの特権を実行でき、コンテントコンテキストと通信したり、スクリプトを実行したりといったことができます。

コンテントコンテキスト(Content Scripts)は特権を持っていませんが、読み込んだページのDOMを操作することができ、拡張コンテキストと相互に通信できます。

ページコンテキストは通常のウェブページで実行されるコンテキストで、コンテントコンテキストとはDOM経由でしかやり取りできませんし、拡張コンテキストとは完全に分断されています

のあたりが今回は重要。


で、とりあえずお勉強として、自分の見ているページのURLを http://localhost:8888/page にpostするchrome extensionを作った。ページロード時に実行される。
github.com/shokai/post_location_href_crxにソースが置いてある。


■chrome拡張のビルドのしかた
rubyを使う人にはcrxmakeというgemが便利だと思う。
chrome拡張をビルドするには、ソースのディレクトリをchromeにドラッグアンドドロップすると.crx形式に固めてくれるが、
crxmakeだとterminalからビルドできる。
post_location_href_crxの中に適当なRakefileを入れておいたので、rake一発でビルドできる。

gem install crxmake
rake -T
rake
で、packageディレクトリができてその中にビルドされた.crxファイルができる。
macだとそのまま
open package/post_location_href.crx
すればchromeで開かれてインストールできるので楽だ。


■chrome拡張に必要なファイル
github.com/shokai/post_location_href_crx/tree/master/src/の中にある物が、クロスドメイン通信するのに最低限必要らしい。
src
|-- background.html
|-- icon.png
|-- icon128.png
|-- icon48.png
|-- main.css
|-- main.js
|-- manifest.json
`-- popup.html


iconを設定しておいたらchromeのツールバーに表示されるようになった
f364e597cb582884a2ba294163072ca5


それぞれのファイル名はmanifest.jsonの中で決めている。
{
"name":"post location.href",
"description":"POST location href to http://localhost:8888/page",
"version": "0.0.2",
"background_page": "background.html",
"permissions": ["tabs" ,"http://*/*", "https://*/*"],
"browser_action": {
"default_icon": "icon.png",
"default_title": "post location.href",
"popup": "popup.html"
},
"icons": {
"128": "icon128.png",
"48": "icon48.png"
},
"content_scripts": [
{
"js": [
"main.js"
],
"css": [
"main.css"
],
"matches": [
"http://*/*",
"https://*/*",
"ftp://*/*"
],
"run_at": "document_start",
"all_frames":true
}]
}


■クロスドメイン通信のしかた
このmanifest.jsonの内容だと、main.jsがコンテントスクリプトとして最初に実行される。
main.js内の最初の方に、バックグラウンドページのコンテキストと通信するコードを書いておけば、新しいページをロードした時にコンテントスクリプト→バックグラウンドページと通信できる。バックグラウンドページではクロスドメインXMLHTTPRequestが使えるので、そこで http://localhost:8888/page に location.href を送れば自分の見た全てのサイトのURLを保存できる。

main.js
var connection = chrome.extension.connect();

connection.onMessage.addListener(function(info, con) {
console.log(info, con);
});

connection.postMessage({url:location.href});

background.html
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>test app</title>
<script>
var api_endpoint = "http://localhost:8888";

chrome.self.onConnect.addListener(function(port, name) {
port.onMessage.addListener(function(info, con) {
xhr = new XMLHttpRequest();
xhr.open("POST", api_endpoint+"/page", true);
xhr.setRequestHeader("Content-Type" , "application/x-www-form-urlencoded");
xhr.send("url="+encodeURIComponent(info.url));
port.postMessage({url:info.url});

});
});
</script>
</html>



受信は、sinatraとかだと適当にこうして受け取れる
require 'rubygems'
require 'sinatra'
require 'json'

post '/page' do
url = params['url']
unless url
status 403
@mes = {
:error => '"url" required'
}.to_json
else
# 適当に保存するコード
@mes = {
:url => url
}.to_json
end
end

0

エディタ保存したらブラウザリロードするsinatra/auto-reloadを作った

エディタを保存したらブラウザを自動リロードするsinatraプラグインを作った。
これでサブモニタにchromeとJavaScriptコンソール置いておくと幸せになれる。
ソースはgithubに。

Google ChromeもしくはFirefox+Greasemonkeyで動作する。エディタには依存しない。
sinatraアプリをローカルではなくサーバーで実行していてもリロードできる。



■インストール

sudo gem install sinatra-auto-reload
rubygems.orgに置いたので、gemコマンドでインストールできる



■使う
sinatraアプリ内で読み込む
require 'sinatra/auto-reloader' if development?
if development? すると、shotgunか -e development オプションを付けて起動した時だけ有効になる。production環境には影響を与えない。



■ブラウザ拡張をインストール
localhost:4567 でアプリを起動しているとして、
http://localhost:4567/sinatra_auto_reload.user.js にgreasemonkeyスクリプトが生成される。
Firefox 3.5+Greasemonkey、もしくはChrome 5.0のuserscriptとしてインストールできる。


スクリーンショット
sinatra-auto-reload Firefox userscript


ホスト名ごとに違うuserscriptとしてインストールされるので、調べ物して同じブラウザで他のページを開いても大丈夫
sinatra-auto-reload Chrome userscript



■監視しないファイルを定義
sinatraアプリ内でauto_reload_ignoresという関数を定義しておく
def auto_reload_ignores
  [/db.*/, /config.yaml/, /log.*/, /pid.*/]
end
配列内に正規表現で書いたファイルは、更新されてもリロードしない。logとかpidに反応しても困るので。


ところでプラグインに変数を渡すのは、こういう方法でいいんですかね?もっとまともなやり方がある気がするんだけど。



■しくみ
sinatra/baseに新しくgetでアクセスできるページを追加し、 http://hostname:port/sinatra_auto_reload にファイルの最終更新日時が出るようにした。
これをuserscriptからsetIntervalで監視して、更新があればリロードする。



■参考
JavaScript部分はこれを参考にした
最速インターフェース研究会 :: Firefoxでの開発を高速化する自動リロードスクリプト



■オススメ
sinatra-reloader か shotgun と一緒に使うのをおすすめします
sinatraはサーバーを再起動しないとrubyのコードの変更を読み込んでくれないんだけど、こいつらを使うと毎回読み込み直してくれるようになる。


■今後
よく考えたらSub URIで動かしている場合に動かなそうだ。あとで修正する。

0

使ってるchrome extensions

LDRにいろいろ付ける系のgreasemonkeyはGM_系の関数がまだ実装されてないので、LDRだけはFirefoxで見ている。
テキストエリアを外部テキストエディタで開くIt’s all text相当の拡張もあったけど、server側のインストールやら起動が面倒なのでblog書くのはFirefox使う。

普段の調べ物とSBMへの投稿、twitterぐらいならこれらを入れてあれば十分。一番重要なのはTaberarelooでdeliciousとはてなブックマークとlivedoor clipにすばやくクロスポストができる事。

意外なことにchrome keyconfigにLDRizeが入っている。AdBlock+は要素の指定がなんか面倒くさい。

pbtweet、chrome FullFeed、fldr_show_fc2_images.user.jsはMacはdev版のchromeなら使える。

GM_系が一通り実装されるのと、URLバーの履歴検索がFirefoxみたいに一部一致でも利用頻度の高い順にソートされて表示されるようになればFirefoxから乗り換えられるな。
chromeのURLバーは前方一致が優先されるので、例えばりそな銀行のログインページに行きたい場合、Firefoxだと「res」まで打てば候補にすぐ出てくるが、chromeなら「ib.res」まで打ち込まないと https://ib.resonabank.co.jp/IB/0102/SC_N_0102_010.aspx が出てこない。ibとか思い出せない。「りそな→reso」なら連想できる。URLがおかしい気もするが、サービス毎にサブドメイン振っているケースはいっぱいあるし、サブドメインは覚えられない。 https://e-navi.rakuten-kc.co.jp/ava_top とか。
chrome extensions