Home > Latest topics

Latest topics 近況報告

たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。

萌えるふぉくす子さんだば子本制作プロジェクトの動向はもえじら組ブログで。

宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

Page 7/248: « 3 4 5 6 7 8 9 10 11 »

evalが危険でそれ以外の方法が安全だと思ってる人へ - Feb 08, 2010

先日、ソース表示タブのアップデート版をAMOにアップロードしたところ、公開申請が却下されました。「不必要なeval()が多すぎる。拡張機能におけるeval()の5つの間違った使い方(原文:Five wrong reasons to use eval() in an extension)をよく読んで出直してこい。(大意)」という感じのコメントが添えられていました。

また、ツリー型タブのアップデート版も別の人から同じコメント付きで公開申請を蹴られました。

全文+コメントを翻訳するくらいには当該エントリを読んだ僕ですから、今更読み返すこともないのですが、こう何度もこのエントリを引き合いに出して申請を蹴られると正直腹に据えかねる物がありますので、自分の考えをちょっとまとめておきたいと思います。

つたない英語力で頑張って英語に翻訳してみました。

evalの使い方に「正しい」も「間違っている」もない

まず何より明らかにしておきたいのは、言葉尻を捉えて反論するのもどうかと言われそうですけれども、evalの使い方には「正しい」も「間違っている」も無いということです。

例えばHTMLの仕様書には、em要素は強調を意味するとか、この要素はこの要素の中には記述できないとかの、色々な定義が書かれています。その定義から外れた使い方をすれば、確かにそれはinvalidな(間違っている)HTMLと言っていいでしょう。

では、evalはどうか。ECMAScriptの仕様書(リンク先のページには3rd Editionと書いてあるけどPDFは5th Editionです)15.1.2.1 eval (x)の項には、evalの仕様としては「与えられた引数をスクリプトとして実行する」という、挙動についての取り決めだけが書かれています。「何のためになら使ってよい」という風な記述は一切ありません。当たり前といえば当たり前ですが。

そこにあるのはただ、「その仕様の通りに動作した結果、何が起こるのか?」という結果についてのリスクの高低だけです。多大なリスクを伴う使い方、全くリスクを伴わない使い方、色々な使い方が考えられますが、動作する限りはそれらはすべて「仕様通りの正しい使い方」です。なので当該エントリのタイトルも本当は「evalの5つの危険な使い方」などとするのが妥当だと僕は思っています。

こういう場合にこういうデメリットがあること考えられる、だからこのような対策が必要だ、あるいはメリットとデメリットを天秤にかけないといけない、というのがリスク管理の考え方です。「間違っているから駄目だ」と思考停止するのは、天秤にかける事すら否定するということです。

例えばSSLを使っていない普通のHTTP接続で閲覧しているWebページの内容は、「第3者によって改竄されている可能性」を完全には否定できません。だからといって、そのページに書かれている情報は一切全く信用してはいけない、ニュースだろうが料理のレシピだろうが一切アテにしてはいけない、という考え方が健康的と言えるでしょうか? また、SSLは接続している先のWebサイトが本当にその運営者が運営するものであるかどうかということは保証してくれますが、運営者が善人か悪人かということまでは保証してくれません。相手が善人であると保証されないのだからネットで買い物はしたくない、カップラーメンもマンガの本もホッチキスの針も、詐欺に遭うのが怖いからネット経由では絶対何も買わない、という考え方は妥当でしょうか? そこまで行くともはやインターネット<ruby><rb>恐怖症</rb><rp>(</rp><rt class="読み">フォビア</rt><rp>)</rp></ruby>でしょう。

前出のエントリで「この使い方は正しい」とされている以外の使い方がされているevalを、個々のケースごとのリスクの高低を無視して一律で否定するのは、僕には単なるeval<ruby><rb>恐怖症</rb><rp>(</rp><rt class="読み">フォビア</rt><rp>)</rp></ruby>としか思えません。

そのevalには本当に、Webからやってきたコードが混入する恐れがあるのか?

