8

I am creating a firefox addon using the SDK. My goal is simple, to intercept a specific iframe and load my own HTML page (packaged as a resource with my addon) instead of the content that was requested originally.

So far I have the following code:

var httpRequestObserver = 
{
    observe: function(subject, topic, data)
    {
        var httpChannel, requestURL;

        if (topic == "http-on-modify-request") {
            httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
            requestURL = httpChannel.URI.spec;

            var newRequestURL, i;

            if (/someurl/.test(requestURL)) {
                var ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);

                httpChannel.redirectTo(ioService.newURI(self.data.url('pages/test.html'), undefined, undefined));
            }

            return;
        }
    }
};

var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
observerService.addObserver(httpRequestObserver, "http-on-modify-request", false);

This code works in that it detects the proper iframe loading and does the redirect correctly. However, I get the following error:

Security Error: Content at http://url.com may not load or link to jar:file:///.../pages/test.html.

How can I get around this limitation?

im_nullable
  • 555
  • 1
  • 6
  • 17

4 Answers4

5

actually man i was really over thinking this.

its already solved when I changed to using loadContext. Now when you get loadContext you get the contentWindow of whatever browser element (tab browser, or frame or iframe) and then just abort the http request like you are doing and then loadContext.associatedWindow.document.location = self.data('pages/tests.html');

done

ill paste the code here removing all the private stuff. you might need the chrome.manifest ill test it out and paste the code back here

Cu.import('resource://gre/modules/Services.jsm');

var httpRequestObserver = {
    observe: function (subject, topic, data) {
        var httpChannel, requestURL;

        if (topic == "http-on-modify-request") {
            httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
            requestURL = httpChannel.URI.spec;

            var newRequestURL, i;

            if (/someurl/.test(requestURL)) {
                var goodies = loadContextGoodies(httpChannel);
                if (goodies) {
                    httpChannel.cancel(Cr.NS_BINDING_ABORTED);
                    goodies.contentWindow.location = self.data.url('pages/test.html');
                } else {
                    //dont do anything as there is no contentWindow associated with the httpChannel, liekly a google ad is loading or some ajax call or something, so this is not an error
                }
            }

            return;
        }
    }
};
Services.obs.addObserver(httpRequestObserver, "http-on-modify-request", false);





//this function gets the contentWindow and other good stuff from loadContext of httpChannel
function loadContextGoodies(httpChannel) {
    //httpChannel must be the subject of http-on-modify-request QI'ed to nsiHTTPChannel as is done on line 8 "httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);"
    //start loadContext stuff
    var loadContext;
    try {
        var interfaceRequestor = httpChannel.notificationCallbacks.QueryInterface(Ci.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
        try {
            loadContext = interfaceRequestor.getInterface(Ci.nsILoadContext);
        } catch (ex) {
            try {
                loadContext = subject.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
            } catch (ex2) {}
        }
    } catch (ex0) {}

    if (!loadContext) {
        //no load context so dont do anything although you can run this, which is your old code
        //this probably means that its loading an ajax call or like a google ad thing
        return null;
    } else {
        var contentWindow = loadContext.associatedWindow;
        if (!contentWindow) {
            //this channel does not have a window, its probably loading a resource
            //this probably means that its loading an ajax call or like a google ad thing
            return null;
        } else {
            var aDOMWindow = contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIWebNavigation)
                .QueryInterface(Ci.nsIDocShellTreeItem)
                .rootTreeItem
                .QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIDOMWindow);
            var gBrowser = aDOMWindow.gBrowser;
            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 where the example in the previous section ends
            return {
                aDOMWindow: aDOMWindow,
                gBrowser: gBrowser,
                aTab: aTab,
                browser: browser,
                contentWindow: contentWindow
            };
        }
    }
    //end loadContext stuff
}

NOTE: Now try this first, I didn't test it yet, if you get a security error when it tries to redirect then create a chrome.manifest file and put it in the root directory. If it throws a security error than you definitely need a chrome.manifest file and that will without question fix it up. I'll test this myself later tonight when I get some time.

The chrome.manifest should look like this:

content kaboom-data ./resources/kaboom/data/ contentaccessible=yes

Then in the code way above change the redirect line from goodies.contentWindow.location = self.data.url('pages/test.html'); to goodies.contentWindow.location = 'chrome://kaboom-data/pages/test.html');.

Noitidart
  • 35,443
  • 37
  • 154
  • 323
  • I went down this approach as well. The problem is that in my case I am trying to capture the navigation of an iframe (which works with this code) and then redirect just the iframe (not the top window). The code provided tries to navigate the top window instead of the iframe. I tried this approach: loadContext.associatedWindow.frameElement.src = 'chrome://kaboom-data/content/test.html'; which gives the same security error even with a chrome.manifest – im_nullable Feb 27 '14 at 23:16
  • Are you sure man? Without a doubt the loadContext.associatedWindow is the contentWindow that loaded, so its the iframe or the frame or the top window. I tested it out not in this scope but previously. Can you tell me how to reproduce your situation ill test it out – Noitidart Feb 28 '14 at 05:07
  • 1
    Extremely heavy testing done by me a couple weeks ago on loadContext and how it gets window of the place it is actually loading can be found [here](https://github.com/Noitidart/loadcontext-and-http-modify-observer) and [here](https://github.com/Noitidart/demo-nsITraceableChannel). Now if you really dont want to do this then you can resort to adding event listener as seen [here](https://gist.github.com/Noitidart/8685542) or you can use web progress listener, you can also use documjent-element-inserted observer as seen [here](https://gist.github.com/Noitidart/8704127). loadContext is easiest tho – Noitidart Feb 28 '14 at 05:15
  • its easiest because you dont have to create a windowListener to add the listener to newly opened windows – Noitidart Feb 28 '14 at 05:16
  • also i fixed a problem in code above. it was aborting even if no loadContext was found. btw the whole goodies.contentWindow.location = blah is what makes just that iframe redirect. we dont do gBrowser.loadURI anymore – Noitidart Feb 28 '14 at 07:23
  • 1
    You are right. contentWindow.location worked as intended. Thanks. – im_nullable Feb 28 '14 at 18:44
  • 1
    Woohoo my first bounty! Thank you man!! :) I learned a lot from helping you too! :) Make sure you watch out for resources that load into top window or something with the same URL pattern you are matching, that could be what was throwing you off at the start. Like if you were matching url pattern of "google" and the top window loaded google.com/img.png that would give you top window so make sure to filter those out – Noitidart Mar 01 '14 at 08:27
