window.AutoPagerize.addDocumentFilter と GM_xmlhttpRequest

http://d.hatena.ne.jp/janus_wel/20080909/1221036019 で書いた addDocumentFilter に関しての記述の補足。結論は addDocumentFilter で追加する関数内で GM_xmlhttpRequest ( 非同期 XMLHttpRequest 含む ) を有効に使うのは非常に難しいということ。

はじまりは faviconize を書く上で addDocumentFilter を使っていたときのこと。どうやっても addDocumentFilter で追加する callback 関数に渡される第一引数の新しいページドキュメントが書き換えられなかったので調べていった結果上記の結論に達した。いやまぁなんで気づかないんだってくらい単純なことだったんだけど。

おれの書いたコードは以下のようなものなんだけど ( イメージね ) とりあえず GM_xmlhttpRequest の response で処理内容が変わるところがキモ。

window.AutoPagerize.addDocumentFilter(
    function(newDoc, requestURL, siteinfo) {
        // 下 2 行は超イメージ
        var anchorNode = getAnchorNode();
        var faviconURL = getFaviconURL();

        GM_xmlhttpRequest(
            method: 'GET',
            url:    faviconURL,
            onload: function(response) {
                var faviconNode = makeFaviconNode(response);
                anchorNode.parentNode.insertBefore(faviconNode, anchorNode);
            }
        );
    }
);

こう書くとどうやっても意図通りにならない。なぜかというと onload が呼ばれたタイミングでは newDoc は元ページに追加済みですでに破棄一直線コースなので insertBefore してもその変更が元ページに現れることがない、っつーこと。図で表すと以下のような感じね。 requestLoad はページ追加処理を行っている関数。

ということで GM_xmlhttpRequest / 非同期 XMLHttpRequest を使って newDoc に変更を加えるのは無理という結論に達した。こういうことがしたいなら素直に addFilter 使っとけってことだね。まぁ即時処理されるものなら何も問題はないはず。むしろ addFilter に比べて evaluate しやすいという特性 ( http://d.hatena.ne.jp/janus_wel/20080909/1221036019 ) を使ってある条件のノードの属性を一斉に書き換えるとかは有用だと思う。普通に appendChild / insertBefore するとかでもいいけど。

とりあえずマジで気付かなかったおれ乙。やっぱり鈍男なのかも…。