更新:我想主题给出了一个错误的观念,即我正在寻找一个现有的插件.这是一个自定义问题,我不想要现有的解决方案.
我想写(或更恰当地,修改和现有)插件.
这是我的要求:
我希望我的插件只能用于特定网站
页面上的数据使用双向散列进行编码
XHR请求会加载大量信息,有时会显示在动画气泡等中.
我的插件的当前版本通过XPath表达式解析页面,解码数据并替换它们
问题出现在鼠标悬停事件中显示的那些冒泡的盒子
因此,我意识到创建一个可以监听所有数据并在运行中解码/编码的XHR桥可能是一个好主意.
经过几次搜索,我遇到了nsITraceableInterface [1] [2] [3]
只是想知道我是否走在正确的道路上.如果"是",那么请提供任何可能适当的额外指示和建议; 如果"不",那么..好吧,请帮助正确的指针:)
谢谢,比
宾.
[1].https://developer.mozilla.org/en/NsITraceableChannel
[2].http://www.softwarehardhard.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组件中没有必要这样做.
步骤基本上是:
创建实现nsITraceableChannel的Object原型; 并创建观察者来监听http-on-modify-request和http-on-examine-response
登记观察员
监听这两种请求类型的观察者将我们的nsITraceableChannel对象添加到监听器链中,并确保我们的nsITC知道链中的下一个是谁
nsITC对象提供三个回调,每个回调都将在适当的阶段调用:onStartRequest,onDataAvailable和onStopRequest
在上面的每个回调中,我们的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
我们在链之前插入自己的监听器.在创建跟踪侦听器时保留该信息并在所有三个回调中传递数据至关重要.否则什么都不会起作用(页面甚至不会加载.火狐本身就是链中的最后一个).
注意:在上面的代码中调用了一些函数,它们是piratequesting附加组件的一部分,例如:parseQuery()
和dumpError()