Создайте дополнение Firefox для просмотра и изменения запросов и ответов XHR.

Обновление: я думаю, что тема дала неправильное представление о том, что я ищу существующий аддон. Это индивидуальная проблема, и мне НЕ нужно существующее решение.
Я хочу НАПИСАТЬ (или, что более уместно, изменить существующий) аддон.

Вот мое требование:

  • Я хочу, чтобы мой аддон работал только для определенного сайта
  • Данные на страницах кодируются с использованием двухстороннего хеширования.
  • Большая часть информации загружается запросами XHR и иногда отображается в анимированных пузырьках и т. д.
  • Текущая версия моего аддона анализирует страницу через выражения XPath, декодирует данные и заменяет их

  • Проблема возникает с теми всплывающими окнами, которые отображаются при наведении курсора мыши.

  • Таким образом, я понял, что было бы неплохо создать мост XHR, который мог бы прослушивать все данные и декодировать/кодировать на лету.
  • После пары поисков я наткнулся на nsITraceableInterface[1][2][3].

Просто хотел узнать, на правильном ли я пути. Если "да", то любезно предоставьте любые дополнительные указания и предложения, которые могут быть уместны; а если "Нет", то.. ну помогите пожалуйста с правильными указателями :)

Спасибо,
Бипин.

[1]. https://developer.mozilla.org/en/NsITraceableChannel
[2]. http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
[3]. http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/


person Jumper    schedule 26.01.2010    source источник


Ответы (3)


nsITraceableChannel действительно подходит. сообщения в блоге Яна Одварко (softwareishard.com) и меня (ashita.org) показывают, как это сделать. Вы также можете увидеть http://www.ashita.org/implementing-an-xpcom-firefox-interface-and-creating-observers/, однако на самом деле это не обязательно делать в компоненте XPCOM.

Шаги в основном:

  1. Создать прототип объекта, реализующий nsITraceableChannel; и создайте наблюдателя для прослушивания http-on-modify-request и http-on-examine-response
  2. регистрировать наблюдателя
  3. наблюдатель, прослушивающий два типа запросов, добавляет наш объект nsITraceableChannel в цепочку слушателей и убеждается, что наш nsITC знает, кто следующий в цепочке
  4. Объект nsITC предоставляет три обратных вызова, каждый из которых будет вызываться на соответствующем этапе: onStartRequest, onDataAvailable и onStopRequest.
  5. в каждом из вышеперечисленных обратных вызовов наш объект nsITC должен передавать данные следующему элементу в цепочке.

Ниже приведен фактический код написанного мной надстройки для конкретного сайта, который, насколько я могу судить, ведет себя очень похоже на ваш.

function TracingListener() {
    //this.receivedData = [];
}

TracingListener.prototype =
{
    originalListener: null,
    receivedData: null,   // array for incoming data.

    onDataAvailable: function(request, context, inputStream, offset, count)
    {
        var binaryInputStream = CCIN("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream");
        var storageStream = CCIN("@mozilla.org/storagestream;1", "nsIStorageStream");
        binaryInputStream.setInputStream(inputStream);
        storageStream.init(8192, count, null);

        var binaryOutputStream = CCIN("@mozilla.org/binaryoutputstream;1",
                "nsIBinaryOutputStream");

        binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));

        // Copy received data as they come.
        var data = binaryInputStream.readBytes(count);
        //var data = inputStream.readBytes(count);

        this.receivedData.push(data);

        binaryOutputStream.writeBytes(data, count);
        this.originalListener.onDataAvailable(request, context,storageStream.newInputStream(0), offset, count);
    },

    onStartRequest: function(request, context) {
        this.receivedData = [];
        this.originalListener.onStartRequest(request, context);
    },

    onStopRequest: function(request, context, statusCode)
    {
        try 
        {
            request.QueryInterface(Ci.nsIHttpChannel);

            if (request.originalURI && piratequesting.baseURL == request.originalURI.prePath && request.originalURI.path.indexOf("/index.php?ajax=") == 0) 
            {

                var data = null;
                if (request.requestMethod.toLowerCase() == "post") 
                {
                    var postText = this.readPostTextFromRequest(request, context);
                    if (postText) 
                        data = ((String)(postText)).parseQuery();

                }
                var date = Date.parse(request.getResponseHeader("Date"));
                var responseSource = this.receivedData.join('');

                //fix leading spaces bug
                responseSource = responseSource.replace(/^\s+(\S[\s\S]+)/, "$1");

                piratequesting.ProcessRawResponse(request.originalURI.spec, responseSource, date, data);
            }
        } 
        catch (e) 
        {
            dumpError(e);
        }
        this.originalListener.onStopRequest(request, context, statusCode);
    },

    QueryInterface: function (aIID) {
        if (aIID.equals(Ci.nsIStreamListener) ||
            aIID.equals(Ci.nsISupports)) {
            return this;
        }
        throw Components.results.NS_NOINTERFACE;
    },
    readPostTextFromRequest : function(request, context) {
        try
        {
            var is = request.QueryInterface(Ci.nsIUploadChannel).uploadStream;
            if (is)
            {
                var ss = is.QueryInterface(Ci.nsISeekableStream);
                var prevOffset;
                if (ss)
                {
                    prevOffset = ss.tell();
                    ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
                }

                // Read data from the stream..
                var charset = "UTF-8";
                var text = this.readFromStream(is, charset, true);

                // Seek locks the file so, seek to the beginning only if necko hasn't read it yet,
                // since necko doesn't seek to 0 before reading (at lest not till 459384 is fixed).
                if (ss && prevOffset == 0) 
                    ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);

                return text;
            }
            else {
                dump("Failed to Query Interface for upload stream.\n");
            }
        }
        catch(exc)
        {
            dumpError(exc);
        }

        return null;
    },
    readFromStream : function(stream, charset, noClose) {

        var sis = CCSV("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream");
        sis.setInputStream(stream);

        var segments = [];
        for (var count = stream.available(); count; count = stream.available())
            segments.push(sis.readBytes(count));

        if (!noClose)
            sis.close();

        var text = segments.join("");
        return text;
    }

}


