2010-01-26 30 views
6

更新:我想這個主題給出了一個錯誤的概念,我正在尋找一個現有的插件。這是一個自定義問題,我不想要現有的解決方案。
我想寫(或更合適的,修改和現有)的插件。創建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/

回答

8

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()

0

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

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; 

還要注意的是,你可以把通用腳本轉換成插件的Firefox

+0

我曾考慮過,但由於unsafeWindow被認爲不安全而決定避免。 關於nsITraceable頻道的任何想法? – Jumper 2010-01-27 07:11:28

+0

如果代碼不好,unsafeWindow只是一個問題。根據你的問題的聲音,我認爲你應該可以使用unsafeWindow。不使用它的唯一原因是,如果你覺得這個網站會被用來試圖破壞你的系統(這不會發生,如果你不釋放代碼,即使你這樣做,它會必須在他們最有可能關心之前獲得大量的使用),即使他們確實破解了你的一些方法,你的代碼應該足夠安全以應付它。如果你擔心這個比FF插件會更糟。 – 2010-01-28 05:57:37