Home > Latest topics

Latest topics 近況報告

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

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

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

Page 3/248: « 1 2 3 4 5 6 7 8 9 »

Firefoxアドオンのe10s(マルチプロセス)対応の方針について得られた知見 - Nov 13, 2014

Firefoxのマルチプロセス設計への移行がいよいよ目前に迫っている。

Firefoxにおけるマルチプロセス化のための仕組みそのものはElectrolysis、略してe10sと呼ばれており、Firefox 4の頃から既に入っていて、Firefox Hacks Rebootedでも詳しく解説してたんだけど、ブラウザのUIを動かすプロセスとコンテンツ領域のプロセスを分けるというのはFirefoxではかなりの大ごとだった。一時は「こんなん無理!」っつって計画が凍結されてたほどだったと記憶してる。

e10s移行が困難だった理由は、多分、Firefoxというアプリケーションそのものの設計にあったのだと思う。FirefoxというWebブラウザは、「GeckoというHTML・XMLレンダリングエンジンの上で動作するJavaScript製のローカルWebアプリケーション」といった感じの設計になっている。なのでコンテンツ領域内の操作のためのコードとUI部分の操作のためのコードが非常に似通っていて、シームレスに連携しやすかった、否、「連携しやすすぎた」と言える。あまりにシームレスに連携できたせいで、ブラウザのUIに関わるコードとコンテンツ領域内の操作に関わるコードが渾然一体の密結合になってしまい、それが「UI部分とコンテンツ領域内との間はテキストメッセージだけを非同期に通信しあう」というe10sベースの設計への移行を阻んでいた、という事なのではないか……と僕は思っている。

どういう理由があったのかは知らないけど、最近のバージョンのFirefoxではe10sベースの設計への本格的な以降のための準備が着々と進められている。上記のような密結合だった部分が「UI領域専用」「コンテンツ領域専用」「両方で共通」といった感じに分離されてきているし、同期処理前提だった部分が非同期処理前提に改められている。 セッション保存の仕組み(ページ内のテキストボックスへの入力内容を保存する所とか)、リンクのクリック操作のハンドリング、その他多数の部分が大幅に書き直されている。アドオンから見た時のAPI的な互換性は可能な限り残すようにしてあるようで、その努力の様子が非常に興味深い。

で、アドオン側での対応をどう進めるかなんだけど、実際にいくつかのアドオンをe10s対応に改修してみて、アドオンの性質によって典型的なパターンがあるようだという事が分かった。

  1. UI領域内だけで処理が完結するアドオンの場合: 特に何もしなくて良い。browsercontentWindowcontentDocumentに一切触れず、コンテンツ領域内からBubblingしてくるイベントも捕捉しないタイプのアドオンは、何も考えなくてもそのままe10sで動く。
  2. UI領域内で発生するイベントをトリガーとして、コンテンツ領域内で何らかの処理を行う(処理の結果は利用しない)アドオンの場合: アドオンの実装を、「UI領域からコンテンツ領域へ、処理スタートの指示のメッセージを送る」「コンテンツ領域で指示のメッセージを受け取って、実際の処理を行う」という2段階に分け、実装の一部をコンテンツ領域側に読み込ませるコードの中に移動する必要がある。 マルチプルタブハンドラの場合、「選択したタブをファイルとして保存する」機能について、ページをファイルとして保存する処理をコンテンツ領域側に移動し、UI領域からのメッセージをトリガーとして実行するためのコードを追加した。
  3. コンテンツ領域内で発生するイベントをトリガーとして、コンテンツ領域内で何らかの処理を行うアドオンの場合: アドオンの実装を、コンテンツ領域側に読み込ませるコードの中に移動して、そこで処理を完結させるようにする必要がある。
  4. コンテンツ領域内で発生するイベントをトリガーとして、UI領域側で何らかの処理を行うアドオンの場合: コンテンツ領域側にコードを読み込ませて、コンテンツ領域内で発生したイベントをUI領域に通知してやる必要がある。 ツリー型タブの場合、タブバーを自動で隠す機能において、コンテンツ領域上でのマウスの移動を検知するために、マウスのボタン操作や移動で発生したイベントをUI領域に通知するコードを追加した。 生のイベントオブジェクトやDOMノードは渡せなくなるので、文字列として渡せる情報だけでもきちんと動くようにする工夫が要る。
  5. UI領域内で発生するイベントをトリガーとして、コンテンツ領域内で何か処理を行い、その結果を受けてさらにUI領域側で何らかの処理を行うアドオンの場合: これが一番厄介なパターン。 例えば今までだったらWebページ中のリンクを収集する操作は同期処理で var linkURIs = Array.map(gBrowser.contentDocument.links, function(aLink) { return aLink.href; }); と書けたけれども、こういう事ができなくなる。 UI領域とコンテンツ領域の境界をまたぐ時に非同期処理を挟まないといけないので、処理を「UI領域からコンテンツ領域へ、リンクを収集する指示のメッセージを送る」「コンテンツ領域で指示のメッセージを受け取って、リンクを収集し、UI領域へ結果を報告するメッセージを送る」「UI領域で報告のメッセージを受け取って、次の処理を行う」という3つの段階に分けなくてはならない。 マルチプルタブハンドラの場合、「選択したタブの情報をクリップボードにコピーする」という機能のために、タブ(で開いているページ)の情報からクリップボードにコピーするための文字列を得る処理をコンテンツ領域側に移動した上で、UI領域からコンテンツ領域へ・コンテンツ領域からUI領域への橋渡しを行うためのコードを追加し、前後の処理をPromiseで繋ぐようにした。

上記の5パターンの最初の方の物ほど実装が容易で、後の方の物ほど実装が面倒になる(その上、メッセージの往復を待たないといけないのでオーバーヘッドも大きくなる)。 特に深い意味もなく後の方のパターンで実装していた機能は、どうにかして前の方のパターンで実装できないか検討した方がいい(Firefox本体の設計変更も、おそらくそういう風に進められたのではないかと思う)。 実際に、情報化タブではWebページのサムネイル画像を取得する処理について、元々はUI領域で「サムネイルが必要だ」となったタイミングでその都度同期処理していたのだけれども、まずサムネイル取得の処理はコンテンツ領域に移し、処理が走るタイミングについて、コンテンツ領域側で発生したイベントをトリガーとしてサムネイル画像をUI領域側にpushで送りつけるように改めた。 これは上のリストで言うと、5番目のパターンから4番目のパターンに設計変更した、ということになる。

それでもどうしても5番目のパターンで実装しないといけないケースというのはあって、前後の処理とどうやって繋ぐか(同期処理だった物をどう非同期化するか)というのが問題になる。 自分の場合は、こういう時はPromiseを使うのがいいと思ってる(以前ならJSDeferredを使ってたんだけど、Mozilla Add-onsのレビューが通らないため、最近になってPromise.jsmのES6 Promise互換APIを使うようになった)。 ツリー型タブでも、コンテンツ領域内にプラグイン(Flashなど)で描画されている領域があるかどうかを調べるための処理で、このパターンが残っている。 Promiseを使うコードはUI領域側のブリッジにまとめてあり、こんな感じになってる。

