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)
だそうで、ここに書いた情報はもう古いものとなるでしょう。