Greasemonkeyのソースがかなりアレな件

今、Greasemonkeyのソースを読んでる。実際にどうやってuser scriptを実装させているかの辺りを読んだ。

しかし、物凄い量のグローバル関数だ....
"GM_"と接頭辞が付いたものは、まぁ許そう。しかし、Config関数(というかクラス?)は止めてくれ。そこからさらに呼んでいるgetScriptFile関数もグローバル、さらにgetScriptDirもグローバルだ。
しかも、Configクラスの呼び出しにnew Config(getScriptFile("config.xml"))としているが、Configクラスの実装は

function Config(){
  this.onload = null;
  this.scripts = null;
  this.configFile = getScriptFile("config.xml");
}

となっていて、引数が全く意味をなしていない。

あと、greasemokeyServiceの根幹とも言うべきdomContentLoadedメソッドも謎だ。
このメソッド、第一引数にオブジェクトを取るが、コード中ではそのオブジェクト内のwrappedJSObjectを使用しているのみ。さらにメソッドの呼び出し元をみてみると、this.gmSrc.domContentLoaded( {wrappedJSObject: unsafeWin }, window);となっている。素直にそのままunsafeWinを渡せよw

まぁこの辺はGreasemonkeyの共通な落とし穴を避ける - minghaiの日記にあるような苦労の軌跡なのかもしれん.....ということにしておこう。

愚痴はこの辺で止めておく。
分かった事。

  • ユーザスクリプトの呼び出しタイミングはブラウザからDOMContentLoadedイベントが発行された時
  • スクリプトはそれぞれのComponents.util.Sandbox上で実行される
  • 実行される時、スクリプトの前後に(function(){\n\n})()が付加される

ここから分かる事は

  • ユーザスクリプト内でwindowのloadイベントをトリガーにスクリプト実行はあまり意味が無い
    • 理由はDOMContentLoadedが成立しているから
    • 場合によってはloadイベントまで待つ必要があるものもあるかもしれないが少ないだろう
  • ユーザスクリプト内で(function(){ ... })()とわざわざクロージャを作る必要は無い

最後にソースを読んだ本当の目的はuserChrome.jsなどからスクリプトの動的(アン)ロード(ページのリロード無しにという意味)が可能かどうか、であったが結論は残念ながら無理。greasemokeyService内にinjectScriptsというメソッドがあり、これが読み込むべきスクリプトサンドボックス上の各オブジェクトを配置して実行するようになっているがプライベートなメソッドであり外部から実行はできないようだ。くそっ。動的(アン)ロードができればgreasemonkeyはもっと面白くなると思ったのに。

追記(2008/02/14)

del.icio.usのコメントでOperaのUserJSの場合は(function(){\n...\n})()を付加しないので互換性のためにできれば無名関数で囲って欲しいところ……。と頂いたり、GreaseKitのソースをチェックアウトして眺めてみた - 0x廃棄階層 - 統治局の方でid:os0xさんが反応してくださったりと、Greasemonkey以外でのユーザスクリプトの実装体型についてコメント頂けたりしてとても嬉しい。
視野が狭くなっていたことを実感した。みんな気付かせてくれてありがとう。

追記(2008/05/14)

だそうで、ここに書いた情報はもう古いものとなるでしょう。