Now browsing the archives for 2月, 2008.

[userChrome.js] リアルタイムタブプレビュー

タブ上にマウスをかざすと、サムネイルプレビューをリアルタイムに表示する userChrome.js 用スクリプトです。
Opera や Seamonkey にある機能とほぼ同じですが、サムネイルのプレビューを一定間隔で更新するので、読み込み中のタブの状態を、タブを切り替えることなくチェックしたりできます。
スクリプトはこちら: xuldev.org :: userChrome.js scripts » Tab Preview

Tab Preview

userChrome.js スクリプト配布ページ

これまでに掲載した userChrome.js 用スクリプトを整理して配布ページを作りました。
いくつかのスクリプトを新たに追加し、これまでのスクリプトも一通り見直して修正しています。
xuldev.org :: userChrome.js scripts

TOP

nsIWebBrowserPersist の基本的な使い方 (3) ~ 各種ヘッダの追加

リファラを指定する

事前準備として、リファラの URL 文字列から nsIURL オブジェクトを生成する。

var refURL = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIURL);
refURL.spec = "http://www.itmedia.co.jp/";
saveURI の場合

saveURI メソッドの第3引数 aReferrer に nsIURI オブジェクトを指定することでリファラをセットできる。
Live HTTP Headers で要求時のヘッダを見ると、「Referer: http://www.itmedia.co.jp/」というヘッダが付いている。

wbp.saveURI(url, null, refURL, null, null, file);
saveChannel の場合

nsIHttpChannel の referrer プロパティに nsIURI オブジェクトを指定する。

channel.referrer = refURL;

任意のヘッダを指定する

saveURI の場合

saveURI メソッドの第5引数 aExtraHeaders にヘッダを文字列として指定すると、HTTP要求時にそのヘッダを追加できる。
ヘッダは「Foo: bar
」のような形式で指定する。ただし、既存ヘッダを上書きすることはできず、必ず追加される。
例えば以下のようにすると、「Accept-Encoding: gzip,deflate, xxx」となる。

