Synchronizing the settings #2

とりあえず Chrome の場合のみ、[cci]chrome.storage.sync[/cci] を通して同期された設定を参照するようにした。Presto Opera、Firefox では設定は同期されない。また Blink Opera では前述の API 自体は使えるのだが、今のところ同期しないので実質的に現状と変わらない。

ところで組んでいて気がついたのだけれど。

この同期されるデータというのが何に属しているのか。もちろん Chrome に入力する Google アカウントだ。一方で Chrome には任意のタイミングでこのアカウントをログアウトしたり、別のアカウントにスイッチするようなインターフェースが用意されている。

ということは、エクステンションからアカウントに属するストレージを参照できる以上、現在のアカウントのある程度の情報やアカウントがスイッチされたというイベントも受け取れることができないと辻褄が合わなくなる。

しかし探してみてもそういうものが見つからない。[cci]chrome.storage.onChanged.addListener[/cci] で内容の変化に対するイベントをリスンすることができるが、例えばアカウントをスイッチした時に何かイベントが発生するということはないようだ。現在のログイン状態を知ることができればまあまあなんとかできるかもしれないが、そういう API もない感じ。いっそ [cci]chrome://sync[/cci] をスクレイピングとか…? いやまさか。

これ、Wasavi に限らず storage API を使っているエクステンションに共通する問題だと思うんだけど。つまり Chrome にログインしたあと、各エクステンションのバックグラウンドをリロードするか、あるいは単に Chrome 全体を再起動しないと辻褄が合わなくなると思うんだけど。

しかし google 様がそんな仕様にするのだろうか。何か勘違いしているのかもしれない。もう少し調べてみよう。

Alternative NTW

Chrome で Ctrl-[ntw] を使うことができない件。

その代わりに Alt-[ntw] でも同じ機能を呼び出すようにした。Selenium でのテストもそちらを使用する。

ついでと言っては何だが、[cci]map[/cci] の左辺に指定するキーストロークで Alt を併用したものを受け付けるようにした。ただし完全ではなく、保証できるのは Alt-[a-z] だけ。この辺は DOM3 KeyboardEvent がフルに実装されればもうちょっとましになると思う(つまり、キーボードレイアウトに依らずにすべてのキーについて何が押されたかを正確に判断できる)のだけど、それはまあ 1 年後くらいかな…。このへんは何故か Chrome のほうが W3C の規格からずれている。

Presto Opera is gone

Selenium を 2.46.0 に更新したところ、2 点。

まず付属の jar から OperaDriver が消えていた。

つまり、そういうことなのだろう。

wasavi はまだ、とりあえずは Presto Opera でも動作するようにする。これはつまりできるだけ Chrome/Firefox/Opera とで共通して動くコードを書くのを心がけるということだ。一方で段階的に Presto Opera の専用コードは削除していくし、新規に書くこともない。たとえば、qeema から既に Presto Opera 対応の部分はざっくり消した。

ただし、前述の通り Selenium は OperaDriver を deprecated にしたわけなので、通しての機能テストを Presto Opera に施すことはできない。従って wasavi が Presto Opera 上でひと通り動作することを保証することはできなくなる。

ところで Presto Opera が脱落すると wasavi が動作すると公式に謳えるブラウザが Chrome と Firefox だけになってしまい若干寂しい。聞く所によると Microsoft Edge は Chrome のそれをパク、いや非常に高い互換性を持つ拡張の仕組みを持つらしいが…。

ただ同時に、どういうわけか Firefox の拡張とも高い互換性を持つ、という噂があったりよく分からない。そんなこと可能なんだろうか。とにかくいよいよ Windows 10 のリリースが近いが、Windows 10 に同梱される Edge にはまだ拡張の仕組みは組み入れられておらず、大体今年中に形になればいいかな程度の完成度だという。

次に。

