0

Considering the following (simplified) example that works fine:

var tabs = window.gBrowser.tabs;
for (var i = 2, len = tabs.length; i < len; i++) {
  var uri = window.gBrowser.getBrowserAtIndex(i).currentURI.spec;
}

The following sometimes fails on the last tab with a message window.gBrowser.getBrowserAtIndex(...) is undefined

var tabs = window.gBrowser.tabs;
for (var i = 2, len = tabs.length; i < len; i++) {
  var uri = window.gBrowser.getBrowserAtIndex(i).currentURI.spec;
  window.gBrowser.removeTab(window.gBrowser.tabContainer.childNodes[i]);
}

I don't understand why it fails and why it only fails sometimes and not all the time.

Is there any other method that can be used instead of gBrowser.getBrowserAtIndex(i) to get the tabs' URL?

erosman
  • 7,094
  • 7
  • 27
  • 46

2 Answers2

2

The root issue is that tabs is not an array but a NodeList, that is a live collection. When you call removeTab() the change is reflected in the NodeList (length, indices).

To avoid these side effects convert it to a true array

var tabsArr = [].slice.call(gBrowser.tabs);
paa
  • 5,048
  • 1
  • 18
  • 22
1

It appears that you are closing tabs inside a loop where you have stored the initial number of tabs len = tabs.length and then comparing against the original number of tabs when the number of existing tabs may have changed due to your actions.

The construct:
for (let i = 2, len = tabs.length; i < len; i++) {...}
is more efficient under conditions where the length of tabs will not change. However, in this case, you are changing the number of tabs and need to compare against tabs.length each time you are checking the terminating condition for this loop. So:
for (let i = 2, len = tabs.length; i < len; i++) {...}
In fact, the only reason your current loop does not always fail is that window.gBrowser.removeTab() is returning prior to the tab actually being deleted. You are in a race to see if you complete the loop prior to any tabs actually being removed.

However, that is not the only problem. You are removing the tab on which you are currently indexed. Under most conditions if the tab is removed, like most arrays, the remaining higher-index tabs are shifted down to be at the index you are already acting upon. The only reason that the current loop does not routinely skip every other tab is that window.gBrowser.removeTab() returns prior to actually removing the tab you are currently indexed on. You only actually skip a tab on the occasions where you are seeing the undefined error.

Your loop really should be something like:
for (let i = tabs.length - 1; i >=2; i--) {...}
This way you start at the end of the list and delete from there down. This prevents both the undefined issue and the possibility of skipping a tab by eliminating the race condition of needing to complete the entire proces prior to any tab actually being removed.

Makyen
  • 31,849
  • 12
  • 86
  • 121
  • Code needs a small correction ... `for (let i = tabs.length -1; i >=2; i--) {...}` ... took me a few restarts to notice it ;) – erosman Oct 06 '14 at 13:00
  • Thanks, I fixed the error. I should not code when that tired w/o actual testing 8-). – Makyen Oct 06 '14 at 14:23