たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。
の動向はもえじら組ブログで。
宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。
以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
すっかり時機を逸してしまった感がありますが、Firefox拡張機能開発チュートリアルのPDFを公開しましたことをここに告知いたします。このサイトかもじら組のFTPサーバからダウンロードできます。Creative Commonsなので一定の条件下で自由に転載、改変、再配布可能です(誰かMDCに転載してHTML版作ってくれないかなあ……と、誰ともなしに無茶振りしておきます)。
内容は、Software Design誌2007年4月号第2特集「Firefox拡張機能開発チュートリアル」として掲載された記事をFirefox Developers Conference Summer 2007でテキストとして頒布するために再録したもの(この度の公開にあたって既知の間違いは修正してあります)で、XULやXPCOMの紹介から、実際のアドオンの作り方、オープンソースなライセンスの紹介まで一通り扱っています。Firefox 2を前提に書かれていますが、Firefox 3やThunderbirdでも基本的にはそのまま利用できます。
8月発売予定のオライリーのFirefox 3 Hacksに書いた内容は、アドオン作るの初めてという人は完全置いてけぼりなので、まずはこちらで予習される事をお勧めします。
なお、このFirefox拡張機能開発チュートリアルでチュートリアルの章を執筆されているGomitaさんが、技術評論社WebサイトにてFirefox 3でのアドオン開発記事を連載されています。御託はいいからとりあえず作らせれ! Firefox 3の機能もソッコー使いたいんだ! という場合はこっちからご覧になるのがよいかもしれません。
クレジットには名前が挙がっていませんが、PDFにしてもらう際にほとんどの作業をご担当いただいたNさん、訂正箇所の反映作業を行っていただいたMozilla Japan吉野さんに感謝です。
やっと一般公開、長かった…
(mal_blue@tumblr)ごめんなさいほんとごめんなさい。実は、2007年のDevConでテキストとして再編集して配りたいという話になった時点で、Creative Commonsにして公開したいという話も同時に出ていたと記憶しているのですが、イベント直前での判断だったのでCCにする場合のメリット・デメリットをきちんと検討できる余裕がなく、イベント終わってからに先送りされて、それっきり忘れ去られてた時間がずっとあって、今年4月のOSC長岡の頃にDevConで刷った冊子がなくなりそうだから誤字を直して増刷しようとなった時にやっとその話がまた動き出して、誤字訂正のついでにライセンスの検討と関係者各位にライセンス変更の確認(CC-BY-SA+MITライセンス)を行って、その時増刷された物の時点でもうこのライセンスになってたから実はスキャンするなり手打ちするなりして再配布されても全然OKな状態だったし、PDFもできあがってたんですが、何故かズルズルと公開が遅れてしまっていました。まあ、先日のFirefox 3 Hacks校正会の時についでに僕の担当箇所のミスを2カ所修正してもらった(nsIConverterInputStreamとnsIConverterOutputStreamを使う所でバッファサイズを決め打ちにしてしまっていたせいで、サンプルをそのまま使うと読み込む内容や書き出す内容が1024バイトでちょん切られる問題があった)ので、公開が遅くなったのが全く無意味だったというわけでもないと言いたいところなんですが。
萌えキャラをという話もありましたが、年齢層の高い複数関係者の反対でボツになりました
というのを見て「ま た P i r o か」とあらぬ疑いをかける人がいそうなので先に言っておきますが、僕は関係者の中では一番最初に反対を表明しましたよ。
あと江村さんに倣って自分も担当章の見出しを晒してみます。
見出しの数だけ見ると大したことないんですが、ゲラ刷りの目次によると、この章だけで100ページ超えてるみたいです。この見出しの下に大量の小見出しがあるので読みにくさ抜群ですね。校正したくないよう……
places.sqliteが数十MBを超えているのりさんやdrryさんの環境では分単位で固まってしまって使い物にならん、ということだったので、お二人に協力してもらって各段階での処理の所要時間を調べてみた。
そしたらどうも、Placesデータベースから文字列をぶっこ抜いて正規表現でマッチングしてるところがものすごく重いらしいということが判明した。ログによると500万文字ある文字列に対してマッチングをかけようとしてたんだから、そりゃあ固まるわ、と。
固まる問題だけでもとりあえず何とかしよう、ということで数千件単位で文字列を取り出し→マッチング→取り出し→マッチング……という風に分割処理するようにしてみたところ、とりあえず固まる問題は何とかなったんだけど、でも検索結果が出るまで延々待ち続けないといけないのは相変わらずで。
と、そこでやっと気づいたんだけど、べつに正規表現でのマッチングとポップアップの内容の更新とを完全に分けて実行する必要はないんですよね。ちょっとずつマッチングしてちょっとずつ検索結果を取得してちょっとずつ結果のリストを更新していけば、全体ではものすごく時間がかかるようであっても、とりあえず最初の方の結果だけは見えるからストレスにはならない。必要な数だけ結果を取り出せたらそこで処理を打ち切ってしまえばいいんだし。
というわけであちこち書き直してみた結果、最初の実装に比べるとびっくりするほど快適に動くようになった。スレッド使ってなくてもそんなに重くない。のりさんにも「これなら常用できそう」と言ってもらえたし。
最終的にやってることは同じなんだけど、やり方を変えるだけでこんなにも体感速度に違いが出るものなんだなあ、ということをこれ以上ないほど実感した日でした。
それはそうと、途中の段階で分割処理を行うようにしたときのログを見てて気がついたんだけど、SQLite(というか RDBMS)ってすごいね。テストしてもらったのりさんの環境の場合、スマートロケーションバーの検索対象になる物だけでも46000件近く、そうじゃない物も含めればきっともっと大量のレコードがあるのに、「並べ替え後の順番で任意の箇所を取り出す」のに1秒もかからないというのには驚いた。今までちゃんとしたデータベースを触ったことがなかったから、こんなの未知の世界だ。世界規模のデータベースと世界規模の処理環境に憧れてグーグルを目指す人の気持ちが、少しは分かったような気がする。
こないだから少しずつ取り組んでる。
当初は皆目見当も付かなかったけど、Firefox 3 Hacksを書くために調べた知識が早速役に立って、Places APIを使ってる「履歴とブックマークの管理」と「ブックマーク」「履歴」の各サイドバーについてはワリとあっさり実装できた。
ロケーションバーについては残念ながら普通のAPIを使っておらず、オートコンプリートの実装の中でガチガチに書かれてて、まともにやっても手出しできない。OR検索さえできればMigemoが使えるのに。ということで、まず最初は、オートコンプリートのコントローラに複数の単語を順番に渡して結果のリストを生成させてそれを最後にまとめる、という事をやってみた。これは結構イイ線いったんじゃないかと思ったんだけど、履歴の件数が増えたりヒットした単語数が増えたりするとえらい事になってしまって、実用的な速度は出なかった。
そこで諦めて腹をくくって、無い知恵絞ってSQL文書いて、なるべく効率よく処理するようにしてみた。これで速度はだいぶ上がって、3文字以上入力した先あたりならかなりサクサク動くようにはなったんだけど、1文字2文字程度しか入力していないとやっぱりズシッと重い感じがある。
XPCOMのスレッドを作る機能を使ってみた所、Firefoxが落ちたし。
ソース表示タブを更新して「やったー」と思ってたらFirefox 3では動くけどFirefox 2では動かないという状態になっていて焦った。0.2.*からFirefoxのブラウザのUIによりシームレスに統合するべく、「view-source-tab:(元のURI)」という独自プロトコルでソース表示するようにして、Chrome URLをユーザの目には見えないようにしてみたんだけど、この際に使ったリダイレクトの方法だとFirefox 3では動くのにFirefox 2では動かないという現象が起こってしまっていた。基本的には過去に訳した記事の通りにやったんだけど、思わぬ所で嵌ってしまった。
この情報、元々はURNサポートの実装を改善するために調べたんだけど、その時は、訳した記事の指示の通りにやるとブラウザ上で表示されるURIがリダイレクト前のURNのままになるのは望んでいなくて、結局nsIContentPolicyを使うようにしていた。
しかし今回はむしろその逆で、about:configみたいにユーザには実体のChrome URLを見せないまま機能させたいというのが目標だ。というわけで元記事のやり方ほぼそのままでnewChannel()で生成するURIだけChrome URLにしてみた所、Firefox 3でのテストでは問題なく動いたのでそのままリリースしたんだけど、これがFirefox 2では動かなかったという次第で。
viewSource.xul等を「リダイレクト先」にした場合、ブラウザ上に表示されるURIとしてはあくまでリダイレクト前の「view-source-tab:(URI)」という物になって、Firefoxからもあくまでそのリダイレクト前のリソースであるという風に見えるようになる。userChrome.cssでabout:configの表示をいじりたかったら 実体のChrome URLの方ではなく@-moz-document url(about:config) { ... }
と書かないと機能しない、ということからもそれが分かる。そのため、viewSource.xulにクロスパッケージオーバーレイで機能を追加していた場合、viewSource.xulにリダイレクトするとその表示結果のURIのもとではクロスパッケージオーバーレイは適用されず、タブの中でうまく動かすためのパッチを当てられないことになる。
しかしこの場合でも<?xul-overlay href="..."?>
で書かれていたオーバーレイは適用される。なので、ソース表示タブで用意したXULドキュメントからオーバーレイでviewSource.xulを読み込んでやれば問題なく動くようになる。というのがFirefox 3上でテストした時の結果だった。
Firefox 2では、クロスパッケージオーバーレイが効かないのはもちろんのこと、上記のように直接ヘッダ部分に書いて指定したオーバーレイも全然読み込まれないということが、リリース後に試してみてやっと分かった。ソース表示タブで用意したXULドキュメントを読ませても何もオーバーレイが適用されないので、画面は真っ白のまま……という状態になっていた。なんてこった。
ちょっと試した限りでは上手くやる方法が見つかりそうにないと思ったので、もう諦めて、Firefox 2の時はドキュメントのURIがリダイレクト先の物に完全に切り替わるnsIContentPolicyを使った方法を使うようにした。今のソース表示タブは、Firefox 3で「view-source-tab:http://...」などと入力するとそのURIのままでソースを表示するけど、Firefox 2の場合は「chrome://viewsourceintab/content/viewer.xul?http://...」という風にChrome URLが丸見えになる。その代わりちゃんと動く。という状態。
誰も書いてないようだったので、activetitlebarcolor属性とinactivetitlebarcolor属性についての説明を勝手に書き加えた。どこにも説明がなかったってことは、これは本当は触っちゃいけないって事なのかもしれんけど、そんなこと知ったこっちゃねえな!(ひどい)
以下、具体的な利用シーンについてちょっと解説。
Mac OS X上では、FinderやSafari、iTunesなどいくつかのアプリケーションで、ウィンドウのタイトルバーとツールバーが結合されたような見た目になっている。これを真似するためにFirefox 3で実装されたのが、activetitlebarcolor属性とinactivetitlebarcolor属性だ。実際には、OS X用デフォルトテーマのバインディングで利用されている。
通常、Mac OS Xではウィンドウのタイトルバーはグラデーションがかかってるんだけど、この属性で #FFF
とか #FEFEFE
とか gray
とかの形で色を指定しておくと、ウィンドウがアクティブな時と非アクティブな時のそれぞれで、タイトルバーの背景がその色一色で塗りつぶされるようになる。と同時に、タイトルバーとウィンドウ内容との間の境界線が表示されなくなる。あとはウィンドウの背景やツールバーの背景をタイトルバーに指定した色と同じ色もしくはそれにつながるグラデーションにしておけば、タイトルバーとツールバーがくっついたデザインのように見えるようになる、というわけ。(逆に、これらの属性をremoveAttribute()
で取っ払ってやれば、タイトルバーの表示を強制的に今まで通りの物に戻すことができる)
なお、これらの属性値を変更した場合、ウィンドウのフォーカスが移動するとかウィンドウの大きさが変わるとかしてタイトルバーが再描画されるまでは、見た目は変化しない。動的に変更する時は、属性値を変えた後に window.resizeBy(-1, 0); window.resizeBy(1, 0);
とかいう感じで強制的に再描画させるといいと思う。
この機能の残念なところは、グラデーションや半透明や背景色といった派手な指定ができない点。あくまで背景を一色で塗りつぶすことしかできない。CSSのbackgroundプロパティと同等の機能をフル機能で使えれば良かったんだけどなあ。
アナウンスに含まれてるかどうか知らないけどものっそ細かい所での変更点で僕が詰まった所。
まず一つめ。Firefox 2まででは、何かオブジェクトをドラッグしてる間はsetTimeoutやsetIntervalで設定したタイマーが発火しないという問題があった(ドラッグ&ドロップ中のタイマーを擬似的に再現する)。これが、いつの時点からかFirefox 3ではWindowsでも普通にタイマーが動作するようになってた。セカンドサーチやツリー型タブで「ドラッグ中に検索バー(タブ)の上でしばらく待つと〜」系の動作がうまく機能しなくなっていたのは、これが原因。Firefox 3以降ではWindows環境でも普通にタイマーを使うようにコードを書き換えたらうまく動くようになった。
二つめ。 個々のフレームに対応するnsIDocShellのオブジェクトを取得するなどで触れたnsIDocShellTreeItemインターフェースについて、Firefox 3ではchildOffsetプロパティがなくなってしまった。XUL/MigemoでFirefox 3においてフレームを跨いだ検索ができなくなっていたのはこれが原因(今フォーカスしてるフレームの次のフレーム、を取得するためにこのプロパティを使っていた)。かっこ悪いけど、親フレームの子フレームリストを取ってきてループ回してchildOffsetに相当する値を算出するようにしたらうまく動くようになった。
なんで今更になってこんな所(後者)をいじったんだか……
Firefox 2まででは、position:fixedな要素はz-indexを適切に指定すれば内容領域の上にも普通に表示できた。なので、ウィンドウ全体を覆い尽くすというのも簡単だった。
でもFirefox 3では仕様が変わって、常にサブフレームの内容が上に表示されるようになったため、CSSのポジショニングだけでは任意の要素を任意の位置に表示することはできなくなってしまった。仮にposition:fixed; top:0; left:0なボックスを置いても、その上にブラウズ領域の中身が表示されてしまう。ウィンドウの内容の上に何かを重ねて表示したい時には汎用のポップアップであるpanel要素を使え、というのがFirefox 3流のやり方らしい。
しかし古い拡張機能をアップデートするにあたってそこらへんのつくりを全面的に書き換えるのは大変な労力を要するし、果てしないregressionの嵐が発生しかねない。できれば最小限の労力で、今までのやり方に一工夫加えるだけで正常に動作するようにしたい。
ということでウィンドウ全体を覆い隠してゴニョゴニョするアレの時に試行錯誤して、CSSのポジショニングをどーしても使いたければサブフレームを新たに生成するしかないっぽいという結論に辿り着いていただけれども、その方法をタブカタログで使おうとしたところDOMDocumentの違いとかで死にそうになったので、諦めて別の方法をいくつか探ってみた。
で、結論としては、visibility:hiddenになったサブフレームの上なら、普通にCSSのポジショニングで要素を重ねられるようです。display:noneとかvisibility:collapseとかをサブフレームに使うとDocument Shell(nsIDocShellのオブジェクト)がぶっ壊れてしまってまともに動かなくなるので、この辺のプロパティをいじるのはヤバイと思って今まで全く触ってなかったんだけど、少なくともFirefox 3ではFirefox 2でもFirefox 3でも、visibility:hidden/visibleの切り替えではDocument Shellは破壊されないようだということに、ああでもないこうでもないといじってるうちにたまたま気がついたのですよ。
でもこれだとタブカタログのように、一旦全面を覆い尽くしてその上に全然別のコンテンツを表示するといった機能は作れるけど、InterNoteのようにページ上に任意の要素を置くっていうことはできそうにない。そういう場合はページ内のDOMDocumentを編集するしかないようだ。
オープンソースなライセンスやコピーレフトなライセンス、クリエイティブコモンズについて、他のライセンスとどう組み合わせられるのかを図にしてみたの内容、特にクリエイティブコモンズの「継承」オプションの有無が何を意味するのか、あたりについてこの数日で図解も交えて大幅な書き換えを行いました。前の版しか見てない人で、今後もう一度読み返すつもりがなかった人は、今のうちに見といた方がいいと思う。
しかし僕のように中途半端な知識しか持ってない人間がこういうまとめを書いただけではてブで500近いブックマーク数を集めるというのは、よっぽどみんなこういう情報に飢えてたのかなあと思う。これに興味を持った人は、以下の記事も見るといいと思った。
Firefox 3のFUEL、調べれば調べるほど酷さが浮き彫りになってくる。norahさんが「これはひどい」と言ってた意味を! 「心」でッ! 「理解」したッ!! いやもうほんと酷すぎます。XULとJavaScriptとXPCOMが入り乱れてる酷い現状を何とかしようとして作られたはずなのに、それで出てきた物が輪をかけて酷いなんて、悪夢だ……
正直、こんな物の使い方の解説を書くのは犯罪じゃないかって気がするよ。使い方とか誰にも知られないまま、ひっそりと廃れるかひっそりと改善されるのを待つか、どっちかにしたほうがいいって。まじで。
例えばこんな世界を想像してごらん。
var listener = new EventListener();
var reference1 = document.getElementById('item');
reference1.addEventListener('click', listener, false);
var reference2 = document.getElementById('item');
reference2.removeEventListener('click', listener, false); // ここでエラーになる。
alert(reference1 == reference2); // false
これが実際に起こるのがFUELの世界なんだぜ……
var reference1 = Application.extensions.get('myextension@mydomain');
reference1.storage.set('privateValue', 'someData');
var reference2 = Application.extensions.get('myextension@mydomain');
var val = reference2.storage.get('privateValue', 'defaultData');
alert(val); // says "defaultData"
alert(reference1 == reference2); // false
誤解してた。Extensionとそのsotrageプロパティの場合は問題ないようだ。でもWindowやBrowserTabへのイベントリスナの登録はやっぱり問題あり。こういう風に「どの場合は問題なくて、どの場合は問題ありなのか」が場合によって異なるというのも非常に困る。
もちろんこの状況でも問題を回避する方法はあるけど、その方法の意味を理解するには、処理系の中で何が起こってるかが分かっている必要がある。そういうことが分からなくても気楽に書けるのがLightweightLanguageのいいところなんじゃないのかー!