sendAsyncCommand : function CB_sendAsyncCommand(aCommandType, aCommandParams)
{
    var manager = this.mTab.linkedBrowser.messageManager;
    manager.sendAsyncMessage(this.MESSAGE_TYPE, {
        command : aCommandType,
        params  : aCommandParams || {}
    });
},
checkPluginAreaExistence : function CB_checkPluginAreaExistence()
{
    return new Promise((function(aResolve, aReject) {
        var id = Date.now() + '-' + Math.floor(Math.random() * 65000);
        this.sendAsyncCommand(this.COMMAND_REQUEST_PLUGIN_AREA_EXISTENCE, {
            id : id
        });
        return this.checkPluginAreaExistenceResolvers[id] = aResolve;
    }).bind(this));
},
handleMessage : function CB_handleMessage(aMessage)
{
    // dump(JSON.stringify(aMessage.json)+'\n');
    switch (aMessage.json.command)
    {
        ...
        case this.COMMAND_REPORT_PLUGIN_AREA_EXISTENCE:
            var id = aMessage.json.id;
            if (id in this.checkPluginAreaExistenceResolvers) {
                let resolver = this.checkPluginAreaExistenceResolvers[id];
                delete this.checkPluginAreaExistenceResolvers[id];
                resolver(aMessage.json.existence);
            }
            return;
    }
},
  • checkPluginAreaExistenceメソッドは、その実行を示すユニークなidを伴ってコンテンツ領域に指示のメッセージを送ると同時に、新たに生成したPromiseのリゾルバ関数を、idと対応付けて保持する。メソッドの戻り値はPromiseとする。
  • コンテンツ領域側では、指示のメッセージを受け取って処理を行い、結果のメッセージをid付きでUI領域に送り返す。
  • 送り返されてきたメッセージをUI領域側で捕捉したら、idに基づいて、保持していたリゾルバ関数の中から対応する物を見付け、送り返されてきたメッセージに含まれていた情報を渡す形で実行する。

という風にする事で、checkPluginAreaExistenceメソッドをthenableなメソッドとして他の処理の中に無理なく組み込める(次のコールバックには、コンテンツ領域側での処理で得られた結果が渡る)ようになってる。 UI領域→コンテンツ領域→UI領域 と境界を2回以上またぐ処理を実装する時は、多少の差異はあれどだいたいこんな感じになるんじゃないだろうか。

必要かどうかで言うと、このパターンでPromiseは必須ではなくて、Promiseのリゾルバ関数を保持・実行する代わりに、単にcheckPluginAreaExistenceメソッドでコールバック関数を受け取って、それを保持・実行するようにしてもいいんだけど。 非同期の処理同士を何度も書き連ねる時のコールバック地獄は見たくない(今はそういう連携をする必要がないとしても、今後いつ必要になるとも限らない)ので、僕は自分で書く非同期処理は、インターフェースとしては基本的にPromiseを使う方向で統一するように考えてる。

Tree Style Tab prevents Firefox from hiding title bar / ツリー型タブをインストールするとタイトルバーを非表示にできなくなる? - May 06, 2014

Q

On Firefox 29 and later, I cannot hide the title bar anymore if Tree Style Tab addon is installed. Is this a bug? How can I hide the title bar?

Firefox 29以降のバージョンでTree Style Tabを使うとタイトルバーを非表示にできません。これはバグではないのですか? どうすればタイトルバーを非表示にできますか?

A

Install Tree Style Tab 0.14.2014050601 (or later) and another addon Tabs on Bottom 0.5 (or later) together. If both addons are installed, the title bar becomes hidden.

This is not a bug but a designed behavior of TST, because that affects ability hiding the title bar: "is the tab bar on top of the window or not?" Old Firefox (Firefox 28 or older) had the "Tabs on Bottom" mode, and the navigation toolbar worked like the title bar - draggable to move the window itself. Because TST moves the tab bar away from the top of the window, TST always chose the mode automatically, for the usability. However, the "Tabs on Bottom" mode has been removed on Firefox 29. If TST moves the tab bar away, you cannot move the window by dragging of the navigation toolbar anymore. To avoid such an inconvenience, now TST forces to Firefox to show its title bar.

On the other hand, Tabs on Bottom addon re-introduce the "tabs on bottom" mode to Firefox 29 and later. Because it is not a "tree" feature, and there may be some people who just want to use "tabs on bottom" mode without "tree" features, I decided to keep it as an independent addon. See also another FAQ.

ツリー型タブ 0.14.2014050601(またはそれ以降)と、別のアドオンであるTabs on Bottom 0.5(またはそれ以降)をインストールしてください。両方のアドオンが同時に有効になっていれば、タブバーが非表示になります。

これはバグではなくツリー型タブの設計上意図された挙動です。何故なら、タイトルバーを隠せるかどうかは、タブバーがウィンドウの最上部にあるかどうかに依存するからです。Firefox 28またはそれ以前の古いFirefoxには「タブを下部に表示」というモードがあり、そのモードではナビゲーションツールバーがタイトルバーのように働くようになっていました(例えば、ドラッグするとウィンドウ自体を移動できるなど)。ツリー型タブはタブバーをウィンドウの最上部から取り除いてしまうので、利便性のために、自動的に「タブを下部に表示」モードを有効にするようになっていました。しかしながら、Firefox 29ではそのモード自体が削除されてしまったため、単にタブバーをウィンドウの最上部から取り除いてしまうと、ナビゲーションツールバーのドラッグ操作ではウィンドウを移動できなくなってしまいました。このような不便を回避するために、現在、TSTはFirefoxに対して常にタイトルバーを表示するように強制する設計となっています。

他方で、Tabs on BottomアドオンはFirefox 29以降のバージョンに対し再び「タブを下部に表示」機能を提供します。その機能は「ツリー」と全く関係がなく、また、単にその機能だけを使いたい(ツリー機能は必要ない)という人がいるであろうという事から、その機能はTSTに統合せずに別のアドオンとしてリリースしています。別のFAQも参照してください。

Double-click on the empty area of the tab bar doesn't open a new tab anymore! / タブバーの余白をダブルクリックしても新しいタブが開かれなくなった - May 06, 2014

Q

On Firefox 29 (and later), I cannot open a new tab by double-click on the empty area of the tab bar, with Tree Style Tab. Is this a bug?

ツリー型タブを使っているとき、Firefox 29以降のバージョンではタブバー上の余白をダブルクリックしても新しいタブを開けません。これはバグではないですか?

A

No, it's not a bug of Tree Style Tab. In old Firefox (Firefox 28 or older), double-click on the empty area of the tab bar opened a new tab actually, but it was a feature of Firefox itself. The action was available only when you choose "Tabs on Bottom" mode, and Tree Style Tab always chose the mode automatically. In the "Tabs on Top" mode, your actions on the empty area of the tab bar worked as on the title bar - for example, it maximized the window itself on Windows. Because the "Tabs on Bottom" mode has been removed on Firefox 29, now double-click on the empty area always works as on the title bar.

Because it is not a feature related to "tree of tabs", I won't include it to TST in the future. Certainly TST already has some features not related to "tree of tabs", but there is a criterion for those exceptions: is the feature provided by Firefox itself, and does TST break it? Then, now Firefox 29 itself doesn't open a new tab by double-click on the tab bar, so TST also never do it. Sorry, but I have to draw a line somewhere.

Instead, you should use another addon who provides the feature: double-click on the empty area on the tab bar to open a new tab, for Firefox 29 and later. For example, my another addon Tabs on Bottom.