wbp.saveURI(url, null, null, null, "Accept-Encoding: xxx
", file);
saveChannel の場合

nsIHttpChannel では、 setRequestHeader メソッドで自由にヘッダを追加できる。
第3引数 aMerge を true にすると既存ヘッダを上書きせずに追加し、 false にすると既存ヘッダを上書きする。

channel.setRequestHeader("User-Agent", "HTTP Downloader", false);

以下のようにすると、「Accept-Encoding」ヘッダ自体を送らなくすることができる。

channel.setRequestHeader("Accept-Encoding", null, false);

関連記事

nsIWebBrowserPersist の基本的な使い方 (1) ~ 基本形
nsIWebBrowserPersist の基本的な使い方 (2) ~ persistFlags
nsIWebBrowserPersist の基本的な使い方 (3) ~ 各種ヘッダの追加
nsIWebBrowserPersist の基本的な使い方 (4) ~ POST メソッド
nsIWebBrowserPersist の基本的な使い方 (5) ~ ダウンロード進捗状況
つづく…?

TOP

nsIWebBrowserPersist の基本的な使い方 (2) ~ persistFlags

saveURI / saveChannel を実行する前に nsIWebBrowserPersist オブジェクトの persistFlags プロパティに各種フラグをセットすることで、ダウンロードや保存の方式のオプションを指定できる。

PERSIST_FLAGS_BYPASS_CACHE

このフラグを立てると、キャッシュを無視して毎回Webサーバからダウンロードする。
厳密には、要求時に「Pragma: no-cache」「Cache-Control: no-cache」の2つのヘッダが付加される。

wbp.persistFlags |= wbp.PERSIST_FLAGS_BYPASS_CACHE;

PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION

このフラグを立てると、gzip圧縮転送された場合に非圧縮状態でファイルへ保存してくれる。
このフラグを立てないと、gzip圧縮転送されたデータは圧縮されたままファイルへと保存してしまうので注意。
あえて圧縮状態で保存する理由が思いつかないので、基本的にこのフラグは必須と考えてよさそう。

wbp.persistFlags |= wbp.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;

PERSIST_FLAGS_APPEND_TO_FILE

Firefox 3 以降で対応。このフラグを立てると、ダウンロードしたデータを既存ファイルへ上書きせずに追記する。

wbp.persistFlags |= wbp.PERSIST_FLAGS_APPEND_TO_FILE;

参考

上記以外にも色々なフラグがある。詳しくは nsIWebBrowserPersist.idl を参照。
nsIWebBrowserPersist.idl

関連記事

nsIWebBrowserPersist の基本的な使い方 (1) ~ 基本形
nsIWebBrowserPersist の基本的な使い方 (2) ~ persistFlags
nsIWebBrowserPersist の基本的な使い方 (3) ~ 各種ヘッダの追加
nsIWebBrowserPersist の基本的な使い方 (4) ~ POST メソッド
nsIWebBrowserPersist の基本的な使い方 (5) ~ ダウンロード進捗状況
つづく…?

TOP

nsIWebBrowserPersist の基本的な使い方 (1) ~ 基本形

指定したURLからダウンロードしてファイルへ保存する際には nsIWebBrowserPersist が便利。
HTTPだけでなく、FTP、file:やchrome:プロトコルなどのURLからのファイル保存も可能ですが、ここではHTTPを前提とします。

nsIWebBrowserPersist の主要メソッド

メソッド 概要
saveURI() 指定したURIからダウンロードしてファイルへ保存する。
saveChannel() 指定したチャネル(nsIHttpChannel など)をファイルへ保存する。
saveDocument() 指定したDOMドキュメントをファイルへ保存する。

基本形

指定したURLのデータをダウンロードし、指定したパスのファイルへと保存するだけの簡単なコードを作る。
まずは URL 文字列から nsIURL オブジェクトを、ファイルパスから nsILocalFile オブジェクトを生成する。

const URL_SPEC = "http://www.mozilla.com/img/firefox-title.png";
const FILE_PATH = "C:firefox-title.png";

// make nsIURL
var url = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIURL);
url.spec = URL_SPEC;

// make nsILocalFile
var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.initWithPath(FILE_PATH);
saveURI の場合

nsIWebBrowserPersist のインスタンスを生成し、先ほどの2つのオブジェクトを引数に saveURI を実行するだけ。

// save URL to file
var wbp = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
          .createInstance(Ci.nsIWebBrowserPersist);
wbp.saveURI(url, null, null, null, null, file);
saveChannel の場合

色々なものを引数で指定できる便利屋的な saveURI メソッドに対して、 saveChannel メソッドは nsIChannel を引数とすることでより詳細な実装が可能。
nsIIOService を使って nsIURL オブジェクトから nsIHttpChannel オブジェクトを生成し、 nsIHttpChannel と nsILocalFile を引数に saveChannel メソッドを実行する。

// make nsIHttpChannel
var ioSvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var channel = ioSvc.newChannelFromURI(url).QueryInterface(Ci.nsIHttpChannel);

// save channel to file
var wbp = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
          .createInstance(Ci.nsIWebBrowserPersist);
wbp.saveChannel(channel, file);

なお、 nsIChannel を saveChannel に渡す前に open / asyncOpen してはならない。

参考

nsIWebBrowserPersist.idl
nsIIOService.idl
nsIChannel.idl
nsIHttpChannel.idl

関連記事

nsIWebBrowserPersist の基本的な使い方 (1) ~ 基本形
nsIWebBrowserPersist の基本的な使い方 (2) ~ persistFlags
nsIWebBrowserPersist の基本的な使い方 (3) ~ 各種ヘッダの追加
nsIWebBrowserPersist の基本的な使い方 (4) ~ POST メソッド
nsIWebBrowserPersist の基本的な使い方 (5) ~ ダウンロード進捗状況
つづく…?

TOP

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