创建Firefox Addon以观察和修改XHR请求;回复

时间:2010-01-26 17:58:13

标签: javascript firefox-addon xpcom xmlhttprequest

更新:我想主题给出了一个错误的观念,即我正在寻找一个现有的插件。这是一个自定义问题,我不想要现有的解决方案 我想写(或更恰当地,修改和现有)插件。

这是我的要求:

  • 我希望我的插件仅适用于特定网站
  • 页面上的数据使用双向哈希编码
  • 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/

3 个答案:

答案 0 :(得分:8)

nsITraceableChannel确实是去这里的方式。 Jan Odvarko(softwareishard.com)和我自己(ashita.org)的博客文章展示了如何做到这一点。您可能还想查看http://www.ashita.org/implementing-an-xpcom-firefox-interface-and-creating-observers/,但是在XPCOM组件中执行此操作并不是必需的。

步骤基本上是:

  1. 创建实现nsITraceableChannel的Object原型;并创建观察者来监听http-on-modify-request和http-on-examine-response
  2. 注册观察员
  3. 观察者监听两种请求类型将我们的nsITraceableChannel对象添加到监听器链中,并确保我们的nsITC知道链中的下一个是谁
  4. nsITC对象提供三个回调,每个回调都将在适当的阶段调用:onStartRequest,onDataAvailable和onStopRequest
  5. 在上面的每个回调中,我们的nsITC对象必须将数据传递给链中的下一个项目

  6. 以下是我编写的特定于网站的插件的实际代码,其行为与我的内容非常相似。

    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是我们之前在链中插入的监听器。在创建跟踪侦听器时保留该信息并在所有三个回调中传递数据至关重要。否则什么都不会起作用(页面甚至不会加载。火狐本身就是链中的最后一个)。

    注意:在上面的代码中调用了一些函数,这些函数是piratequesting附加组件的一部分,例如:parseQuery()dumpError()

答案 1 :(得分:0)

Tamper Data Add-on。另请参阅How to Use it页面

答案 2 :(得分:0)

您可以尝试制作一个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的插件