いいえ、それはツリー型タブのバグではありません。古いバージョン(Firefox 28以前)のFirefoxでは確かにタブバーの余白をダブルクリックすると新しいタブが開かれましたが、それは「タブを下部に表示」モードの時だけ使える、Firefox自体の機能です。ツリー型タブは単に、「タブを下部に表示」モードを常時有効にしていただけです。元々、「タブを上部に表示」モードでは、タブバーの余白での操作はタイトルバー上での操作と同等に扱われていました(例えば、ダブルクリックでウィンドウを最大化するなど)。「タブを下部に表示」モードがFirefox 29で削除されてしまったため、タブバーの余白でのダブルクリックは常にタイトルバー上での操作と見なされます。

この機能は「タブのツリー」というコンセプトに直接は関係しないため、今後もツリー型タブにこの機能を含めるつもりはありません。ツリー型タブはFirefox本体に元々備わっている機能をなるべく阻害しないことを目標にしており、そのために実際にツリーと関係無い機能を入れている部分もありますが、この機能については現在はFirefox本体にそもそも含まれていないので、そのような特別な対応をする予定も残念ですがありません。

なので、どうしても必要な場合は、代わりに、タブバーの余白のダブルクリックで新しいタブを開く機能を提供している別のアドオン(例えばTabs on Bottom)を併用してください。

4年越しで溜飲を下げた話 - May 02, 2014

Mozilla Corporationの人から、「なんでツリー型タブはFull Reviewを受けてないんだい? about:addonsからの検索にヒットしないから、ユーザがインストールするのに面倒だよ。You申請しちゃいなよ!(意訳)」的なメールを頂いた。ありがたいことだ。

Mozillaのアドオンポータルサイトである所のMozilla Add-onsでは、アドオンを登録するにあたってPreliminary Review(事前審査)とFull Review(完全審査)という2段階の審査がある。登録したアドオンはまずPreliminary Reviewでセキュリティ面などの大雑把な審査を必ず受けて、通過できなければそのアドオンはAMOへの掲載すらかなわない。ここで問題なしとなれば晴れて「実験的アドオン」としてサイトに掲載されるようになるけど、Firefoxのアドオンマネージャからの検索にはヒットせず、おすすめアドオンとしてもノミネートされないので、これではまだ単に「載っただけ」。そこで一定の支持を得たら、次の段階としてFull Reviewを申請して品質面その他を審査してもらえる。Full Reviewを無事通過すれば、Firefoxのアドオンマネージャからの検索にヒットするようになったり、おすすめとして紹介して貰えたりするようになる。定番アドオンと言われるような物は、代替はこのFull Reviewを通過した状態になってる。

前から見てる人は把握してるかもしれないけど、僕が作ってるアドオンのうちいくつかは、Preliminary Reviewのみ通過していて実験的アドオンのままになっている。新しく作って登録した物は当然なんだけど、それだけでなく、過去にはFull Reviewを通っていた物が、ある時を境に審査にパスしなくなってそれっきりになっている物もある。ツリー型タブもその1つだ。

何故かというと、簡単に言えば、最初のFull Reviewの頃には審査が甘かったから通過できていたのが、その後の審査基準見直しで通らなくなったということ。ただ、僕としてはこの時の裁定にはあまり納得がいってなくて、evalが危険でそれ以外の方法が安全だと思ってる人へ英訳)というエントリでタラタラ不満をぶちまけたりもしてた。

そういう経緯があったから、大人げないオッサンとしては「いやーFull Reviewしたいのは山々なんですけどねーeval()使ってるから駄目っておたくんとこの人達が言うもんですからねー」と嫌味全開で返したくもなったんだけど、そこは我慢して、審査基準に合わないと言われたからFull Reviewしてないんですよ、審査基準が変わったなら再申請するのはやぶさかでないけどそうでないなら多分通過しないと思いますよ、でもまあ確かにeval()使ってるとレビューしにくいだろうししょうがないっすね、と、そんな感じで返信するに留め……られるほどにはやっぱり大人になりきれておらず、嫌みったらしく上記のエントリ(英語の方)のURIを貼り付けてメール送信してしまいました。ああ、なんとも底の浅い人間である事よ……

でもまあ、その時のレビュワーの人とは無関係であるにせよ、公式サイドの人から「Full Reviewに値する」という評価を貰えた事には「やってやったぜ」という胸のすく思いで、4年越しで溜飲を下げたのでした。

Why I don't provide "disabe animation of tab dragging operations" feature for Tree Style Tab? - Nov 06, 2013

This is the English translation of my another entry.

I decided to reject a pull request for TST, adding new secret preference to disable animation effects around drag and drop of tabs, because it contradicts the principle of the TST project. This entry describes why I rejected the pull request.

Why I introduced such an animation effect?

Because it is introduced by Firefox itself. After the animation effect is activated on Firefox, I updated TST to follow it. However, it was unwelcome update for some people and I got many requests like "it is hard to operate tabs with drag-and-drop", "I want an option to disable animation effects during tabs are dragged." Actually, I can see two issues on the issue tracker:

The patch of the pull request adds a secret preference to do it. It is enough small and clear. If I added the option, I wrote just same patch.

But I disagree to merge the pull request, because I think that the approach of the patch is similar to a story: "Firefox's Gecko engine is too buggy and less compatibility to WebKit, so why don't you delete all codes of Gecko and introduce WebKit with Firefox-like UI?" In other words, it is very easy to add new option which is requested by people, however, I'm extremely reluctant to do it beacuse it is opposed to my polify on Tree Style Tab project.

Basic premises and my policies.

Basically, this project depends on Mozilla Firefox project --which is very large and uncontrollable by me-- and it is unavoidable to be tossed up and down by the storm of changes in Firefox. Actually, on my another project, I had to rewrite the addon for new versions of Firefox again and again. I learned through the bitter experience that I should have some strict policies on my addon projects:

  • Don't re-implement a feature included in Firefox itself. For example, Tab Mix Plus has its own session management mechanism, because TMP project is started before the session management feature is introduced by Firefox itself. If the TMP project was started after that, they project team didn't implement such a custom session manager. There is no merit to implement a custom feature which conflicts to Firefox's.

    To reduce maintenance cost, and to keep better compatibility with other addons which are developed based on Firefox's APIs, I think I should update and re-construct my addons for new APIs introduced by Firefox. It is better than I struggle to keep old custom implementations against Firefox's changes.

    • However, sometimes I decide to keep my custom implementations, when it is hard to rewrite codes for Firefox's new API for me.

      For example, TST uses a library "JSDeferred" to process asynchronous operations easily. On the other side, lately Firefox uses Promise and Task for the same purpose. I know I should rewrite TST based on them instead of JSDeferred, but I still don't do it because: 1) TST is strongly designed based on JSDeferred. 2) Promise/Task are not available on the current ESR (Firefox 17). (In other words, I'll merge pull requests to do such a reconstruction, if there is no disadvantage about compatibility with other addons.)

    • When the API of Firefox's library is too untrustworthy for me, I decide to use my custom library based on very low stable APIs.

      For example, Firefox has a system named "preference" which can save/restore users' configurations. Because it is not developer-friendly (ex. there are three deferent types --boolean, integer, and string--, and hard to observe changed configurations dynamically), Firefox provides some libraries like FUEL. But, such libraries are untrustworthy and risky for me because Firefox team sometimes changes those APIs despite they promised those APIs are developer-friendly --obviously they should be stable and safe--. I don't want to spend time to update my codes for such unstable APIs, so I actually use my custom library modules/lib/prefs.js which is based only on very stable low APIs. It is one of reasons why I don't update my codes for Promise/Task yet.

      Anyway, I think that FUEL APIs are untrustworthy because Firefox team created FUEL just for third party add-on developers, not themselves. Because Promise/Task are used everywhere in Firefox, Firefox team will keep them stable for themselves.

     
  • Don't depend on deprecated features. For example, "E4X" became obsolete on lately Firefox. TST used E4X in some cases, so I had to decide that I keep E4X for TST or I give up. Some projects (not mine) decided to re-implement E4X by themselves, but I decided to give up and rewrite codes without E4X.
  • Don't include features not related to the main concept. The basic concept is: what I want to use, one feature per one addon, and, as minimum as possible.

    In my old blog entry (note: written in Japanese), I told that: features which I never use or unrelated to the main concept may satisfy users in the short term, but it will shorten life of the project in the long term. Basically I develop and publish my addons on GitHub because I need it and I want to keep it available for me. So I don't want to introduce changes which can disrupt the concept.

  • Provide higihly compatible, natural look-and-feel for Firefox's built-in features and other addons. For example, Firefox has a feature "auto hide toolbox" for the fullscreen mode started by F11 key. And, TST also provides "auto hide tab bar" feature. Yes, it is not related to "tree" feature. But if TST doesn't have the feature, you'll see unexpected vertical tab bar in the fullscreen mode. You press F11, you expect that the web page becomes fullscreened, then the vertical tab bar should not appear on the scene.

    This is the main reason why I took much time to update TST to support drag-and-drop animation effects. Before the animation effect is introduced to Firefox, I respected behaviors of drag-and-drop around layers and objects on Adobe Illustrator. This behavior is still available when you drag a link to the tab bar.