前出のエントリで挙げられている5つのケースのうち、1番目(JSON形式のデータのパース)、2番目(プロパティ名が動的に変化する時に、そのオブジェクトのプロパティにアクセスするため)、4番目(HTMLやXULにインラインで記述されたイベントハンドラの実行)の3つのケースでは、Webからやってきた信頼されていないコードを特権付きで実行してしまう可能性があることに触れています。これはWebアプリケーションにおいてのSQLインジェクションに対する注意喚起と同様の物と言えます。

この観点でevalの使い方に神経質になることは大事です。僕も、evalを使うすべての人は、この点に気をつけなくてはならないと思います。この理由で公開申請が却下されたのであれば、その判断は実に妥当だと思いますし、反論の余地は全くありません。

では、今回のケースはどうでしょうか。

ソース表示タブツリー型タブでは、evalはFirefox本体の関数を書き換えるためにだけ利用しており、そこにWebから取得したコードが混入するとは考えにくいです。もしこの理由で申請が却下されたのであれば、それは、レビュアーがきちんとコードを読まずに、単に機械的チェックにおけるevalの警告だけを見て判断を下したという可能性が出てきます。これは言い換えると、「evalが機械的チェックで検出されたからNG、検出されなかったからOK」という風な安直な判断を下すようになっている可能性があるということです。

もしそうなのであれば、そのようなレビュー姿勢は宜しくないものだと自分には思えます。そういう機械的なチェックをくぐり抜けるための抜け道はいくらでもあります。難読化されたコードなどはその典型ですし、難読化されていない「読みやすそうなコード」でも、ぱっと見分からないようにごまかすのは簡単です。もし機械的なチェックの結果だけを見て判断を下しているのであれば、そういうケースによる危険なコードの混入の可能性は否定できないことになります(そういう場合、時間をかけてでも・判断が遅くなってでもきちんと精査するべきでしょう)。そういうケースに対しても対処できるようにというのが、わざわざ人の手でレビューすることになっている理由の1つであると自分は信じています。

evalによる関数の書き換えのリスクは、それ以外の方法のリスクと比べてそんなに高いのか?

ソース表示タブツリー型タブで使われているevalはすべて、前出のエントリで挙げられている5つのケースのうち5番目のケース、「Firefox内で使われている関数の内容を書き換えるため」に該当するものです。前出のエントリでは、この使い方を以下の理由から非難しています。

  • 書き換える対象の関数の内容が変化した時に、ブラウザ自体がまともに動作しなくなってしまう恐れがある。
  • せっかく修正されたセキュリティ上の問題を、再び持ち込んでしまう恐れがある。

そして、代わりに推奨するやり方として以下のような「無理のないやり方」を挙げています。

  • 関数を別の関数で置き換えた上で、置き換え後の関数内で元の関数をfunc.apply(this, arguments)で呼ぶ。
  • Object.watch()を使って、特定の変数やプロパティの値が変化したタイミングで処理を行う。
  • そういう「無理のないやり方」では実現できないことをしようとしているのであれば、もう素直に諦める。

換言すると、前出のエントリの筆者は「このような無理のないやり方の方が、evalによる関数の置き換えに比べて、前述のような事態が発生するリスクが少ない」と述べていると言えます。

僕には、これはアンフェアな言い方と思えます。リスクの多寡で問題を語るのであれば、ここに挙げられていないリスクについても考慮するべきでしょう。そうすることで初めて、僕のような人が「evalによる関数の書き換え」という手法を選択している理由が明らかになります。僕だって、考えなしにevalを使っているわけではありません。メリットとデメリットを天秤にかけた上で、その方がリスクが小さいと考えてevalを使っているのですから。

リスク:Firefox自身の機能や他のアドオンと競合する可能性について

アドオンを「機能を実現する方法」の観点で見ると、以下のように分類できます。

  1. Firefoxや他のアドオンが提供するAPIに則ったアドオン。ツールバーへのボタン追加、サイドバーパネルの追加、nsIContentPolicyによるコンテンツフィルタリング(AdBlock Plus)、Jetpack Featureなど。
  2. Firefoxや他のアドオンがAPIを提供していない箇所に改変を加えるアドオン。
    1. XBLを使って機能を加えるアドオン。
      1. 新規にXBLを適用するアドオン。
      2. 既存のXBLを上書きするアドオン。Tab Mix Plusなど。
    2. JavaScriptの関数のはたらきを変更して機能を加えるアドオン。
      1. 関数を置き換えるアドオン。
      2. Object.watch()等で本来の処理に割り込むアドオン。
      3. 関数をevalで書き換えるアドオン。
    3. CSSでXUL要素の見た目を変えるアドオン。Personas、テーマなど。