Chrome はいくつかのキーボード・ショートカットを予約している。たとえば [cci]Ctrl+T[/cci] とか [cci]Ctrl+W[/cci] とか [cci]Ctrl+P[/cci] とか、つまりブラウジングするにあたってとても基本のもの。予約しているというのはどういうことか。スクリプトからそれらのキー入力を得ることができないのだ。これらのキー入力の場合 keydown イベント等自体が発生しないのでどうしようもない。google 神に歯向かう手段がまったく用意されていない。

とはいえ wasavi ではこれらの特別なショートカットのいくつかにも機能を割り当てている。たとえば行入力中の [cci]Ctrl+W[/cci] はカーソルの前の単語を削除する。

従来、それらの機能をテストする際は、当然ながら Ctrl と W を同時に押したような擬似的なキーストロークを生成するとテストできないので、そのかわり U+0017 を生成していた。[cci]Ctrl+W[/cci] は wasavi においては U+0017 とみなされるのでまあこれでよかろーということなのだが。ところが数日前に Chrome が version 44 に上がったせいかこの手法が通じなくなってしまった。U+0017 を生成しても Chrome に [cci]Ctrl+W[/cci] とみなされるようになってしまったようなのだ。

そんなわけで多くのテストが失敗するようになってしまった。うーんどうしたものかな。

be a developer #2

例の Firefox の署名周り。公式のドキュメントとしては https://dev.mozilla.jp/2015/02/extension-signing-safer-experience/ あたり。

このドキュメントには書いていないが、ものすごく重要なことがある。それは AMO に提出した拡張がレビューされるまで、月単位で待たなければならないということだ。レビューされるのが早いか、弥勒菩薩が降臨するのが早いか…という勢いである。枯れた安定した拡張ならともかく、新しく次々と更新されるホットなそれにとっては AMO はまったく頼りない場所であるということだ。

それを補うために、従来はベータチャネルというものがあって、1.0.0beta みたいに名づけたバージョンはベータ版扱いになり、アップした際の機械的なコードバリデータでのチェックでエラーさえ出なければとりあえず即 AMO 上に乗る仕組みがあった(ただし、当然 AMO がレビュー済みというお墨付きは得られない。また事前に full レビューを済ましたものでなければならない)。

今回署名周りの変更に従って、どうもこのベータチャネルのフローも変化している感じがする。コードバリデータでエラーも警告も一切でない 100 点満点の結果を出せば、おそらく従来通り即 AMO に乗るのだろう。しかし警告を出すと人の手によるコードレビューに送るフローしか選択できなくなってしまう。そして人の手によるレビューは前述の通り月単位で時間がかかるのである。したがって、「不安定かもしれないけど即公開される」というベータチャネルの意義を AMO 自身が完全にぶっ壊しているということになる。

ここで、「警告を出さないクリーンなコードにしないお前が悪いんじゃん」と思われるかもしれない。本質的にはそうだろう。しかし現実的にはコードバリデータがどうも信用できないのだ。警告ではないものにまで言いがかりをつけてくるポンコツに思えて仕方がないのだ。

たとえば以下のように、setTimeout() や setInterval() の第 1 引数に関数リテラルではないものを渡すとバリデータは警告を出す。しかも、reject severity: high という扱いで。

handler = function () { ... };
:
:
setTimeout(handler, 100);

これが何故警告の対象になるのかというとおそらく、コールバック関数を変数経由にすると、その変数の中身を外部から書き換えられてしまい、任意の関数を送り込まれる危険性がある……的なものだと思う。だからコールバックは関数リテラルで書けよとバリデータは言ってくる。

しかしこの場合、問題の本質はコールバックを変数経由にした場合にその変数が外部から操作可能なスコープにあるかどうかであって、関数リテラルで書いているかどうかではないと思うのです。そういう視点ではなく、バリデータは単に字面しか見ていない感じがする。信用できない。

ちなみにこれを

handler = function () { ... };
:
:
setTimeout(function(){handler()}, 100);

と書き換えると警告は出ないのだが、これじゃあバリデータくんが危険視するらしき危険性はまったく除去されてない。これを見逃すというのは、やはりバリデータは単に字面しか見ていない感じがする。信用できない。

