宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
ソース表示タブの障害として、「外部のテキストエディタを使う設定にしてる時に、Windowsのアカウント名が日本語だったりするとファイルを開けない」という報告を受けた。それを修正しようとして挫折した。以下はそのまとめ。
当該機能は実はソース表示タブ自身が提供しているわけではなく、Firefox 2以降くらいからFirefox自体に元々備わっている隠し設定のための設定UIを提供しているだけで、実際の処理はFirefox本体の物に丸投げしている。具体的にはviewSourceUtils.jsのopenInExternalEditor()
とかそこら辺。
ただ、Firefox本体のこのコードは国際化のことをきちんと考えられていないようで、テキストエディタの実行ファイルのパスに日本語の文字などが含まれているとダメという既知のバグがある(原因はgetExternalViewSourceEditor()
の中でgetCharPref()
で取得したパス文字列をUTF-8バイト列からUCS2のUnicode文字列に変換していないせい)。なのでソース表示タブではこの部分に動的にパッチを当てる形で問題を回避するようにして、もう少し実用に耐えるようにしようとしてみている。
今回の問題の原因は、エディタの実行ファイルではなく、開こうとしている対象のファイルのパスの扱いにある。
Firefoxは、ソースを表示しようとしている対象がローカルのファイルだとそれをそのまま、Web上のファイルであればそれをダウンロードしたテンポラリファイルを、エディタに渡そうとするんだけど、どうもその時にnsIProcessのrun()
メソッドで渡す引数がUCS2のままなのがいけないらしい。試しに自分の環境(Windows Vista日本語版)で試したところ、引数のeditor.run(false, [path], 1);
とか書かれてる箇所で渡されてるパス文字列をnsIScriptableUnicodeConverterあたりでShift_JISに変換してやったらうまく動いた。IDL定義ではstringの配列としか書かれてないけど、ここはプラットフォームごとのデフォルトの文字エンコーディングにしておかないといけないようだ。
実験段階で明らかなように、nsIScriptableUnicodeConverterで適切なエンコーディングに変換してやれば問題は解決できる。残る問題は、じゃあ一体どのエンコーディングに変換すればいいのか、ということだ。
設定UI上にエンコーディング選択用のドロップダウンリストを付けるという手もあるけど、ユーザにそんな事を意識させるのはダサイ。なるべくなら自動で判別できるようにしておきたい。ということでその方法を模索してみた。
nativePath
から辿ってみた現在のFirefoxでは、ローカルファイルはnsIFileインターフェースで処理されている。このIDL定義を見ると、プラットフォームのデフォルトのエンコーディングでのパス文字列をそのまま保持しているとおぼしきnativePath
プロパティの定義には[noscript]
と書かれていて、JavaScriptからはアクセスできないようになっている。実際、試してみても無理だし。
ちなみに、昔おそらくnsIFileの代わりに使われていたと思われるnsIFileSpecだとnativePath
には[noscript]
が付いてない。なので、これを使ってやればいいか?と思ったけど、リンク先のソースの位置を見ると分かる通りこれは既に廃止されたモジュールで、今のFirefoxのバイナリには入ってないっぽい。インスタンスを作ろうとしてもエラーになる。
nsIFile/nsILocalFileを実装してるWindows用のコード(nsLocalFileWin.cpp)のGetNativePath()
の定義を見ればヒントを掴めるんじゃないか?と思ってみてみたら、CopyUnicodeToNativeという関数?が使われてた。コードが書かれてるnsNativeCharsetUtils.cppを見てみると……おおお、なんか答えっぽい物があるぞ。ifdefでプラットフォームごとに関数の中身の定義をそれぞれ変えてて、例えば頭の方を見てみると、Mac OS XとBeOSでは単純にUTF-8に変換してるという事が分かる。
ただ、下の方に書いてあるWindows用とUnix/Linux用の処理は、見ても訳が分からんかった……単純にこのエンコーディングに決め打ち、という訳にはいかないみたいで、ここからさらに色んな所のコードを見に行かなきゃいけないようだったので、この時点で挫折した。
nsIScriptableUnicodeConverterはエンコーディング名を指定してUCS2との間で相互変換するものだけど、その時に「プラットフォームのデフォルトのエンコーディング」を指すようなキーワードがあったりしないしないか?と思って、そっち関係のコードも見てみた。
nsICharsetConverterManager.idlとかnsUConvModule.cpp見てて、どこをどう辿ったのか忘れたけど、GetDefaultCharsetForLocale()
というメソッドがnsIPlatformCharset.hで定義されているという所まで辿り着いた。
メソッド名から見て、ロケールを指定しないといけないようだけど、それさえなんとかなればこれでいけるか? と思ってこれにJavaScriptからアクセスできないか試してみたけど、ダメだった。Components.classes['@mozilla.org/intl/platformcharset;1']
にはアクセスできるんだけど、QueryInterface(Components.interfaces.nsIPlatformCharset)
してみたら「そんなインターフェースねえよ」と怒られてしまった。検索してみても、どうもC言語用のコードしか無いみたい。
無理でした。以上。
C(C++?)を書けるようになって、上で辿り着いたメソッドの実行結果を返すだけのコンポーネントでも書けば、サクッと解決できるのだろうか。もう勉強するしかないのか?
コンテキストメニュー拡張で使ってるけど、文字コードを選択するドロップダウンリストは以下のようにすれば作れる。
<menulist id="charset"
label="(choose an charset)"
datasources="rdf:charset-menu"
ref="NC:DecodersRoot">
<template>
<menupopup>
<menuitem uri="..."
label="rdf:http://home.netscape.com/NC-rdf#Name"
value="..."/>
</menupopup>
</template>
</menulist>
ただし、XULにこれを書いただけだと空のリストになってしまう。onloadとかそこらへんでCc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService).notifyObservers(null, 'charsetmenu-selected', 'other');
とすればポップアップの中身が生成される。設定ダイアログで使う時なら、その後でpreference要素のvalue
を見るなどして、前回の選択状態を復元してやる必要がある。
どう見ても拡張機能からのその場しのぎなハックで何とかすべき問題じゃありません。バグ立ててください。
ていうかすでに立ってるみたいですが。
https://bugzilla.mozilla.org/show_bug.cgi?id=484246
Unicodeの引数をそのまま子プロセスに渡して起動する処理は
nsWindowsRestart等に実装されている(updater.exeはこっちを使う)
という罠なんですよね。
アップデート周りのバグ修正
https://bugzilla.mozilla.org/show_bug.cgi?id=453733
の時に統合したほうが良いねという話は出ましたが、誰も作業をしていないのでまだ統合されていません。
nsAppRunner.cppにincludeされているのでもしかしたらそちら方面から呼べるのかもしれません。
1つ言い忘れてたのが、UnicodeのファイルパスはUnicodeのまま扱う方法を考えるべきだということ。対応するローカルエンコーディングの文字が無いものは「開けない」だけで済むけど、アクセント記号付きのアルファベットみたいに違う文字にマッピングされる文字は意図したものと違うファイルが起動されるリスクを抱え込むことになります。
Bug 411511でnsIProcessにrunwメソッドが追加されて、根本的に修正されたようです。
https://bugzilla.mozilla.org/show_bug.cgi?id=411511
つねづね主張されているとおり、runがrunwに変わったことで動的パッチが失敗するのでソース表示タブは何もしなくても問題ないでしょうか。
run()の部分ではなくその前のpath = ... の所にコードを挿入する(pathという変数の内容の文字エンコーディングを変換する)ようになっているので、動的なパッチはこの状態でも適用されると思われます。
ただ、runw()がUCS2のパス文字列を受け取る事を期待しているにもかかわらず、変換後のShift_JIS等で渡されてしまう事になるので、結果として期待通りに動かなくなってしまっていると思います。
どちらにしても対応が必要という事ですね。
の末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2009-06-06_charset.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。
writeback message: Ready to post a comment.