1つのアドオンが複数の分類にまたがる場合も多いのですが、このうち「他のアドオンとの競合の可能性」という点で本当に低リスクであると言い切れるのは、1の「APIに則ったアドオン」の枠から外れないものだけです。それ以外はどんなアドオンであっても、Firefox自体の挙動や他のアドオンに何らかの影響を与える可能性があります。テーマですら例外ではありません。

前述の「無理のないやり方」の説明では、さもそのようなやり方を採用することによって競合のリスクが全面的に低減されるかのような書き方がなされていますが、場合によってはこの方が却って競合してしまう場合すらあります。実際、Firefox 3.6上でサイドバーの開閉状態の変化を捕捉するためにIDがsidebar-boxである要素のhiddenプロパティに対してObject.watch()で監視を行おうとしたところ、XUL要素の挙動が破壊されて、サイドバーの開閉自体が行われなくなってしまいました。(これは結局、DOMAttrModifiedイベントを監視してhidden属性の値の変化を見るという方法で回避することにしました。)

別の例として、「無理のないやり方」で関数を丸ごと置き換えられてしまうと、evalで元の関数を書き換えようとしていた他のアドオンが、関数を書き換えられないせいで期待通りに動作しなくなってしまうということも考えられます。……「そんな邪道なやり方をしている方が悪いのだ」という非難は無しですよ? 他のアドオンに与える影響を考えていないのは、お互い様なのですから。

また、これはリスクとは別の話ですが、「evalによって特定の箇所に特定のコードを注入することによって関数の定義内容を変えてしまわなければ実現できない機能」というものもあります(前出のエントリに対するコメントでも、Dorando氏が例を示しています)。そのような「無理なやり方」でやらなければならない事はするべきではない、というのが前出のエントリの著者の結論のようですが、後述する物も含めたリスクの点で明らかな優位性を主張できないのであれば、「無理なやり方はするべきでない、なぜならevalの間違った使い方だからだ」「evalの間違った使い方はするべきでない、なぜならそれは無理なやり方だからだ」というトートロジーに過ぎません。

リスク:Firefoxのバージョンが変わると予期しない結果になる可能性について

この点のリスクは、evalによる関数の書き換えでも、「無理のないやり方」と呼ばれている関数の置き換えでも、同様に存在します。前出のエントリでは「置き換え対象の関数の内容が変わったらどうするんだ」ということを指摘していますが、それを言えば、関数の名前が変われば「無理のないやり方」も破綻します。セキュリティアップデートでは関数の名前やそれぞれの働きが変わることはない、というのはただの思い込みです。そんなルールはどこにもありません。

また、「置き換えた関数A」→「置き換えた関数B」の順で処理が行われることを期待してコードを書いてる場合に、「置き換えた関数A」だけが実際には置き換えられなかったとすると、「置き換えた関数B」が動作する上での前提が崩れますので、その時どんなトラブルが起こるかは予想できません。これはFirefoxのバージョンが変わった時だけでなく、他のアドオンと同時に利用した時の競合についても全く同じ事が言えます。

いずれの場合にせよ、Firefox本体や他のアドオンのバージョンアップに対して、改変を加える箇所のソースコードをその都度調査し直さなければならないという、メンテナンスのコストの問題は、どちらにも共通して存在しています。「evalと関数置き換えのどちらの方が元のコードの変化に強いのか」ということを論じてもあまり意味はなく、どちらの手法を使うにせよ、Firefoxのバージョンアップに際してきちんと動作を検証しているかどうかや、前提が崩れた時のためのフェイルセーフがきちんと考えられているかどうかの方が、リスクに対する備えとしては重要且つ効果的なのではないか? というのが僕の考えです。

