ダウンロードマネージャに進捗状況を表示させつつダウンロードする

拡張機能にて、ある URL からファイルをダウンロードするには、 Downloading Files – MDC で解説されているように nsIWebBrowserPersist::saveURI を使うのが一般的です。この方法でダウンロードをすると、ダウンロードマネージャの UI 上に進捗状況が表示されず、バックグラウンド処理のような感じでダウンロードが進行します。

では、ダウンロードマネージャに進捗状況を表示させつつダウンロードするには、どうすれば良いのでしょうか?そのためには、 Firefox 3 以降で導入された nsIDownloadManager インタフェースの API を利用します。

ダウンロードマネージャ

ここからは例として Google のロゴ画像をダウンロードし、ローカルファイルとして保存する手順を解説します。なお、ソースコード中の Cc, Ci は、それぞれ Components.classes, Components.interfaces への参照です。

まず、ダウンロード元のURLから、 nsIURI オブジェクトを生成します。

var sourceURL = "http://www.google.com/intl/en_ALL/images/logo.gif";
var ioSvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
var sourceURI = ioSvc.newURI(sourceURL, null, null);

次に、保存先ファイルのパスから、 URL が file:// 形式の nsIURI オブジェクトを生成します。
なお、変数 targetPath にセットするファイルパスは、各自の環境に合わせて適宜修正してください。

var targetPath = "C:\\***.gif";
var targetFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
targetFile.initWithPath(targetPath);
var targetURI = ioSvc.newFileURI(targetFile);

次に、ダウンロードを行うための nsIWebBrowserPersist のインスタンスを生成します。 persistFlags プロパティには、お好みに応じてフラグを設定してください。今回は、保存先ファイルがすでに存在する場合は上書きするフラグ、キャッシュを使わずに最新のデータをダウンロードするフラグ、 gzip 圧縮などがされている場合に自動で展開するフラグの3つを設定します。

var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].
              createInstance(Ci.nsIWebBrowserPersist);
persist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
                       Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE |
                       Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;

いよいよ、今回の要となる nsIDownloadManager のサービスを呼び出し、 addDownload メソッドによってダウンロードマネージャへ新しいエントリを追加します。 addDownload メソッドの個々の引数についての説明は、下記コード中のコメントを参照ください。

var dlMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
var dl = dlMgr.addDownload(
	Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,	// ダウンロードマネージャ上での表示形式
	sourceURI,	// ダウンロード元の nsIURI オブジェクト
	targetURI,	// 保存先ファイルの nsIURI オブジェクト
	null,	// ダウンロードマネージャ上での表示名。 null なら保存先ファイル名となる。
	null,	// nsIMIMEInfo オブジェクト。詳細不明だが null で問題なし。
	Math.round(Date.now() * 1000),	// ダウンロード開始時刻。現在時刻を指定すればよい。
	null,	// 一時ファイルを作ってダウンロードする際に nsILocalFile を指定する。
	persist	// 先ほど生成した nsIWebBrowserPersist オブジェクトを渡す。
);

addDownload メソッドの戻り値は、ダウンロードマネージャにより管理される個々のエントリに対応した nsIDownload オブジェクトとなっています。この nsIDownload オブジェクトから nsIWebBrowserProgressListener インタフェースを呼び出して以下のようにすると、 nsIWebBrowserPersist 側のダウンロード進捗状況の変化がダウンロードマネージャ側へ伝わるようになります。

persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);

最後に、 nsIWebBrowserPersist::saveURI メソッドを実行し、実際にダウンロードの処理を開始させます。なお、 saveURI の引数にダウンロード元の nsIURI オブジェクトと保存先ファイルの nsILocalFile オブジェクトを渡す必要がありますが、それぞれ nsIDownload オブジェクトの source, targetFile プロパティから参照可能です。もちろん、これまでの一連の処理で登場した変数 sourceURI, targetFileの2つを渡しても構いません。

persist.saveURI(dl.source, null, null, null, null, dl.targetFile);

以上のような手順でダウンロードマネージャと連携しつつダウンロードした場合、単に nsIWebBrowserPersist::saveURI を使ってダウンロードした場合とは異なり、ダウンロード中に Firefox を終了させても再起動時に自動的にレジュームが開始されるというメリットがあります。したがって、巨大なファイルをダウンロードするような拡張機能では利用価値の高い方法となるかもしれません。

saveURL ヘルパー関数

ここまでかなり長いコードを書いてダウンロードマネージャに進捗状況を表示させつつダウンロードする方法を解説しましたが、実はブラウザウィンドウ (browser.xul) のように chrome://global/content/contentAreaUtils.js が読み込まれているウィンドウ内であれば、 saveURL というヘルパー関数を使って以下のようにいとも簡単に実現可能です。

saveURL("http://www.google.com/intl/en_ALL/images/logo.gif", "logo.gif", null, true, true, null);

5番目の引数を false に変えることでファイル選択ダイアログを表示させることなどもできますし、実際の拡張機能ではこの saveURL 関数を使うケースの方が多いかもしれませんね。

関連ドキュメント