Question

I work on an addon that observes all the http requests and populate some info corresponding to each tab. I want to clear the data for the tab which I close. However, I can't get to identiy tab close event in a cleaner/saner way. So I have the contentWindow (LoadContext -> associatedWindow) for which the request was made, and the browser for that content window (which is for the tab).

  1. I realized that to do a browser.addEventListener, I will have to listen for DOMNodeRemoved event - which is deprecated as per MDN (and should rather use mutation observers). Moreover, I won't get the events when a popup window is closed.
  2. If I do a contentWindow.addEventListener and look for unload event, the results are quite unpredictable. Some times, I don't get the events at all.
  3. If I get this tab element from gBrowser.tabs and listen for TabClose event, I can get all close events for the tab. But then again, it won't inform of any popup-window closes (although I do get a tab element for the popup-window)

So what is the easiest/cleanest way to know any tab/popup-window close ? I don't use the addon SDK, btw.

EDIT : Looking for a simpler, valid-for-all-cases solution. ie. I get notified of close event be it a tab or a popup-window (or any other types of windows if they exist!). I believe that might be possible given that I hold a browser element which contains the DOM window, and the DOM get removed (unloaded) upon a close.

EDIT-2 : Realized that all the problem was due to one thing : there is no TabClose event from Firefox when the last tab closes (which is a window close). Life would have been much easier if we were notified of the tabs that get closed when a window closes. Any ways, I will go with a modified version of @Noitidart's solution - to have a separate window listener, in addition to the TabClose listener. And find the tab elements in it and do my clean up. Sigh! Will do a bit more search in this, and see if this qualifies for a bug/enhancement. Thanks to @Noitidart and @Blargh for their suggestions.

Was it helpful?

Solution 2

Here's my modified version of @Noitidart's answer. I use an expando property on browser (eg: _myextensionTabId ) to index my cached array.

var WindowListener = {
/* Same as @Noitidart's listener */
  onOpenWindow : function(xulWindow) {
    /* Get DOM window, call other window specific modules init */
    myExtension.initWindow(domWindow);
    myHttpObserver.initWindow(domWindow);
  },
  onCloseWindow: function(xulWindow) {
    /* Get DOM window, call other window specific modules uninit */
    myExtension.uninitWindow(domWindow);
    myHttpObserver.uninitWindow(domWindow);
  },
};

var myHttpObserver = {
    ..  
    initWindow: function(window) {
      window.gBrowser.tabContainer
              .addEventListener('TabClose', tabClosed, false);
    }, 

    uninitWindow: function(window) {
      dump ("Uninit window http observer\n");
      window.gBrowser.tabContainer
                .removeEventListener('TabClose', tabClosed);
      for (var browser of window.gBrowser.browsers) {
        if (browser._myextensionTabId) {
          // Do any cleanup.
        }
      }
    },
  .. 
    /* Other http observer stuff, methods that sets the expando property etc. */
};

function tabClosed(e) {
  var browser = Services.wm.getMostRecentWindow("navigator:browser")
                           .gBrowser.getBrowserForTab(e.target);
  dump("Tab closed. \n");
  if (browser._myextensionTabId) {
    dump("My extension tab id : " + browser._myextensionTabId);
    // Do any cleanup
  }
}

OTHER TIPS

I say hold a reference to the tab. This is how i do it in addons MouseControl and WorkspaceHopper.

so like check:

if (tab.parentNode) {

}

do a try catch too, if it catches then tab obviously doesnt exist.

but for tabClose you might as well use the tabclose event and for window popup, use the windowsMediator listner and listen to onWindowClose

var windowListener = {
    //DO NOT EDIT HERE
    onOpenWindow: function (aXULWindow) {
        // Wait for the window to finish loading
        let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
        aDOMWindow.addEventListener('load', function () {
            aDOMWindow.removeEventListener('load', arguments.callee, false);
            windowListener.loadIntoWindow(aDOMWindow);
        }, false);
    },
    onCloseWindow: function (aXULWindow) {},
    onWindowTitleChange: function (aXULWindow, aNewTitle) {},
    register: function () {

        // Load into any existing windows
        let DOMWindows = Services.wm.getEnumerator(null);
        while (DOMWindows.hasMoreElements()) {
            let aDOMWindow = DOMWindows.getNext();
            windowListener.loadIntoWindow(aDOMWindow);
        }
        // Listen to new windows
        Services.wm.addListener(windowListener);
    },
    unregister: function () {
        // Unload from any existing windows
        let DOMWindows = Services.wm.getEnumerator(null);
        while (DOMWindows.hasMoreElements()) {
            let aDOMWindow = DOMWindows.getNext();
            windowListener.unloadFromWindow(aDOMWindow);
        }

        //Stop listening so future added windows dont get this attached
        Services.wm.removeListener(windowListener);
    },
    //END - DO NOT EDIT HERE
    loadIntoWindow: function (aDOMWindow) {
        if (!aDOMWindow) {
            return;
        }

        //do stuff here

    },
    unloadFromWindow: function (aDOMWindow) {
        if (!aDOMWindow) {
            return;
        }
        //do stuff here
    }
};

For a one solution fits tab and window you can try window observers: https://developer.mozilla.org/en-US/docs/Observer_Notifications#Windows

Probably want to use the outer-window-destroyed observer, I think each browser element within tab is an outer window. I'm not sure. It might trigger everytime the document changes though, so you would have to QI and get the tab and check if its closed/closing or still open.

Try it out, share with us what you learn, I'm very interested. On close i think it gets destroyed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top