【问题标题】:Create Firefox Addon to Watch and modify XHR requests & reponses创建 Firefox 插件以观看和修改 XHR 请求和响应
【发布时间】:2010-01-26 17:58:13
【问题描述】:

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

这是我的要求:

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

【问题讨论】:

    标签: javascript firefox-addon xpcom xmlhttprequest


    【解决方案1】:

    nsITraceableChannel 确实是这里的路。 Jan Odvarko (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 本身是链中的最后一个)。

    注意:上面的代码中调用了一些函数,它们是 piratequesting 插件的一部分,例如:parseQuery()dumpError()

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 这个主题可能给了你一个错误的想法。我不是在寻找现有的解决方案。请阅读完整的问题。谢谢:)
      • @Jumper,Tamper Data 可能是开始构建附加组件的良好基础。您必须删除大部分 UI 代码,但您需要监控和更改 XHR 请求的代码将在其中。
      • 是的,过去粗略看了一眼。它似乎使用了观察者服务。问题中的链接(谈论 nsITraceableChannel)也使用 Observer。今天将再次查看 tamperData 代码。
      【解决方案3】:

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

      【讨论】:

      • 我考虑过,但决定避免,因为 unsafeWindow 被认为是不安全的。对 nsITraceable 频道有任何想法吗?
      • unsafeWindow 仅在您的代码不佳时才会出现问题。根据您的问题,我认为您应该可以使用 unsafeWindow。不使用它的唯一原因是,如果您认为将使用它的网站会尝试以某种方式破解您的系统(如果您不发布代码,这不会发生,即使您这样做,它也会在他们很可能关心之前必须得到大量使用),即使他们确实以某种方式破解了您,您的代码也应该足够安全以处理它。如果您担心这一点,那么 FF 插件会更糟。
      猜你喜欢
      • 2011-01-29
      • 1970-01-01
      • 1970-01-01
      • 2013-09-18
      • 2018-03-30
      • 2017-12-10
      • 2017-01-11
      • 2019-08-16
      • 2015-10-22
      相关资源
      最近更新 更多