API Check

http://d.hatena.ne.jp/Constellation/20090928/1254125755
という話ですー.

http://d.hatena.ne.jp/taizooo/20101108/1289214694#c

具体的には, GM_apiLeakCheck関数でのcheckが入りました. このcheckはComponents.stack https://developer.mozilla.org/en/Components.stack を使って実行中のscriptファイル名を取得(こんなことできるんですね), そのファイル名が, nullでない+GMファイルでない(userscript)+chrome空間内のファイル(他の拡張)でない => web上のscriptファイル がGM_APIを読んでいる => errorを発する, という割と厳しいCheckです. これで,

unsafeWindow.GM_xmlHttpRequest = GM_xmlHttpRequest;

として, web page内で参照することはできなくなりました.

http://d.hatena.ne.jp/Constellation/20090927/1254057213
const gmSvcFilename = Components.stack.filename;
https://github.com/greasemonkey/greasemonkey/blob/master/components/greasemonkey.js#L22
// Examines the stack to determine if an API should be callable.
function GM_apiLeakCheck(apiName) {
  var stack = Components.stack;

  do {
    // Valid stack frames for GM api calls are: native and js when coming from
    // chrome:// URLs and the greasemonkey.js component's file:// URL.
    if (2 == stack.language) {
      // NOTE: In FF 2.0.0.0, I saw that stack.filename can be null for JS/XPCOM
      // services. This didn't happen in FF 2.0.0.11; I'm not sure when it
      // changed.
      if (stack.filename != null &&
          stack.filename != gmSvcFilename &&
          stack.filename.substr(0, 6) != "chrome") {
        GM_logError(new Error("Greasemonkey access violation: unsafeWindow " +
                    "cannot call " + apiName + "."));
        return false;
      }
    }

    stack = stack.caller;
  } while (stack);

  return true;
}
https://github.com/greasemonkey/greasemonkey/blob/master/components/greasemonkey.js#L47

例えば, いままでLDR上で書いていた

var win = unsafeWindow;
win.Keybind.add('J', function(){
  var item = win.get_active_item(true);
  if (!item) return;
  GM_openInTab(item.link);
  win.Control.go_next();
});

なんていうscriptが当然のように動かなくなります. これは登録functionが実際にはLDRのjs上から実行されているため, 由来(Components.stack.filenameの先頭)がweb上scriptになってるんですね.

で, せっかくのAPI Checkなんですが,

var win = unsafeWindow;
win.Keybind.add('J', function(){
  var item = win.get_active_item(true);
  if (!item) return;
  setTimeout(GM_openInTab, 0, item.link);
  win.Control.go_next();
});

ってかいたら動きます. setTimeoutによって, GM_openInTabのstack.filenameが切り替わっているからです.

http://d.hatena.ne.jp/Constellation/20090928/1254125755

基本の基本がわかってないんだけど、

document.addEventListener("DOMNodeInserted", hogehoge(e), false);

で呼び出された hogehoge は unsafeWindow 上で実行されてるって事なのね。で、

document.addEventListener("DOMNodeInserted", function(e){setTimeout(hogehoge(e),0)}, false);

ってすると?