2007年 12月 27日

jsm メモ

  • 謎な環境で eval される。
    • Component.classes とかはもちろんあるけど、IO はない。alert もない (めんどす)
  • Components.utils.import の第二引数を省略すると、現在の名前空間に import される (これどうやってるんだろ)
  • resource Protocol の setSubstitution に http: を指定して import しようとすると失敗する?
  • なんか instanceof がうまくいかない
    • 謎な環境なせいで prototype のオブジェクトが別になってる?

とりあえず File のユーティリティをちょこっと (名前空間とかまだちゃんと考えてないのでサンプル程度に) 書いてみた。なんかすぐ影響力のでかい人 (海外の) とかが別の作りそうで気がすすまない (無駄になる気がする)

Components.classes["@mozilla.org/network/io-service;1"]
          .getService(Components.interfaces.nsIIOService)
          .getProtocolHandler("resource")
          .QueryInterface(Components.interfaces.nsIResProtocolHandler)
          .setSubstitution("coderepos.org", IO.newURI("file:///Users/cho45/coderepos/platform/gecko/jsm/"));
          // .setSubstitution("coderepos.org", IO.newURI("http://svn.coderepos.org/share/platform/gecko/jsm/"));
Components.utils.import("resource://coderepos.org/file.jsm");

var file = IO.getFile("Temp", "test.txt")
File.write(file, "foobar");
alert(File.read(file)); //=> foobar

File.open("/tmp/hoge.txt", "write text", function (stream) {
    stream.writeString("foobarbaz");
});

これテストコードどうやって書けばいいんだ…… MozRepl ?

どうでもいいけど↓の「jsm メモ」を、用語も何もしらない気持ちで読むとなんか笑ってしまう。

jsm 考えないといけないこと

  • どこにリソースほぞんするか
    • resource ディレクトリだと特権もってないユーザが起動したときこまる
    • profile のどっかなのかなぁやぱ
  • resource: でどこにわりあてるか
    • ドメイン (どこのドメイン?) + なんちゃら
  • すぐに使えるように、ダウンロードを自動でやるやつ (ないとめんどくさくて使えないよね……)
    • 安全じゃない
  • テスト方法
  • コンセプト
    • jslib みたいに完全なラッパにしない (nsIFile とか、nsIScriptableIO とかはできるかぎり活用する)
    • めんどくさいし、他の XPCOM のラッパを全部書かなくても十分つかえるから
  • とっかかりをどうするか
    • 管理する Add-on つくる ← 嫌 (ユーザにはどんなライブラリを使っているかなんて関係ないから)
    • 一関数にまとめてそれだけコピペ (どんぐらい短かくできる?)
2007年 12月 26日

拡張機能

userChrome.js に対して拡張機能にまとめるメリットがいまいちわかってない……

  • 複数ファイル使ったりとかの場合はむしろ必須
  • 設定が Tools -> Add-ons からアクセスできる (という仕組みが用意されている)
  • Mozilla Add-ons に登録できる


デメリット

  • userChrome.js にくらべて作るのがめんどくさい
    • 一回 Rakefile 書けばすみそうだけど
  • 気軽に書きかえられない
    • 書きかえてすぐコミット、とかができない
      • インストールしたあとにチェックアウトディレクトリへ symlink しなおせばいいだろうけど、それもめんどくさい (これは自分はいいけど、他の人がコミットしずらくなる)
  • ↑とかぶるけど設定画面ちゃんと作らないと使えない


あと拡張機能の content とかを content.jar とかに圧縮してるけど、あれのメリットがよくわからない。書きかえにくくなって嫌だ……chrome.manifest も書きかえないといけないし……

Fx3b2 フルズームの座標ずれ

なんか小数点以下の扱いに問題ありそう (縮小時だけ)

ejs.js はやくなった

コンパイル時間はともかく、実行時間が replace による簡単な置換の2倍程度まではやくなった。

COUNT = 500;

var t = "aaaa<%=s.foo%>bbbbb<%=s.bar%>ccc";
var e = EJS(t);
var f = EJS(t, {useWith:true});
var m = {foo:"test", bar:"foobar"};
var b = [
	function compile () {
		EJS(t);
	},
	function processing () {
		e.run(m);
	},
	function processing_with_with () {
		f.run(m);
	},
	function replace () {
		t.replace(/<%=s\.(\w+)%>/, function (_,a) {
			return m[a];
		});
	}
];


for (var i = 0; i < b.length; i++) {
	var fun = b[i];
	print(fun.name);
	var res = 0;
	for (var j = 0; j < COUNT; j++) {
		var now = (new Date).getTime();
		fun();
		res += (new Date).getTime() - now;
	}
	print(res + "ms / " + (res/COUNT) + "ms");
};
Spidermonkey
compile
283ms / 0.566ms
processing
25ms / 0.05ms
processing_with_with
37ms / 0.074ms
replace
12ms / 0.024ms

Rhino
compile
5094ms / 10.188ms
processing
500ms / 1ms
processing_with_with
670ms / 1.34ms
replace
279ms / 0.558ms

ejs.js の高速化より Spidermonkey と Rhino の速度差のほうに驚いた

replace の正規表現に g がついてないから、実際は replace はもうすこし遅いですね

2007年 12月 25日

拡張機能