Firefoxのアドオンやテーマの正体は「動的に適用されるパッチ」です。ソースコードに対して、そこら辺に転がってる野良パッチをあててビルドする、というのはLinuxを使ってる人にはよくある光景ではないかと思いますが、「パッチが書かれた時のバージョンと違うバージョンのソースにパッチを当てること」がいかに危険かは、言うまでもないでしょう。Firefoxのアドオンを「アドオン作者によって動作確認されたバージョン」以外のバージョンにインストールすることは、それと全く同じ事です。

リスク:メンテナンス性、ソースの可読性が低下する可能性について

evalによる関数書き換えの場合、ソース中には「書き換える対象の関数名」「書き換えたい前のコード片」「書き換え後のコード片」だけが書かれることになります。

このコードを解読するためには「書き換える対象の関数」の内容を参照する必要があるため、その内容が頭に入っていない場合(大抵はそうでしょう)、解読が難しくなりがちです。その代わり、ソースコードの量自体は必要最低限になります。また、書き換える対象がロジックに絡まない「widthをheightに書き換える」という風な部分なのであれば、コードの読みやすさという点でもさほど問題は無いと言えます。このあたりは、「evalで関数を書き換える時の、互換性や後々のメンテナンス性を高く保つためのノウハウ」が重要になってくる所です。

関数の置き換えを行う場合も、ソースコードは結局は断片的な物になります。「前処理の関数」「元の関数をラップする関数」「後処理の関数」「元の関数の中で特定のプロパティに変更が加えられた事を捕捉して処理を割り込ませる関数」などが細切れに定義されますし、また、元の関数の処理結果の受け取り方法として「戻り値」以外の情報を使うのであれば、結局は元の関数の内容が頭に入っていないといけないことになります。

元の関数の中に変更を加えなければどうにもならない機能をどうにかして実現したい場合(前出のエントリのコメントで指摘されているようなケースなど)、こちらの手法にこだわるのであれば、「元の関数に必要な変更を施した後の関数」をアドオンの中で定義しておく以外に手はありません。そうなると、Firefoxや他のアドオンのコードと全く同じコードがそのアドオンの中に大量に含まれるようになってしまいます。これは、以下のような問題に繋がります。

  • それぞれのライセンスが衝突してしまうために、コードを丸ごと引用できないという事態が起こり得る。その機能を実現できない、という苦渋の決断を迫られる。
  • 前出のエントリ内でも指摘されている「せっかくFirefox(や他のアドオン)の側でセキュリティ上の問題が修正されても、関数を古いバージョンのそれに基づいた改変版に丸ごと置き換えてしまうことで、再びセキュリティ上の問題が持ち込まれてしまう」問題が起こり得る。
  • 単純にソースの行数が増加する事それ自体も問題となる。

リスク:公開申請時のレビューが通りにくくなる可能性について

これは「メンテナンス性、ソースの可読性が低下する可能性」にも共通する話です。AMOのエディターは最終的にはコードレビューによってそのアドオンの安全性を精査する必要があり、コードがトリッキーなものであればあるほど、また、コードの行数が増えれば増えるほど、コードレビューは大変な物になります。その意味で、関数の部分的な書き換えも関数全体の置き換えも、コードレビューの手間が増えるという点では一長一短です。

もっとも、今回の事例のように「evalを使っていること」を理由に公開申請が却下されることが相次ぐのであれば、eval無しでトリッキーで可読性の低い分量の多いソースコードの方が、比較的レビューが通りやすいという事になるのかもしれませんけれどもね。その場合、これもやはり「レビューを通してもらうためにはevalを使わない方がいい、なぜなら、evalを使っているとレビューが通らないからだ」という、何の説明にもなっていないトートロジーになる訳ですが。

まとめ

上記のような理由で、僕はソース表示タブツリー型タブの中で使われているevalが不必要な物ばかりであるとは考えていません(むしろどれも必要だからそうしているというのが自分の立場です)し、それらのevalの個々の用法が「これを読んで出直せ」と提示されたエントリの中で書かれている「代替案」に比べて目立ってリスキーであるとも思っていませんし、その「代替案」で代替できるとも思えません。

「evalの方が危険だ」と言っている人のうち何割の人が、本当にMozillaのコードを読んだことがあるのか、読み続けてきたのか、APIが用意されていない箇所でブラウザの機能を拡張する事に取り組んできたのか、継続的にFirefoxのバージョンアップに追従し続けたことがあるのか、他のアドオンとの競合を解消する方法を模索し続けてきたのか、僕には疑問に思えます。