Mozilla は自分のところで javascript エンジンを作っているのだから、自慢のなんとかモンキーをベースにもっとちゃんとしたバリデータを作れると思うのです。それをやっていない。バカでも作れる、grep に毛が生えたようなツールしか作っていない。

他にもたくさん「なんでこれが警告扱いになるのか意味わかんねーです…」というものをたくさんお出しされてとってもげんなり来る。

もっと真面目にやってほしい。

Longterm goal

中長期的な目標のうち大きめのものを一応メモしておこう。

まずはモデルとビューを分離したい。現状では wasavi が保持しているテキストの実体は iframe に表示されている html 文書そのものなのだ。それにより再表示の部分は完全にブラウザ任せにできるメリットはあるのだけど。しかしせいぜい 100 行程度の小さな文書であればいいのかもしれないが、それ以上になるとメモリ消費量や再表示の速度などなかなかきつくなってくると思う。

なので、テキスト自体は単なる String の配列か、あるいは XMLDocument にしておいて、表示の部分はまさにビューに表示すべき領域だけを描画するようにしたい。スクロール処理も含めてそのためのプロトコル的なものを考える必要がある。

それからモデルとビューの分離は、もうひとつ重要な変化をもたらす。現状 wasavi のエディタ部分のコードはすべて iframe 側にある。つまり wasavi のエディタとしての機能をもたらすコードは原則的には iframe の生成のたびに評価・実行されるということだ。コードは現状一万行以上あるわけで、これは大いにリソースの無駄だと思う。もちろん、ほとんどの場面でブラウザが持つコードキャッシュがそれをカバーしてくれるわけだけど、常にそうとは限らない。

これが、モデルとビューを分離するとどうなるか。エディタとしてのコードをバックエンド側に常住させることができるのだ。機能の区分けとしてはあくまでフロントエンドなので位置づけが難しい(フロントエンドのバックエンド側?)が wasavi が起動するたびにバックエンド側で

instances.push(new WasaviFrontend)

的に常駐しているコードからインスタンスを生成して割り当てれば、起動のたびに巨大なコード群を評価させる必要はなくなる。iframe 側が担当するのはキー入力の受付と、バックエンドへの送出、バックエンドから送られてくる再表示コマンド列の評価実行だけということになる。

Synchronizing the settings

以前、wasavi の設定を同期可能にするとして、どうしたものかというのを少し考えたことがあって、そのときは exrc に秘密の情報を書いている場合もあるかもしれないし、全自動で同期させるのはどうなのかということで棚上げにしていた。しかしじゃあ半自動ならいいのかというとそれはそれでなかなか使い勝手が悪そうだ。ということでやっぱり全自動でいいんじゃないのかなあという気分になりつつある。そもそも exrc に秘密の情報を書くことがあるだろうか。ない。

具体的には、現在は設定の保持にバックグラウンドの localStorage を使っているところを、例えば Chrome であれば chrome.storage.sync 系の API を使う。少し面倒なのは、localStorage であれば値の代入や読み出しは同期的に行えるのに対して chrome.storage は非同期的なのでその辺の修正は必要だという点。しかし実は下準備はすでにしてあって、バックグラウンドの初期化は Promise を使うようにしたので同期だろうが非同期だろうが特に問題はない。

Firefox はどうなのか。Firefox にも同期の仕組みはあるのだが、エクステンションに公開されたストレージ API というのはない感じ。少なくとも Add-on SDK にはない。そのかわり割と有名なテクニックとして about:config にリストされる Firefox 自身の設定のうち services.sync.prefs.sync.extensions. で始まるものは extensions. 以降の部分のキーが同期対象になるというものがある。

これ、同期が完了する前に読み出したりしたらどうなるんだろうか。Chrome の同期 API の場合は同期された値を読みだした時のコールバックを使用するわけなのだけど。Firefox ではどうなるの? 逆に同期完了前に値を設定したりしたらどうなるの? わたしの救助信号とどいてるの? Oh, 答えて。

The submission

issue #87