Reasons why the pull request is unacceptable for me.

Based on the above policies, I disagree to merge this pull request to TST's master, because:

  • Firefox has no option to disable animation effect of dragging tabs.
    • There are different and large codes for both cases: with and without animation.
    • However, animation-less operations in rare cases seem to be inhospitable (for me), contrastively basic operations with animation are developed actively.
    • So I forecast that codes for animation-less operations can be removed from future versions of Firefox. It is risky that I develop TST strongly based on such a disappearing implementation.
  • Because there is no option to enable/disable animation effects, I think that Firefox team basically designs Firefox to do drag-and-drop operations with animations. Then, I should keep TST along the design of Firefox itself.

    • I believe that animation effects in GUI often provide better user experiences. I'm affirmative about such a policy of Firefox project.
    • Actually disabling animation effects by userChrome.css or other ways sometimes break Firefox itself. For example, the internal operation to finalize closed tabs uses "TransitionEnd" DOM event to trigger itself, but it didn't workcorrectly because the event was never fired if the animation efect was disabled by userChrome.css. (I don't kwnow the bug is already fixed or not.)

      I don't think my addon should be specially hospitable for people who live without animation effects, because Firefox itself disfavors them.

     
  • I disagree to add new option to disable animation effects, for a request like "it is hard to operate tabs with animation effects." Instead, I think I should improve usability of tab operations with animation effects, to reduce frustrations around such operations.
    • If you don't have to operate tabs manually, you won't suffer from animations. If you have to operate tree of tabs manually to put them as you want, then TST should build tree of tabs automatically more intelligently to set you free from stressful manual operations.
    • If it is hard for you to drop the tab to the favorite position with animation effects, then TST should make it easy, instead of disabling animation effects.
  • Currently I have no plan to use Firefox without animation effects. I don't want to maintain codes for a feature I never use.
  • Of course, disabling animation effects is useful for some people who suffer from visual transitions, about accessibility. However, remember, Firefox itself is not providing ability to disable animations, for such people. And, this addon is "Tree Style Tab." I think TST should not include custom accessibility features which is missing on Firefox itself --it should be done by Firefox project--.
    • If you seriously suffer from the problem, you should file a bug for the issue, induce developers to implement ability to disable animation effects. Or, you should create a new addon which is based on a simple concept: disabling animation effects. (Note, I never develop such an addon because I never use it!) The user script zzzz-removeTabMoveAnimation.uc.js for userChrome.js seems to be written for the purpose.

The conclusion: fork this project freely.

This is just my personal, current opinion. Of course I don't think this is the final truth of the topic. If you have information which can solve my worrying, or if you explain compelling reasons that I should do it, then I possibly merge such a change.

Otherwise, I'm sorry but I never merge such pull requests to my master repository. Then please fork this project, extend, maintain, and release it for people who have same distress - it is my stance on this project. To keep my codes forkable -- this is one of reasons why I distribute all codes of TST under OSS licenses.

ツリー型タブにおける、タブのドラッグ時のアニメーション効果についてのポリシー - Sep 13, 2013

English version of this entry is available.

ツリー型タブに頂いたプルリクエストについて、ポリシー上の理由から取り込めないなあと思ったのだけれども、これまで、参照しやすい形でまとまった文章としてポリシーを表明していなかった気がするで、何故この変更を取り込まないか(タブをドラッグ&ドロップするときのアニメーション効果をOFFにできるようにしないのか)の判断理由を頑張って文章にしてみました。アーカイブとして、少し手直し&追記してこちらにも転記しておきます。

Firefox本体の挙動としてタブのドラッグ&ドロップ操作にアニメーション効果が適用されるようになって以後、ツリー型タブにも、その挙動に合わせるための変更を随時行ってきました。

しかしながら、この変更は万人にとって望ましい結果をもたらしたとは言えず、「タブをドラッグ&ドロップで操作しにくい」「タブのドラッグ&ドロップ操作時のアニメーション効果を無効にできるようにして欲しい」といった意見・要望が何度か寄せられています。今すぐにパッと挙げられるだけでも、Issue Tracker上には以下の2つのIssueがありました。

メールを通じて寄せられた意見も合わせると、無視できない数の人がこの件に関心を持っているようだ、という印象を僕は持っています。

冒頭に挙げたプルリクエストは、ドラッグ&ドロップ時のアニメーションを無効化する設定をツリー型タブに加える、という物でした。変更箇所は最小限で、内容もおかしなことをしている部分はなく、コードとしてはそのまま取り込んで全く違和感のないパッチです。

ですが、自分の認識としては、このパッチで示されている方向での対応というのは言わば、「FirefoxのGeckoエンジンはバグだらけだしWebkitと互換性が低いから、Geckoを全部消して、WebkitにFirefox風のUIを付けた物を次のバージョンのFirefoxにしますよ」というような物なのではないか、と考えています。……という例えは極端なのでもう少し普通の言葉で言い直すと、「確かに皆が望む通りに対処することは簡単なのだけれども、プロジェクトの運営ポリシー的に、この点で皆が望む通りの対処の仕方をする事には非常に慎重である」ということになります。

大前提として、このプロジェクト(ツリー型タブというアドオンの開発プロジェクト)は、Mozilla Firefoxという自分には制御のできない別のプロジェクトに依存している・振り回される事を避けられないプロジェクトである、という事をまず押さえておく必要があります。