XPCOMコンポーネントのIDL定義で「FROZEN」というフラグが立っている物以外のすべてのAPIは、いくらでも変更される可能性があります。FROZENになっているAPIは、Preferenceの読み書き、W3CのDOM関連など、全体から見れば本当にごく一部だけです。ブラウザ内のJavaScriptのレイヤで定義された関数に至っては、それが変更されない保証などどこにもありません。「Firefoxのバージョンに依存しないAPIが提供される」と思われていたFUELですら廃止される可能性が出てきている、Mozillaはそんな世界なのです。それで「evalの方が危険だ、同じ名前の関数で置き換える方がマシだ」と言うのがどれほど馬鹿げた話か。

ですので僕にとっては、evalの使用を理由として公開申請が却下された今回の出来事に対しては、不当な判断を下されたという思いが強いです。

そろそろFirefoxからChromeへの移行を本気で検討した方がいい気がしてきた - Feb 07, 2010

B.B.S/2628 "タブバーのスクロール後タブバーが正しくリペイントされない" - outsider reflex

Tree Style TabとJetpackを入れるとタブバーをスクロールするとタブの残像が残ったままになる。

[str]
1. Firefox3.6を新しいプロファイルにTree Style Tab-0.9.2010020502とJetpack-0.7をインストールし起動する
2.タブバーがスクロールするように多数のタブを開く
3. スクロールバーまたはホイールスクロールによりタブバーをスクロールする

[actual]
タブバーが正しくリペイントされずタブの残像が残る

[expected]
正しくリペイントされる。

Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.2pre) Gecko/20100206 Namoroka/3.6.2pre ID:20100206050755

B.B.S/2629 "Re: タブバーのスクロール後タブバーが正しくリペイントされない" - outsider reflex

widgetがどうとかのGecko 1.9.2での修正(異常に縦に長いページのレンダリングが壊れる問題に対する修正)の影響だと思われます。JetpackではSlidebarの実装のためにtabbrowserと同じ大きさのbrowser要素をopacity:0の状態でtabbrowserの下に重ねていますが、こいつのせいでレンダリングがおかしくなっています。

タブのcollapsed属性をいじってるTMPの多段タブならともかくTab Kitでも問題が起こらないのはなんでなんだ?と思ってスタイルシートを調べてみた所、scrollboxでスクロールさせてると駄目で、その中のbox.scrollbox-innerboxに対するoverflow:autoでスクロールさせてる場合はこの問題が起こらないようです。

しかしscrollboxをoverflow:autoにした場合はnsIScrollBoxObjectのインターフェース経由でスクロール位置の操作が可能なのに対し、box.scrollbox-innerboxをoverflow:autoにした場合はnsIScrollBoxObjectのインターフェースが取れません。結果、画面の外に新しいタブが開かれた時にその位置まで自動的にスクロールさせるという事ができなくなります。

「Jetpack 0.7と組み合わせても問題が起こらないが、画面外にタブが開かれるとフォーカスを見失う」
「画面外のタブにフォーカスが行くと自動的にそこまでスクロールしてくれるが、Jetpack 0.7とは同時に利用できない」
どっちがいいでしょうか。僕は後者です。(どうせ現状のJetpackはなくなるそうだし、その過程でなんとかしてもらいたいと思ってます)

掲示板の方では「現状のJetpackはRebootで置き換えられるそうだからそっちに期待」的な事を書いたけど、下手したらReboot後でもずっとこのままなのかもなーという風な悲観的な予想も捨てきれずにいます。

norahさん曰く、同じ問題がサードパーティ製のテーマとJetpackとの組み合わせにおいても発生するそうです。既存のアドオン、既存のテーマについては「冷遇」して、JetpackとPersonasへの移行を促したいという事をMozillaの偉い人が言ってたらしいけど、実装レベルで着々と実力行使が進んでる感がありますね。

対するGoogle Chromeでは、タブの縦置きが試験的に実装され始めてるそうです。こんな事を書いたばかりですが、そろそろ本気でGoogle Chromeへのエクソダスを図った方がいいような気がなんとなくしてきました。

