Now browsing the SCRAPBLOG weblog archives.

XULPlanet の XPCOM リファレンスが Firefox 3 / Mozilla 1.9 ベースに更新

結局、XULPlanet はまだ存続するようだ。
特に使用頻度の高い XPCOM のリファレンスが、 Firefox 2 / Mozilla 1.8 ベースから Firefox 3 / Mozilla 1.9 ベースへと更新された。確かに FUEL とか Places の API も反映されている。

XPCOM Reference

このリファレンスは各 XPCOM インタフェースの IDL ファイルから Perl のスクリプトで一括変換して生成しているみたいだが、最近一部のリファレンスに、元の IDL ファイルにコメントで記載されている情報が抜け落ちていることに気付いた。

mozilla mozilla/embedding/components/webbrowserpersist/public/nsIWebBrowserPersist.idl
Interface Reference – nsIWebBrowserPersist

この2つを比べるとわかるが、IDL の方にコメントで記載されている「No special persistence behaviour.」という内容(”PERSIST_FLAGS_NONE” についての説明)は、 XULPlanet のリファレンスには掲載されていない。おそらく「/**」~「*/」という形式のコメントを無視してリファレンスを生成しているのだろうか。とりあえず Neil Deakin 氏のブログにコメントしてみた。

TOP

nsIWebBrowserPersist で gzip 圧縮されたデータをダウンロードする

HTTPでダウンロードする際は nsIWebBrowserPersist::saveURI や saveChannel が便利だ。
しかし、gzip圧縮形式で転送されたデータ(つまりWebサーバが「Content-Encoding: gzip」ヘッダを付加した場合)をダウンロードすると、圧縮されたままの状態でファイルへと保存してしまう。

非圧縮状態でファイルへ保存するためには、 nsIWebBrowserPersist::persistFlags プロパティに PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION フラグを立てる。
あえて圧縮状態で保存する理由が思いつかないので、基本的にこのフラグは必須と考えてよさそう。

参考

nsIWebBrowserPersist.idl

余談

ScrapBook でgzip圧縮されたページを保存すると画像がすべて壊れて表示されなかったり、 Amazon や はてな のページを保存すると、ツリーの favicon が消滅してしまう、といった問題はすべて上記の問題によるものだった。さきほど修正して新しいバージョンをリリースした。

なぜこんな簡単なことに今まで気付かなかったのだろうか…。
しかも気付く前に Bugzilla にバグを立ててしまった。
Bug 416817 – nsIWebBrowserPersist should decompress data if server responses with "Content-Encoding: gzip" header

TOP

_base_href 属性

<html>
  <body>
    <base href="http://www.mozilla.com/">
    <a href="/en-US/firefox/"><img src="/img/firefox-title.png"></a>
  </body>
</html>

上記テストケースのように、 <body> 内に <base> を配置した HTML を Firefox で開くと、 <a> や <img> に対して _base_href という属性が勝手に付加される。実際に DOM インスペクタにて確認可能。
もちろん <a> と <img> だけでなく、 <object> や <embed> も影響あり。

TOP

FireGestures ボツネタ ~ 複数機能のポップアップ

※このエントリの内容は FireGestures のボツネタ機能に関する説明です。

機能

通常マウスジェスチャでは、ひとつのジェスチャにはひとつの機能しか割り当てることができないが、ひとつのジェスチャに対して複数の機能割り当てを許可し、そのジェスチャを行うとポップアップを表示して実行したい機能を選択できるという機能。

例えば、↑のジェスチャに対して、「新しいウィンドウ」「新しいタブ」の2つの機能を割り当てる。
すると、↑のジェスチャを行うと下図のようなポップアップが表示され、本当に実行したい機能を選択できる。

あまり使わない機能群に対して同一ジェスチャを割り当てておけば、覚えなければならないジェスチャの数も減らせて効果的である。

実装

とりあえず実装してみたところ、小1時間ほどでできた。改修したポイントは以下の5点。

  • 機能のタイプの定義へ、新たにポップアップ型を追加。(現状は標準装備型とユーザスクリプト型の2種類)
  • 設定画面にて、複数機能に対して同一ジェスチャの割り当てを許可する(現状は同一ジェスチャを割り当てようとするとダイアログ表示)。
  • マッピング管理では、同一ジェスチャに対して複数の機能が割り当てられている場合、それらの機能のIDをタブで連結した文字列にて保持する。
  • ユーザが行ったジェスチャに対してポップアップ型機能が割り当てられている場合、ポップアップを表示する(1度目の機能実行)。
    さらにポップアップからメニュー項目を選択すると、そのメニュー項目に割り当てられているコマンドを実行する(2度目の機能実行)。つまり、2段階で目的の機能を実行する形となる。
  • 実際にポップアップを表示する処理は、元々あるホイールジェスチャなどから表示するポップアップの実装を流用。

