このサイトの記事更新は2019年11月に終了されました。過去記事アーカイブを公開しています。
投稿されたすべてのトピック
Windows版のIMEのコードのログをとる方法
Windows版のIMEのコードは、Firefox 3.6以降 (Gecko 1.9.2以降)、nsWindow
から分離され、nsIMM32Handler
クラスで処理されるようになりました。この修正時に、このクラスのログをリリースビルドでも記録することができるようにしています。これにより、開発者が所有していないサードパーティ製のIMEでのみ発生するバグがあったとしても、バグを再現できる人が開発者に対してログを提出することで原因を調査を助けることができるようになりました。
ログをとるには、環境変数を二つ、追加する必要があります。
まず、NSPR_LOG_FILE
という環境変数を追加してください。この環境変数にはログファイルのパスを設定します。c:\fx_imm32.log
のように、Windowsのパス形式で書き込み可能なパスを記述してください。
次に、NSPR_LOG_MODULES
という環境変数を追加してください。この環境変数にはnsIMM32HandlerWidgets:1
を設定してください。
これらの環境変数を追加した後に、Firefox 3.6以降を起動すれば(すでに起動している場合はFirefoxを再起動してください)、NSPR_LOG_FILE
に指定したファイルが作成され、ログが逐一記録されていきます(ログファイルはUTF-8です)。
nsIMM32Handler
は非常に多くのログを記録します。ですので、なんらかのバグのためにbugzillaにログを提出する場合は、起動させた直後にバグを再現させ、そのまますぐに終了してください。余計な動作はログを読みにくくしますし、また、入力された文字は全てログに残るので、提出する際にプライバシーの問題が出てきます。
テスト後には環境変数を削除するのを忘れないようにしてください。ログを出力している分、動作速度は落ちてしまいますし、ログの出力先がSSDの場合はSSDの寿命を縮めてしまうかもしれません。
ログをリアルタイムで表示させたい場合、私はSSLogMonというソフトを使っています。もし、他にもこのようなソフトをご存じの方が居たら、紹介してもらえるとありがたいです。
2月20日(土) に大阪で Mozilla 勉強会を開催します
Firefox の拡張機能や Jetpack などのアドオン、あるいはアプリケーションプラットフォームとしての Mozilla の技術についての勉強会を 02 月20日(土) に大阪で開催することになりました。
今回の Mozilla 勉強会では Kanasan.JS で行われた Jetpack ワークショップで講師をされた mollifier さんと、WISS2009 にて Firefox の拡張機能を用いた研究でベストペーパー賞を受賞された京都大学の山本岳洋さんをお招きしての勉強会になります。
時間割や懇親会、Lightning Talk の募集などについて詳しくは勉強会のページをご覧ください。
ご参加いただける方は ATND にてご登録をお願いします。
サーバメンテナンスのお知らせ
1月29日(金) の午前 3 時頃からサーバのメンテナンス(移転)を予定しています。しばらくの間ページや投稿を編集できない、あるいはサイトにアクセスできなくなるなります。
サーバへの負荷が高くなってきたために新サーバに移転するのですが、それに伴い IP アドレスも変更されます。ご利用のネットワークによっては、DNS の変更が反映され新しいサーバにアクセスできるようになるまで数時間以上かかることもあります。
ご不便をおかけしますが、よろしくお願いします。
追記:
他のサービスの移転時に問題があったためこちらのサイトの移転についても併せて延期しました。
ブラウザウィンドウのサムネイルを描画する(改訂版)
タブカタログ拡張機能のようにWebページのサムネイル画像を表示する拡張機能の多くは、 html:canvas 要素の二次元描画コンテクストの drawWindow メソッドへWebページの window オブジェクトなどを引数で渡してサムネイルの描画を行っています(canvas を使って図形を描く – MDC)。
この drawWindow メソッドの引数にWebページの window オブジェクトではなく、ブラウザウィンドウの ChromeWindow オブジェクトを渡すことで、ブラウザウィンドウのサムネイル画像を描画することも可能です。しかし、以下のようにブラウザタブ内に表示されたWebページまでは描画されず、背景色で塗りつぶされたようになってしまいます。
この問題を解決するには、ブラウザウィンドウのサムネイルの上に、Web ページのサムネイルを重ねて描画する必要があります。この記事では、その方式を紹介します。
XUL
ブラウザウィンドウのサムネイル描画用の html:canvas 要素を生成します。
<html:canvas id="testCanvas" />
JavaScript
最初にサムネイルの拡大/縮小率を定めておきます。ここでは50%に縮小することとします。
const scale = 0.5;
サムネイル描画の対象とするブラウザウィンドウとして、 nsIWindowMediator を使って直近のブラウザウィンドウの ChromeWindow オブジェクトを取得します。
var win = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator).
getMostRecentWindow("navigator:browser");
簡便のため、 html:canvas 要素への参照を取得しておきます。
var canvas = document.getElementById("testCanvas");
ブラウザウィンドウのサイズに合わせ、 html:canvas 要素のサイズを調整します。
var w = win.innerWidth;
var h = win.innerHeight;
canvas.width = w * scale;
canvas.height = h * scale;
二次元描画コンテクストを取得し、ブラウザウィンドウのサムネイルを描画します。
var ctx = canvas.getContext("2d");
ctx.save();
ctx.scale(scale, scale);
ctx.drawWindow(win, 0, 0, w, h, "rgb(255,255,255)");
引き続き、Webページのサムネイルを描画しますが、その前に原点を左上(座標0, 0)から移動させる必要があります。原点を移動させるためには、二次元描画コンテクストの translate メソッドを使います。移動量は、ブラウザウィンドウに対する現在のブラウザ(xul:browser 要素)の位置から算出します。
var rect = win.gBrowser.mCurrentBrowser.getBoundingClientRect();
ctx.translate(rect.left, rect.top);
最後にWebページのサムネイルを重ねて描画します。なお、 win.content
は win
に対応するブラウザウィンドウの現在のタブに表示されたWebページの window オブジェクトを表します。
w = win.content.innerWidth || win.content.document.documentElement.clientWidth;
h = win.content.innerHeight || win.content.document.documentElement.clientHeight;
ctx.drawWindow(win.content, win.content.scrollX, win.content.scrollY, w, h, "rgb(255,255,255)");
ctx.restore();
以上のコードに適当に肉付けして実行してみると、以下のようにブラウザタブ内のWebページも含めてブラウザウィンドウのサムネイルが描画されます。
注意
今回はブラウザウィンドウ内の現在のブラウザタブのWebページだけが見えている前提となっていますが、分割ブラウザ (Split Browser)を使っている場合、この限りではありません。そのような場合にも対応させるには、さらなる工夫が必要となります。
補足
元々筆者はブラウザウィンドウのサムネイルを描画するために、ブラウザウィンドウ用とWebページ用の2つの html:canvas 要素を xul:stack 要素で重ねる方式を考え付きましたが、Piroさんのアドバイスにより、この記事で紹介したように原点を移動させつつ1個の html:canvas 要素に対して2回 drawWindow メソッドを使用する方式が可能なことがわかりました。
ブラウザウィンドウのサムネイルを描画する
タブカタログ拡張機能のようにWebページのサムネイル画像を表示する拡張機能の多くは、 html:canvas 要素の二次元描画コンテクストの drawWindow メソッドへWebページの window オブジェクトなどを引数で渡してサムネイルの描画を行っています(canvas を使って図形を描く – MDC)。
この drawWindow メソッドの引数にWebページの window オブジェクトではなく、ブラウザウィンドウの ChromeWindow オブジェクトを渡すことで、ブラウザウィンドウのサムネイル画像を描画することも可能です。しかし、以下のようにブラウザタブ内に表示されたWebページまでは描画されず、背景色で塗りつぶされたようになってしまいます。
この問題を解決するには、ブラウザウィンドウのサムネイルの上に、Web ページのサムネイルを重ねて描画する必要がありそうです。ここでは、 Tab Flick 拡張機能で実際に使っている、 html:canvas 要素を2個重ねる方式を紹介します。
XUL
ブラウザウィンドウのサムネイル描画用とWebページのサムネイル描画用の、2つの html:canvas 要素を xul:stack 要素で重ねます。
<stack>
<html:canvas id="outerCanvas" /><!-- ブラウザウィンドウ用 -->
<html:canvas id="innerCanvas" /><!-- Webページ用 -->
</stack>
JavaScript
最初にサムネイルの拡大/縮小率を定めておきます。ここでは50%に縮小することとします。
const scale = 0.5;
サムネイル描画の対象とするブラウザウィンドウとして、 nsIWindowMediator を使って直近のブラウザウィンドウの ChromeWindow オブジェクトを取得します。
var win = Components.classes["@mozilla.org/appshell/window-mediator;1"].
getService(Components.interfaces.nsIWindowMediator).
getMostRecentWindow("navigator:browser");
簡便のため、2つの html:canvas 要素への参照を取得しておきます。
var oCanvas = document.getElementById("outerCanvas");
var iCanvas = document.getElementById("innerCanvas");
ブラウザウィンドウに対する現在のブラウザ(xul:browser 要素)の位置の相対関係から、Webページ用の html:canvas 要素の位置を調整します。
var rect = win.gBrowser.mCurrentBrowser.getBoundingClientRect();
iCanvas.style.left = Math.round(rect.left * scale) + "px";
iCanvas.style.top = Math.round(rect.top * scale) + "px";
この時点で2つの html:canvas 要素に枠線を付加して表示してみると、以下のようになっています。
なお、この時点では html:canvas 要素のサイズはまだ調整されていません。
次の処理へ移る前に、あらかじめ関数を作っておきます。引数で指定したウィンドウのサムネイルを、指定した html:canvas 要素に描画する関数です。
// @param aWindow ウィンドウのオブジェクト
// @param aCavas html:canvas要素
// @param aScale サムネイルの拡大/縮小率
function drawPreviewForWindow(aWindow, aCanvas, aScale) {
var w = aWindow.innerWidth || aWindow.document.documentElement.clientWidth;
var h = aWindow.innerHeight || aWindow.document.documentElement.clientHeight;
aCanvas.width = Math.round(w * aScale);
aCanvas.height = Math.round(h * aScale);
var ctx = aCanvas.getContext("2d");
ctx.clearRect(0, 0, aCanvas.width, aCanvas.height);
ctx.save();
ctx.scale(aScale, aScale);
ctx.drawWindow(aWindow, aWindow.scrollX, aWindow.scrollY, w, h, "rgb(255,255,255)");
ctx.restore();
}
先ほど作った関数を使い、ブラウザウィンドウと、そのブラウザウィンドウの現在のタブに表示されたWebページの両方のサムネイルを描画します。なお、 win.content
は win
に対応するブラウザウィンドウの現在のタブに表示されたWebページの window オブジェクトを表します。
drawPreviewForWindow(win, oCanvas, scale);
drawPreviewForWindow(win.content, iCanvas, scale);
以上のコードに適当に肉付けして実行してみると、以下のようにブラウザタブ内のWebページも含めてブラウザウィンドウのサムネイルが描画されます。
なお、最終的に2つの html:canvas 要素は以下のような位置関係・サイズになっています。
別の方式…1個の html:canvas 要素で実現可能か?
今回は2つの html:canvas 要素を重ねる方式を紹介しましたが、別の方式として、1個の html:canvas 要素に対して2回 drawWindow メソッドを使用してブラウザウィンドウとWebページの2つのサムネイルを重ねて描画する方式も思いつきました。しかし、筆者が調べた限りでは、 drawWindow メソッドは必ず html:canvas 要素の左上位置(座標 0, 0)から描画を開始するため、Webページのサムネイルを正しい位置に重ねることができないようでした。したがって、別の方式は断念しました。
about:hacks – Mozilla’s newsletter for web developers – Issue 1
- 原文: http://us1.campaign-archive.com/?u=168bf22f976f5a68fe5770d19&id=6c2d73c957
- 次号: about:hacks ニュースレター第2号
ようこそ
これはウェブ開発者向け Mozilla ニュースレター about:hacks の発刊第1号です。about:hacks は、クールなデモやチュートリアル、Firefox のリリース情報、ウェブ標準の最新動向、Mozilla Developer Network プロジェクトの近況を取り上げる予定です。このメールは、私たちの行った11月のアンケートで Mozilla Developer Network からのニュースを受け取ることを選択された方々に送られています。もし今後このニュースレターを受け取りたくない場合には、簡単に購読を取りやめることができます。
この号では…
- Firefox 3.6
- デモ: Processing とマルチタッチ
- チュートリアル: HTML5 video
- 開発ツール: Firebug と Eventbug
- Mozilla を支える技術: Direct2D, Gecko Layers, Web Sockets, Plugin のプロセス分離
- 標準技術ニュース: File API, @font-face, Storage
- MDCニュース: Firefox 3.6, CSS互換性
- ご意見をお待ちしています
Firefox 3.6
Firefox 3.6 Beta 5 が出荷され、近いうちに RC 版も続く予定です。RC 版に達するということは、すなわち明日にでも出荷して良いと確信できるものの、致命的なバグが無いことを確認する幅広いテストを行う必要のあるビルドになったということを意味します。もしあなたがウェブサイトを開発しているのであれば、これがリリース版で反映されるべきバグを発見し報告する最後のチャンスとなります。今こそ RC 版をダウンロードし、私たちにフィードバックを送るときです。ウェブ開発者に関係する Firefox 3.6 の最新情報について、詳しくは Mozilla Developer Center をご覧下さい。
デモ
Firefox 3.7 における Processing とマルチタッチ
David Humphrey や Al MacDonald、その他多く皆様の支援によって、ウェブ上での Proccessing 実行環境の実現に向けて取り組んでおります。Processing はメディアアートやビジュアライゼーションためのプログラミング言語であり、アーティストやデザイナー、研究者などにも使われています。これまで Processing は Java 言語によって実装されていましたが、高速な JavaScript エンジンや Canvas、WebGL などの技術の登場によって、ウェブ上に展開することが可能となってきました。Processing.js は Processing をブラウザ上で直接動かすことができる取り組みです。
Processing.js が John Resig によって開始されるまで、Al は Processing の能力とマルチタッチを使った技術やデモ作成をリードしてきており、この技術は Firefox 3.7 でもサポートされます。
チュートリアル
HTML5 video
11月のアンケートの中に「Firefox と Safari での HTML5 video 技術の良いデモはどんなものであるか?」という質問がありました。ここではその答えの一つとして、Safari や IE で ogg 形式の動画がサポートされるまで mp4 や Flash にフォールバックされる ogg 動画埋め込みマークアップの方法 HTML5 video fallbacks を紹介します。
開発ツール
Firebug
Firebug 1.5 がベータ版であるが公開され、最新版をダウンロードして試すことができるようになりました。このバージョンは、Firefox 3.5 および今度の Firefox 3.6 で動作します。ネットパネルの精度の向上と新しいブレークポイントと言った Firebug1.5 の新機能についてはこちらをご覧下さい。
Eventbug
もしあなたが多数のイベントリスナーを用いた開発を行っているのであれば、Firebug に連動される Eventbug 拡張 を調べてみることをおすすめします。この拡張は、ウェブ開発の中で長い時間を掛けていた、特定の要素に登録されている全てのイベントリスナーを調べることができます。現バージョンでは最新の Firefox 3.7 アルファ版を対象としています。Jan Odvarko はあなたのフィードバックをお待ちしております。
Mozilla を支える技術
Mozilla を支える技術について皆様に知って頂くことにも私たちは価値があると考えています。私たちは各リリースにおいて幾千もの人々の協力によって成り立っている世界でもっとも大きいオープンソースプロジェクトの一つですが、その膨大な技術の中には面白い技術やモジュール、プロジェクトがまだまだあります。ここでは、ウェブ開発者に対して興味深いであろう Mozilla プロジェクトをいくつかご紹介します。
Direct2D
私たちは Direct2D を Firefox でのサポートに向けて取り組みんでおります。これはウィンドウズ(およそ90%のユーザが使っているOS、もしかするとあなたもその90%かな?)上のグラフィクス描画速度の向上を目指すものです。Direct2D のサポートによって、よりリッチで面白いグラフィカルなアプリケーションが作成できるようになります。Direct2D の高速化を実感できるデモとしては Vlad の SVG 写真デモが挙げられます。注:これはグラフィクスの描画速度についてのデモであり、JavaScript の処理速度についてではありませんので、SunSpider のテストはここでは関係がありません。
Gecko Layers
Gecko は Firefox を支えるレイアウトエンジンです。先の Dictect2D に限らず、他の 2D および 3D に有効なハードウェアアクセラレーションレイヤー技術も検討しています。これによって、透明または半透明コンテンツの合成などの速度向上、ビデオ上にコンテンツをオーバーレイすることやブラウザの全体的な応答性を向上させる高速なアニメーションが期待できます。もしこういった超低レベル層の技術詳細に興味をお持ちならば、Gecko Layers の wiki をご覧下さい。そこにはグラフィクスギークに興味深いであろう読み物があります。
Web Sockets
Firefox に HTML5 Web Sockets を実現するためのパッチが現在レビュー段階にあり、Firefox 3.7 の開発プロセスにこの技術が入ることを願っています。Web Sockets とは何なのかご存じ無い方に説明しますと、それはいわゆる”hanging GET“と呼ばれる現象を排除し、ウェブブラウザとウェブサーバが双方向にコミュニケーションできるシンプルなプロトコルです。HTTP のセキュリティモデルと同様で、つまり接続は同一生成元ポリシーが適用されます。Web Sockets による接続は遅延が少なく DOM 上のとてもシンプルな API で利用できます。サーバにおける Web Sockets の実現も容易です。私たちは、3.7 の開発プロセスに Web Sockets が入ることで何が引き起こされるのかにとても興味を持っています。現在のパッチが十分にレビューされ検証されることを期待しています。
Plugin のプロセス分離
Firefox の開発版では、プラグインを別プロセスで走らせる能力を持たせる開発がなされており、Firefox 3.6 に続くリリースで含まれることが望まれます。ほとんどのウェブ開発者は同一プロセス、別プロセスの違いを意識しなくてもいいでしょう。しかし、この技術は大幅に Firefox の信頼性を向上させる可能性が高く、また、ブラウザもより高速になることから、注目に値する技術です。
標準技術ニュース
File API
File API の Public Working Draft の初期版が公開されました (フィードバックはこちら)。このいくつかは Firefox 3.6 に取り込まれる予定です。将来的にも Public Working Draft のうち、仕様変更が無いであろう機能を実装したものです。まだ議論中の機能については、今回は実装しないことを決めています。現在のインターフェイスについては、Firefox 3.6の開発者ページにて見ることができます。File APIの仕様も同様に他のインターフェイスに影響します。特に、DataTransferオブジェクトのドラッグ&ドロップでは現在 File API をサポートしています。
@font-face
Firefox 3.6 では CSS プロパティである @font-face を利用する人々のための WOFF のサポートを取り込みました。そのフォーマットや新しいフォントへの活動はこちらをご覧下さい。
Storage
何らかの構造化オフラインストレージが欲しいという声が以前から開発者に求められていました。Safari や Google Gears (今はどうやら放置している?) は両方とも SQLite をウェブに直接さらけ出していました。しかしながら Mozilla ではウェブページから直接 SQL を利用できることについて懸念しています。互換性や不完全仕様などの要因により、バージョンによってパフォーマンスに大きな違いが出てしまうなどが予想され、開発者は都度対応しなければならなくなると考えられるからです。
代わりのものとして、Microsoft と Mozilla の両者とも、ウェブにおいては SQL databse よりも indexed database がより合理的な選択であると考え、 Oracle の Nikunj Mehta が編集した indexed sequential storage API に興味を抱いてきました。それは低レベルそしてデータベース周りのツール、SQL や CouchDB のような実装を行うライブラリ開発者を対象としていますが、実にシンプルな API になっています。ここから議論をフォローすることができます。
MDCニュース
Firefox 3.6
Mozilla Developer Center には Firefox 3.6 でウェブ開発者に影響を与える変更点の全リストがあります。新しいドキュメントには CSS グラデーションの方法や新しい File API についてのドキュメントも追加されています。
CSS Compatibility
MDC の CSS に関する記事には互換性情報が掲載されており、どのブラウザがどの機能をサポートしているかをすばやく知ることができます。ページの下に各プロパティの互換表が掲載されています(例として、-moz-linear-gradient ページの末尾をご覧下さい)。この表はコミュニティによってメンテナンスされているもので、あなたも更新できます。是非サインアップしこの活動にご協力ください。
ご意見をお待ちしています
about:hacks で扱う問題にはみなさまからの質問の答えも含ませていきたいです。このニュースレターで思ったこと、次回のニュースレターでカバーしてもらいたいことなど、ご意見をください。
about:config を扱う拡張機能についての質問
はじめまして、こんにちは。
takataと言います。
自作でThunderbirdのアドオンを作ろうと考えています。
内容は、独自で作ったlocalパッケージを
Thunderbird >環境設定>設定エディタ
で書き換えて適応するのではなく、
1)ツールバーに表示された拡張機能のアイコンをクリック
2)ラジオボタン表示
3)言語選択→「OK」クリック
4)適応(再起動)
とう具合に行いたいと思っています。
ツールバーにアイコン表示、ラジオボタンののったダイアログ表示ということは
自分でできると思います。
分からない点は
1)about:configを扱うアドオンを作るにはどこをオーバレイすればいいか
2)言語選択後、自動でThunderbirdを再起動させる方法
です。
DOM Inspector を使って調べてみたのですが、
chrome://global/content/config.xul がページであること?
各項目がまとめてNodename が treechildren、id がconfigTreeBody であること
がわかりましたが、general.useragent.local 単独で扱う方法がわかりませんでした。
環境は
Mac OS X バージョン 10.5.8
Thunderbird バージョン 2.0.0.23
手元にある資料は
FIREFOX 3 HACKS (オライリージャパン発行)
Firefox拡張機能開発チュートリアル
Firefox 拡張機能開発 準備編
Firefox 拡張機能開発 入門編
です。
始めたばかりで、基本中の基本を聞いていたり、
的外れなことを言っているかもしれませんが、
どうかその場合でもアドバイスをいただけないでしょうか。
また、投稿場所がおかしかったりしてもご指摘お願いしたいです。
どうかよろしくお願いします。
<code>gtk_im_context_reset()</code>が動かない
LinuxのIME処理には重要なAPIであるgtk_im_context_reset()
が一部の環境下、特に新しいプラットフォームで動かなくて困っています。
今のところ分かっている情報から推測すると、uimと、iBusを利用していることが原因なのかもしれません。私の手元ではUbuntu 9.04 (SCIM)では問題ありませんが、Ubuntu 9.10 (iBus)では機能しませんし、早くから、Debianのsidでuimを利用するとマウスクリック時にうまく確定されないという報告がありました。
Geckoは一般的なGTK2アプリとは違って、IMのコンテキストをウインドウ単位で生成し、これをウインドウ内のネイティブウィジット間で共有しています。また、一つのネイティブウィジット内でも、<input>
や<textarea>
が複数存在するため、どちらにしても複数のエディタ間でIMのコンテキストを共有しないといけない、特殊なアプリケーションです(Webブラウザで馬鹿正直にエディタごとに用意すると、とんでもないことになりかねません)。
このため、Geckoではフォーカスが移動する時や、マウスのクリック時に処理を単純にするためにXPレベルで未確定文字列を強制的に確定しようとします(これが好ましいかどうかは別にして、現在の設計ではこれ以外の解決策をとるのは短期的には現実的ではありません)。ですが、現状、Linuxではこれがうまく機能しなくなっています。
実際にこの問題を確認するには、未確定文字列がある状態で、そのエディタ内をクリックしてみてください。
Ubuntu 9.10でテストしてみると、見た目では確定されているのですが、そのままスペースキーを叩くと確定したはずの文字列で変換が始まります。もちろん、そのまま確定すると文字がダブることになります。
この問題に関して、原因を究明する情報が不足していて困っています。この環境なら再現しない、この環境、このIMだと再現する、といった情報や、IMの開発コミュニティの情報をお持ちでしたら、是非それを教えてください。お願いします。
追記(2010/01/8/14:30):
uim-jaに投稿してみたところ、やはりIM側の実装に大きく依存するようです。
ファイル書き込み処理を別スレッドで行う
注意: この記事の内容は Firefox 3.6 以降で追加される新機能について触れています。
Firefox ではブラウズ中のセッション状態を保存するために、デフォルトで10秒に1回、JSON形式のデータをプロファイルフォルダ下の sessionstore.js へ書き出す処理を行っています。
しかし、 Firefox 3.5 まではこの処理が原因で YouTube の動画閲覧中にプチフリーズが頻発するといった現象が見られたようです。そこで、 Firefox 3.6 以降では、ファイル書き込み処理を別スレッドで行うことで、このプチフリーズが発生しないよう改善されることになりました (Bug 485976 – Move writing sessionstore.js off the main thread)。
この別スレッドでのファイル書き込み処理は nsIAsyncStreamCopier という XPCOM にて実装されていますが、 NetUtil.jsm という JavaScript モジュールをインポートすることで、拡張機能などから簡単に利用することができます。
サンプル
以下、別スレッドにてファイルへ文字列を書き出すサンプルを作ってみます。なお、ソースコード中の Cc
, Ci
は、それぞれ Components.classes
, Components.interfaces
への参照です。
最初に、 JavaScript モジュールをインポートします。当然インポートは最初に一度だけ行えば良く、ファイルへの書き出しを行うたびに行う必要はありません。
Components.utils.import("resource://gre/modules/NetUtil.jsm");
次に、書き出し先のファイル(nsILocalFile オブジェクト)を生成します。なお、変数 path
の値は各自の環境に合わせて適宜修正してください。
var path = "C:\\***.txt"; var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); file.initWithPath(path);
次に、 nsISafeOutputStream によって安全にファイルへ出力するためのストリームを生成します。どういうことかと言うと、ファイル書き込み中は「test-1.txt」のような別名の一時ファイルへ書き込み、書き込みが完了したら本来の「test.txt」へ上書きすることで、ファイルが破損しにくい仕組みとなっています(参考)。
var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]. createInstance(Ci.nsIFileOutputStream); ostream.init(file, -1, -1, 0);
次に、ファイルへ書き込む文字列から、入力用のストリームを生成します。
const TEST_DATA = "this is a test string"; var istream = Cc["@mozilla.org/io/string-input-stream;1"]. createInstance(Ci.nsIStringInputStream); istream.setData(TEST_DATA, TEST_DATA.length);
なお、日本語を含む文字列を UTF-8 エンコードでファイルへ書き出す場合、以下のように nsIScriptableUnicodeConverter を使って入力用ストリームを生成します。
const TEST_DATA = "これはテスト用文字列です"; var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. createInstance(Ci.nsIScriptableUnicodeConverter); converter.charset = "UTF-8"; var istream = converter.convertToInputStream(TEST_DATA);
最後に、 NetUtil.asyncCopy を使い、入力用ストリームを出力用ストリームへコピーし、別スレッド上でファイルへの書き込みを行います。3番目の引数は別スレッドでのファイル書き込みが完了した際に呼び出されるコールバック関数です。
NetUtil.asyncCopy(istream, ostream, function(result) { if (Components.isSuccessCode(result)) alert("ファイル書き込み成功"); });
今回のサンプルでは書き出す文字列が少ないため、別スレッドで処理が行われていることを体感できないと思います。そこで、以下のように長大な文字列を生成して試してみると、 Firefox がフリーズすることなくファイルへの書き出しが行われることが体感できるかと思います。ただし、 for ループ自体が重いため、ファイル書き出し前にフリーズが発生します。
var TEST_DATA = ""; // ループ回数を少しずつ増やしながら調整してください for (var i = 0; i < 100; i++) { TEST_DATA += "this is a test string\n"; }
リファレンス
ダウンロードマネージャに進捗状況を表示させつつダウンロードする
拡張機能にて、ある 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 関数を使うケースの方が多いかもしれませんね。