MozillaZine

Intercepting URL in Firefox extension

Talk about add-ons and extension development.
AndreiKo
 
Posts: 13
Joined: December 19th, 2006, 5:28 am

Post Posted December 19th, 2006, 5:40 am

I need a Firefox extension which performs some actions (redirect and others) before the URL is open. Simply using Mozilla's window.addEventListener("load", Handler, ..) does not work since I have to catch the URL before the page is loaded. Not too exotic task on my thought. I also read a couple of relevant posts here, however I rushed into problems.

What exactly I did (I use Firefox 2.0, WinXP SP2):

I implemented nsIURIContentListener interface.
Code: Select all
// MyOverlay.js
//implementation of nsIURIContentListener
var myListener =
{
   QueryInterface: function(iid)
    {
      alert("QueryInterface "+iid);
        if (iid.equals(Components.interfaces.nsIURIContentListener) ||
            iid.equals(Components.interfaces.nsISupportsWeakReference) ||
            iid.equals(Components.interfaces.nsISupports))
            return this;
        throw Components.results.NS_NOINTERFACE;
    },
    onStartURIOpen: function(uri)
    {
        alert("onStartURIOpen "+uri);
        return false;
    },
    doContent: function(contentType, isContentPreferred, request,
contentHandler)
    {
      alert("doContent");
      return false;
    },
    isPreferred: function(contentType, desiredContentType)
    {
            alert("isPreferred");
         return false;
    },
    canHandleContent: function(contentType, isContentPreferred,
desiredContentType)
    {
      alert("canHandleContent");
        return false;
    },
    GetWeakReference: function()
    {
   alert("GetWeakReference");
   return this;
   }
}
// I set up the content listener: attempt 1
var wnd = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShell)
.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
wnd.parentURIContentListener = myListener;


I get the following error the java script error log: Error: uncaught exception: [Exception... "Cannot modify properties of a WrappedNative" nsresult: "0x80570034 (NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN)" location: "JS frame ::
chrome://foxmon/content/FoxmonOverlay.js :: <TOP_LEVEL> :: line 44" data: no]

Line 44 is docShell.parentURIContentListener = myListener;

Trying to get the exact nsIWebBrowser interface from the window using QueryInterface did not succeed. After googling for a while I found another method to get nsIWebBrowser interface:
Code: Select all
// Setting up the content listener: attempt 2
var wnd = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShell)
.QueryInterface(Components.interfaces.nsIInterfaceRequestor);

var treeItem = wnd.QueryInterface(Components.interfaces.nsIDocShellTreeItem);
var treeOwner = treeItem.treeOwner;
var interfaceRequestor = treeOwner.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
var webBrowserChrome = interfaceRequestor.getInterface(Components.interfaces.nsIWebBrowserChrome);
if (webBrowserChrome)
{
    var chromeFlags = webBrowserChrome.chromeFlags;
    var res = chromeFlags &
    Components.interfaces.nsIWebBrowserChrome.CHROME_ALL;
    var res2 = chromeFlags &
Components.interfaces.nsIWebBrowserChrome.CHROME_DEFAULT;
    if ( res == Components.interfaces.nsIWebBrowserChrome.CHROME_ALL ||
res2 == Components.interfaces.nsIWebBrowserChrome.CHROME_DEFAULT)
{
     var wb = webBrowserChrome.webBrowser;
}
}

This code works fine until the selected line where an attempt to get webBrowser member of webBrowserChrome returned me

Error: uncaught exception: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIWebBrowserChrome.webBrowser]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame ::chrome://foxmon/content/FoxmonOverlay.js :: <TOP_LEVEL> :: line 57" data: no]

After that I tried the following
Code: Select all
// Setting up the content listener: attempt 3
var uriLoader = Components.classes["@mozilla.org/uriloader;1"].getService(Components.interfaces.nsIURILoader);
uriLoader.registerContentListener(myListener);

Things went slightly better: I traced (with alerts) two calls to myListener.QueryInterface: first one was a request for nsISupports, the
second one was a request for nsISupportsWeakReference when the Firefox started. However onStartURIOpen was not called when I pointed my browser to my favourite URL.

Any suggestions?
PS: Actually I need to hook and process URLs from the C++ component, I used JavaScript primary to try it out.
-- Andrei

AndreiKo
 
Posts: 13
Joined: December 19th, 2006, 5:28 am

Post Posted December 20th, 2006, 8:13 am

