自 Firefox 21 起,以下答案已被取代,现在 nsIHttpChannel.redirectTo() method 可以很好地完成这项工作。你可以重定向到一个 data: URI,这样就可以了:
Components.utils.import("resource://gre/modules/Services.jsm");
const Ci = Components.interfaces;
[...]
onModifyRequest: function(channel)
{
if (channel instanceof Ci.nsIHttpChannel && shouldRedirect(channel.URI.spec))
{
let redirectURL = "data:text/html," + encodeURIComponent("<html>Hi there!</html>");
channel.redirectTo(Services.io.newURI(redirectURI, null, null));
}
}
原始答案(过时)
每个通道都有其关联的stream listener,当接收到数据时会收到通知。要伪造响应,您需要做的就是获取此侦听器并为其提供错误的数据。而nsITraceableChannel 实际上就是这样做的方法。您需要自己替换频道的通常侦听器,该侦听器不会做任何事情,之后您可以取消频道而不会通知侦听器。然后你触发监听器并给它你自己的数据。像这样的:
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const Cc = Components.classes;
const Ci = Components.interfaces;
[...]
onModifyRequest: function(channel)
{
if (channel instanceof Ci.nsIHttpChannel && channel instanceof Ci.nsITraceableChannel)
{
// Our own listener for the channel
var fakeListener = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener,
Ci.nsIRequestObserver, Ci.nsIRunnable]),
oldListener: null,
run: function()
{
// Replace old listener by our fake listener
this.oldListener = channel.setNewListener(this);
// Now we can cancel the channel, listener old won't notice
//channel.cancel(Components.results.NS_BINDING_ABORTED);
},
onDataAvailable: function(){},
onStartRequest: function(){},
onStopRequest: function(request, context, status)
{
// Call old listener with our data and set "response" headers
var stream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
stream.setData("<html>Hi there!</html>", -1);
this.oldListener.onStartRequest(channel, context);
channel.setResponseHeader("Refresh", "5; url=http://google.com/", false);
this.oldListener.onDataAvailable(channel, context, stream, 0, stream.available());
this.oldListener.onStopRequest(channel, context, Components.results.NS_OK);
}
}
// We cannot replace the listener right now, see
// https://bugzilla.mozilla.org/show_bug.cgi?id=646370.
// Do it asynchronously instead.
var threadManager = Cc["@mozilla.org/thread-manager;1"]
.getService(Ci.nsIThreadManager);
threadManager.currentThread.dispatch(fakeListener, Ci.nsIEventTarget.DISPATCH_NORMAL);
}
}
这段代码的问题仍然是如果频道被取消,页面仍然显示为空白(所以我评论了那行) - 听众似乎仍在查看频道并注意到它被取消了。