今までできていたことが少しずつできなくなっていく、締め付けが強まっていくストレスというのは、いざ自分が締め付けられる立場になるととても辛いものですね。それならいっそ、最初から制限がきつくても将来性のある新天地に移ってしまおう、と考えても変じゃないよなあと思いました。Mozilla SuiteからFirefoxに移行した時以来、レンダリングエンジンの違いで言えばDonutからMozillaに移行した時以来なので、今更それがほんとに可能なのかという不安はありますが。

ツリー型タブのGoogle Chrome版は作らないの?(Tree Style Tab for Google Chrome) - Feb 05, 2010

Q

Any plans to add Tree Style Tab to Google Chrome or Chromium?

ツリー型タブをGoogle ChromeもしくはChromiumに移植する計画は無いの?

A

Now I have no plan to export Tree Style Tab to Chrome. The fact "Tree of tabs are permanently shown, without any operation" is the best benefit of Tree Style Tab for me. Currently there seems to be no API to do it. I don't want to use such a feature if clicking on a toolbar button (or any other operation) is required to show the view.

Moreover, it is the main reason of my development that I want to improve my PC environment. Because I'm not planning to switch to Chrome, I will not develop Chrome extensions.

By the way, there are some similar extensions VerticalTabs, Tab Menu, and Tree Style Tabs (Beta) (Note: it has same name but it is NOT my product!). They possibly help you.

Updated at 2012-11-22: More extensions, Sidewise Tree Style Tabs and Tabs Outliner are available now. They are good alternative of Tree Style Tab on Google Chrome.

今の所、私自身がツリー型タブをChromeに移植する予定はありません。私は、「ツリー型タブ」の便利な所は「タブの一覧(ツリー)が常時表示されている」点だと思っています。現状のChromeの拡張機能向けのAPIでは、そのような拡張機能は残念ながら作ることができないようです。ボタンをクリックしないとツリーが表示されないような機能は、自分は使いたくないです。

また、私自身の開発のモチベーションは主に「自分が日常的に使う環境を使いやすくしたい」という所に支えられています。私は今の所Chromeを常用するつもりが無いため、Chrome用拡張機能を開発するモチベーションがありません。

なお、似たような拡張機能でVerticalTabsTab MenuTree Style Tabs (Beta)(注:同じ名前ですが私が開発した物ではありません!)というものがあります。まずはこれらを試してみてください。

2012-11-22追記:その後、Sidewise Tree Style TabsおよびTabs Outlinerという拡張機能が公開されています。これらを使うと、ツリー型タブにより近いユーザー体験をGoogle Chrome上で得られます。

ツリー型タブでタブバーの「新しいタブ」ボタンを隠せなくなった(How to hide "New Tab" button in the tab bar?) - Feb 05, 2010

Q

I just updated the Tree Style Tab addon and the new tab button that used to be hidden is now visible. Is there a way to hide it? I always use Ctrl-T, so it just wastes space.

ツリー型タブを更新したら、今まで隠れていた「新しいタブ」ボタンが表示されるようになってしまいました。これを隠す方法はありませんか? 私はCtrl-Tをいつも使っているので、このボタンは不要です。

A

Sorry, I removed the option because it is not a feature related to "tree". Instead, you'll hide the button by customizing your userChrome.css like:

.tabs-newtab-button {
    display:none !important;
}
/* hide the "shadow" */
tabbrowser
  .tabbrowser-strip
  .tabbrowser-tab
  .tabs-container
  .scrollbox-innerbox {
    background-image: none !important;
}

Note: These examples work on Tree Style Tab 0.8.2009100101 or later.

すみません。「ツリー」と関係ないものだったので、その機能は削除してしまいました。代わりにuserChrome.cssに上記のようなスタイル指定を書き加える事で、ボタンを非表示にできます。

縦置きタブバーの下にサイドバーを統合するUnified Sidebarをリリースしたよ - Feb 05, 2010

とりあえずMozilla Add-onsに置いた

