宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
ツリー型タブとJetpackが同時にインストールされているとコンテンツ表示領域に何も表示されなくなってしまう、という問題の原因がやっと分かった。そこからさらに調査をして、表題のような「画面の再描画を任意に停止・再開させる」方法が見つかった。
先にやり方だけ書いとくと、こうするとできる。
var rootContentViewer = window.top
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.contentViewer;
rootContentViewer.hide(); // これで画面の描画が止まる
gBrowser.addTab(); // これによって起こる変化は画面上に現れない
gBrowser.addTab(); // この変化も画面上に現れない
gBrowser.addTab(); // 同上
rootContentViewer.show(); // ここでやっと描画が再開される
24日追記。この方法には重大な弊害があることが分かりました。使用を検討している人はより安全な方法を使うようにして下さい。
以下は、これに辿り着いた経緯のお話。
ツリー型タブには「タブバーを自動的に隠す」機能がある。これを有効にすると、タブバーが小さく折り畳まれる。その状態でタブバーの近くにポインタを持って行くと、タブバーがコンテンツ表示領域に覆い被さるような形で展開される。という機能を実現するにあたって、タブバーの幅を動的に増やす→その分コンテンツ表示領域が縮まるので、幅が変わらないようにコンテンツ表示領域を画面外にネガティブマージンで広げる→表示がズレるので、コンテンツ領域の描画位置をgBrowser.markupDocumentViewer.move(xOffset, yOffset)
でずらす という事をやっているのだけれども、何も考えずにやるとこの工程が全部見えてしまうため、タブバーの表示・非表示が切り替わる瞬間に画面がチラついてだいぶ見苦しいことになる。
そこでこのチラつきをなんとかしたくて、以前、ウィンドウ全体を覆い尽くすHTML Canvasを一番手前に表示してその裏でゴニョゴニョして最後にCanvasを非表示にすることで画面のチラつきを隠すfullScreenCanvas.xulというライブラリというのを作った。せっかくだから他にもいくつかのアドオンで流用した。
しかしよく調べてみた所、このライブラリのやっていることが原因で、冒頭のような衝突が起こっているらしいということが分かった。
opacity: 0
の状態でウィンドウ内に配置している。
position: fixed
でcanvasをウィンドウ全体を覆い隠せる位置に配置する。
Firefox 3以降ならpanel要素が使えるのだからそれで代用してみようか(panel要素はポップアップウィンドウ扱いなので、他の部分に影響を与えないで一番手前に任意の内容を表示できる)、と思ってまずはそれを試してみた(browser要素の中にcanvasを置くのではなくpanel要素の中にcanvasを置くようにした)のだけれども、Windows環境ではそれなりの速度で動いたものの、Ubuntu上ではpanelが表示されたり消えたりするときにものすごいチラつきが発生して、却って逆効果にすらなってしまっていた。
そもそもの問題として、全部のフレームを走査してそれぞれをdrawWindow()
で描画するということをやっていたからその分重くなっていたというのも、無視できないポイントだったと思う。
似たようなことをやっていて似たような悩みを抱えていたであろうアドオンの中で、一番理想に近い事をしていたのがAutohideだった。Autohideではそのものズバリ「描画を停止する」「描画を再開する」といった機能を提供するC++製のXPCOMコンポーネントのバイナリを含んでいて、低層の部分にアクセスして実現しているようだ。しかしソースは公開されていないので、結局何をどうすればいいのかは分からずじまいだった。
調べてみた所nsIContentViewerインターフェースにenableRendering
という真偽値のプロパティがあることが分かった。これをfalseにすれば再描画が一時的に行われなくなるのではないか? と思って、やってみた。
そのビューのnsIContentViewerには、nsIDocShellのcontentViewerプロパティでアクセスできる。また、nsIDOMWindowから辿ってそのフレームのnsIDocShellにもアクセスできる。ということでこれを組み合わせて、トップレベルのウィンドウのnsIContentViewerのenableRendering
をfalseにしてみた。
var rootContentViewer = window.top
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.contentViewer;
rootContentViewer.enableRendering = false;
しかしこれは、期待に反して何の効果もなかった。というかそもそもこのプロパティが本来何を行うためのものなのか自体が分かってなかったんだけど。SetEnableRendering
等のキーワードでC++のコードの部分も検索してみたけど、何がどうなってるのかは結局分からなかった。
それでもうだいぶ諦め気分になって、せめて「内容がズレて表示される→元の位置に表示し直す」という処理がガタついて見えるのだけでもなんとかごまかしたい、と思ってnsIContentViewerの他のメソッドを試していた。gBrowser.markupDocumentViewer.hide()
とやると(gBrowser.markupDocumentViewer
はgBrowser.docShell.contentViewer
へのエイリアス)、確かに名前の通りフレームの内容が描画されなくなる(=グレー1色で塗りつぶされたような状態になる)。そういえばこれはまだ試してなかったな、と思ってトップレベルのフレームのnsIContentViewerのhide()
も試してみた。「ウィンドウ全体がグレー1色で塗りつぶされたようになるのかなー」とか考えてた。
そしたら今度は予想に反して、その瞬間に描画されていた内容が保たれたままで、それ以降の再描画だけが止まった。さらに、show()
メソッドを呼んだら、再描画が止まっていた間の変化が一気に画面上に現れた。おおお!!! 一番やりたかった事があっさりできてるじゃないか!!!!!!
ということで、冒頭に書いたようなコードで描画の一時停止と再開が簡単にできる事が分かった。少なくとも、Windows VistaとUbuntuで試した限りではこういう動作になった。
なんで今まで気付かなかったのか……と思って実験してみたら、Firefox 3.0やFirefox 3.5やMinefield 3.7a1preでは前述のような動作になったんだけど、Firefox 2ではhide()
を呼んだ後にDOMツリーに何か変更があると勝手に描画が再開されてしまった(Firefox 3以降でも、ウィンドウのフォーカスが変わるとかウィンドウ内をクリックするとかのトリガーで描画が再開される)。一番最初に再描画の事を調べた頃はまだ最新版はFirefox 2だったと思うので、それで気付かなかったんだろう。当然Firefox 2にも対応するアドオンではこの方法は使えないという事にもなるんだけど、Firefox 3以降のみをサポートする方針にしたおかげで、今ならこの方法をためらいなく使える。
ということで、今までfullScreenCanvas.xulを使ってた所は全部これで置き換えることにした。無駄な処理が大幅に減ったので、「自動で隠す」等のもたつきがだいぶ小さくなって実用的になったと思う。自分がツリー型タブの「自動で隠す」をあまり使ってなかった(自分で実装したくせに……)のはfullScreenCanvas.xulによるもたつきにイライラしてしまったからだったんだけど、今後は安心して使えるようになるだろうか。
……と書いてましたが、冒頭にも追記したとおり、この方法には重大な弊害があります。弊害の内容、および弊害のない安全なやり方は新しいエントリで紹介していますのでそちらも併せてご覧下さい。
の末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2009-12-18_stop-rendering.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。
writeback message: Ready to post a comment.