問題点

「リンクをタブで開く」のようにジェスチャの始点のリンクに対して何かするような機能をポップアップから実行する場合に痛い問題が発生。ジェスチャ開始位置のDOM要素への参照は、ジェスチャ実行開始~終了までの間は保持するものの、ジェスチャに割り当てられた機能を実行後にメモリリーク対策として破棄してしまう。よって、2度目の機能実行(ポップアップのメニュー項目を選択)をした時点では、ジェスチャ始点にあるDOM要素はもはや保持されておらず、リンク先に対する処理が実行できない。

ジェスチャ開始位置のDOM要素への参照を破棄するタイミングを変えることができるように修正しようとしたが、ロジックがだいぶ複雑化しそうなので早々に諦めた。

TOP

タブの複製機能

Firefox 3 (Minefield) で新たに追加されたタブの複製機能(Ctrl キーを押下しながらのタブのドラッグ&ドロップ)が、戻る・進むの履歴などのセッション状態も含めてタブを複製できるようになった。

これに伴い、 nsISessionStore インタフェースに以下の3メソッドが追加された。

メソッド 概要
duplicateTab() 指定したタブを、セッション状態も含めて複製する
getTabState() 指定したタブのセッション状態を JSON 文字列として取得する
setTabState() タブのセッション状態を表す JSON 文字列からタブを復元する

duplicateTab はすでに閉じたタブの状態を復元するのではなく、現在開いているタブの状態を複製する機能なので、 browser.sessionstore.enabled が false でも動作するようだ。

nsISessionStore::duplicateTab 使用例

// an example code to duplicate |tab| in the current window
var newTab;
try {
    newTab = Cc["@mozilla.org/browser/sessionstore;1"]
             .getService(Ci.nsISessionStore)
             .duplicateTab(window, tab);
}
catch (ex) {
    // fall-back
    newTab = gBrowser.loadOneTab(gBrowser.getBrowserForTab(tab).currentURI.spec);
}

xul:tabbrowser 要素自体に定義された duplicateTab メソッドがまさに上記のことをやっているので、これを使う方が手っ取り早い。

var newTab = gBrowser.duplicateTab(tab);

参考

Bug 393716 – Add single tab save/restore to session store API

TOP

Ts/Txul regression

Bugzilla でたまに見かける「Ts/Txul regression」とかいうのは、以下のような項目の測定をした結果、パフォーマンス低下が見られた、という意味らしい。

Ts: 起動時間
Tp: ページのロード
Txul: 新規ウィンドウのオープン

Policy on Handling Performance Regressions

TOP

nsIPopupBoxObject::setConsumeRollupEvent (2)

前の記事に書いた時点では、 Firefox 3 で新たに追加された nsIPopupBoxObject::setConsumeRollupEvent メソッドは Windows でのみ動作し、 Linux と Mac OS X では効果が無かった。

Linuxのその後の状況

1月11日にLinuxで動作しない件についてバグを立てた。
Bug 411903 – nsIPopupBoxObject#setConsumeRollupEvent has no effect on Linux
1月20日頃、以下のバグへのパッチ適用に伴い、Linuxでもめでたく正常動作するようになった。
Bug 188126 – gtk2: autocomplete/popup widgets should not block clicks outside of themselves

Mac OS Xのその後の状況

2月3日にMac OS Xで動作しない件についてバグを立てた。有識者による確認待ち。
Bug 415440 – nsIPopupBoxObject#setConsumeRollupEvent has no effect on Mac OS X
Neal Deakin 氏によると、 Mac OS X では単純にまだ実装されていないだけのようだ。

TOP

nsIPopupBoxObject::setConsumeRollupEvent

ポップアップを開いた状態でポップアップの外側のどこかをクリックしたときの挙動は、ウィジェットやプラットフォームに依存する。わかりやすい例として、コンテントエリア内で右クリックメニューを開き、そのままページ内のどこかのハイパーリンクをクリックすると、

プラットフォーム 挙動
Windows ポップアップが閉じ、リンク先へ遷移する
Linux ポップアップが閉じるだけ
Mac ポップアップが閉じるだけ

これに対して、検索バーの検索エンジンのドロップダウンリストを開いた状態で、コンテントエリア内のハイパーリンクをクリックしたときの挙動は、プラットフォームによらずドロップダウンリストが閉じるだけで、リンク先へは遷移しない。