ツリー型タブに対して表題のような要望が寄せられることが何度かある。ツリーと関係ないからツリー型タブにそういう機能を入れる気はないんだけど、まあそういうニーズはあるんだろうなというのは理解できる。なので以前にuserChrome.cssでそれっぽくする方法を考えてみたりもした。その時の結論としては「ちゃんとアドオンにしないと駄目だね」という感じだったんだけど、結局誰もアドオン化してくれてなかったようだったので、仕方がないから自分で作った。

このアドオン自体はタブを縦置き表示する機能は持ってません。単にサイドバーの表示位置を変えるだけです。タブを縦置きするにはuserChrome.cssで頑張るか以下のアドオンを使って下さい。

他にもタブを縦置きするアドオンがあれば、なるべく連携するようにはしたいので、このエントリにコメント付けるなどして教えて下さい。

Firefox上のあらゆる操作を一次元で記録してアンドゥ・リドゥ可能にするライブラリ(実験段階) - Jan 04, 2010

ツリー型タブのAPIでこんなのが欲しいというのがあったら言っておくれと書いたところ、Alice0775さんからタブバーのドラッグ操作とかツリーのドラッグ操作とかをアンドゥする機能が欲しい(大意)という要望をいただいた。

そういえばこの前のMozilla勉強会の後の懇親会で、新しいタブが開かれたり新しいウィンドウが開かれたりした時に「戻る」ボタンで元のタブや元のウィンドウに戻れないことについて、あらゆる操作がアンドゥ可能になってないといけないんじゃないの?とかそんな感じの話が出ていたと思う。なので実験的にそういう物を作り始めてみた自動テスト)。

var current = TreeStyleTabService.currentTabbarPosition;
window['piro.sakura.ne.jp'].operationHistory.doUndoableTask(
  // やり直し可能にしたい処理
  function() {
    TreeStyleTabService.currentTabbarPosition = newPosition;
  },

  // 履歴の名前(省略可)
  'TabbarDNDOperations',

  // ウィンドウごとの履歴の場合の対象ウィンドウ(省略可)
  window,

  // 履歴の項目
  {
    // 項目名
    label  : 'タブバーの位置変更',
    // アンドゥの時に実行する内容
    onUndo : function() {
      TreeStyleTabService.currentTabbarPosition = current;
    },
    // リドゥの時に実行する内容(省略可)
    // →省略時は上記の「やり直し可能にしたい処理」が自動的に
    //   onRedoとして登録される
    onRedo : function() {
      TreeStyleTabService.currentTabbarPosition = newPosition;
    }
  }
);

という感じでアンドゥ・リドゥ時の動作を登録して、window['piro.sakura.ne.jp'].operationHistory.undo('TabbarDNDOperations', window)とかwindow['piro.sakura.ne.jp'].operationHistory.redo('TabbarDNDOperations', window)とか書くとヨロシク処理してくれる……という風な感じ。「あらゆる操作を一次元で記録して」とタイトルに書いてるけど、自動的に記録するんじゃなくてアドオン作者が手作業で記録する前提で、「履歴の記録」「履歴の呼び出し」の所を管理する手間を軽減するだけのライブラリなんで、そこの所はお間違えなきよう。

関数をそのまま登録するというのが乱暴と言えば乱暴なんだけど、柔軟性を高くしようと思ったらこうするのが手っ取り早いかなーって思いまして。一応ウィンドウごとの履歴とグローバルな履歴の両方を持てるようにしてみてる。イメージ的には、Adobe製品のヒストリ機能のような物を目指してる。

で、枠組みは用意したんだけど、タブバーの位置の移動みたいな単純な機能はいいとして、ツリーの移動みたいなややこしい物をどうやってアンドゥ・リドゥさせるかで暗礁に乗り上げてる。

  • マルチプルタブハンドラとの連携も考慮する必要がある。
  • ツリーの移動だけじゃなく複製も考慮しないといけない。
    • 複製のアンドゥは複製されたタブのクローズでいいけど、リドゥは? もう一回複製させる?
  • 移動・複製元のタブの一部が閉じられていた場合を考え始めると破綻する。
    • ツリーの移動だけを記録すると、ツリーを移動した→移動先のツリーのタブを1つ閉じた という操作の後でアンドゥした時に、移動されたツリーを元に戻せない(元のウィンドウに戻すべきタブが見つからなくなる)。
    • となると、タブを閉じる・タブを開く・(ツリーに関係なく)タブを移動する、といったあらゆる動作を記録しないといけない。