ツリー型タブ以外も含めた自作のFirefoxアドオン全体で、Firefoxの仕様変更に追従するために大きな変更が必要だったことが過去に何度かあり、その時の苦い経験から、現在のところ、自分はこれらのアドオン開発のプロジェクトにおいて以下のような方針を立てるようにしています。

  • Firefox本体で実装している物と同じ目的の物は、できる限り、アドオン側で独自に実装し直さない。
    例えばTab Mix Plusはタブのセッション管理機能について、Firefox本体にそのような機能が入る前から開発されてきたという経緯から、Firefox本体のセッション管理機能とは全く別のセッション管理機能を持っています。が、今新たにTab Mix Plusを開発するとしたら、独自の仕組みを持つメリットは全くありません。 メンテナンスコストの削減と、「Firefox本体の機能と親和性が高くなるように開発されている」他のアドオンと併用したときの相互運用性を高く保つという、2つの観点から、独自の仕組みをメンテナンスし続けるよりは、Firefox本体で実装された仕組みにうまく載っかるように改修する道を選ぶべきであると、自分は考えています。
    • ただ、アドオン内の各部分が独自の仕組みに強く依存した設計になっており、Firefox本体で採用された新しい仕組みに基づく形に改修するのが非常に難しい(工数が大きい)場合には、独自の仕組みを保ち続ける選択を取る場合があります。
      例えば、TSTでは非同期な処理と非同期な処理の連携をうまく取るために、JSDeferredというライブラリを使用しています。一方で、最近のFirefoxではPromiseTaskといった仕組みが導入されており、本来であれば、JSDeferredではなくこれらに基づく形で全体を作りなおすべきところです。が、ESR版Firefoxではまだこれらが使えないという事情もあって、移行はできていません。
      (逆に言うと、そのような手のかかる変更に現在のところ自分は着手できていないが、問題視はしているということで、この点を解消するプルリクエストであれば、他のアドオンとの互換性などいくつかの懸念が払拭できていれば、是非とも取り込んでいきたいと思っていると言えます。)
    • Firefox本体で提供されているライブラリのAPIの安定性がいまいち信頼できないという場合、そのより低位の、より変更が少ないと予想されるAPIに基づいて、自分でライブラリを作成して使用しているという場合もあります。
      例えば、Firefoxではユーザ設定を保存する機能としてpreferenceという仕組みがありますが、真偽値・整数値・文字列値という型の違いがあったり、設定の変更を監視するのが面倒だったりということで、より簡単に使えるようにするためのライブラリをFirefox自身がいくつか提供しています。FUELもそのひとつです。しかしながら、それらの「シンタックスシュガー」的な用途で作られたライブラリは、ライブラリ自体のAPIが彼らの都合でコロコロ変えられてしまうリスクが結構あり、それで泣かされるのは非常にアホらしいという認識が自分にはあります。なので、僕は自作のアドオン群では、preferenceを扱う最も低いレイヤのAPIであるnsIPrefBranchを直に操作するライブラリ(modules/lib/prefs.js)を自分で作って使っています。
      PromiseやTaskへの移行に踏み切れていない理由の1つとしては、このような懸念もあります。が、FUELのようなライブラリがFirefox内部でほとんど使われていないために捨てられてしまうリスクがある(彼らは、自分が使う物は大事にメンテナンスしますが、「アドオン作者が使うように」といった感じでお仕着せの便利ライブラリとして用意した物については、彼ら自身が使っていないので冷遇しがちという印象が僕にはあります。)のに比べると、PromiseやTaskはFirefoxの中の深い部分で既に多用され始めているので、今更捨てられてしまうリスクはそれほどには高くないかな……という目算もあります。
  • Firefox本体で廃止が決まっている機能、廃止される見込みがある機能には、なるべく依存しない。
    例えば、記憶に新しいところではE4Xの廃止というトピックがありました。文字列のヒアドキュメント代わりにE4Xを使用している箇所が、E4Xの廃止で動かなくなるという事に対して、E4Xを諦めるか、E4Xを使い続けるのか、という2つの道があり得ました。プロジェクトによっては、E4Xを自前で実装し直すことでそれ以外の箇所の変更を最小限に留めるという道を選んだところもあったようですが、自分は、E4Xを捨てる道を選びました。このあたりの判断については過去に会社のブログで詳しく書いていますので、そちらも見てもらえると幸いです。
  • プロジェクトのコンセプトから外れる事は、なるべくしない。
    コンセプトは「自分が使いたいものを、1機能につき1アドオンとして、最小の形で開発する」ということ。
    過去のエントリで詳しく書いていますが、自分が使わない機能や、コンセプトから外れる機能を盛り込むことは、短期的にはユーザの満足度を高める結果に繋がる可能性がありますが、長期的には、プロジェクトの寿命を縮めるリスクを増やす事だと自分は考えています。GitHub上に置いてある僕のアドオンは、基本的にはどれも「自分が使いたいから・使い続けたいから」開発をしているので、自分が使い続けにくくなる可能性が高まる変更は、なるべく取り込みたくないと考えています。
  • 細かい挙動はFirefox本体の本来の挙動に可能な限り合わせる。違和感が無いように作り込む。
    例えばFirefox本体の機能として、F11キーでフルスクリーン表示に切り替えた時に、ツールバーが画面の外に隠れるというものがあります。ツリー型タブには「タブバーを自動的に隠す」機能がありますが、単機能だシンプル化だと言っておきながら一見するとツリーとは関係ないこのような機能を未だに含め続けているのには、このF11でのフルスクリーン表示時の挙動をFirefox本体の挙動に自然にマッチするようにしたいからというのが大きな動機としてあります。
    タブのドラッグ&ドロップ操作のアニメーションに、ツリー型タブでそれなりの労力を割いて対応している最大の理由も、これです。(Firefoxにタブのドラッグ&ドロップのアニメーションが実装されるまでは、UIが似ているAdobe Illustratorのオブジェクトとレイヤーの操作性を参考に作り込んでいました。これは今でも、タブ以外のオブジェクトをタブバー上にドラッグした時に見ることができます。)

以上を踏まえてこのプルリクエストで触れている問題を考えると、自分は以下の理由から、この変更を行わない方が良いという考えを持っています。

  • Firefox本体に、タブのドラッグ&ドロップ時のアニメーションをOFFにする機能が無い。
    • アニメーションするかしないかでFirefox内でのコードパスが大きく異なり、どちらもそれなりに大きな規模である。
    • が、アニメーションのための開発が活発に行われていたのに対して、アニメーション無しのドラッグ&ドロップについては冷遇されている(気が、自分はする)。
    • ということは、今後、アニメーション無しのドラッグ&ドロップは実装が削除される可能性もある(と、自分は見ている)。 削除される可能性がそれなりにある実装に強く依存してしまうのは、リスクが高い。
  • ON/OFFの設定がないという事は、Firefox的には、タブのドラッグ&ドロップは基本的にアニメーションするものであるというスタンスだと考えられる。であれば、Firefoxに依存するプロジェクトは、Firefoxの方針に従っておくのが得策であろう。
    • アニメーション効果を適用することは、見た目のキレイさだけでなく、使い勝手の向上にも関係するので、自分はそのようなスタンスを取ることには肯定的である。
    • アニメーション効果をユーザが無理矢理無効化すると、正しく動かなくなる、という部分がFirefoxの中にすらある。
      具体的には、タブを閉じる処理は内部でのデストラクタを走らせるトリガーとして「アニメーションが完了した」というイベントを使っており、アニメーションが無効化されているとデストラクタが走らないのでタブがきちんと閉じられないという問題があった(今その問題がどうなっているかは把握していない)。
      Firefox本体の側がアニメーションOFFでの利用をここまで冷遇している以上、そのような使い方をこのアドオンで手厚くサポートする必然性を、自分は感じない。
  • 「アニメーションがあるとタブをドラッグ&ドロップで操作しにくい」、という要望に真正面から「アニメーション効果をOFFにできるようにしました」と応えるよりも、そういう要望が出にくくなるような形での解決を図るべきだと考えている。
    • そもそもドラッグ&ドロップで操作する必然性が薄ければ、問題なくなるのではないか。手動でツリーを編集しなければ意図した通りにならない、という状況があるのなら、ユーザに手動でのツリー編集という手間を強いる方向よりは、ツリー編集しなくてもストレスを感じないように、ユーザの意図を汲んで自動的にいい感じにツリーを構築するようにした方が、より望ましいのではないか。
    • アニメーション有りの状態でドラッグ&ドロップすると狙った位置にドロップしにくい、という問題があるのであれば、それは、アニメーションをOFFにすることで対処するのではなく、アニメーション有りでも狙った位置にドロップできるようにするのが、正しい解決ではないのか。
  • 僕自身は、アニメーションをOFFにして使うつもりがない。自分が使わない機能に対するメンテナンスコストを抱え込みたくない。
  • 視覚的に連続した変化を認識しにくい、アニメーションされた方が却って認識しづらくなる、といった性質あるいは障害を持っている人がいる、という事を考慮すると、アニメーションをOFFにできるようにすることは、アクセシビリティ上意義のある事だと思う。
    が、Firefox本体がアニメーションをOFFにできるようになっていない以上、「タブをツリー表示できるようにする」というコンセプトのこのアドオンが、独自にアクセシビリティ上の配慮を盛り込むのはおかしいのではないか。
    • この点を真面目に考えるのであれば、「FirefoxのBugzillaで問題提起して、Firefox本体の側でアニメーション効果のON/OFFをできるようにする」あるいは、「Firefoxのアニメーション効果を無効にするという1つのコンセプトに則ったアドオンを開発する」という道を選ぶべきだと、自分は思う。(アドオンではないが、userChrome.js用のzzzz-removeTabMoveAnimation.uc.jsというスクリプトは、そのための物と言えそう。)