hRO = {

    observe: function(request, aTopic, aData){
        try {
            if (typeof Cc == "undefined") {
                var Cc = Components.classes;
            }
            if (typeof Ci == "undefined") {
                var Ci = Components.interfaces;
            }
            if (aTopic == "http-on-examine-response") {
                request.QueryInterface(Ci.nsIHttpChannel);

                if (request.originalURI && piratequesting.baseURL == request.originalURI.prePath && request.originalURI.path.indexOf("/index.php?ajax=") == 0) {
                    var newListener = new TracingListener();
                    request.QueryInterface(Ci.nsITraceableChannel);
                    newListener.originalListener = request.setNewListener(newListener);
                }
            } 
        } catch (e) {
            dump("\nhRO error: \n\tMessage: " + e.message + "\n\tFile: " + e.fileName + "  line: " + e.lineNumber + "\n");
        }
    },

    QueryInterface: function(aIID){
        if (typeof Cc == "undefined") {
            var Cc = Components.classes;
        }
        if (typeof Ci == "undefined") {
            var Ci = Components.interfaces;
        }
        if (aIID.equals(Ci.nsIObserver) ||
        aIID.equals(Ci.nsISupports)) {
            return this;
        }

        throw Components.results.NS_NOINTERFACE;

    },
};


var observerService = Cc["@mozilla.org/observer-service;1"]
    .getService(Ci.nsIObserverService);

observerService.addObserver(hRO,
    "http-on-examine-response", false);

В приведенном выше коде originalListener — это слушатель, которого мы вставляем перед собой в цепочку. Крайне важно сохранить эту информацию при создании прослушивателя трассировки и передать данные во всех трех обратных вызовах. В противном случае ничего не будет работать (страницы даже не будут загружаться. Сам Firefox является последним в цепочке).

Примечание: в приведенном выше коде есть некоторые функции, которые являются частью надстройки для поиска пиратов, например: parseQuery() и dumpError()

person Jonathan Fingland    schedule 29.01.2010

Дополнение для подделки данных. См. также страницу Как использовать.

person Sam Hasler    schedule 26.01.2010
comment
Возможно, тема натолкнула вас на неверную мысль. Я не ищу существующее решение. Пожалуйста, прочитайте полную задачу. Спасибо :) - person Jumper; 26.01.2010
comment
@Jumper, Tamper Data, вероятно, будет хорошей основой для начала создания вашего дополнения. Вам придется вырвать большую часть кода пользовательского интерфейса, но код, который вам нужен для мониторинга и изменения запросов XHR, будет там. - person Sam Hasler; 26.01.2010
comment
Да, бегло посмотрел в прошлом. Похоже, он использовал службу Observer. Ссылки в вопросе (которые говорят о nsITraceableChannel) также используют Observer. Сегодня снова посмотрю на код tamperData. - person Jumper; 27.01.2010

Вы можете попробовать создать скрипт Greasemonkey и перезаписать файл XMLHttpRequest.

Код будет выглядеть примерно так:

function request () {
};
request.prototype.open = function (type, path, block) {
 GM_xmlhttpRequest({
  method: type,
  url: path,
  onload: function (response) {
   // some code here
  }
 });
};
unsafeWindow.XMLHttpRequest = request;

Также обратите внимание, что вы можете превратить скрипт GM в надстройку для Firefox.

person Matthew FL    schedule 27.01.2010
comment
Я думал об этом, но решил избежать, потому что unsafeWindow считается небезопасным. Любая идея о канале nsITraceable? - person Jumper; 27.01.2010
comment
unsafeWindow — это проблема, только если у вас плохой код. Судя по звучанию вашего вопроса, я думаю, что вы должны быть в порядке, используя unsafeWindow. Единственная причина не использовать его — если вы чувствуете, что сайт, на котором он будет использоваться, попытается каким-то образом взломать вашу систему (этого не произойдет, если вы не выпустите код, и даже если вы это сделаете, это будет должны получить много использования, прежде чем они, скорее всего, будут заботиться), даже если они каким-то образом взломали вас, ваш код должен быть достаточно безопасным, чтобы справиться с этим. Если вы беспокоитесь об этом, то аддон FF будет хуже. - person Matthew FL; 28.01.2010