0

I'm currently trying to build a firefox extension that determines a proxy for each http request based on Regular Expressions. The Proxy that has been used for loading a page should be remembered for any new request coming from that page, ie. any image/script/css file needed for that page, any outgoing links or ajax requests. That also means that the proxy needs to be remembered for each open tab. This is where I run into my problem: Up until now I tried to mark each open tab by inserting a unique id as an attribute of the browser element of the tab, and looking for this id in an implementation of the shouldLoad() method of nsiContentPolicy. The code I'm using for this is shown below, and it was extracted from the addon sdk's getTabForContentWindow method in tabs/utils.js.

shouldLoad: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra)
  {
      var tabId = null;

      if (!(context instanceof CI.nsIDOMWindow))
      {
        // If this is an element, get the corresponding document
        if (context instanceof CI.nsIDOMNode && context.ownerDocument)
          context = context.ownerDocument;

        // Now we should have a document, get its window
        if (context instanceof CI.nsIDOMDocument)
          context = context.defaultView;
        else
          context = null;
      }

      let browser;
      try {
        browser = context.QueryInterface(CI.nsIInterfaceRequestor)
                        .getInterface(CI.nsIWebNavigation)
                        .QueryInterface(CI.nsIDocShell)
                        .chromeEventHandler;
      } catch(e) {
        this.console.log(e);
      }

      let chromeWindow = browser.ownerDocument.defaultView;
      if ('gBrowser' in chromeWindow && chromeWindow.gBrowser &&
           'browsers' in chromeWindow.gBrowser) {

      let browsers = chromeWindow.gBrowser.browsers;
      let i = browsers.indexOf(browser);
      if (i !== -1)
        tabId = chromeWindow.gBrowser.tabs[i].getAttribute("PMsMark");
      }     
    return CI.nsIContentPolicy.ACCEPT;
  }

This works fine for any load that does not change the displayed document, but as soon as the document is changed(ie. a new page is loaded), the variable browser is null.

I have looked at the other mechanisms for intercepting page loads described on https://developer.mozilla.org/en-US/Add-ons/Overlay_Extensions/XUL_School/Intercepting_Page_Loads , but those seem to be unsuitable for what I want to achieve, because as far as I understand they work on HTTP requests, and for a request to exist, the proxy already needed to be determined.

So, if anybody knows a way to catch imminent loads before they become requests, and at the same time, it's possible to find out which tab is responsible for those loads-to-be, I'd be glad if they could let me know in the answers! Thanks in advance!

pmuench
  • 15
  • 6
  • As described in the comments to Noitidart's suggestion below, listening to http-on-modify-request events does not solve the problem, because the event is fired after determining the proxy for the request. – pmuench Apr 14 '14 at 10:09
  • It is however doable by listening to http-on-opening-request events. – pmuench Apr 14 '14 at 10:27

1 Answers1

0

https://developer.mozilla.org/en-US/docs/Code_snippets/Tabbed_browser#Getting_the_browser_that_fires_the_http-on-modify-request_notification

Components.utils.import('resource://gre/modules/Services.jsm');
Services.obs.addObserver(httpObs, 'http-on-opening-request', false);
//Services.obs.removeObserver(httpObs, 'http-on-modify-request'); //uncomment this line, or run this line when you want to remove the observer