The solution is (thanks to Boris Zbarsky)

Code: Select all
// Setting up the content listener
var wnd = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIWebNavigation)
                        .QueryInterface(Components.interfaces.nsIDocShell)
                        .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                  .getInterface(Components.interfaces.nsIURIContentListener);
wnd.parentContentListener = myListener;
-- Andrei

5States
 
Posts: 6
Joined: February 7th, 2009, 10:36 am

Post Posted February 7th, 2009, 11:03 am

In using this script, every link on every page is now opening in a new tab.

Are there any adjustments that can be made to ensure that links/URLs are opened in the existing tab?

Also, it could be my version of FF which is:
Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6

Thanks in advance!

5States

AndreiKo
 
Posts: 13
Joined: December 19th, 2006, 5:28 am

Post Posted February 7th, 2009, 11:22 am

Code: Select all
var myListener =
{
   QueryInterface: function(iid)
    {
        if (iid.equals(Components.interfaces.nsIURIContentListener) ||
         iid.equals(Components.interfaces.nsISupportsWeakReference) ||
            iid.equals(Components.interfaces.nsISupports))
            return this;
        throw Components.results.NS_NOINTERFACE;
    },
    onStartURIOpen: function(aUri)
    {
           // do here some processing of aUri and based on this:
           // return false; -> to let the URL go,
           // return true; -> to stop loading this url
           // getBrowser().mCurrentTab.linkedBrowser.loadURI("http://redirect.com"); return true; -> to redirect
           return false;
    },
    doContent: function(aContentType, aIsContentPreferred, aRequest, aContentHandler )
    {
          throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    },
    canHandleContent: function(aContentType, aIsContentPreferred, aDesiredContentType)
   {
          throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    },
    isPreferred: function(aContentType, aDesiredContentType)
   {
        try
       {
            var webNavInfo =
            Components.classes["@mozilla.org/webnavigation-info;1"]
                        .getService(Components.interfaces.nsIWebNavigationInfo);
            return webNavInfo.isTypeSupported(aContentType, null);
        }
      catch (e)
      {
            return false;
        }
    },
      GetWeakReference : function()
   {
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    }
}

// Setting up the content listener
var wnd = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIWebNavigation)
                        .QueryInterface(Components.interfaces.nsIDocShell)
                        .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                  .getInterface(Components.interfaces.nsIURIContentListener);
wnd.parentContentListener = myListener;


This code works just fine for me for Firefox 2.x-3.x. Any suggerstions/corrections are appreciated.
-- Andrei

5States
 
Posts: 6
Joined: February 7th, 2009, 10:36 am

Post Posted February 8th, 2009, 1:13 am

Andrei:

Thanks for that post. Justed tested your latest entry and it works perfect for me as well (3.0.6) . Not sure why my original code was opening a new window - when I have the time, maybe I will analyze it more.

Thanks again!

5States

5States
 
Posts: 6
Joined: February 7th, 2009, 10:36 am

Post Posted February 9th, 2009, 2:54 pm

Andrei:

One quick question - how could I go about obtaining html source from a URL within your myListener function?

I tried creating this function which opens an iframe, but it does not seem to work in XUL environment:

Code: Select all
function getHTMLSource_IFrame(urlToGet) {
   var ifrm = document.createElement("IFRAME");
   ifrm.setAttribute("src", urlToGet);
   ifrm.id = "ifrm"
   ifrm.style.width = 1+"px";
   ifrm.style.height = 1+"px";
   document.body.appendChild(ifrm);

   return document.getElementById("ifrm").innerHTML;
}


How can I tweak the above code so that I can get the source of http://domain.com/index.html for use in the myListener function? Based on debug statements, this one is failing around the "document.body.appendChild(ifrm);" line - I have tried several variations, but all have failed.

Any help would be much appreciated!

Thanks in advance!

5States

max1million
 
Posts: 2810
Joined: November 15th, 2004, 5:03 am

Post Posted February 9th, 2009, 11:31 pm

You want just text can get it with an XMLHttpRequest see responseText

5States
 
Posts: 6
Joined: February 7th, 2009, 10:36 am

Post Posted February 16th, 2009, 10:07 am

@max1million: Thanks for pointing me in the right direction. In case anyone wants to save some time, here is the code I came up with:

