宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
Tree Style Tab 0.13.2011121401で、長年の課題だった点に対する改善を入れてみた。現実逃避です。
まず1点、ツリーの復元にすごく時間がかかるという問題。。
今まではSSTabRestoringイベントのタイミングでツリー構造を復元してたんだけど、これだとタブがちょっとずつしか復元されないせいでツリーもちょっとずつしか復元されない→ツリー全体が復元されるまで場合によってはものすごく時間がかかる、という問題があった。でもSSTabRestoringイベントが発行される前の時点ではnsISessionStoreのsetTabValue()
で保存した情報を取り出せないからどうしようもなくて、ずっと放置してた。
これが最近のバージョンのFirefoxではSSTabRestoringより前でもセッションに保存された情報を取り出せるようになってたので、じゃあってんで、真っ先にツリー構造だけ復元するようにしたという次第。
前のエントリではnsIObserverでメッセージを拾うとか何とか書いたけど、結局その方法はやめた。最終的にどうやったのか、という事を簡単に書いておく。
おさらいとして、Firefoxが備えているセッション復元機能は以下のような流れで処理を行っている。
問題は、1と2の間に割り込んで、2で使い回される事になるタブのツリー構造を一旦リセットしないといけないけど、そのためのAPIが無い(どのタブが使い回される事になるのか、を知る方法が全く無い)っていう事。
PurgeHistory()
というメソッドが呼ばれていて、nsISHistoryにはnsISHistoryListenerインターフェースを備えたリスナを登録できるようだったので、これを使えばいけるか!?と思ったけど、nsISHistoryには1個しかリスナを登録できなくてアドオンでリスナを追加するとFirefox自身が追加したリスナが失われてしまって、全体的に動作がぶっ壊れてしまった。とかなんとかああでもないこうでもないといじり回していて、もう最後の手段という事で、<browser/>
のstop()
を置き換えてフラグを立てたり下ろしたりするようにした。nsSessionStoreは1と2の間で、使い回す予定のタブをプライベートメソッドのrestoreHistoryPrecursor()
に渡してそれらの内容を初期化するという事をやってて、その中で必ずstop()
を呼んでいたので、stop()
が呼ばれた時にJavaScriptのスタックを辿って、restoreHistoryPrecursor()
から呼ばれていたら「このタブはこれから使い回されようとしている」というフラグを立てるようにした。restoreHistoryPrecursor()
は他の場所からは絶対に呼ばれないから、これが一番確実だと思う。
そうすると、
restoreHistoryPrecursor()
のタイミングで、「これからセッション情報が復元されようとしているタブ」にフラグが立つ。という具合にして、個々のタブのセッションの復元完了を待たずに一気にツリー構造を復元できるようになるわけです。
この改善は、Firefoxのセッション復元APIを使ってるSession Managerとの併用でもちゃんと活かされる。Tab Mix Plusみたいに独自のオレオレセッション復元機能を持っててそれを使ってる環境は……知らない。多分動かないだろうけど対応する気力無い(そもそも、僕自身Session Managerは使っててもTMPは使ってない)。
次、2点目。タブで開いてる内容がPDFだったりFlashだったりすると「タブバーを自動で隠す」が機能しないという問題。
ポインタの位置に応じて自動的にタブバーを表示したり隠したりという事をやるためのベタなやりかたは、Firefoxのウィンドウ(XULドキュメント)でキャプチャリングフェーズでmousemoveイベントを監視するという方法だ。ポインタがタブバーの近くに来たらタブバーを表示して、コンテンツ領域の上に載ったら(タブバーから離れたら)タブバーを隠す、という感じ。AutoHideというアドオンはそうしてたし、ツリー型タブもそうしてる。
ところが、何度か中野さんが解説されてた気がするけど、Firefoxはプラグイン(FlashとかPDFとか)の描画領域の上で発生したクリックやら何やらのイベントは全部プラグインが握りつぶすようになってる。そのため、普通にmousemoveイベントを監視してても、プラグインの領域にポインタが載ってる間は何もイベントが発生しない。ポインタがタブバーから離れた事はmouseout等で検知できても、コンテンツ領域に載ったという事を検知する事ができない。
(ちなみに、ツリー型タブではタブバーの「近く」にポインタがあるときはタブバーを隠さないで、ちょっと離れたら初めてタブバーを隠す、という風にしている。そうしないと、タブバーの幅を変えるためにスプリッタをドラッグしようとしても、ちょっとポインタがぶれただけですぐタブバーが隠れてしまってストレスが溜まってしょうがない。なので、mousemoveでポインタの座標を監視するという方法に頼らざるを得ないのです。)
同じ理由で、マウスジェスチャ系のアドオンもプラグインがあるとジェスチャがまともに動かないという事が結構あるみたい。そこをどうにかしようとすると、CとかC++とかでコンポーネントを作ってもっとプラットフォーム寄りの所でポインタの位置を拾うしかなくて、「プラグインの上でもジェスチャできる」が売りのMonkey Gesturesは実際そのようにしてたと思う。
ネイティブなコンポーネントを書くというのは嫌っていうかそんな手間かけたくなかったので代わりにどうやったかっていうと、すごく単純な話で、background: rgba(0,0,0,0.01) !important; -moz-appearance: none !important;
にした<panel/>
をコンテンツ領域の上に重ねて開くようにした。たったそれだけ。そうするとXULの要素の上だから普通にmousemoveイベントが発生して、ポインタの正確な位置を取れるようになった。
透明なポップアップを表示する時には、セオリーというか注意点がいくつかある。
background: transparent !important;
で完全な透明にしてはだめ。完全に透明な<panel/>
は何のイベントも拾ってくれない。なので、rgba(0,0,0,0.01)
(ものすごく薄い半透明の黒)のように、何かとにかく色を付けておく必要がある。background: transparent !important;
で完全透明にしつつborder
でちょっとだけ線を表示して、<panel/>
の中に不透明な部分を作ってやる。
<panel/>
の左右にだけ枠線を付けるという風にして、領域が矩形にならないようにする。透明な<panel/>
を使う方法にはいくつか弊害があって、今まではそれを気にして採用を避けてた。が、今回のアップデートではそれぞれ「できる所までは頑張ろう、どうしても無理な所はもう知らん」というスタンスで割り切ることにした。
<panel/>
がクリックされてしまうと、「クリックしたのに何も起こらない」という状況が発生してしまう。
<panel/>
を使うようにした。タブバーが隠れて<panel/>
が閉じられた後でなら普通にクリックできるので、タブバーが隠れる前にコンテンツ領域をクリックするようなせっかちな人の事はもう諦める事にした。<panel/>
以外の部分(ツールバーボタンなど)をクリックすると、環境によっては、透明な<panel/>
が閉じられるだけで終わってしまう。
popupBoxObject.setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_NO_CONSUME)
である程度は抑制できるっぽい(Ubuntu 10.04LTSのcompizだと、Firefoxのウィンドウの中に対してはclick-throughが通用するようになったが、他のアプリケーションのウィンドウをいきなりクリックした時は相変わらずだった)。どうしても駄目な時については、もう、諦める。なんで方針を変えた(安全に完璧に提供できない弊害の多い方法は使いたくない→弊害があっても使う)かというと、「同じ要望がもう数えるのも嫌になるくらいしょっちゅう来ててウンザリしたから」っていうのが最大の理由だ。
いや、つまり「PDFとか表示してるとタブバーが隠れてくれない」問題は、割と誰でもぶち当たってしまう問題なわけですよ。それに対して、実際に調べたわけではないけど、多分上に挙げたような「弊害」が実際に致命的な問題になるような人は多分そんなに多くないだろうと思うわけですよ。どっちの人を大事にしたらいいかっていう事で天秤にかけて、前者を救う事にした。という話なのです。
そういう意味では、TMPの独自のセッション復元機能を使ってる人を見捨てるってどうなんっていう言い方もあるだろうけど、あんなもん(TMP独自のセッション復元機能)ぶっちゃけ捨てていいですよ。Firefox本体のセッション復元機能に勝ってる部分、ありますか? ないですよね? いやよく調べないまま書いてるんだけどさ。
の末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2011-12-14_fastrestore.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。
writeback message: Ready to post a comment.