3

Good day. i have problem with porting chromium extension to firefox. i need to detect all outgoing request and id's of tabs to which it belongs. to detect requests i using system/events api, but i can't find a way how to detect id of tab from incomming events. As i understand this events is xpcom objects and i should use QueryInterface to get some interface to get some other interface to get some other interface to get some other interface ..... to get some other interface to get id of tab from it (just like in COM implementation in windows), but i can't find which interface i need, can't find documentation about this events at all...

code which i using in chromium:

chrome.webRequest.onBeforeRequest.addListener(
 function(info) {
     if(info.tabId)
         //do stuff here
 }

so it's what i want to achieve from firefox...

code which i currently write for firefox:

exports.main = function(options)
{
    //stuf here ....
    ........
    function listener(event)
    {
        var channel = event.subject.QueryInterface(Ci.nsIHttpChannel);
        console.log(channel);
        //TODO: get tab here somehow
    }
    events.on("http-on-opening-request", listener);
}

i have looked on xpcom docs few days, but still have not enough info to implement this simple thing... so if someone have success with this, please help.

sss123next
  • 295
  • 1
  • 3
  • 13
  • You could take a look at https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Progress_Listeners and https://developer.mozilla.org/en-US/docs/Listening_to_events_on_all_tabs - afaik you can get the tab id this way but I'm not sure if it captures all requests (like ajax or content referenced the html document) – kapex Aug 08 '14 at 12:51
  • thx, if i correctly understand this docs, it does something like 'require("sdk/tabs").on('ready', function(tab)' which is not what i need, i am trying to implement some kind of history of requests for tab, but i not only need changes of tab state itself, but also detect server redirects and similar things to track it all... So ideally i want to track all requests and obtain id of tab from events somehow, or ignore event if it not belong to any tab, i have implemented logic like this for chromium and it will be convenient to have something similar with firefox to not rewrite lot of code. – sss123next Aug 08 '14 at 22:41
  • Web progress listeners do a bit more and get several events for each request. They do show redirects which are indicated by some flags: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIWebProgressListener#State_Transition_Flags – kapex Aug 08 '14 at 23:04
  • this looks very close, another question, how can i get gBrowser object ?, as i understand each tab implements one of gBrowser for each frame ?, so it's possible to get gBrowser for "top frame" of tab ? P.S. i am sorry for lame questions, i am very new to firefox extensions developing.. – sss123next Aug 08 '14 at 23:10

2 Answers2

4

I just found a code snippet for getting the browser that fires the http-on-modify-request notification. The code there seems to be broken but I used some of it to create this function to get a tab from the channel.

const getTabFromChannel = (aChannel) => {
  try {
    let notificationCallbacks = aChannel.notificationCallbacks || aChannel.loadGroup.notificationCallbacks;
    if (!notificationCallbacks)
      return null;

    let domWin = notificationCallbacks.getInterface(Ci.nsIDOMWindow);
    let chromeTab = tabsUtils.getTabForContentWindow(domWin);
    return getSdkTabFromChromeTab(chromeTab);
  }
  catch (e) {
    // some type errors happen here, not sure how to handle them
    console.log(e);
    return null;
  }
} 

This function converts the low-level tab to a high-level tab. Depending on which one you need you could skip this function of course. Again, in the latest SDK you probably can replace it with tabs.viewFor(chromeTab).

const tabs = require("sdk/tabs");
const tabsUtils = require("sdk/tabs/utils");

const getSdkTabFromChromeTab = (chromeTab) => {
  const tabId = tabsUtils.getTabId(chromeTab);
  for each (let sdkTab in tabs){
    if (sdkTab.id === tabId) {
      return sdkTab;
    }
  }
  return null;
};

There seems to be a problem that the listener fails when switching between windows when using system/events. Use Services.obs.addObserver instead:

const httpRequestObserver = {
    observe: function (subject, topic, data) {
        var channel = subject.QueryInterface(Ci.nsIHttpChannel);
        console.log("channel");
        var tab = getTabFromChannel(channel);
        if(tab) {
          console.log("request by tab", tab.id);
        }
    }
}

exports.main = function() {
  Cu.import('resource://gre/modules/Services.jsm');
  Services.obs.addObserver(httpRequestObserver, 'http-on-opening-request', false);
}

I can only hope that it works for all the requests you need to detect. The documentation already mentions some cases where it won't work:

Note that some HTTP requests aren't associated with a tab; for example, RSS feed updates, extension manager requests, XHR requests from XPCOM components, etc.

kapex
  • 28,903
  • 6
  • 107
  • 121
  • thank you, you have made my day ! ) but now i have another problem, if firefox window losses focus (left mouse click on some other window) event handler stopped to work, so i guess i need to reattach handler to firefox window ? – sss123next Aug 10 '14 at 14:04
  • thank you, i should sleep now, i will check when i wake – sss123next Aug 11 '14 at 06:20
  • thank you again, finally it's working as it required – sss123next Aug 11 '14 at 16:54
