読者です 読者をやめる 読者になる 読者になる

Firebug Command Line APIを弄る($xのコンテキスト)

Firebug

[追記]
id:amachangがもっとスマートな方法を書いてくれました。
Firebug を「カクカク化」して $x の第 2 引数でコンテキストノードを指定できるようにする。 - IT戦記
[/追記]

kuさん$xの第二引数にコンテキストを指定できるようにするハックをという発言をみて、これならできそうかなと試してみました。結果だけ見たい方は最後の結論だけどうぞ。

まず、下準備として、 Firefox の拡張機能をふとした時に手元でチョコチョコいじれるようにしとくと楽しい - IT戦記を参考にFirebugを弄りやすい状態にしておきましょう。

では、調査。
Firebugのコンソールで、

uneval($x)

もしくは、

$x.toSource()

と打ちます。
すると、

(function (xpath) {return FBL.getElementsByXPath(baseWindow.document, xpath);})

と出力されると思います。
これをみれば、$xが引数にxpathしか受け取っていなくて、baseWindow.documentが現在開いているページのdocumentであることがなんとなくわかりますね。

さて、FBL.getElementsByXPathでgrepして該当する部分を探してみると、//chrome/content/firebug/commandLineAPI.jsに$xのソースがありました。
他のAPIのソースも基本的にこのファイルに書かれていますね。

早速$xを書き換えてみます。引数にcontextを追加し、nullだったらbaseWindow.documentに置き換えるようにします。

    this.$x = function(xpath,context)
    {
        context = context || baseWindow.document;
        return FBL.getElementsByXPath(context, xpath);
    };

こんな感じですね。
これだけで完了!と思って再起動して試してみたのですが、contextを指定すると要素を取得できませんでした。

そこで、FBL.getElementsByXPathの中を更に調べてみると、

this.getElementsByXPath = function(doc, xpath)
{
    var nodes = [];

    try {
        var result = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
        for (var item = result.iterateNext(); item; item = result.iterateNext())
            nodes.push(item);
    }
    catch (exc)
    {
        // Invalid xpath expressions make their way here sometimes.  If that happens,
        // we still want to return an empty set without an exception.
    }

    return nodes;
};

第一引数のdocがdocument.evaluateとしても使われています。
つまり、「document」と、document.evaluateの第2引数に使う「context」の2つをgetElementsByXPathに渡してあげるのが良さそうです。
よって、$xとgetElementsByXPathを下記のように編集します。

結果

//commandLineAPI.js 引数にcontextを追加、contextがnullの場合はbaseWindow.documentを代入
    this.$x = function(xpath,context)
    {
        context = context || baseWindow.document;
        return FBL.getElementsByXPath(context, xpath, baseWindow.document);
    };
//lib.js 引数にbasedocを追加、basedocがnullの場合はdocを代入
this.getElementsByXPath = function(doc, xpath, basedoc)
{
    var nodes = [];
    basedoc = basedoc || doc;

    try {
        var result = basedoc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
        for (var item = result.iterateNext(); item; item = result.iterateNext())
            nodes.push(item);
    }
    catch (exc)
    {
        // Invalid xpath expressions make their way here sometimes.  If that happens,
        // we still want to return an empty set without an exception.
    }

    return nodes;
};

以上で、$xにcontextを指定できるようになりました。
簡単ですね。