以上の事は、あくまで現時点での自分の考えがそうであるというだけで、絶対普遍の真理であるとは考えていません。ここに挙げた数々の懸念点を払拭できる材料があったり、あるいは、そのような懸念があってもなおこのようにするべきであるという強い動機があって、僕が同意できるのであれば、変更を取り込むことにはやぶさかでないです。

でも、そうでない限りは、この種の変更については「フォーク版を作ってそっちで自由に開発していって下さい。そして、その機能を必要とする人自身の手でメンテナンスしていって下さい。」というのがこのプロジェクトの方針ということになります。僕がこれらのアドオン群にオープンソースライセンスを適用しているのには、そのような自由を保障しておきたかったからという理由もあります。

冷たいようですが、どこで線を引くのか・何を基準に線を引くのかを熟慮した結果、このような線引きに落ち着いたということで、同様の不満を抱いている方々にはどうかご理解を頂ければ幸いです。

Tabs Outlinerとツリー型タブ - Sep 21, 2012

Tabs OutlinerというGoogle Chrome/Chromium用拡張機能がある。説明をよく読むと、Firefoxでツリー型タブを使っていた人が、Google Chromeに乗り換えるにあたって作った物らしい。

以前試した時は、リンクからタブを開いてもツリーが構築されないというのが「えっ」という感じであんまりよく試さないでアンインストールしちゃったんだけど、今日なんとなくまたインストールしてオプションを開いてみたら「Tree Style Tabs」ってチェックボックスがあって、そこに書いてあった説明を見たら「これONにしたら自動でツリーになるようになる」的な感じっぽかったので試してみたら確かにリンクから開いたタブが勝手に子タブになってくれた。

ということで、ツリー型タブにロックインされてしまってるせいでFirefoxからGoogle Chromeに乗り換えられないという人(時々そういう話を目にするのです)にとってこれはマジに救世主になるかもしれないなと思ってもうちょっとだけ試してみてた。タブの切り替えがダブルクリックじゃないとだめだったり、他の拡張機能との連携はさすがにできないっぽかったり、Google Chrome本体のメニューから終了したらツリー構造が保存されなかったりで、僕自身が乗り換える先としては厳しいなあ……とは感じたけど、そういう所が気にならない人なら、普通に使えるんだと思う。

そんな「Tree Style Tabs」モードなんだけど、チェックボックスの下にすごく長い注意書きが付いてた。僕が読み取れた大意としては、「そういう要望がものっそ沢山寄せられるから機能を付けてるけど、自分(作者)はこのモードはお薦めしない。試してみてもいいけど、このモードだけで使うんじゃなくて、このモードをOFFにしたTabs Outliner本来の状態でも是非使ってみて欲しい。」ということが書いてあるようだった。同様のことがTabs Outlinerの配布ページにも書いてあって、作者の人によると、ツリー表示は情報の一覧性が悪くて、フラットなタブのリストの方が可読性が高いぞ、と。ツリー、ずいぶん嫌われたもんですね……

この見解の違いは、タブとタブのツリーという物をその人がどう捉えているのか? という所から生じているのだと思う。

僕にとって、タブのツリーは(生存期間は短いけれども)ある情報を起点にしてあちこちさまよい歩いた足跡そのものであって、どのページからどのタブへ辿り着いたのかが視覚化されているということが非常に重要だ。僕はその関係性を目で追って「分かれ道になったノードはどれだったっけ」と探している(ちなみに、この時見ているのは主にfaviconと情報化タブが提供するサムネイルで、タブのラベル文字列は注視していない)。1階層のグループ化しかできないPanoramaだけでは不十分なのは、「新しいタブを開いて分かれ道になったノードがどれだったか」という情報が欠落してしまうからだ。

そのくらい憶えておけよって言われるだろうけど、僕はほんとに頭が悪いというか物覚えが悪いというかバカなので、「そのくらい」がもう悲しいくらい絶望的なほどに覚えられない。覚えられないから、そのまま視覚化して置いておく。僕にとってタブのツリーは、脳に収まりきらない情報を置いておく外部記憶になっていると言える。メインメモリの中に情報が乗り切らなくて、画面の中にしか置いておけないのだから、多少アクセス速度が遅いとしても、そこに置いてある情報を毎回取りに行く。そういう使い方をしていると思う。

でも、「そのくらい」を覚えるのが苦にならない人にとっては、オーバーヘッドが大きすぎてまどろっこしいのかもしれない。タブ同士の関係はこぼれることなくメインメモリの上に載りきっているから、わざわざ画面の中にまで同じ情報を残しておく必要性が無い。そういうことなんじゃあないだろうか、と思う。

極端なことを言えば、ツリー型タブというのは脳味噌の性能が極めて低い障碍者がフツーの人に後れを取らないように、フツーの人に合わせて作られた社会の中で日常生活を送るために不足分を補うために使う、義肢とか車椅子とかそういう物に相当するのかもしれない。電動車椅子に乗れば確かに誰でもラクに移動できるけど、自分の足で立って歩ける人は、車椅子を降りて歩いた方が自由にもっといろんな所に行ける。また、電動車椅子に慣れきってしまうと、脚がひなびて自力で立てなくなってしまうかもしれない。使わなくても生きていけるなら、使わない方が健康でいられるのかもしれない。ツリー型タブという物については。

ツリー型タブがあると遅くなるとかツリー型タブが重いとかその辺の話について - Sep 17, 2012

ツリー型タブが重いという声をたまに見かけるんだけど、そういうのを見かける度に「ほんとかなあ?」とついつい思ってしまう。