このように、ポップアップの外側をクリックしたときにポップアップを自動で閉じる(ロールアップする)だけで、実際にクリックした何かに対するクリックイベントが発生しないような挙動のことを、「クリックの横取り」と呼ぶ。クリック横取りに関する動作仕様の詳細は、 nsMenuPopupFrame::ConsumeOutsideClicks のソース内に記載されており、通常は以下のルールに従った挙動となる。

ウィジェットの種類 ポップアップの親要素 挙動
メニュー <menu> か <popupset> Windows: クリックの横取りなし
Linux / Mac: クリックの横取りあり
オートコンプリート <textbox type=”autocomplete”> プラットフォームによらず、クリックの横取りなし
コンボボックス <menulist> プラットフォームによらず、クリックの横取りあり

Firefox 3 では新たに追加された nsIPopupBoxObject::setConsumeRollupEvent メソッドを使って、クリックの横取りの動作仕様を無理やり変更できるようになった。このメソッドの引数は3種類あり、0(デフォルト、ウィジェット・プラットフォーム依存)、1(クリックの横取りを強制的にありにする)、2(クリックの横取りを強制的になしにする)の3種類が指定可能である。実際に試すために以下の手順を試す。

  1. テストケースを開く
  2. [ROLLUP_DEFAULT] ボタンをクリックしてポップアップを開く
  3. ポップアップを開いたまま [TEST] ボタンをクリックする
  4. [ROLLUP_CONSUME] ボタンについても同様に手順2~3を繰り返す
  5. [ROLLUP_CONSUME] ボタンについても同様に手順2~3を繰り返す

すると、 Windows + Firefox 3.0b3pre では期待通り以下の結果が得られたものの、 Linux / Mac + Firefox 3.0b3pre では期待に反した結果となった。

最初に押下するボタン プラットフォーム [TEST] ボタン押下後の挙動
[ROLLUP_DEFAULT] Windows ポップアップが閉じるだけ
Linux ポップアップが閉じるだけ
Mac ポップアップが閉じるだけ
[ROLLUP_CONSUME] Windows ポップアップが閉じるだけ
Linux ポップアップが閉じるだけ
Mac ポップアップが閉じるだけ
[ROLLUP_NO_CONSUME] Windows ポップアップが閉じてダイアログが表示
Linux ポップアップが閉じるだけ
Mac ポップアップが閉じるだけ

どうやら Linux / Mac では nsIPopupBoxObject#setConsumeRollupEvent の引数に ROLLUP_NO_CONSUME を渡したときの効果が何も無いようなので、 Bugzilla へバグを立ててみた。ここまで自分が述べたことに確信を持っているわけではないので、有識者の判断を待つこととする。

Bug 411903 – nsIPopupBoxObject#setConsumeRollupEvent has no effect on Linux

関連記事

Firefox 3 でのポップアップ仕様変更
nsIPopupBoxObject::enableRollup
nsIPopupBoxObject::enableKeyboardNavigator

TOP

XULPlanet の未来

入門者のためのチュートリアル、各種オブジェクトやXPCOMのリファレンスとして長らく重要な役目を果たしてきた XULPlanet であるが、ここにきて転換期を迎えているようだ。

チュートリアルの内容はすでに MDC へ移行しており、 XULPlanet の内容はもはや古くなってしまっている。チュートリアルの作者でもある Neil Deakin 曰く、今後唯一期待できることは Firefox 3 リリース後に XPCOM リファレンスの内容を一括アップデートする程度であるとのこと。 XULPlanet を生き返らせるための何か新しい道を模索するか、あるいはすべてのコンテンツを削除して単なる MDC へのリダイレクトへとするか、問われている。

個人的にはチュートリアルには大変お世話になったし、 XPCOM のメソッドの使い方とかは未だに XULPlanet で検索することがあるので、完全に無くなってしまうのは惜しい気がする。

TOP

JavaScript コードモジュール

Using JavaScript code modules – MDC の和訳が完了した。
「JavaScript コードモジュール」とは、 Firefox 3 の JavaScript 製 XPCOM で頻繁に見かける以下のようなやつのことです。

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

上記の例のように共通で使用するユーティリティ的な関数群をモジュール化して、色々な場所からインポートして利用することができるだけでなく、あらゆる JavaScript のスコープで共有可能なシングルトンのオブジェクトとしての利用も可能です。今までは同様のことを実現するためには JavaScript 製 XPCOM の定義が必要であり、若干敷居が高かった。

まあ Firefox 3 以降ということで実践投入はだいぶ先のことになりそうだが、 JavaScript コードモジュールによって拡張機能の実装方式の自由度が高まることは間違いないでしょう。

TOP