1

see this addon here: https://addons.mozilla.org/en-US/firefox/addon/ghforkable/?src=search

in the chrome.manifest file we set the contentaccessible parameter to yes

you dont need sdk for this addon. its so simple, just ocpy paste that into a bootstrap skeleton as seen here:

Bootstrap With Some Features, Like chrome.manifest which you will need

Bootstrap Ultra Basic

if you want to really do a redirect of a page to your site, maybe you want to make a custom about page? if you would like ill throw togather a demo for you on making a custom about page. you can see a bit hard to understand demo here

Noitidart
  • 35,443
  • 37
  • 154
  • 323
  • Unfortunately, this does not help me get over the hump. I have other functionality that I have already implemented in the addon so it would be tough for me to go back and start all over again without using the SDK. -- It seems like setting up a content policy would help when redirecting an internet iframe to a local resource. – im_nullable Feb 25 '14 at 01:43
  • upload your addon onto github (i prefer github, but something else will work too), ill set it up for you. – Noitidart Feb 25 '14 at 05:44
  • 1
    Uploaded. I have invited you to my private repo. – im_nullable Feb 25 '14 at 21:28
  • Thanks can you please pacakage it into xpi so I can install, I dont know how to install addon-sdk addons when there's no install.rdf – Noitidart Feb 26 '14 at 00:15
1

posting my trials here so it can help all:

trail 1 failed - created chrome.manifest file with contents content kaboom-data resources/kaboom/data/ contentaccessible=yes

var myuri = Services.io.newURI('chrome://kaboom-data/content/pages/test.html', undefined, undefined);
httpChannel.redirectTo(myuri);

Error Thrown

Security Error: Content at http://digg.com/tools/diggthis/confirm? may not load or link to jar:file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/jid1-g4RtC8vdvPagpQ@jetpack.xpi!/resources/kaboom/data/pages/test.html.

trial 2 failed - created resource in bootstrap.js

alias.spec = file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/jid1-g4RtC8vdvPagpQ@jetpack.xpi

alias updated to spec: jar:file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/jid1-g4RtC8vdvPagpQ@jetpack.xpi!/

   let resource = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
  let alias = Services.io.newFileURI(data.installPath);
  Cu.reportError('alias.spec = ' + alias.spec);
  if (!data.installPath.isDirectory()) {
    alias = Services.io.newURI("jar:" + alias.spec + "!/", null, null);
    Cu.reportError('alias updated to spec: ' + alias.spec);
  }
  resource.setSubstitution("kaboom_data", alias);

...

var myuri = Services.io.newURI('resource://kaboom_data/resources/kaboom/data/pages/test.html', undefined, undefined);
httpChannel.redirectTo(myuri);

Error Thrown

Security Error: Content at http://digg.com/tools/diggthis/confirm? may not load or link to jar:file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/jid1-g4RtC8vdvPagpQ@jetpack.xpi!/resources/kaboom/data/pages/test.html.

CONCLUSION in both trials above it was the weirdest thing, it wouldnt show the resource or chrome path in the security error thrown but it would give the full jar path. Leading me to believe that this has something to do with redirectTo function.

The solution that did work was your solution of

var gBrowser = utils.getMostRecentBrowserWindow().gBrowser;
var domWin = httpChannel.notificationCallbacks.getInterface(Ci.nsIDOMWindow);
var browser = gBrowser.getBrowserForDocument(domWin.document);

//redirect
browser.loadURI(self.data.url('pages/test.html'));

however I changed this to use loadContext instead of this method because it is the recommended way. also gBrowser to getMostRecentBrowserWindow will fail if the url load is slow and in that time the user swithces to another tab or window

I also changed to use Services.jsm as you had imported Cu anyways. Using Services.jsm is super fast not even blink fast. Its just a pointer.

Im still working on trying to the redirectTo method working its really bothering me. The changes I made are to my local copy.

Noitidart
  • 35,443
  • 37
  • 154
  • 323
  • actually if you create an iframe in html and set it to the chrome path, even if contentaccessible it wont load. it has to do with security [HERE](https://developer.mozilla.org/en-US/docs/Displaying_web_content_in_an_extension_without_security_issues) there is a way to whitelist tho maybe @WladimirPalant can help here as he wrote that article – Noitidart Feb 26 '14 at 07:34
  • also follow this topic i asked for help and found a similar issue to when we set src of iframe to chrome location. [url to topic](http://forums.mozillazine.org/viewtopic.php?f=19&t=2805023) – Noitidart Feb 26 '14 at 07:53
  • ok man i cant do it, i tried lots. let me give you my code though, since its a private repo how should i give you the code? the code shows how to use loadContext and Services.jsm – Noitidart Feb 27 '14 at 04:05
1

Have you considered turning your local HTML file into a data URL and loading that?

Matthew Gertner
  • 4,487
  • 2
  • 32
  • 54
  • Yes. The problem with that was I could not reference associated JS/CSS files. I would have to embed everything into a single html file. – im_nullable Feb 28 '14 at 16:47