もちろん、素のFirefoxに対して余計な処理を加えるわけだから、そりゃあ、元の状態より重くはなると思う。でも、「重い」って言ってる人の言ってる「重い」は、なんというか、そういうレベルのことを言ってるわけではないって気がする。もっと鈍重な、全体的に動作がヤバイくらい緩慢になる、そういうのを指してる気がしてる。

で、そういう現象が起こるとして、本当にそれがツリー型タブのせいなのかどうか。ツリー型タブを使っている時と使っていない時とで、同じ数のタブを開いていて、同じ時間だけブラウジングした状態で、ツリー型タブがある場合だけ顕著に性能が劣化するのかどうか。というのが、この件で自分が一番気になっているポイントなのです。

何故僕はこうまで頑なに「ツリー型タブが原因で遅くなっているのだ」と素直に認めようとしないのかというと、基本的にツリー型タブはブラウザのコンテンツ領域や履歴にはタッチしないように設計しているつもりなので、「コンテンツ領域に起因するメモリリークがある」とか「履歴を消去したら軽くなった」とかの報告があるという事自体が、どうにも不可解なのです。

実際、about:memoryではタブ毎にメモリの使用状況を確認できますが、「about:memoryを見ると、メモリが解放されていないことが分かる」と言われた再現条件をこちらで試行しても問題が再現せず、その後報告者の環境でもツリー型タブだけをインストールした状況では問題が再現しなくて、どうやら他のアドオンがこの問題の原因になっているのではないかという話になったこともありました。

ツリー型タブがあるとタブを開く数が増える&1つ1つのタブの生存期間が長くなる傾向があると思うので、他のアドオンやFirefox自身が潜在的に抱えてはいるものの普段の利用では無視できる程度だったという問題があったとしたら、それらがより顕在化しやすい状態になるとは思います。例えばあるアドオンを入れていると全体の動作が0.1秒遅くなるとして、5つくらいタブを開いている状況ではそれが気にならなかった。でも、ツリー型タブを入れてタブを50とか開きっぱなしにしていると、些細な重さでもそのまま×50されるから、シャレにならない重さになる。こういう事は十分にあり得ます。でもツリー型タブ単体でそういう事が起こるとはどうしても自分には考えにくいのです。

「コンテンツ領域への参照を残しているせいでメモリリークが発生する」というのは自分もアドオン開発初期にはよくやらかしていたミスなので、かなり後の方になって開発したツリー型タブの頃にはそこらへんのことは想定に入れられるようになっていて、多少速度を犠牲にしてでも安全になるように、だいぶ気をつけて設計したという認識があります。「そこまでやらんでもええんちゃうん」って所まで偏執的にやってることすらあります。Firefox本体のコードでもaddEventListenerした物をremoveEventListenerせずに放りっぱなし(多分ガーベジコレクション任せ?)になってるのとか見ると、怖いなーって思ってしまうくらいです。

その一方で、僕は「これこれこのアドオンと衝突してるんだけど」という報告を受ける度にそのアドオンのソースを見るという事がこれまで結構あったのですが、ソース見てげんなりすることが割とありました。これメモリリークするやろ、とか。で、そういう問題を解決しようと思ったら、根本的なアーキテクチャの変更が必要だったりして。そういうとこまで見だすときりが無いし、大体それは僕の領分でもないので、ツリー型タブとの衝突の原因になってるとこだけ見てあとは見て見ぬふりしてるんですけども。

そういう惨状を見てるから、僕の心情としてはついつい、まず最初に他の原因の方を疑ってしまいます。それらの可能性がなくてちゃんと明らかに「ツリー型タブが原因だ」と断言できる、という所にまで絞り込まれてないと(はい。ほんとにツリー型タブが原因で問題が起こってる可能性も、もちろんあるとは思ってます。そこまで完全否定はできないです。)、調べようという気になかなかなれないです。これって、思い上がりすぎですかね?

あと、それとは全然別の話として、ツリー構造が何らかの理由で壊れてしまうせいで、無限ループが発生してフリーズしてしまう……という事は時々起こるみたいなので、そういうのはもう完全にツリー型タブの責任なので今後も粛々と直していきたいとは思ってます。

いずれの場合にしても、メンテナンスに十分に時間を割けない現状では「ツリー型タブのせいで問題が起こってる、かもしれない」という段階では動けなくて、「間違いなくツリー型タブのせいで問題が起こってて、この手順で100%再現できる」という所まで絞り込んでもらってることが、こっちで対処できるためには絶対に欠かせない条件という感じです。できれば「この部分が問題になっている」という所まで明らかにしてもらえてると嬉しいし、もっと言えば修正パッチをpull requestでもらえるのがベストなんですが。

それから、何と言おうとツリー型タブを入れてFirefoxの動作がおかしくなり、ツリー型タブを削除したことでこれらが解決されたことは事実なのです。 という風な話はもう何度あったか覚えてられないくらいにあって、GitHubのIssue Trackerを探してもゴロゴロでてくるんだけど、詳しく聞いてみると大概は他のアドオンとの衝突です(全部がそうだというわけではないですが、感覚的には、そうである場合の方が多かったという印象です)。こういうのも、じゃあどのアドオンと衝突してるのかという所まで明らかにさえしてもらえれば、何か手の打ちようが出てきくる可能性がありますので、より快適な生活を望む場合には、衝突の解消に協力してもらえたらなーって思います。

セッションの復元とツリー構造の維持 - Aug 07, 2012

ツリー型タブを3ヶ月ぶりに更新した。

地味な修正が多い中で特に地味な修正だけど、セッション復元でツリー構造が壊れるという問題について一定の改善を見られたのではないかと思ってる。実際に効果があったのかどうかは、今後同様のバグ報告が減るかどうかで見るしかない。

何故ツリーが壊れるか、という事の前にそもそもツリーが壊れるってどういう事やねん、という話なんだけど、ツリー型タブを使っててごく希に、「リンクから新しい子タブを開いても子タブにならない」とか「親にあたるタブには折り畳みのためのつまみが表示されているのに、子になったはずのタブはインデントされておらず、つまみをクリックしても子になったはずのタブが折り畳まれない」とかそういう怪しい挙動になる事があった。

何故そういう事が起こるのか。これはタブの管理方法に理由がある。

ツリー型タブでは、個々のタブに一意なIDをあらかじめふっておいて、tab.setAttribute(TreeStyleTabService.KPARENT, parentTab.getAttribute(TreeStyleTabService.kID)) とか tab.setAttribute(TreeStyleTabService.kCHILDREN, childTabs.map(function(aChild) { return aChild.getAttribute(TreeStyleTabService.kID); }).join('|')) とかいう感じにIDの文字列をキーにして互いの関係を保持している。親のタブや子のタブを取得する時は、TreeStyleTabService.getParentTab(tab) とした時にその中で tab.getAttribute(TreeStyleTabService.KPARENT) して取得したIDを使って(DOM3 XPathなどで)実際の要素を改めて取得するという風になってる。何故こうしたかというと、

  • タブの要素ノードに直接 tab.parentTab = parentTab のようにしてしまうと、うっかり参照を消し忘れたらいつまで経ってもメモリが解放されないんじゃないか、という心配があった。(意図しない事態の発生を防ぐため)
  • 実際には setAttribute() するのと同じタイミングで SessionStore.setTabValue(tab, TreeStyleTabService.kPARENT, parentTabId) のようにしてセッションに情報を複製しており、クラッシュ時のツリーの復元などに役立てている。(ツリー構造の永続的な保存のため)

といった理由があってのことだった。

