FireFox拡張がすぐ文字化けファイルを吐く件について

FirefoxなどのMozillaアプリケーションは、内部文字列表現にユニコードを使っています。1文字で漢字を表せる例のあれです。で、それをそのままファイルストリームに吐き出すと、なんと、ユニコードの下位8ビットより上が、問答無用で切り捨てられてしまいます。

0x1234, 0x5678 FF内部でいうこれが

0x0034, 0x0078 こうなって

0x34, 0x78 こんなファイルになっちゃう

なので、Firefoxの拡張などで、内部文字列をローカルファイルに書き出す場合は、すべて下位の8ビットで表現可能なユニコード文字セット ---- つまりUTF-8の文字列に変換してから保存するようにしないといけません。

さてこれ、文字列が ASCII 文字だけで済む言語の国では、やってもやってなくても同じだから、アメリカとヨーロッパではあまり問題にならず、結果、けっこう多くの拡張でおろそかにされているように思います。意識してたのは NewsFox ぐらいでした。マルチバイト文字を使う言語圏の人として、自分の使っている拡張が対応してない場合は、ハックして、ぜひ作者にパッチを送りましょう。

やるべきことを擬似的に書くと…

0x1234, 0x5678 FF内部でいうこれを

0x0012, 0x0034, 0x0056, 0x0078 いったんこんな感じにして

0x12, 0x34, 0x56, 0x78 こんなふうにファイル保存しよう

で、それをやる方法はこうです。(雰囲気を表してるだけなので、0xedがないとか言わないでね)

nsIFileOutputStream を grep して、そのインターフェースで出てきたオブジェクト (streamとか) が

stream.init(file, 0x04 | 0x08 | 0x20, 0664, 0);
stream.write(data, data.length);

のようにしているところを見つけます。ここで、data が内部文字列のままだったら、以下の方法で UTF-8 に変換して write しましょう。

var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].getService(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = 'UTF-8';
utf8Text = converter.ConvertFromUnicode(nativeText);

ついでに、UTF-8のテキストをネイティブのテキストにするにはこう。これで読み込み時もなんとかなるかも。

nativeText = converter.ConvertToUnicode(utf8Text);

ただし、ファイルの読み込み元がXMLの場合は、XML自体にエンコーディング指定があるので、本当にUTF-8が妥当なのかわかりません。任意URI(ローカルパスもOK)のXMLファイルを読むには、こんな方法が妥当なようです。

var fix = Components.classes['@mozilla.org/docshell/urifixup;1'].getService(Components.interfaces.nsIURIFixup);
var fixedURI = fix.createFixupURI(
    theFilePathURI,  // ファイルのパス
    fix.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP);      
var reader = new XMLHttpRequest();
reader.open("GET", fixedURI.spec, false);
reader.overrideMimeType("application/xml");
reader.send(null);
var domTree = reader.responseXML;