创建Firefox插件来观察和修改XHR请求和响应

问题描述:

更新:我想这个主题给出了一个错误的概念,我正在寻找一个现有的插件。这是一个自定义问题,我不想要现有的解决方案。
我想写(或更合适的,修改和现有)的插件。创建Firefox插件来观察和修改XHR请求和响应

这里是我的要求:

  • 我想我的插件到一个特定的网站只有
  • 页面上的数据使用的是2单向散列编码工作
  • 信息的一个很好的协议是装通过XHR请求,有时 在动画气泡等显示
  • 我插件的最新版本通过XPath的解析页面 表情,对数据进行解码,并将其替换

  • 问题与被鼠标悬停事件显示

  • 因此那些bubblified箱子进来,我意识到,这可能是创建一个XHR 桥可以听所有的数据是个好主意和动态
  • 一些搜索后解码/编码,我碰到nsITraceableInterface [1] [2] [3]

只是想知道,如果我是正确的道路上。如果“是”,那么恳请 提供任何可能适合的额外指示和建议; 如果“否”,那么..好吧,请帮助正确的指针:)

谢谢,
Bipin。

[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/

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

的步骤是基本上是:

  1. 创建对象原型实施nsITraceableChannel;创造观察者监听HTTP-ON-修改请求和HTTP-上审视响应
  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()

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

+0

主题给你一个错误的想法,也许。我不在寻找现有的解决方案。请阅读完整的问题。谢谢:) – Jumper 2010-01-26 18:33:21

+0

@跳线,篡改数据可能是一个很好的基础,开始建立你的附加。您必须撕掉大部分UI代码,但您需要监控和更改XHR请求的代码将位于该代码中。 – 2010-01-26 19:15:20

+0

是的,在过去有一个粗略的看。它似乎使用观察者服务。问题中的链接(即关于nsITraceableChannel的讨论)也使用Observer。 今天再看看tamperData代码。 – Jumper 2010-01-27 07:13:17

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

还要注意的是,你可以把通用脚本转换成插件的Firefox

+0

我曾考虑过,但由于unsafeWindow被认为不安全而决定避免。 关于nsITraceable频道的任何想法? – Jumper 2010-01-27 07:11:28

+0

如果代码不好,unsafeWindow只是一个问题。根据你的问题的声音,我认为你应该可以使用unsafeWindow。不使用它的唯一原因是,如果你觉得这个网站会被用来试图破坏你的系统(这不会发生,如果你不释放代码,即使你这样做,它会必须在他们最有可能关心之前获得大量的使用),即使他们确实破解了你的一些方法,你的代码应该足够安全以应付它。如果你担心这个比FF插件会更糟。 – 2010-01-28 05:57:37