たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。
の動向はもえじら組ブログで。
宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
Could you add the option to Tree Style Tab or Multiple Tab Handler, to keep tabs with same domain/host in alphabetical order. In other words auto grouping by domain or host.
ツリー型タブまたはマルチプルタブハンドラに対して、同じドメインまたは同じホストのタブをアルファベット順で表示させる、というオプションを付けてもらえませんか? 言い換えるとつまり、ドメイン名やホスト名によるタブのグルーピングということです。
Group/Sort Tabs provides the feature, so try it please. However it doesn't work with my Tree Style Tab.
I have no plan to do it on the Tree Style Tab, because I believe that tree of tabs is a "visualized history of web browsing". In the concept, "tree of tabs" and "auto-sorting by domain" mix is like oil and water. (By the way Tab Kit provides both features, so it possibly become your favorite instead of TST.)
Group/Sort Tabsというアドオンがその機能を持っているので、試してみてください。しかし、このアドオンはツリー型タブとは併用できないようです。
私はタブのツリーを「ブラウズ履歴を視覚化した物」と考えていますので、ツリー型タブにこの機能を付ける予定はありません。このコンセプトにおいて「タブのツリー」と「ドメインによる自動的な並べ替え」は相性が悪いです。(ちなみにTab Kitはその両方の機能を提供します。ツリー型タブよりもそちらの方があなたにとって気に入るかもしれません。)
多機能オールインワン型のタブ系アドオンをdisったら、こんな反応があった。
ただエンドユーザーからみると、タブ機能のために細かい拡張をいくつも入れてられるか、とも思っちゃうのよね。
そんな時のためにFirefoxには複数のアドオンを一発でインストールできる仕組み(ユーザ側で工夫したら複数のアドオンを一括インストールできる、のではなくて、アドオンを公開する側がユーザに「このリンクを辿ればこれらのアドオンを全部インストールできます」的なインターフェースを提供できる)が備わっているのですよ!! 全然活用されてないけど!!!!!
やり方は2つある。
ツリー型タブ、マルチプルタブハンドラ、情報化タブ、ソース表示タブ、ブックマークを新しいタブで開く、リンクを新しいタブで開く、ロケーションバーから新しいタブを開くの7つを一括インストールさせたい場合を例に説明しよう。
これはtabextensions3や学生向けアドオンパックが実際に使ってる方法だ。
まず、こういうinstall.rdfを作る。em:type="32"
というのがミソ。
<?xml version="1.0" encoding="UTF-8"?>
<RDF:RDF xmlns:em="http://www.mozilla.org/2004/em-rdf#"
xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<RDF:Description RDF:about="urn:mozilla:install-manifest"
em:id="tabextensions3@piro.sakura.ne.jp"
em:type="32">
<em:targetApplication>
<RDF:Description em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
em:minVersion="3.6"
em:maxVersion="4.0b2pre" />
</em:targetApplication>
</RDF:Description>
</RDF:RDF>
このinstall.rdfと、7つのアドオンのXPIをまとめてZIP圧縮して、ファイル名を「〜.xpi」に変えれば完成。
この時、install.rdfに書く対象アプリケーションのem:minVersion
とem:maxVersion
は、全部のアドオンの最大公約数的な値になるようにしないといけない。
例えばminVersionが3.5の物と3.6の物、maxVersionが4.0b2preと4.0b8preの物があるんだったら、「全部揃った状態で確実に動くのは3.6〜4.0b2preの間」ということでそのように指定する。
この方法の詳しい話はMDCにドキュメントがある。
この方法には、配布するファイルが1個だけでよくなるし、JavaScriptとかを使う必要がないというメリットがある。
その反面、同梱するアドオンを増やしたり減らしたり更新したりしたい時には、パッケージをその都度作り直さないといけないというデメリットがある。
これは台湾のMozillaコミュニティのプロモーション用サイトで実際に使われてる方法だ。
こんな感じのスクリプトを実行するだけ。
var targets = {
treestyletab : {
URL : 'http://piro.sakura.ne.jp/xul/xpi/treestyletab.xpi?update'
},
multipletab : {
URL : 'http://piro.sakura.ne.jp/xul/xpi/multipletab.xpi?update'
},
informationaltab : {
URL : 'http://piro.sakura.ne.jp/xul/xpi/informationaltab.xpi?update'
},
viewsourceintab : {
URL : 'http://piro.sakura.ne.jp/xul/xpi/viewsourceintab.xpi?update'
},
openbookmarkintab : {
URL : 'http://piro.sakura.ne.jp/xul/xpi/openbookmarkintab.xpi?update'
},
openlinkintab : {
URL : 'http://piro.sakura.ne.jp/xul/xpi/openlinkintab.xpi?update'
},
newtabfromlocationbar : {
URL : 'http://piro.sakura.ne.jp/xul/xpi/openlinkintab.xpi?update'
}
};
InstallTrigger.install(targets);
InstallTrigger.install()
にはインストールさせたいアドオンのリストをハッシュとして渡す。
ハッシュ値を渡して安全なインストールを可能にするとかの工夫もできる。これも詳しい話はMDCに書いてある。
この方法には、いちいちパッケージを作らなくていいというメリットがある。台湾コミュニティのサイトのように、「ユーザがリストにチェックを入れていって、最後にボタンを押したらチェックが入ってるアドオンだけを一括インストールする」という風な事が簡単にできる。
その代わり、配布するファイルがバラバラになるし、JavaScriptが有効になってないと利用できないというデメリットがある。
AMOにコレクションという機能が加わった時、これを使って「お薦めのアドオンを一括インストールできますよ!」という見せ方ができるようになるものとばかり思ってた。そういうものが絶対に必要だろ、と思ってたから、すごく期待してた。でも実際に出てきた機能はただの「リンク集作成機能」でしかなかったのですごくガッカリした。
せっかくこういう機能があるのに、それを使わないでTab Mix Plusを「お薦めアドオン」として前面に押し出してたりするんですよ。何考えてるんですかねホント。
ちょっと前に、Tab Utilities Mini、というのが出てきたらしいですね。
改めて、今現在それ系のアドオンがどれだけあるのかリストアップしてみて、乾いた笑いしか出てこなかった。
PlusにLiteにCEにMiniに……それはギャグで言っているのか? タブ統合型アドオンの比較表みたいな物がないと見分けつかないよ。既にこれだけあるのにまだ同じ事をやるんだ、っていう事に呆れる。
しかも、よく見たらTab UtilitiesとTab Utilities MiniとTab Utilities Liteって作者同じじゃないすか。ますます意味が分からない。
/ || ̄ ̄|| ∧_∧ |.....||__|| ( ) どうしてこうなった・・・ | ̄ ̄\三⊂/ ̄ ̄ ̄/ | | ( ./ / ___ / || ̄ ̄|| ∧_∧ |.....||__|| ( ^ω^ ) どうしてこうなった!? | ̄ ̄\三⊂/ ̄ ̄ ̄/ | | ( ./ / ___ ♪ ∧__,∧.∩ / || ̄ ̄|| r( ^ω^ )ノ どうしてこうなった! |.....||__|| └‐、 レ´`ヽ どうしてこうなった! | ̄ ̄\三 / ̄ ̄ ̄/ノ´` ♪ | | ( ./ / ___ ♪ ∩∧__,∧ / || ̄ ̄|| _ ヽ( ^ω^ )7 どうしてこうなった! |.....||__|| /`ヽJ ,‐┘ どうしてこうなった! | ̄ ̄\三 / ̄ ̄ ̄/ ´`ヽ、_ ノ | | ( ./ / `) ) ♪
すげー悪く言うけどさ、ほんとすげー悪く言うけどさ、今日はセンスタソになっちゃうけどさ、タブブラウザ拡張(Tabbrowser Extensions、TBE)を作ってた僕自身も含めて、こういう事やる人ってほんっとセンスないよね。
日本のメーカーがiPhoneを作るとこうなるって話なんかもそうだけど、センスが無い人は「カタログスペック上の数値が高い物」「カタログ上の機能が多い物」という観点からしか「良さ」をアピールできないんだよね。まあ、そんなセンス無しでもiPhoneを触ったら「キモチイイ!!!」って感覚は多分抱くんだよね。ただ、残念な事に、その感想が脳内のフィルタをいくつも通ってその人の口から出力される時には、どーいうわけか「あれもできる、これもできる、だから素晴らしい」的なくっだらない表現に「翻訳」されちゃうわけですよ。
同じ事が、そういう人が物を作る時にも起こるわけですよ。物を作る以上、そりゃあ、自分で「こりゃ駄目だ」って思うものよりは「これは良い物だ」って思える物を作りたくなる。でもセンスがないから、どう作れば「良い物」になるかがわかんない。しょうがないから、自分が「良い」って思った事があるものを切り貼りして集めてくるしかないワケですよ。そういう風にセンスの無い人間が、下手に決定権を持っちゃったり実行力を持っちゃったりするから、こういう物が世の中に出てきてしまうんだ。
で、こういうアドオンってほんと罪作りだと思うんだ。
カタログスペックだけはやたら高いから、よく分かってない人は「多分良い物なんだろう、カタログスペックはいいみたいだから」って同じ発想で飛びついちゃうんだよきっと。「それらの機能が本当に自分を幸せにしてくれるのかどうか」なんて分からないままに。
それで機能の8割くらいが結局使われなかったとしても、まあ、ユーザが満足してるならそれでいいじゃんっていう考え方はある。メモリだのなんだのの面で無駄が大きくても、そんなの関係ない。「無駄がないけど目的は達成されない」のと「無駄はあるけど目的は達成される」のとだったら、後者は前者に対して圧倒的に正しい。
でもさあ。「無駄がある」だけじゃなく「囲い込まれてしまう」っていう事まで加わってしまったら、それはさすがにあかんやろって思うんですよ。なんで多機能オールインワン型のアドオンが囲い込みになってしまうのかという話は過去にも詳しく書いたから、そっちを読んで欲しいんだけど。囲い込みたくて囲い込んでるんじゃなくて、囲い込もうというつもりはなかったのに結果的に囲い込みになってしまう、囲い込んだ後にちゃんと維持し続けていけるだけのキャパシティもないのにそういう状況を作り出してしまう、それが良くない。囲い込みって、やる方にも体力がいるんだよ。あらゆる物を自前で用意しなきゃいけない。そんなの個人で趣味でやれるような事じゃない。それでは作り手も使い手もみんな不幸になってしまうよ。
僕はFirefox 1.5までのバージョンにしか対応できていなかったTBEに自分自身が激しくロックインされてしまって、Firefox 2にいつまで経ってもアップグレードできなかった。そんな馬鹿みたいな事に他人まで巻き込んだらあかんよ……
(ツリー型タブから「ロケーションバーから新しいタブを開く」「リンクを新しいタブで開く」を分離することにしたきっかけがこの出来事だったので、それについて書こうと思って色々思い返したり調べ直したりしてたんだけど、結局こういう話に落ち着いた。)
Tree Style TabとMultiple Tab Handlerのドラッグ&ドロップ周りのコードを色々書き直した。
発端は、「Tab Utilitiesと一緒に使うとタブのドラッグ&ドロップが期待通りに動かない」という報告だった。しかし、「また衝突かよー」と思いながらメールの内容を読むと、想像していたのとはちょっと状況が違った。
僕はてっきり、Tab Utilitiesのイベント処理とツリー型タブのイベント処理がかちあって変な事になってるんだとばかり思ってたんだけど、そうではなくて、Tab Utilitiesが提供する「複数のタブを選択してまとめてドラッグ&ドロップする」機能で複数のタブを1つのタブの上にドロップしても、選択されていたタブのうち1つしかドロップ先のタブの子タブにならない、という状況だった。その人はすでにTab Utilitiesの作者にも問い合わせていたようで、Tab Utilitiesの作者の回答に「ツリー型タブにマルチプルタブハンドラのためのコードしか含まれていないのがそもそもの問題だ。ツリー型タブの方で修正してもらってくれ。」みたいなコメントがあったそうだ。
それでTab Utilitiesを見てみたら、確かに複数のタブを選択する機能があったんだけど、その時のタブの情報の受け渡しにHTML5のドラッグ&ドロップのイベントの複数のデータの指定の仕組みが使われているようだった。
という事情があって、マルチプルタブハンドラではドラッグ操作のイベントそのものには介入しないようにしてたんだけど、Tab Utilitiesがそういう事をやってるんだったら、「複数タブのドラッグ操作」に特化したアドオンであるマルチプルタブハンドラが黙って見てるわけにはいかない。なので、マルチプルタブハンドラではバージョン0.6から、ついでにツリー型タブもバージョン0.11.2010120101から、タブをドラッグ&ドロップするときはドラッグしようとしているすべてのタブをデータトランスファーに渡すようにした。ついでにドラッグフィードバックイメージも自前で生成するようにして、複数タブのドラッグ時はドラッグ中のすべてのタブのサムネイルを重ねて表示するようにしてみた。
それで「ああやっと時代に追いつけたわ」と安心してたんだけど、Windowsでテストしてみたら、せっかく生成したドラッグフィードバックイメージがぜんぜん表示されないんでやんの。どうも、Windows版のFirefox 3.6以前は、mozSetDataAt()
で複数のデータを指定するとドラッグフィードバックイメージが表示されないというバグがあるようだ。Ubuntu上のFirefox 3.6や、Windows上でもMinefieldでは問題なかったんだけど。頑張りの報われなさに切なくなった。
ツリー型タブの場合は、タブのドラッグ&ドロップでツリーを編集する場合があるから、マルチプルタブハンドラみたいにTabMoveイベントをトリガーとして後から必要な処理を行うということはできなかった。なので、Firefox自身のドラッグ&ドロップの処理に介入する形の設計にせざるを得なかった。
その後HTML5のドラッグ&ドロップのAPIが実装されて、Firefox 3.5ではFirefoxのタブのドラッグ&ドロップのためのコード自体もそれをベースに書き直された。これはまだFirefox 3.0と同じようなやり方でドラッグ&ドロップの処理に介入できる余地があった。
しかしFirefox 4ではタブのドロップとかドラッグオーバーとかの処理がXBLのイベントハンドラの中にベタ書きされるようになってしまって、メソッドの中に処理を注入するやり方ではドラッグ&ドロップの操作に介入できなくなってしまった。なので、仕方ないからdragstartとかdropとかのイベントにキャプチャリングフェーズで割り込んでツリー型タブの側で全部自分で処理するようにした。
という経緯があって、結果的にツリー型タブのドラッグ&ドロップ周りの処理はFirefox 3.0向けとFirefox 3.5~3.6向けとFirefox 4向けとで3種類の方法が同居してかなりシッチャカメッチャカな状態になっていた。あとタブバー自体のドラッグ&ドロップの処理もあって、それぞれがあちこちに散らばっててだいぶ訳のわからんことになってた。
そんな状態だったから、昨日のリリースでは案の定リグレッションしてドラッグ&ドロップが盛大にぶっ壊れてた。これはもう駄目かもわからんねと思ったので、Firefox 3.0向けのコードを全廃したついでにFirefox 3.5~3.6に対してもFirefox 4向けのやり方を使うようにして、ドラッグ&ドロップのコードを専用のクラスに集めて整理することにした。
安定性とメンテナンス性を取った結果、タブのドラッグ&ドロップのイベント処理はFirefox本体のものを完全に無視する形になっているので、他のアドオンとの機能の両立という点では残念なことになっているかもしれない。タブのドラッグ&ドロップに介入するためのきちんとしたAPIが整理されていれば、こんな思いをしなくて済むのになあ。
今まではTree Style Tabでタブバーを横置き(上または下にタブバーを配置するモード)にした時特有の機能というのは特に設けていなかった、というか横置きで階層化されたグループ化なんて全然使いにくいし誰も使わないだろと思って割となおざりにしてたら、案外使われてるみたいで時々このモード特有のバグ報告を貰ってたわけですが、Opera 11でタブスタッキングとかいう機能が加わるとかで「そんなんXULでもできるわい!!! つうかもっと前からやっとるわい!!!」とカッとなって、タブが重なってるっぽく表示するようにしてみた。
種を明かしてしまうと、元々「折り畳まれたタブ」はvisibility: collapseで見えないようにしてたのを、collapseにせずにvisibleのままで親のタブの下にz-indexを調整して重ねるようにしたというだけ。諸々の事情でMinefieldでしかこの表示にはならないようになってる(Firefox 3.6だと今まで通り)。
このようにしたおかげで、
というメリットを図らずも得られてしまった。
しかしIDEA*IDEAの記事の小さいスクリーンショットと動画だけ見てこういう表示にしたんだけど、もっと大きなスクリーンショットだと、実際は全然違う形だった。まあ、今更だからこのままいくけどさ……
あと、このリリースから「リンクをクリックした時にそれを自動的にタブで開く」系の機能と「ロケーションバーからの入力で常にタブを開く」系の機能を別のアドオンに分割することにした。
既にあったOpen Bookmarks in New Tabと並べて「新しくタブを開く系3兄弟」的な。
といった理由があっての決定です。多機能なのがいい人はツリー型タブを捨ててTab Kitあたりに乗り換えるといいと思います。
背景。
Web標準に対応しなくて自社独自の技術だけ使い続ける、というのはまあよくある事だから、ことさらMozillaだけをあげつらう事はないと思うんですよ。VMLには対応するけどSVGには対応しないというIE8までの方針であるとか、そういうのはありふれてる。
また、標準仕様はあるけどまだ実装されてない、っていうのもしょうがないと思うんですよ。仕様そのものが曖昧だとか、仕様が膨大すぎて対応できないとか、需要がそもそも無いとか。
釈然としないのは、
これが僕には2枚舌に見えるってこと。
APNGに新たに対応して今後はそっちをメインで使っていくよ、というのはまあ別にいいんですよ。でも、今まで一応対応してたMNGをこれを機にばっさり切り捨てちゃう今まで一度は対応してたことがあったMNGを顧みることもなく別の物をっていうのはどうなん? それじゃあ、なんかの動画形式で動画を埋め込んでて、その形式に対応してなかったらMNGにフォールバックして、みたいな事ができなくなっちゃうじゃんできないまんまじゃん? フォールバック先にはやっぱり、仕様が標準化されてて安心して使える形式を選びたいじゃん? そういう感じでMNGを使ってた使いたい人がいたかもしんないじゃん? 僕はまだ使ってなかったけど、静止画の簡単なアニメーションを公開する事があったら是非そうしたかった。Web標準ってそうやって、地ならしっていうか下支えっていうかそういう所でも活きてくる物だと思ってたんですよ。
いやMozillaの言い分もわかるっちゃ分かるんですよ?
「MozillaはWeb標準を限定的にしか尊重しません。自分たちの組織を維持できなくなるレベルでコミットする事はありません。自己犠牲でWeb標準のために殉死するつもりはさらさら無いです。」というのは真っ当な判断だと思うんですよ。
でも、だったら、「お前はWeb標準を大事にしてない」って他者を非難する資格も無いんじゃないの? 自分の事は棚に上げるの? そういうみっともない事をしないでくれよ。Mozillaだけはそういう事をしてくれるなよ。
っていうモヤモヤがずっとある。
ツリー型タブのGoogle Chrome版は作らないの?という問い合わせを一時期よくもらってたんだけど、とうとう出た。Google Chrome版Tree Style Tab。といっても僕が作ったんじゃなくて、ジェバンニが一晩でやってくれました。的な。
ただ、やはり僕が懸念していたように、形態としては「ツールバーのボタンをクリックしたらポップアップでツリーが出る」という物のようで、実際試してみても違和感があった…… 僕は「ツリーが常に見えている事」がTSTの常用には欠かせないと思っているので、この形の物はやはり辛い。
Side Tabs(起動オプション --enable-vertical-tabs で有効になる)が入っている最近のChromeでなら、あともうちょっと頑張ればなんとかなりそうな気がしなくもない。ページのタイトルの頭にスペースを挿入して擬似的にタブをインデント表示するとか。
Developers Conferenceでの発表向けの内容に盛り込めそうにない話を書いていく。
話のテーマを決めるにあたって、とにかく最初は1つJetpack SDKでアドオンを書いてみようと思ったんですよ。
でも、「こう書くと動きますよ」って言われてもなんだか信じられなかったというか、どういう経路でどういう風にしてそのコードが読み込まれるのか分からなくて、スクリプトの書き方次第じゃ他のコードに悪影響を及ぼしたりすることもあるんじゃないのか?(prototoype.jsのObject汚染みたいな感じで)とか、名前空間の衝突は大丈夫なのか? とか、そういうどーでもいい所が気になって気になって、仕組みが分からないまま使うのは怖いと思った。何をやってよくて、何をやったら駄目なのか、というのが分からないのが怖かった。
その境目を見極めたくて、Jetpack自体の成り立ちを探ってみたのですよ。
特に謎だったのが、他のモジュールを読み込むrequire()
。これが一体どこで処理されているのか。main.js(Jetpackでアドオンを作る時の主たる実装になるファイルの固定の名前)もどこかからrequire()
されているのなら、require()
の実装を見たら謎が解けるんじゃないかなーと思った。
exports
やrequire()
はここで定義されている。
EXPORTED_SYMBOLS
を使うやり方)にも対応してるみたい。Components.utils.evalInSandbox()
が呼ばれていることが分かった。ライブラリのモジュールもmain.jsも全部これで実行される。
require("chrome")
の実態が、これ。Jetpackとは、この基本的な仕組みに基づいて、個々のモジュールの名前空間を分け、それらを安全に連携できるようにしたもの。と言える。MozLabでmodule_manager.jsというものが過去に使われていて、それを想起させられた。
module_manager.jsはJavaScriptコードモジュールが無かった頃にJavaScriptそのものの言語仕様を超えた所で高度なモジュール化を行おうとしていたらしく、渡されたモジュールの名前からパスを生成してファイルを読み込んでサンドボックス内で実行してそのグローバルオブジェクトを保持し続けて……ということを普通のJavaScriptの機能とmozIJSSubScriptLoaderでやってた。Jetpackの仕組みはそれのずっとスマートなバージョンなのかなと思った。
今までの開発手法しか知らなくてJetpackがさっぱり分からないという僕みたいな時代後れの人でも、securable-module.jsとcuddlefish.jsを起点にすれば、他のモジュールも無理なく読んでいけるんじゃないだろうか。
僕はJetpackに対していろんな意味で期待していたはずなのに、自分がJetpackでバリバリ開発する姿はいまいち想像できないでいる。Jetpackの話が出始めた頃よりも、JetpackがrebootされてSDKとなってからの方がむしろ、「あれ、なんか僕ってJetpack使ってなさそうなんじゃね?」感が強くなっていっている気がする。Jetpackは僕を幸せにしてくれるのか? くれないのか? それが分からない。
Firefox Developers Conferenceでの発表について「Jetpackからよりディープな世界へのステップアップや、あるいはその逆に、これまでの手法でアドオンを開発していた人達がJetpackにステップアップするには? という風な話題」というオーダーをもらって、ようやっと重い腰を上げてJetpack SDK(とPython)をインストールしてみた。で、とりあえずJetpackの流儀というのを理解しなきゃと思って実際にアドオンを書いてみようとしていきなり挫折して、ドキュメントを読んでみようとしたけどどこから読めばいいのかすら分からなくて頭クラクラで、ヤケクソでJetpack SDKそのもののコードを読んでみたりして、という事をやりながらモヤモヤと考えてた。何故僕はこんなにも、Jetpackに乗り切れていないのか。
Jetpack SDKのドキュメントをちょっと読んで雰囲気を眺めただけでも、僕の今までの知識はまるで役に立たないということはよく分かった。
アドオンを構成するコードは、大雑把に言うと
の2つのレイヤに分けることができる。
これまでのアドオン開発においては、2の部分の開発コストが非常に大きかった。W3CのDOMであるとかCSSであるとかのWeb標準の知識がベースにあるとはいっても、XULやXPCOMというMozilla specificな技術を覚えなければならないし、覚えた上で、さらに工夫しないといけない。はっきり言って、アドオンのコードのほとんどは2のための物で、1のためのコードなんてのはほんの一部分だけだったりする。2の部分をどうやって解決するかというのが問題の大部分を占めていて、ほとんどの人はおそらくその段階で挫折してしまって、本題である1の部分に取りかかる事すらできないのだと思う。
僕は、そこ(2のレイヤ)に膨大な時間を注ぎ込んできた。そこに時間を取られるせいでFirefox本体の開発に貢献とかそんなとこまで頭が回らないよ、というくらいの勢いで。その結果蓄積された大量のバッドノウハウこそが、今の僕の武器であり価値なんだと思う。
でも、そういうバッドノウハウはすぐに陳腐化する。また、せっかく覚えたXULやXPCOMの知識も無駄になる時がある。特に、Gecko 2.0からはすべてのインターフェースが凍結されなくなるということは、これからはnsIPrefBranch::getBoolPref()
のような頻出のインターフェースすら安心して使えなくなるという事で、かかるコストと得られる物が全然釣り合わないんじゃないのか。
という事を考えると、「だからこそJetpackなんだよ」という話は理解できる。Jetpack SDKは、2の部分をアドオン開発者の代わりにカバーするライブラリ集でもある。Gecko 2.0以降のインターフェースの不安定さをJetpack SDKが吸収して隠蔽してくれるから、開発者は2の部分のためにかける労力が要らなくなって、全力を1の部分の開発に注げるようになる。という寸法だ。1と2の両方に(特に2の部分に異常に多くの)力を注がなくてはいけないロートルの開発者と、1のことだけ考えて開発していればいい新しい開発者、どっちの方が生産性が高くてクリエイティブな結果を沢山生み出せるか。あるいは、どっちの方が労力が少なく済んで、長くメンテナンスし続けられるか。そういう話だ。
しかし、ホントにそんなにうまくいくのかな?
今でも、過去にFUELという「アドオン開発者向けの、Firefox組み込みのライブラリ」が作られて、それが「XULやXPCOMといった小難しい物を隠蔽すること」を目指していたはずなのに、仕様が十分に錬られてないわ利用者(アドオン開発者)にとっての使いやすさという視点が欠けてるわ実装もあまりにやっつけ仕事でヘビーな利用には全然耐えられないわ(普通に使うとメモリリークしまくる)で、あっという間に「ああ、そんな物もあったっけ……でも使ってる人いるの?」な位置に落ちぶれてしまった。搭載当初は何も特別な準備をしなくても使えるように作られてたのに、Firefox 4からは「コイツの初期化に時間かかるから、初期状態で使えるようにわざわざしておく必要ないよね」と「一級市民のAPI」の地位を追われてしまった。という事実がある。あの頃「FUELで状況はよくなるはずだよ!」という風な事を言って、紹介を書いたりして安易に勧めていた人達は、自分も含めて戦犯として裁かれなきゃいかんね。
そういう前例を見ると、Jetpackも、今は威勢のいいことを言っていても、これから先Firefoxの仕様が変わった時に、「JetpackのライブラリのAPIを維持してFirefoxの仕様変更を頑張って吸収する」方向ではなく「JetpackのライブラリのAPIを変えてFirefoxの仕様変更に合わせる」方向に流れてしまうんじゃないのか、「XULやXPCOMといったコロコロ変わる厄介なAPIの上に、Jetpackというこれまたコロコロ変わる新しい厄介なAPIが加わっただけ」になってしまうんじゃないだろうか、と僕は思ってしまうんだ。それじゃあただの第2のFUELだ。
そこで頑張って自分で貢献するんだよ、って言われても、そもそも前述の2の所の開発で悩まされる事から解放されるためのJetpackのはずなのに……って思うわけですよ。
また、Jetpack SDKの提供するAPIをキモく感じてしまった、というのもある。APIで提供される機能がどうこう以前に、API越しでしかFirefoxに触れないっていうことがストレスになった。
例えばタブのコンテンツ領域になってるフレームの生に近いAPIには、今までだったらgBrowser.mTabContainer.childNodes[n].linkedBrowser.docShell
でアクセスできた。べつにフツーにフツーのアドオンを作るだけだったらそんなとこ触る必要ないけど、いざというときにはそういう低レベルのAPIから裏口を叩けば何とかなるという、なんていうんだろ、安心感? みたいな? そういうのがあった。
しかしJetpackではこれが隠蔽される。APIで用意された範囲の機能にしかアクセスできない。ということは、やりたいことができそうに無かった時、今までよりもずっと手前の時点で諦めなきゃいけないんじゃないかって気がして、今すぐそれをやりたいかどうか以前に、もう、それができなくなるってだけで息苦しくて窮屈で「うああああ嫌だ嫌だ嫌だ」となってしまう。
まだJetpackがrebootされるよりも前の頃、生のXUL要素に触れるraw
というプロパティがあった事について、僕はそれを激しく非難した。そんなのがあったら将来的なAPIの互換性を保てなくなってしまうじゃないか、と。その認識は今でも変わっていない。しかし自分がいざ当事者になってみて初めて、その窮屈さ不自由さを身に染みて実感した。(僕がChromeの拡張機能に手を出そうとしなかったのは、これを本能的に避けていたからなのかもしれない。)
そもそも自分がMozillaに肩入れしてるのは何でだったのか。よく考えてみるまでもなく何度も言ってるけど、プロダクトそのものがW3CのWeb標準の技術に基づいているから、そしてそれらの技術にちゃんと対応してたから、というのが一番最初にあった理由なんだよね。
「W3CのDOM? XML? CSS3? そんなの全然Webで使えないじゃん、IEで使えない物に意味なんてないよ。」
「W3Cの仕様なんて、現実見てない頭でっかちの奴らが決めたものだろ? 名前がやたら長ったらしい(例:document.getElementById(id)
はIEのDOM0だとdocument.all.id
に相当)し、キモすぎる。」
「デファクトスタンダード(事実上の標準仕様)こそがすべてだよ。デジュールスタンダード(標準はこれです、という形で作られた標準仕様)なんかに意味はないよ。」
そんなWeb標準冬の時代に、CSS2のポジショニングにも疑似要素にも疑似クラスにもかなりのレベルで対応してて、W3Cの仕様書にある通りの書き方でちゃんとレンダリングしてくれるGeckoは、実に素晴らしいものだと本気で思ったし、ブラウザのUI自体すらもW3Cの仕様通りのWeb標準技術をベースにして作られてると知った時にはもう、泣いて喜ぶ勢いでしたよ。
「ああ、世間であんなに冷遇されてるWeb標準が、ここにはちゃんと息づいてる!!」それが僕のMozillaとの関わりの始まりだった。僕にとってMozillaは、Web標準の象徴だった。W3C信者だった僕にとっては、魅上照ばりに「あなたが神か」てなもんでした。
でも気がついたら、RDFを使う部分はどんどん減らされて、MNGサポートもドロップして代わりにAPNGなんてMozilla独自の画像形式になってしまって、SOAPサポートもドロップして(だったよね?確か。)……そんな感じで「世間ではまだ広く使われてないけどWeb標準の技術に対応してる、だからまだマイナーなWeb標準の技術でも実際に動くアプリケーションを僕でも作って実証できる、Web標準はちゃんと役に立つんだって事を証明できる」と思ってた余地がどんどん減っていって。その一方で、「次のスタンダードはWebKitだ!」と「標準」のお株を奪われて、新しいWeb標準への準拠度で後れを取って、性能面でも引き離されちゃって。
さらにはユーザの側からも開発者の側からも「XULなんてクソ重い無駄なもんなくしちまえ」みたいな声が出てきて。Jetpackって、XPCOMが廃止されてもXULが廃止されても拡張機能向けのAPIの互換性を保てるようにっていうことで出てきたんだったと記憶してるんですけど、ということは、Jetpackが最高にうまくいったらXULもなくなっちゃうって事ですか? CSS3で外観を変えたり、XPathでUI要素をゴソッと収集したり、そういうのがなくなっちゃうって事ですか? みたいな。
切ないね……
だいたいさあ、安定したAPIになる保証もないのにオレオレAPIをどんどん重ねてくっていうのが気にくわんのですよ!! document.allとかlayersとか混沌として色々分断されてた世の中が、せっかくWeb標準のおかげで見通し良くなってまとまってきたと思ったのに! prototype.jsとかDojoとかMochikitとかjQueryとかYUIとかExtJSとかオレオレな実装が乱立してきてさあ!! なんなの!! もう!!! Web標準だけあればいいじゃん!!!! そんでもってUI用の言語として開発されたXULでいいじゃん!!!! なんでHTMLで無理矢理UI作ろうとするんだよ!!!!! XMLっていう仕組みがあるんだから、それに則った上で用途に適した道具を選ぼうよ!!!!! そういう事を考えもしないで、見慣れてるからってだけでdivだのspanだのでオレオレUI作ってさあ!!!!!!!! なんなんだよそれ!!!!!!!!!
はあ……
そんな感じで考えれば考えるほどダウンな気持ちになってきたんだけど、さらに調査を進めていく中で、Jetpackの今のAPIの基礎になる部分はCommonJSの仕様に則る形で開発されているという事を知って、「おおっ!?」と思った。
僕がJetpackの前のAPIでとても「うへぇ」ってなってたのは、ライブラリの読み込み方の部分だった。Greasemonkey等で広く使われてたDocComment風の記法じゃない、jetpack.future.XXX
とかのアクセス方法、あれがすごい気持ち悪かった。なんでここでまた無駄にオレオレルールを増やすかなあ!? と思って色々萎えた記憶がある。
でも今のJetpackでは、ライブラリを呼ぶ時はrequre('ライブラリ名')
、ライブラリを作る時はexports.プロパティ名
に値や関数をセットするというルールになっていた。これは、サーバサイドでJavaScriptを実行するnode.js等の環境でAPIを共通化しようということで議論が進められている、CommonJSのルールだ(そうだ)。これは、Webの開発者にとって親しみやすい物にしよう、独自拡張オレオレルールでなんでもやるんじゃなくて足並み合わせる所はきちんと合わせていこうという意図の顕れだと、僕には思えた。MNGとAPNGの件では見損なったけど、この件では見直した。
あと、Mozillaの中の構造とか全然知らない人にペアプログラミング形式で「ちょっとしたアドオンを作ってみましょうか」とJetpackベースでのやり方を指南(?)しようとして、ああやっぱりこのアプローチは間違ってないんだなということを実感した。
初学者とかWebデベロッパーとか、Mozillaヲタじゃなくてもアドオンを作りやすくなってると思う。Pythonをインストールしなきゃならんとかコマンドラインでやらなきゃならんとかの点は、これからどうにでもなることだ。今まで取りこぼされていた人達をすくい上げる基礎になる物が、今のJetpackには確かに揃いつつあるのだと思う。今までの「動的に適用されるパッチ」でしかなかったアドオンの路線のままでは絶対になし得なかったことが、Jetpackでなら確かに可能になるのだと思う。
しかしまあ、改めて考えると、僕がやっていた事も先人から見れば十分「なんだあの窮屈な世界は」てなもんなんだろうね。C++の生の実装に触ることなくその上に幾層にも積み上げられた物の上で僕はずっと遊んできたわけだけれども、その世界での制限には目を向けることもなく、全てを所与の物として受け入れてありがたがっていた。そんな僕が、GreasemonkeyやGoogle Chromeの拡張機能やJetpackに対して「なんだあの窮屈な世界は」なんて言うのはまったく笑い話でしかないのだろう。
下の層に目を向ければ、ハードウェア寄りのレイヤではそれこそ鬼のような非互換の嵐で、それを吸収するためのOSのレイヤがあって。上の層に目を向ければ、プラットフォームどころかデバイスの違いすら吸収するWebがあって。その中間層であるところのブラウザを作る段階で、プラットフォーム間の互換性がどうだのバージョン間の互換性がああだの言ってるのは、ちゃんちゃらおかしい。
そういう中途半端な所に(Web製作の世界から)逃げ延びて住み着いていた僕が、上の層から僕の居場所をなくそうと迫ってくるJetpackに恐怖を抱いて、拒絶反応を示していた。版図を広げようとしている若手の前で竹槍を振り回してた。ああ、実に老害だ……
それに、Web標準Web標準とわめいてみても、WebKitが中心になってしまった今のWebじゃあGeckoの方がはるかにオレオレ実装なはぐれ者だしおまけに10年以上前のコードを引きずってる骨董品だし、XULもXBLもXMLという仕組みの上に成り立っているとはいってもそれ自体はみんなの合意が得られた標準仕様じゃないわけだし。「神!私は仰せの通りに!」と崇めてるうちに、僕はWeb標準とMozillaのオレオレの見境が付かなくなってたのか……
とりとめがないままにモヤモヤした物を吐き出してきたわけだけれども、それと平行してJetpackの内側を覗いてみたりして、光明も見えたりして、自分の勘違いも見えてきて、いくらかスッキリした気はする。
あと、最近は人生っていいものかもしれないなあと思うようになってきたので、老害と言われようともそれでも往生際悪く地味に追いすがっていこうと思ってます。オメガギーク!
僕がアドオン開発でXBLの利用を避けることが多いのは、XBLを使ってると、他のアドオンやサードパーティ製のテーマと衝突した時ににっちもさっちもいかなくなってしまうことが多かった(という印象が強い)り、複数のバージョンのFirefoxに対応しようと思うとドツボにハマったりしたからだ。
例えばツリー型タブのようなアドオンを作る時に、「タブのDOMノードに、子タブの一覧を取得するためのchildTabs
というプロパティを追加したいな」と思ったとする。思ったっていうか、タブブラウザ拡張でかつてツリー表示機能を実装した時には実際そうしてたんだけど。それをXBLでやるとこんな風になるだろう。
<binding id="tabbrowser-tab"
extends="chrome://browser/content/tabbrowser.xml#tabbrowser-tab">
<implementation>
<property name="childTabs" readonly="true">
<getter><![CDATA[
...
]]></getter>
</property>
</implementation>
</binding>
同時に、こういうCSSも書くことになる。
.tabbrowser-tab {
-moz-binding: url("mybinding.xml#tabbrowser-tab");
}
XBLではextends
で他のバインディング定義を継承することができる。chrome://browser/content/tabbrowser.xml というのは、Firefox 3.6でタブブラウズ関係の機能を定義してるバインディングなので、これで「Firefox本来のタブの機能に加えて新しい機能を定義する」という事が簡単にできる。
が、これは同時に欠点でもある。XULの機能のかなりの部分はXBLで定義されているので、extends
を書き忘れるとマトモに動かなくなってしまうことが結構ある。だから、独自のバインディングを適用する時は、適用先の要素に既にバインディングが適用されているかどうかを調べて、現在適用されているバインディングのURIを独自のバインディングのextends
に書いておかないといけない。
そして、「元々適用されているバインディングのURI」は簡単には同定できない。Firefoxのバージョンによっても変わるし、WindowsかMac OS Xかによっても変わるし、サードパーティ製のテーマでバインディングが上書きされているかもしれないし、アドオンがバインディングを適用しているかもしれない。ひょっとしたらユーザがuserChrome.cssでバインディングの指定を変えているかもしれない。継承の順番は、自分のXBL→tabbrowser.xml→tabbox.xml→general.xml かもしれないし、自分のXBL→テーマのXBL→tabbrowser.xml→tabbox.xml→general.xml かもしれないし、自分のXBL→他のアドオンのXBL→テーマのXBL→tabbrowser.xml→tabbox.xml→general.xml かもしれない。「こう書いておけば大丈夫」という単一の継承元のURIは存在しないのだ。
さらにタチが悪いことに、XBLの定義ファイルの中に書かれたextends
は動的には書き換えられない。現在適用されているバインディングのURIをgetComputedStyle()
で取得しても、それを後からXBLのextends
に指定するという事はできない。(ひょっとしたらdata: URLを使えばできるかもしれないけど、僕はそんなのやりたくないし見たくもない……)
だから、XBLを使うアドオン同士ではバインディングの優先権の取り合いになる。CSSは後から読み込まれたもの・セレクタの指定の詳細度が高いものほど優先的に適用されるから、後からインストールしたアドオンのせいでそれまで使っていたアドオンのバインディングが適用されなくなる、なんてこともしょっちゅうあった。タブブラウザのタブ要素なんて、激戦区中の激戦区と言っていいだろう。
そう考えると、ほんの2~3個のプロパティだとかメソッドだとかを追加するためだけにこれだけのリスクを負うのは到底割に合わない。それだったら、要素のDOMノードに直接プロパティを加えるのを諦めて、コントローラやサービスになるようなクラスを作ってそっちに必要な処理を集約させる方がずっと楽だ。(だからツリー型タブでは、前述の例のようなバインディングを使う代わりに、gBrowser.treeStyleTab.getChildTabs(aTab)
で子タブの配列を得るように設計した。)
というのが、僕の出した結論だった。
そういう話なので、僕は、「何が何でもXBLを使うな」とまで言うつもりはない。前述のようなバインディングの適用の優先権争いが起こらない場所、例えば独自のXULRunnerアプリケーションだったり、独自のサイドバーパネルだったり……という部分でなら、XBLはいくらでも使っていいと思ってる。
例えば、派生の要素型がたくさんあるようなケースではXBLの継承が威力を発揮する。会社で一時期やってたXULRunnerアプリのプロジェクトでは、継承を多用することで結構工数を削減できた(と思う)。また、anonymous contentsの追加は(特に、既に存在しているDOMツリーのノードの親子関係の間に安全に割り込むような物は)、XBLでなければできないことだ。
ただ、XBLは、1つのウィンドウの中にいろんな人が好き勝手に書いたコードが同居する「Firefoxのアドオン」という分野とは、すこぶる相性が悪い。JavaScriptで書く物でもCSSで書く物でも「最初にやった者勝ち」な所はどこかしらあるけれども、XBLの場合はそれが顕著だし、「後から来た者が上手く隙間に入り込む」という風な余地が全然無い。だから僕は、XBLをなるべく避ける形でコードを書くように努めてるんだな。