2013年1月8日火曜日

firefoxアドオン : httpリクエストのキャンセルをするアドオンのサンプル

とりあえず、httpリクエスト送信前にリクエストを察知できるイベントでhttp-on-modify-requestってのがあります。 http-on-modify-requestのリスナを書くと、こんな感じです。

var httpRequestObserver = {
    observe: function (aSubject, aTopic, aData)
    {
        if (aTopic !== "http-on-modify-request")
            return;
        
        var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
        
        ...
    },
    
    QueryInterface: XPCOMUtils.generateQI(["nsIObserver"])
};

ここで、aSubjectをnsIHttpChannelにキャストしているコードはよく見ます。 でも、nsIHttpChannelに通信を止めるようなメソッドはありません。 nsIHttpChannelだけじゃhttpリクエストのキャンセルはできないんですね。

「じゃあこのイベントでhttpリクエストのキャンセルはできないのか?」というと、できます。 aSubjectがどのインターフェースを実装しているか調べると、こんなに出てきました。

nsIStreamListener、nsICacheListener、nsIHttpChannel、nsIHttpAuthenticableChannel、nsITraceableChannel、nsIChannel、nsIWritablePropertyBag2、nsIUploadChannel、nsIRequest、nsITransportEventSink、nsIEncodedChannel、nsIProxiedChannel、nsICacheInfoChannel、nsIPropertyBag2、nsIApplicationCacheContainer、nsIProtocolProxyCallback、nsIHttpChannelInternal、nsIRequestObserver、nsISupports、nsICachingChannel、nsIAsyncVerifyRedirectCallback、nsIWritablePropertyBag、nsIApplicationCacheChannel、nsIUploadChannel2、nsISupportsPriority、nsIResumableChannel、nsIPropertyBag、nsITimedChannel

調べ方は前の投稿参照。

サンプルコードなどで見つかるのがnsIHttpChannelへのキャストってだけで、他にも色々あるんですね。 で、aSubjectはnsIRequestを実装している事が判明しました。 これでcancel可能です。

それを使ってコードを書いたらこうなりました。 リクエスト先のホスト名が「.yahoo.com」で終わっていたらリクエストの送信をキャンセルします。 (ちなみに、yahoo.comを選んだのはタマタマであり、意味はありません。)

// bootstrap.js
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;

Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");

const TOPIC = "http-on-modify-request";
var httpRequestObserver = {
    observe: function (aSubject, aTopic, aData)
    {
        if (aTopic !== TOPIC)
            return;
        
        var request = aSubject.QueryInterface(Ci.nsIRequest)
                              .QueryInterface(Ci.nsIHttpChannel);
        
        if (endsWith(request.URI.host, ".yahoo.com") )
        {
            log("drop ... " + request.URI.spec);
            request.cancel(Cr.NS_ERROR_FAILURE);
        }
        else
        {
            log("allow ... " + request.URI.spec);
        }
    },
    
    QueryInterface: XPCOMUtils.generateQI(["nsIObserver"])
};

function endsWith(text, pattern)
{
    var textLen = text.length;
    var patLen = pattern.length;
    return patLen <= textLen && text.indexOf(pattern, textLen - patLen) !== -1;
}

function log()
{
    let text = "[HttpRequestCanceller] " + Array.join(arguments, " ");
    Services.console.logStringMessage(text);
    dump(text + '\n');
}

function install (aData, aReason)
{
}

function uninstall(aData, aReason)
{
}

function startup (aData, aReason)
{
    log("Start");
    Services.obs.addObserver(httpRequestObserver, TOPIC, false);
}

function shutdown (aData, aReason)
{
    log("Shutdown");
    Services.obs.removeObserver(httpRequestObserver, TOPIC);
}

う~ん。letとvarが混ざっているな。 まぁ、いいか?