なんとなく、ツリー型タブからは分離して「タブバー上のあらゆる操作をアンドゥ可能にするアドオン」を新しく作った方がいいような気がしてきた。で、ツリー型タブが入ってる時はそいつのアンドゥ履歴の中に「タブバーの位置変更」の項目が混ざってくる、みたいな連携の仕方。

ツリー型タブとTabberwockyを同時に入れられるようにしたよ - Dec 25, 2009

Tree Style Tab 0.8.2009122501Tabberwocky用のコードを入れました。とりあえず、選択範囲のリンクをタブで開く機能と、新しいタブを現在のタブのすぐ隣に開く機能については、ちゃんと動くことを確認してます。他にうまく動かない機能があったら言って下さい。Tab Mix Plusに比べたら全然コードの量も少ないんで、たぶん対応できると思う。

Tab Mix Plusと組み合わせた時の問題もどうにかしようと思ったんだけど、見てみたらTMPの中にTST用のコードが入ってたので、さっくり諦めた。お互いがお互いに手を出すともはや収拾が付かなくなるから。なのでちょうどいい機会だと思って、TMPのフォーラムに「いいかげんこの状況なんとかしようよ」的な提案を書き込んでみた。そのついでに、ソースコード中で「PUBLIC API」と書いておきながら説明を書き忘れてたAPIをドキュメントに追加した

他のアドオンと連携を取りやすくするためのAPIを加えるのはやぶさかじゃないので、要望があれば是非言ってください。最近の例では、TreeStyleTabService.currentTabbarPositionTreeStyleTabService.treeViewEnabledはメールで「こういう事をしたいんだけどどうすればいいのさ」と問い合わせを受けたので追加したAPIです。

画面の描画内容を一時的にロックしておいて、裏であれこれして最後にまとめて描画させる方法の再考 - Dec 24, 2009

画面の描画を一時停止する方法を先日書いたけど、案の定というかやっぱりというか、重大な弊害があることが分かった。また、その弊害にぶち当たらない安全なやり方も見つけることができた。

安全に画面の描画を一時停止・再開する方法は、以下の通り。

var baseWindow = window.top
                   .QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIWebNavigation)
                   .QueryInterface(Ci.nsIDocShell)
                   .QueryInterface(Ci.nsIBaseWindow);
baseWindow.setPosition(window.innerWidth, window.innerHeight); // これで画面の描画が止まる

gBrowser.addTab(); // これによって起こる変化は画面上に現れない
gBrowser.addTab(); // この変化も画面上に現れない
gBrowser.addTab(); // 同上

baseWindow.setPosition(0, 0); // ここでやっと描画が再開される

以下、前のエントリに書いたやり方にどういう弊害があるのか、および、このエントリで紹介するやり方の方がどのくらい安全なのかについて詳しく説明する。

続きを表示する ...

ツリー型タブ0.8.2009122103 - Dec 21, 2009

ツリー型タブはバグをつぶし始めたらきりがなくなってきたので、適当なところで打ち切ってリリースしました。バグ報告への返信で「I'll update as soon.」とか書いちゃったからというのもある。

画面の描画内容をロックするアレについては、結局ライブラリ化しましたwindow['piro.sakura.ne.jp'].stopRendering.stop()で描画停止、window['piro.sakura.ne.jp'].stopRendering.start()で再開します。複数の機能で停止/再開をネストしても大丈夫なように、呼び出し回数をカウントするようにしてあります。start()し忘れると変なことになるので注意して下さい。

他にもいくつかAPIを追加したので、自作アドオンと連携して動作させてみたい人は参考にして下さい。

画面の描画内容を一時的にロックしておいて、裏であれこれして最後にまとめて描画させる方法 - Dec 18, 2009

ツリー型タブと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日追記。この方法には重大な弊害があることが分かりました。使用を検討している人はより安全な方法を使うようにして下さい。

以下は、これに辿り着いた経緯のお話。

続きを表示する ...

Page 7/248: « 3 4 5 6 7 8 9 10 11 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のコメント

最近のつぶやき