在Firefox扩展中更改HTTP响应

时间:2009-11-08 05:15:21

标签: javascript firefox-addon

如何在Firefox扩展中更改HTTP响应正文?我已经使用下面的代码设置了一个http-on-examine-response观察者和一个nsIStreamListener对象。在我获取数据,解析并更改它之后,如何将更改的响应推送回firefox浏览器?例如,假设我在启用扩展程序的情况下访问Google.com,扩展程序应截取响应并将“google”的每次更改更改为“goggle”。因此,当页面加载时,用户将在任何地方看到“goggle”。

function TmSteroidsObserver()
{
  this.register();
}


TmSteroidsObserver.prototype = {
  observe: function(subject, topic, data) {

    if (topic == "http-on-examine-response") {

    }
    else if (topic == "http-on-modify-request") {
       var channel = subject.QueryInterface(Components.interfaces.nsIChannel);
       var listener = new StreamListener(channel);
    }

  },

  register: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                                .getService(Components.interfaces.nsIObserverService);
    observerService.addObserver(listener, "http-on-modify-request", false);
    observerService.addObserver(listener, "http-on-examine-response", false);
  },

  unregister: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                            .getService(Components.interfaces.nsIObserverService);
    observerService.removeObserver(this, "http-on-modify-request");
    observerService.removeObserver(this, "http-on-examine-response");
  },

  QueryInterface : function(aIID) {
    if (aIID.equals(Components.interfaces.nsISupports) ||
        aIID.equals(Components.interfaces.nsIObserver))
      return this;
    throw Components.results.NS_NOINTERFACE;
  }

}

function StreamListener(channel) {

    channel.notificationCallbacks = listener;
    channel.asyncOpen(listener, null);

}

StreamListener.prototype = {
  mData: "",
  mChannel: null,

  // nsIStreamListener
  onStartRequest: function (aRequest, aContext) {
    this.mData = "";
  },

  onDataAvailable: function (aRequest, aContext, aStream, aSourceOffset, aLength) {
    var scriptableInputStream = 
      Components.classes["@mozilla.org/scriptableinputstream;1"]
        .createInstance(Components.interfaces.nsIScriptableInputStream);
    scriptableInputStream.init(aStream);

    this.mData += scriptableInputStream.read(aLength);
  },

  onStopRequest: function (aRequest, aContext, aStatus) {
    if (Components.isSuccessCode(aStatus)) {
      // request was successfull
      this.mCallbackFunc(this.mData);
    } else {
      // request failed
      this.mCallbackFunc(null);
    }

    this.mChannel = null;
  },

  // nsIChannelEventSink
  onChannelRedirect: function (aOldChannel, aNewChannel, aFlags) {
    // if redirecting, store the new channel
    this.mChannel = aNewChannel;
  },

  // nsIInterfaceRequestor
  getInterface: function (aIID) {
    try {
      return this.QueryInterface(aIID);
    } catch (e) {
      throw Components.results.NS_NOINTERFACE;
    }
  },

  // nsIProgressEventSink (not implementing will cause annoying exceptions)
  onProgress : function (aRequest, aContext, aProgress, aProgressMax) { },
  onStatus : function (aRequest, aContext, aStatus, aStatusArg) { },

  // nsIHttpEventSink (not implementing will cause annoying exceptions)
  onRedirect : function (aOldChannel, aNewChannel) { },

  // we are faking an XPCOM interface, so we need to implement QI
  QueryInterface : function(aIID) {
    if (aIID.equals(Components.interfaces.nsISupports) ||
        aIID.equals(Components.interfaces.nsIInterfaceRequestor) ||
        aIID.equals(Components.interfaces.nsIChannelEventSink) || 
        aIID.equals(Components.interfaces.nsIProgressEventSink) ||
        aIID.equals(Components.interfaces.nsIHttpEventSink) ||
        aIID.equals(Components.interfaces.nsIStreamListener))
      return this;

    throw Components.results.NS_NOINTERFACE;
  }
};

3 个答案:

答案 0 :(得分:11)

您可以使用nsITraceableChannel拦截响应。

您应该修改所需数据,并将其传递给innerListener的OnDataAvailable

以下链接可以帮助您更好地理解这一点。

http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/

http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/

答案 1 :(得分:1)

对于将来在Firefox Quantum中寻求实现此方法的读者,这里有an API that lets you filter responses。使用长文档mentioned here的方法,我能够可靠地更改(临时)插件的background.js中所需的内容,如下所示:

browser.webRequest.onBeforeRequest.addListener(
    function fixenator(details) {
        let filter = browser.webRequest.filterResponseData(details.requestId);
        let decoder = new TextDecoder("utf-8");
        let encoder = new TextEncoder();
        let str = '';

        filter.ondata = event => {
            str += decoder.decode(event.data, {stream: true});
        };

        filter.onstop = event => {
            str = str.replace(/searchPattern/g, 'replace pattern');
            filter.write(encoder.encode(str));
            filter.close();
        }

        return {};
    },
    {
        urls: ['https://example.com/path/to/url']
        //, types: ['main_frame', 'script', 'sub_frame', 'xmlhttprequest', 'other'] // optional
    }
    , ['blocking']
);

答案 2 :(得分:0)

观察员服务只是给你的听众打电话。 Firefox将接收请求,致电您的听众并发送回复。请参阅Mozilla文档Creating HTTP POSTs