必須拡張機能のうち、自分で書けそうなやつで userChrome.js にできそうなやつは自分で書いたほうがいいですね。バージョンアップで動かなくなったら自分で直せるし (拡張機能になってると直すのめんどい……)、ノウハウたまるし……

だんだんインストールしてる拡張が減っていきます。

コードのよみかた

last_char = s.charAt(s.length - 1)

何が解りにくいって、どの単語が変数でどの単語がプロパティでどの単語がメソッドで…という区別が付かない。

http://d.hatena.ne.jp/hama_shun/20071224/1198504421

区別がつかなくても読めはするはず? (むしろ、どれがプロパティで……どれがメソッドで……っていうのは定義とか考えるとめんどくさいよなぁ……)

このコードでまず重要なのは、s が何であるか、ということだけだから、そこから読めば疲れないんじゃないかなぁ。

  • last_char はこの行で代入されているので、この先を読むにあたっては、この行さえ理解できれば、この前でどんな使われかたをされていようが関係ないし、先を読むのに必要なだけなのでとりあえず無視できる。
  • charAt, length は、s のプロパティアクセスなのだから (ドット演算子があるから)、s が何かわかればそれのリファレンスを読めばなにかわかる。

このコードだけから推測すれば、charAt がある標準オブジェクトは String しかないので sString だと思われる (sString の s だとおもうし、last_char に代入してることからも、s は String だなぁというのが想像できる)


自分がこの行を読むときのプロセスは (かなり冗長にかくと)

  1. last_char に代入してるなぁ
  2. last_char って名前から右側でやってそうなことを想像
  3. → 最後の文字が代入されるはず?
  4. last_char という名前はいったん忘れる
  5. s.charAt(s.length - 1) を頭にいれる
    1. ドットでくぎる
    2. s ってなんだろ (前からさがす)
    3. (この場合は前に定義がないので) charAt をよんでるし s だから String か
    4. charAt は位置を引数にとって文字を返すメソッドか (String のリファレンスよむ)
    5. 引数が s.length - 1 か
      1. s.length は s の長さか (String のリファレンスよむ)
      2. 長さ - 1 だから最後の文字の位置か
    6. s.charAt(s.length - 1) は最後の文字を取得か
  6. last_char に代入か (もどってくる)
  7. 最後の文字を取得して last_char に代入か
  8. 「last_char は s の最後の文字」だけ覚えて次の行を読む……

脳内スタック多い人はもっと別の読みかたできそうだよあぁ……

userChrome.js で設定画面をつくる

XUL で UI つくりたいなぁってちょっと思ったんです。なんかいろいろやったんですがこうしたら一応できた。なんかもっと、スマートな方法がありそうだけど……

function openChromeWindow (xml, opts) {
	// create temporary content dir.
	var t = IO.getFile("Temp", "content" + Math.random() * 0xffff);
	t.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0700);

	// write manifest
	var f = IO.getFile("PrefD", "extensions");
	f.append("{1280606b-2510-4fe0-97ef-9b5a22eafe64}"); // userchromejs
	f.append("chrome.manifest");
	var prev = read(f);
	write(f, "content userchromejs file://" + t.path + "/\n");

	// write chrome xul
	var c = t.clone();
	c.append("temp.xul");
	c.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0700);
	write(c, xml.toXMLString());

	// refresh chrome registry
	Components.classes["@mozilla.org/chrome/chrome-registry;1"]
	          .getService(Components.interfaces.nsIChromeRegistry)
	          .checkForNewChrome();

	// open
	window[opts.fun || "openDialog"]("chrome://userchromejs/content/temp.xul", opts.name || "temp", opts.opts || "chrome");

	// remove temp files and restore original
	t.remove(true);
	write(f, prev);

	// refresh chrome registry
	Components.classes["@mozilla.org/chrome/chrome-registry;1"]
	          .getService(Components.interfaces.nsIChromeRegistry)
	          .checkForNewChrome();


	function read (f) {
		var res = "", str, strm = IO.newInputStream(f, "text");
		while (str = strm.readString(4096)) res += str;
		strm.close();
		return res;
	}

	function write (f, str) {
		var strm  = IO.newOutputStream(f, "text");
		strm.writeString(str);
		strm.close();
	}
}

こんなふうにつかう

default xml namespace = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
openChromeWindow(
	<window>
		<script type="application/javascript">
			const prefs = Components.classes["@mozilla.org/preferences;1"]
			                        .getService(Components.interfaces.nsIPrefBranch);
			alert(prefs.getCharPref("browser.startup.homepage"));
		</script>
	</window>
);
  • 一つのウィンドウとして開きたい
  • chrome 特権を行使したい
    • chrome 特権を行使するには chrome: じゃないとだめらしい
    • chrome: は manifest かかないとだめらしい

nsIScriptableIO

nsIScriptableIO は XPCOM 直接使うよりは遙かにマシだけれど、なんかいまいちだよなぁ……さらにラッパを書きたくなるよ……


こう書きたい

var content = IO.openFile(f, "read text");
// 上と同じ
var content = IO.openFile(f, "read text", function (stream) {
    var res = [];
    while (str = strm.readString(4096)) res.push(str);
    return res.join("");
}); // 自動で close

IO.openFile(f, "write text", "content");
// or
IO.openFile(f, "write text", function (stream) {
    stream.writeString("content");
}); // 自動で close

なんであんなインターフェイスなんだろ……