3

The article Listening to events on all tabs describes how to set up web progress listeners for tabs. With this listener you can get requests and redirects.

const tabsUtils = require("sdk/tabs/utils");
const listener = {
  QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]),
  onLocationChange: (browser, progress, request, uri) => {
    let tab = tabsUtils.getTabForContentWindow(progress.DOMWindow);
    // ...
  },
  onStateChange: (browser, progress, request, state) => {
    let tab = tabsUtils.getTabForContentWindow(progress.DOMWindow);
    // ...
  } 
  // ...
};
getChromeWindow(sdkWindow).getBrowser().addTabsProgressListener(listener);

At some point you may need to convert between low- and high-level tabs or chrome/dom/sdk windows which is implemented really bad and confusing. An sdk window in this case is one you get with windows.browserWindows, the chrome window has a reference to the gBrowser. If you are using the latest sdk maybe this helps: https://developer.mozilla.org/en-US/Add-ons/SDK/High-Level_API/tabs#Converting_to_XUL_tabs and https://developer.mozilla.org/en-US/Add-ons/SDK/High-Level_APIs/windows#Converting_to_DOM_windows. I used this function to get the chrome window from a sdk window: https://bugzilla.mozilla.org/show_bug.cgi?id=695143#c15

const { BrowserWindow } = require('sdk/windows');
const { windows } = require('sdk/window/utils');

function getChromeWindow(sdkWindow) {
  // to include private window use the as second argument
  // { includePrivate: true }
  for (let window of windows('navigator:browser'))
    if (BrowserWindow({window: window}) === sdkWindow)
      return window;

  return null;
}
kapex
  • 28,903
  • 6
  • 107
  • 121
  • thank you, i need to read a lot about all of this, but i got main idea and it's working. – sss123next Aug 09 '14 at 07:51
  • i have made some tests and found what this method does not detects server redirects in some cases, for example if we have opening url in tab which use server redirect to another url, and if this 'another url' just do another redirect it is not detected by this method – sss123next Aug 09 '14 at 08:45
  • i have also added handler for onRefreshAttempted, and it does not see this type of redirects. – sss123next Aug 10 '14 at 11:29
  • Ok, I didn't know that it doesn't detect all redirect. You probably need to stick with system/events then... I never used it so I don't think I can help with that. – kapex Aug 10 '14 at 11:40
  • this is sad, for now system/events does all the job except tab info, i need just id of tab for my code to work, as system/event provide Ci.nsIHttpChannel, i believe what it's entry point to obtain all other data i need, but i just can't find how exactly – sss123next Aug 10 '14 at 12:24
  • and you method detect redirects, but not all..., i have server for tests with few redirects in line, i mean one after another, your code detects first url which i enter in tab, but not see all intermediate urls – sss123next Aug 10 '14 at 12:27
  • Wait a minute, I think I just found something :) – kapex Aug 10 '14 at 12:44