たいていのフォームで textarea って最後の方(つまり、submit ボタンの近く)にあるから、[cci]:wq[/cci] でついでにサブミットしちゃえばいいんじゃね? というアグレッシブな提案。

wq コマンドは非常に基本的なものなので、それによって便利になる web ページは随分あるだろうが、何もかもめちゃくちゃになる web ページも同じくらいあるだろう。

ということで wq にそういう機能を持たせるのではなく、新しいコマンドを新設する方向で考えている。例えば [cci]:wqs[/cci] (write-quite-submit) のような。

Clipboard or Selection handling on Linux

issue #37

Linux というか X11 が動いている環境上での vim は [cci]*[/cci] レジスタと [cci]+[/cci] レジスタで PRIMARY セレクションと CLIPBOARD セレクションを使い分けるはずであるが、wasavi ではそうなっていないというもの。

とはいうものの、ブラウザのエクステンションから操作できるのは CLIPBOARD セレクションだけである。とりあえず [cci]*[/cci] だけでなく [cci]+[/cci] レジスタも有効なレジスタと見なし、[cci]*[/cci] のエイリアスとして扱うように修正。

Console integration

wasavi が何らかのメッセージを出力する場合、いくつかの出口がある:

  • バックログ・コンソール: 出力が複数行に及ぶ時に wasavi の画面全体を覆う形で表示される。モードは backlog_prompt に移行する。これは command や insert/overwrite などと同じレベルに位置する立派な vi のモードの一つだ。lastMessage は最終的にバックログの全内容を代入される
  • ステータスラインの message: 出力が 1 行のみの場合は単にステータスラインにそれを表示し、モードは backlog_prompt へは移行しない。lastMessage は出力される 1 行そのものを代入される
  • ステータスラインの notice: これは message とほとんど同じなのだが、lastMessage を更新する場合としない場合がある:
    • 通常の notice: ステータスラインに何らかの文字列を表示する。lastMessage は更新しない。これを使用するのは例えば、[cci]n[/cci] コマンドなどで現在使用している検索文字列をステータスラインに表示する時など
    • silent な notice: 内部的に何らかのメッセージは生成するが、表示はせず、エラーベルを鳴らす。lastMessage は更新する。これを使用するのは例えば、行頭・行末・テキスト戦闘・テキスト末尾を超える [cci]hjkl[/cci] 各コマンドなど
  • lastMessage これは内部的な変数で、出力先にかかわらず出力した結果を保持する。機能テストの際にこれを参照する

ここで重要なのは、バックログと message は本質的に同じバッファを共有しなければいけないということと、message と notice の使い分けを正しく行わなければならないということなのだが。前者に関して徹底されていなかった(対症療法的な修正がたくさんコミットログに残ってて結構恥ずかしい)のでそのへんを見直して修正した。

後者は明確なルールがあるわけではない。ただ実際に使ってみた際にログが溜まりまくってうぜえ!的なものは notice、エラーメッセージ的なものは message に回すように見直した。

be a developer

現在の常用のブラウザはここのところ Firefox beta だったのだが、例の「拡張は署名済みじゃないと入れさせませんぞー!」という仕様変更に対応するため Firefox Developer Edition に鞍替えすることにした。

ということで入れてみた。プロファイルも別扱いになるので、いくつかの設定はやり直す必要があるものの(フォントとか)、それ以外の設定は Sync 経由で復帰することができる。

若干気づいたこと。

  • 全体的なテーマが黒基調になっているのだけど、黒い背景に微妙に色味の違う黒いテキストボックス…みたいに見難くて、色の選択が下手だ。ちゃんとしたデザイナーを雇ったほうがいいと思います
  • cfx 経由で動かすと、browser.xul のパースエラーで起動しなくなる。起動時の xml パースエラー自体は過去にも存在したバグで、ローカライズド版で発生するらしい。それがまた発生したのか直してなかったのかわからないが、とりあえず [cci]LANG=C firefox[/cci] などとするとパースエラーは出ず起動する。まあ従来の Aurora を置き換えたのが Developer Edition だからね。この程度のバグは普通にたくさん包含しているのでしょう