Code: Select all
function getHtmlSource_ByXMLHttpRequest(urlToGet,asyncFlag) {
   // alert("in getHtmlSource_ByXMLHttpRequest, urlToGet is " + urlToGet);
   var sourceToReturn="";

   // alert("getHtmlSource_ByXMLHttpRequest2(urlToGet) is " + getHtmlSource_ByXMLHttpRequest2(urlToGet));

   var req = new XMLHttpRequest(); 
    req.open('GET', urlToGet , asyncFlag);
   req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;

   if(asyncFlag) {
      req.onreadystatechange = function (aEvt) { 
         if (req.readyState == 4) { 
            if(req.status == 200) {
               dump(req.responseText); 
               return req.responseText;
            }
            else 
               dump("Error loading page\n"); 
         } 
      };
      req.send(null);
   }
   else {
      req.send(null);
      if(req.status == 200) {
      sourceToReturn=req.responseText;
      // alert("in getHtmlSource_ByXMLHttpRequest, sourceToReturn33 is " + sourceToReturn);
      dump(req.responseText);
      }
   }

   return sourceToReturn;   
}   


Thanks again all...

5

ZheCk
 
Posts: 2
Joined: March 18th, 2010, 10:20 am

Post Posted March 18th, 2010, 10:57 am

Hi all,
I found the topic very interesting and it ALMOST does what I need.

I am looking for a similar solution, but it has to intercept the URL of each tab, and then load another URL after a computation.

With this solution, it computes the new URL, but it opens it in the main page.

i.e., if I middle click on a link, or right-click and choose to open the page in a new tab, the new tab is loaded with the "normal" link, and the "custom" link is loaded in the current open page.

I tried to catch when a new tab is open, with
Code: Select all
onLocationChange: function(aProgress, aRequest, aUri)
, but then it opens the new tab with the "normal" URL, and it changes it to the "custom" URL as I go on that tab.

Any hints?

Thanks

ZheCk
 
Posts: 2
Joined: March 18th, 2010, 10:20 am

Post Posted March 19th, 2010, 9:03 am

Hi guys,
I found a turnaround wich is absolutely NOT elegant, but it does the trick.

I basically check every address that is going to be open, and to redirect it on HTTPS it checks in all open tabs if that address is present, or if a new tab has been opened for it. Otherwise, it opens the link in the current tab.
I have been trying it this morning and it works ok.

Any better solutions will be welcome.
:idea:


Code: Select all
if (aUri.asciiSpec.search(urlToChange) != -1)
{
   var myURL = aUri.asciiSpec.split(":");
   
   if ( myURL[0] == 'http' )
   {
      var newAddress = "https:" + myURL[1];

      var browserEnumerator = wm.getEnumerator("navigator:browser");
      var found = false;
      var oldSelectedTab = getBrowser().mCurrentTab;
      
      while (!found && browserEnumerator.hasMoreElements())
      {
         try
         {
            var browserWin = browserEnumerator.getNext();
            var tabbrowser = browserWin.gBrowser;
            
            var numTabs = tabbrowser.browsers.length;
      
            for (var index = 0; index < numTabs; index++)
            {
              var currentBrowser = tabbrowser.getBrowserAtIndex(index);
               
              if ( (currentBrowser.currentURI.asciiSpec == aUri.asciiSpec) || (currentBrowser.currentURI.asciiSpec == "about:blank") )
               {
                            tabbrowser.selectedTab = tabbrowser.tabContainer.childNodes[index];
                  tabbrowser.selectedTab.linkedBrowser.loadURI(newAddress, null, null);
                  
                  tabbrowser.selectedTab = oldSelectedTab;
            
                found = true;
                break;
              }
            }
         }
         catch(e)
         {
            //alert('Exception: ' + e.name + "\n Message: " + e.message);
            Components.utils.reportError(e);
         }
      }
      
      if (!found)
      {
         tabbrowser.selectedTab = oldSelectedTab;
         tabbrowser.selectedTab.linkedBrowser.loadURI(newAddress, null, null);   
      }

niq24601
 
Posts: 1
Joined: May 13th, 2010, 2:20 pm

Post Posted August 24th, 2010, 10:58 am

I thought I already posted about this, but it doesn't seem to show up ...

I tried using this (with ZheCk's trick for open in new window/tab/drag cases), and it works fine until the incoming request involves a POST. When that happens I don't know how to extract the posted data and forward it on to the new url. Any thoughts?

Return to Extension Development


Who is online

Users browsing this forum: No registered users and 0 guests