これはそれなりに堅牢なやり方なはずだと思ってたんだけど、実際にできあがってからJavaScriptデバッガでプロファイリングしてみたら、IDの文字列からタブの要素ノードを探すという処理が呼ばれる回数が突出して多くて、それが結構無駄な待ち時間を増やす元になっているらしかった。それで、もう少し高速に動作するような仕組みを後から加える事にした。setAttribute(TreeStyleTabService.kID, id) するのと同じタイミングで、gBrowser.treeStyleTab.tabsHash[id] = tab; としてハッシュテーブルを作っておき、それ以後はそちらだけを参照するという、ごくシンプルな物だ。

上記のおかしな挙動は、このハッシュテーブルが原因だっていた。このハッシュテーブルの仕組みが上手く働くためには、「ハッシュテーブルに入っていないタブはない(すべてのタブがハッシュテーブルで管理されている)」という前提が必要になる。もし万が一何らかの理由でハッシュテーブルの管理から漏れてしまうと、そのタブはAPI経由では永久に見つけられないことになってしまう。そのタブを親として子タブを登録する、という風な事はできるのに、その子タブに保存された情報から親のタブを探そうとしても、ハッシュテーブルに無いタブだから見つけられない。こういう不整合の発生は考慮に入っていなかったので、親子関係に基づくインデント幅の調整などの処理が中途半端な所で止まってしまって、上記のような色々と不可思議な現象が発生していた。

色々調べた結果、大量のタブが一気にセッション復元された時に「現在のタブ」になっていたタブが、このハッシュテーブルの管理から漏れてしまうことがあるようだという事が分かった。なので、そのような場合もちゃんとハッシュテーブルに入るようにした。これによって、手元で再現性100%だったケースについては問題が起こらないようになった。これまでに報告があった全ての問題がこれと同一の原因かどうかは不明だけれども、今までよりは問題が起こりにくくなったという事は言えると思う。

あと似たような問題で、タブの復元のタイミングによっては「ツリー構造だけの高速な復元」ができなくなるという現象も起こっていた。ツリー型タブが完全に初期化されるよりも前にFirefoxのセッション復元の仕組みによってタブが復元されていた、という想定外の状況が発生していて、それで色々な前提が崩れてしまっていた。理屈から言ってなんでそうなるのかがてんで分からなくて、どうにかしてタブが復元されるよりも前のタイミングに割り込みたかったんだけど、どうも無理っぽかったので、既にタブが復元されてしまっていた場合でもちゃんとそれらのタブを「後から初期化」するようにした。

そういう地味な修正ばかりが入っている版です。

tabFx2Compatible.xul、tabFx2Compatible.css、tabFx2Compatible.xmlを使わないようにした - Feb 09, 2012

ツリー型タブ情報化タブの今日付でのリリース分から、tabFx2Compatibleという自作のライブラリ(?)を使わないようにした。当初はマルチプルタブハンドラでも使ってたけど、ツリー型タブ・情報化タブに先駆けて一足先に使わないようにしていた。今回残りの2つでも利用をやめたことで、TBEやり直し組のアドオンからはこのライブラリが全く姿を消したことになる。作ったのが2008年の2月からだから、丸4年くらいは使ってたのか。

このライブラリは元々、Firefox 3での仕様変更に追従するために仕方なく作った物だった。

Firefox 2ではタブの中に任意の要素(サムネイル描画用のcanvasだったりカウンタ表示用のlabelだったり)をXBLのコンテナ要素を使って動的に追加できていた。しかしFirefox 3になる時、具体的には2007年の12月頃に、高速化のためにそういう冗長性を排除する変更が行われてしまって、JavaScriptで動的にタブの内容を追加するということが原理上不可能になってしまった。

正確には、方法はあった。Firefox本体がタブに適用していたXBLによるバインディングを独自のバインディングで置き換えて、それを使ってタブの内容を変更するというやり方だ。しかし、XBLのバインディングは同時に1個だけしか適用できないという制限がある。複数のアドオンが同じ事をやろうとしたら、結局どれか1つのアドオンしかまともに動かないという事になってしまう。他の人の作る物との互換性という話に限らず、リッチでファットなAll-in-One型のアドオンであったTBEを捨てて各機能ごとに個別のアドオンに開発し直す道を選んでいた僕にとっては、自分の手がける物同士の互換性を維持するためにも、これは致命的な問題だった。

前述のライブラリは、特定のアドオンのために特化したバインディングを適用するのではなく、Firefox 2時代の「ある程度好きなようにタブの内容を増やせる余地がある」、冗長性を持った汎用的なバインディング定義を、Firefox 3向けに復活させるという物だった。

風向きが変わったのは2010年9月の事だった。Firefox 3.7のモックアップで示された視覚的なデザインを実現するために、タブのバインディングに再び冗長性が付与された。メジャーバージョンとしては、この変更はFirefox 4から反映されている。僕はこの仕様変更をうけてtabFx2Compatibleを改修し、Firefox 4以降のタブのバインディングの構造と、Firefox 2以前のタブのバインディングの構造の、両方を併せ持つ状態に変更した。

という経緯を見ると分かるかもだけど、このtabFx2Compatibleというライブラリは本質的に、Firefox 3.0から3.6までの間のバージョンに対応するためには欠かすことができなかった。ツリー型タブ等のアドオンの対応バージョン範囲の下限がこの範囲に重なっている間は、このライブラリは絶対に使う必要があったので、いくらTMPやVimperator等とバインディングの衝突の危険性があったとしても、構成ファイル群から外すことができなかった。

今回、Firefox 10のESR(延長サポート版)がリリースされたことにより正式にFirefox 3.6の死期が確定したので、サポート終了を待たずにFirefox 3.6のサポートを打ち切って、それと同時にtabFx2Compatibleも廃止することにした。レガシーなFirefox 2由来のDOMツリー構造を前提にして書かれたスクリプトやスタイルシート等を、全面的にFirefox 4以降の既定のDOMツリー構造に合わせて変更する作業は、それなりの手間を要したけれども、これでカビの生えた過去から決別できるのなら安い物だと思った。

選択肢の1つとして、tabFx2Compatibleを今後も使い続ける(Firefoxのタブのバインディングの構造が変わったら、その度にtabFx2Compatibleを更新していく)というやり方もあった。今この瞬間にかける労力を最小化する事を選ぶのなら、そういう選択もあり得たと思う。でも、tabFx2Compatibleは元々Firefox 3から3.6までの暗黒期を乗り越えるためだけに用意された物だったのだから、用済みになったのなら、思い切って捨てた方が今後のためになると思った。

こういう切り替えをできる時にやっておかないと、またTBEみたいな事になってしまう恐れがある。TBEでは、タブの移動等といった基本的な機能すらFirefox本体に備わっていなかった頃から開発していたため、TBE自身で独自の「タブを移動する」などのAPIを実装していた。そういう古いオレオレAPIから、Firefox 1.5くらいから新しくFirefox本体に備わったTabMoveとかのDOMイベントベースでのやり方にスイッチする事の手間を惜しんで、「とりあえず今動いてるから」と独自のオレオレAPIを維持することに固執してしまったがために、TBEはFirefox 1.5とともに時代に取り残され死んでしまった。そんな愚はもう繰り返してはいけない。

それにつけても、あの辺(Firefoxのタブまわり)の開発をやってる人達の意向に振り回されっぱなしだった4年間だったなー……と思うと、なんか感慨深い。

Page 3/248: « 1 2 3 4 5 6 7 8 9 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のコメント

最近のつぶやき