Firefox の拡張には、キーボード周りを含めたブラウザのインターフェースを「強力に」書き換える能力があるわけなのだが、それらと wasavi の相性は一般的によくない。wasavi もまたすべてのキーボード入力を自分で消費するからだ。すると、あるキー入力が他の拡張に横取りされてしまうとか、wasavi と同時に処理されてしまうと言った不具合が起きる。
それを避けるために、Vimperator と Keysnail 用のプラグインスクリプトを用意してある。これらは主に 2 つの仕事を受け持つ:
- タブの切り替えを監視し、アクティブなタブのドキュメント上に実行中の wasavi が存在するかどうかで対象の拡張を一時的にサスペンドさせるかを制御する(ちなみにこの辺の処理は e10s が有効だと完璧に破綻する: プラグインは Chrome 権限上で動いている、そして content を直接触るためだ)
- ドキュメントのカスタムイベント [cci]WasaviStarted[/cci] [cci]WasaviTerminated[/cci] を監視し、各イベントに応じて適宜対象の拡張を一時的にサスペンドさせるかを制御する
ところで、こういったインターフェースを再定義する拡張はもちろんこれだけではない。issue では VimFx との相性がよくないというものが上がっているので、とりあえずそれに対応してみることにしよう。
* * *
VimFx というのはなんぞやというと、実はあまり知らないのだが、本質的には Vimperator と同様に Firefox のインターフェースを vim っぽくする系の拡張である。ただし、Vimperator がインターフェースを「ドラスティックに」変えるのに対して VimFx はもうちょっと大人しいようだ。
プラグインの機構はない。ただし、Chrome 権限のスクリプトから使用できる API が提供されている。
さて、対象の拡張が変わったとしても、やるべきことは上記の 2 点なのは変わらない。ただし 1 は上記のとおり、e10s を見越して content 内で処理する必要がある。普通の DOM のイベントでタブがアクティブ・非アクティブになったというのを判定するには Page Visibility という割と新し目の API で定義される visibilitychange イベントを使えば良いだろう。一方で、wasavi の状態によって他の拡張にアクセスするのは content ではなくバックエンドの chrome スクリプトでなければならないので、つまり visibilitychange イベントでやることはアクティブなタブだったらバックエンドにメッセージを投げる、ということになる。そんなわけで:
window.addEventListener('visibilitychange', function () {
if (document.hidden) return;
extension.postMessage({
type: 'visibility-change',
wasaviRunning: !!wasaviFrame
})
}, false);
というような感じ。
このメッセージをバックエンドで受け取り、wasavi が起動しているようなら VimFx をサスペンドさせればいいのである。
* * *
というわけで色々作って、VimFx の API を使ってどうこうするところまで来たのだが。
ドキュメントによれば、API はそれを提供するオブジェクトのインスタンスを通して使用する。インスタンスの取得は
let {classes: Cc, interfaces: Ci, utils: Cu} = Components
Cu.import('resource://gre/modules/Services.jsm')
let apiPref = 'extensions.VimFx.api_url'
let apiUrl = Services.prefs.getComplexValue(apiPref, Ci.nsISupportsString).data
Cu.import(apiUrl, {}).getAPI(vimfx => {
// Do things with the `vimfx` object here.
})
こんな感じで、ここで [cci]getAPI[/cci] に渡しているコールバックに渡される仮引数 vimfx がそのインスタンスなのだが。困ったことにその中に VimFx をサスペンドさせるようなそのものズバリの機能はないのである。
というより VimFx 自体にサスペンドという概念がない。その代わり、すべての入力を素通りさせて VimFx は関与しない ignore モードというのがあって、normal モードから ignore モードへ遷移することで結果的にサスペンドが行える。で、このモードを遷移するための [cci]enterMode()[/cci] メソッドはどこに属しているのかというと VimFx のコマンド群のそのハンドラに渡される vim パラメータが持っているらしい。しかしこれを API から直接的に得る手段はない。うーんどうすれば……。
しかし、間接的に得る手段はある。[cci]vimfx.on()[/cci] でいくつかのイベントハンドラを登録することができて、その時も vim パラメータが渡される。これを取っておいて、必要なときに使えばいけるかもしれない。
* * *
というわけで、期待した動きをするようになった。