var httpObs = {
    observe: function (aSubject, aTopic, aData) {
        if (aTopic == 'http-on-opening-request') {
            /*start - do not edit here*/
            var oHttp = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel); //i used nsIHttpChannel but i guess you can use nsIChannel, im not sure why though
            var interfaceRequestor = oHttp.notificationCallbacks.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
            //var DOMWindow = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow); //not to be done anymore because: https://developer.mozilla.org/en-US/docs/Updating_extensions_for_Firefox_3.5#Getting_a_load_context_from_a_request //instead do the loadContext stuff below
            var loadContext;
            try {
                loadContext = interfaceRequestor.getInterface(Components.interfaces.nsILoadContext);
            } catch (ex) {
                try {
                    loadContext = aSubject.loadGroup.notificationCallbacks.getInterface(Components.interfaces.nsILoadContext);
                    //in ff26 aSubject.loadGroup.notificationCallbacks was null for me, i couldnt find a situation where it wasnt null, but whenever this was null, and i knew a loadContext is supposed to be there, i found that "interfaceRequestor.getInterface(Components.interfaces.nsILoadContext);" worked fine, so im thinking in ff26 it doesnt use aSubject.loadGroup.notificationCallbacks anymore, but im not sure
                } catch (ex2) {
                    loadContext = null;
                    //this is a problem i dont know why it would get here
                }
            }
            /*end do not edit here*/
            /*start - do all your edits below here*/
            var url = oHttp.URI.spec; //can get url without needing loadContext
            if (loadContext) {
                var contentWindow = loadContext.associatedWindow; //this is the HTML window of the page that just loaded
                //aDOMWindow this is the firefox window holding the tab
                var aDOMWindow = contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
                var gBrowser = aDOMWindow.gBrowser; //this is the gBrowser object of the firefox window this tab is in
                var aTab = gBrowser._getTabForContentWindow(contentWindow.top); //this is the clickable tab xul element, the one found in the tab strip of the firefox window, aTab.linkedBrowser is same as browser var above //can stylize tab like aTab.style.backgroundColor = 'blue'; //can stylize the tab like aTab.style.fontColor = 'red';
                var browser = aTab.linkedBrowser; //this is the browser within the tab //this is what the example in the previous section gives
                //end getting other useful stuff
            } else {
                Components.utils.reportError('EXCEPTION: Load Context Not Found!!');
                //this is likely no big deal as the channel proably has no associated window, ie: the channel was loading some resource. but if its an ajax call you may end up here
            }
        }
    }
};
pmuench
  • 15
  • 6
Noitidart
  • 35,443
  • 37
  • 154
  • 323
  • Well, this is http/https only, of course. And I'm not quite sure if you can still mess around with proxies at this point. Oh, and not every `nsILoadContext` has an `associatedWindow` (or `topWindow` for that matter), so better improve the error handling. ;) – nmaier Apr 08 '14 at 17:54
  • hey @pmuench see here for a better error handling version: http://stackoverflow.com/a/22060455/1828637 – Noitidart Apr 09 '14 at 05:50
  • oh wait @namaier im confused now, in which situations does loaddcontext not have associated window? if its loaded in tab it always has associatedwindow right? if its not loaded in tab then it shouldnt even have loadcontext right? – Noitidart Apr 09 '14 at 05:54
  • 1
    The docs (in the idl) state it might be null. So that's the contract. Out of the [4 implementations in central](http://dxr.mozilla.org/mozilla-central/search?q=associatedWindow), only one might return a window, 1 does return null (webapps.jsm) and 2 always throw (LoadContext, OfflineCacheUpdateParent, both e10s apparantly). Also add-ons are free to implement the interface and return null, following the contract (e.g. Lightning does this). – nmaier Apr 09 '14 at 08:54
  • Well, I finally got around to testing the code. Unfortunately, it's like I expected: The Observer is notified after the proxy choice has already been made. Thanks anyway, Noitidart! – pmuench Apr 14 '14 at 10:06
  • 1
    I shouldn't be so hasty sometimes. It works when using http-on-opening-request instead of http-on-modify-request. – pmuench Apr 14 '14 at 10:26
  • Thanks for the update on the `http-on-opening-request` i learned something there – Noitidart Apr 14 '14 at 15:57
  • Can you share how you are setting the proxy Im interested in learning that – Noitidart Apr 14 '14 at 16:00
  • I don't have much time right now to go into the details, but basically, I use nsIProtocolProxyService like in [How to Change Firefox Proxy using XPCOM](http://stackoverflow.com/questions/10605183/how-to-change-firefox-proxy-settings-using-xpcom). – pmuench Apr 15 '14 at 14:18