mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 00:32:11 +00:00
Bug 1555060 Stop using dom structure for <tabs>/<tab> relationships
A bunch of existing code assumes that <tab> elements are the immediate and only children of a <tabs> element, and uses dom apis to traverse relationships between these elements. To simplify conversion of <tabs> to a custom element (and hopefully improve readability a bit at the same time!), introduce new apis: On <tab> this.parentNode -> this.container this.nextElementSibling -> this.container.findNextTab(...) this.previousElementSibiling -> this.container.findNextTab(...) On <tabs> this.children -> this.allTabs Differential Revision: https://phabricator.services.mozilla.com/D34648 --HG-- extra : source : f4e21e465f384b90fa1e768141c4db708748bf66 extra : histedit_source : 95d8a4242e8e04df9e29c2b647558d37e910b845
This commit is contained in:
parent
c716c93bbf
commit
f1d137eff5
@ -46,7 +46,7 @@
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
|
||||
|
||||
var tabs = Array.from(tabBrowser().tabContainer.children);
|
||||
var tabs = Array.from(tabBrowser().tabContainer.allTabs);
|
||||
// For preloaded tabs, there might be items in this array where this relation
|
||||
// doesn't hold, so just deal with that:
|
||||
var panels = tabs.map(t => t.linkedBrowser.closest("tabpanels > *"));
|
||||
|
@ -105,6 +105,10 @@ class MozTabbrowserTab extends MozElements.MozTab {
|
||||
}
|
||||
}
|
||||
|
||||
get container() {
|
||||
return gBrowser.tabContainer;
|
||||
}
|
||||
|
||||
set _visuallySelected(val) {
|
||||
if (val == (this.getAttribute("visuallyselected") == "true")) {
|
||||
return val;
|
||||
@ -271,7 +275,7 @@ class MozTabbrowserTab extends MozElements.MozTab {
|
||||
|
||||
on_mousedown(event) {
|
||||
let eventMaySelectTab = true;
|
||||
let tabContainer = this.parentNode;
|
||||
let tabContainer = this.container;
|
||||
|
||||
if (tabContainer._closeTabByDblclick &&
|
||||
event.button == 0 &&
|
||||
@ -392,7 +396,7 @@ class MozTabbrowserTab extends MozElements.MozTab {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
let tabContainer = this.parentNode;
|
||||
let tabContainer = this.container;
|
||||
if (tabContainer._closeTabByDblclick &&
|
||||
this._selectedOnFirstMouseDown &&
|
||||
this.selected &&
|
||||
@ -416,7 +420,7 @@ class MozTabbrowserTab extends MozElements.MozTab {
|
||||
return;
|
||||
}
|
||||
|
||||
let tabContainer = this.parentNode;
|
||||
let tabContainer = this.container;
|
||||
let visibleTabs = tabContainer._getVisibleTabs();
|
||||
let tabIndex = visibleTabs.indexOf(this);
|
||||
|
||||
@ -464,7 +468,7 @@ class MozTabbrowserTab extends MozElements.MozTab {
|
||||
}
|
||||
|
||||
_mouseleave() {
|
||||
let tabContainer = this.parentNode;
|
||||
let tabContainer = this.container;
|
||||
if (tabContainer._beforeHoveredTab) {
|
||||
tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
|
||||
tabContainer._beforeHoveredTab = null;
|
||||
|
@ -205,8 +205,7 @@ window._gBrowser = {
|
||||
},
|
||||
|
||||
get tabs() {
|
||||
delete this.tabs;
|
||||
return this.tabs = this.tabContainer.children;
|
||||
return this.tabContainer.allTabs;
|
||||
},
|
||||
|
||||
get tabbox() {
|
||||
@ -634,12 +633,12 @@ window._gBrowser = {
|
||||
|
||||
syncThrobberAnimations(aTab) {
|
||||
aTab.ownerGlobal.promiseDocumentFlushed(() => {
|
||||
if (!aTab.parentNode) {
|
||||
if (!aTab.container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const animations =
|
||||
Array.from(aTab.parentNode.getElementsByTagName("tab"))
|
||||
Array.from(aTab.container.getElementsByTagName("tab"))
|
||||
.map(tab => {
|
||||
const throbber = tab.throbber;
|
||||
return throbber ? throbber.getAnimations({ subtree: true }) : [];
|
||||
@ -2416,9 +2415,7 @@ window._gBrowser = {
|
||||
index = Math.min(index, this.tabs.length);
|
||||
}
|
||||
|
||||
// Use .item() instead of [] because we need .item() to return null in
|
||||
// order to append the tab at the end in case index == tabs.length.
|
||||
let tabAfter = this.tabs.item(index);
|
||||
let tabAfter = this.tabs[index] || null;
|
||||
this.tabContainer.insertBefore(t, tabAfter);
|
||||
if (tabAfter) {
|
||||
this._updateTabsAfterInsert();
|
||||
@ -2842,7 +2839,7 @@ window._gBrowser = {
|
||||
aTab.removeAttribute("bursting");
|
||||
|
||||
setTimeout(function(tab, tabbrowser) {
|
||||
if (tab.parentNode &&
|
||||
if (tab.container &&
|
||||
window.getComputedStyle(tab).maxWidth == "0.1px") {
|
||||
console.assert(false, "Giving up waiting for the tab closing animation to finish (bug 608589)");
|
||||
tabbrowser._endRemoveTab(tab);
|
||||
@ -3180,17 +3177,16 @@ window._gBrowser = {
|
||||
}
|
||||
|
||||
// Try to find a remaining tab that comes after the given tab
|
||||
let tab = aTab;
|
||||
do {
|
||||
tab = tab.nextElementSibling;
|
||||
} while (tab && !remainingTabs.includes(tab));
|
||||
let tab = this.tabContainer.findNextTab(aTab, {
|
||||
direction: 1,
|
||||
filter: _tab => remainingTabs.includes(_tab),
|
||||
});
|
||||
|
||||
if (!tab) {
|
||||
tab = aTab;
|
||||
|
||||
do {
|
||||
tab = tab.previousElementSibling;
|
||||
} while (tab && !remainingTabs.includes(tab));
|
||||
tab = this.tabContainer.findNextTab(aTab, {
|
||||
direction: -1,
|
||||
filter: _tab => remainingTabs.includes(_tab),
|
||||
});
|
||||
}
|
||||
|
||||
return tab;
|
||||
@ -3730,9 +3726,7 @@ window._gBrowser = {
|
||||
// invalidate cache
|
||||
this._visibleTabs = null;
|
||||
|
||||
// use .item() instead of [] because dragging to the end of the strip goes out of
|
||||
// bounds: .item() returns null (so it acts like appendChild), but [] throws
|
||||
this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
|
||||
this.tabContainer.insertBefore(aTab, this.tabs[aIndex] || null);
|
||||
this._updateTabsAfterInsert();
|
||||
|
||||
if (wasFocused)
|
||||
@ -3751,9 +3745,10 @@ window._gBrowser = {
|
||||
},
|
||||
|
||||
moveTabForward() {
|
||||
let nextTab = this.selectedTab.nextElementSibling;
|
||||
while (nextTab && nextTab.hidden)
|
||||
nextTab = nextTab.nextElementSibling;
|
||||
let nextTab = this.tabContainer.findNextTab(this.selectedTab, {
|
||||
direction: 1,
|
||||
filter: tab => !tab.hidden,
|
||||
});
|
||||
|
||||
if (nextTab)
|
||||
this.moveTabTo(this.selectedTab, nextTab._tPos);
|
||||
@ -3797,7 +3792,7 @@ window._gBrowser = {
|
||||
let newTab = this.addWebTab("about:blank", params);
|
||||
let newBrowser = this.getBrowserForTab(newTab);
|
||||
|
||||
aTab.parentNode._finishAnimateTabMove();
|
||||
aTab.container._finishAnimateTabMove();
|
||||
|
||||
if (!createLazyBrowser) {
|
||||
// Stop the about:blank load.
|
||||
@ -3820,9 +3815,10 @@ window._gBrowser = {
|
||||
},
|
||||
|
||||
moveTabBackward() {
|
||||
let previousTab = this.selectedTab.previousElementSibling;
|
||||
while (previousTab && previousTab.hidden)
|
||||
previousTab = previousTab.previousElementSibling;
|
||||
let previousTab = this.tabContainer.findNextTab(this.selectedTab, {
|
||||
direction: -1,
|
||||
filter: tab => !tab.hidden,
|
||||
});
|
||||
|
||||
if (previousTab)
|
||||
this.moveTabTo(this.selectedTab, previousTab._tPos);
|
||||
@ -5452,8 +5448,12 @@ var TabContextMenu = {
|
||||
let lastVisibleTab = visibleTabs[visibleTabs.length - 1];
|
||||
let tabsToMove = contextTabIsSelected ? selectedTabs : [this.contextTab];
|
||||
let lastTabToMove = tabsToMove[tabsToMove.length - 1];
|
||||
let isLastPinnedTab = lastTabToMove.pinned &&
|
||||
(!lastTabToMove.nextElementSibling || !lastTabToMove.nextElementSibling.pinned);
|
||||
|
||||
let isLastPinnedTab = false;
|
||||
if (lastTabToMove.pinned) {
|
||||
let sibling = gBrowser.tabContainer.findNextTab(lastTabToMove);
|
||||
isLastPinnedTab = !sibling || !sibling.pinned;
|
||||
}
|
||||
contextMoveTabToEnd.disabled = (lastTabToMove == lastVisibleTab || isLastPinnedTab) &&
|
||||
allSelectedTabsAdjacent;
|
||||
let contextMoveTabToStart = document.getElementById("context_moveToStart");
|
||||
|
@ -2,7 +2,7 @@ add_task(async function() {
|
||||
var win = openDialog(AppConstants.BROWSER_CHROME_URL, "_blank", "chrome,all,dialog=no");
|
||||
await SimpleTest.promiseFocus(win);
|
||||
|
||||
let tab = win.gBrowser.tabContainer.firstElementChild;
|
||||
let tab = win.gBrowser.tabs[0];
|
||||
await promiseTabLoadEvent(tab, getRootDirectory(gTestPath) + "test_bug462673.html");
|
||||
|
||||
is(win.gBrowser.browsers.length, 2, "test_bug462673.html has opened a second tab");
|
||||
@ -17,7 +17,7 @@ add_task(async function() {
|
||||
var win = openDialog(AppConstants.BROWSER_CHROME_URL, "_blank", "chrome,all,dialog=no");
|
||||
await SimpleTest.promiseFocus(win);
|
||||
|
||||
let tab = win.gBrowser.tabContainer.firstElementChild;
|
||||
let tab = win.gBrowser.tabs[0];
|
||||
await promiseTabLoadEvent(tab, getRootDirectory(gTestPath) + "test_bug462673.html");
|
||||
|
||||
var newTab = BrowserTestUtils.addTab(win.gBrowser);
|
||||
@ -25,7 +25,7 @@ add_task(async function() {
|
||||
win.gBrowser.removeTab(tab);
|
||||
ok(!win.closed, "Window stays open");
|
||||
if (!win.closed) {
|
||||
is(win.gBrowser.tabContainer.childElementCount, 1, "Window has one tab");
|
||||
is(win.gBrowser.tabs.length, 1, "Window has one tab");
|
||||
is(win.gBrowser.browsers.length, 1, "Window has one browser");
|
||||
is(win.gBrowser.selectedTab, newTab, "Remaining tab is selected");
|
||||
is(win.gBrowser.selectedBrowser, newBrowser, "Browser for remaining tab is selected");
|
||||
|
@ -91,12 +91,12 @@ add_task(async function() {
|
||||
ok(selectedTab.selected,
|
||||
"Ctrl+Tab*2 -> Ctrl+W -> Ctrl+Shift+Tab*2 keeps the selected tab");
|
||||
}
|
||||
gBrowser.removeTab(gBrowser.tabContainer.lastElementChild);
|
||||
gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
checkTabs(2);
|
||||
|
||||
await ctrlTabTest([1], 1, 0);
|
||||
|
||||
gBrowser.removeTab(gBrowser.tabContainer.lastElementChild);
|
||||
gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
checkTabs(1);
|
||||
|
||||
{ // test for bug 445768
|
||||
|
@ -3,21 +3,20 @@ function test() {
|
||||
BrowserTestUtils.addTab(gBrowser);
|
||||
BrowserTestUtils.addTab(gBrowser);
|
||||
|
||||
var tabs = gBrowser.tabs;
|
||||
var owner;
|
||||
|
||||
is(tabs.length, 4, "4 tabs are open");
|
||||
is(gBrowser.tabs.length, 4, "4 tabs are open");
|
||||
|
||||
owner = gBrowser.selectedTab = tabs[2];
|
||||
owner = gBrowser.selectedTab = gBrowser.tabs[2];
|
||||
BrowserOpenTab();
|
||||
is(gBrowser.selectedTab, tabs[4], "newly opened tab is selected");
|
||||
is(gBrowser.selectedTab, gBrowser.tabs[4], "newly opened tab is selected");
|
||||
gBrowser.removeCurrentTab();
|
||||
is(gBrowser.selectedTab, owner, "owner is selected");
|
||||
|
||||
owner = gBrowser.selectedTab;
|
||||
BrowserOpenTab();
|
||||
gBrowser.selectedTab = tabs[1];
|
||||
gBrowser.selectedTab = tabs[4];
|
||||
gBrowser.selectedTab = gBrowser.tabs[1];
|
||||
gBrowser.selectedTab = gBrowser.tabs[4];
|
||||
gBrowser.removeCurrentTab();
|
||||
isnot(gBrowser.selectedTab, owner, "selecting a different tab clears the owner relation");
|
||||
|
||||
@ -25,8 +24,8 @@ function test() {
|
||||
BrowserOpenTab();
|
||||
gBrowser.moveTabTo(gBrowser.selectedTab, 0);
|
||||
gBrowser.removeCurrentTab();
|
||||
is(gBrowser.selectedTab, owner, "owner relatitionship persists when tab is moved");
|
||||
is(gBrowser.selectedTab, owner, "owner relationship persists when tab is moved");
|
||||
|
||||
while (tabs.length > 1)
|
||||
while (gBrowser.tabs.length > 1)
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ add_task(async function() {
|
||||
|
||||
// Now switch to the first tab. We shouldn't flush layout at all.
|
||||
await withPerfObserver(async function() {
|
||||
let firstTab = gBrowser.tabContainer.firstElementChild;
|
||||
let firstTab = gBrowser.tabs[0];
|
||||
await BrowserTestUtils.switchTab(gBrowser, firstTab);
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return gBrowser.tabContainer.arrowScrollbox.hasAttribute("scrolledtostart");
|
||||
@ -132,7 +132,7 @@ add_task(async function() {
|
||||
// removals to put the tab strip out of the overflow state, so we'll just
|
||||
// keep testing removals until that occurs.
|
||||
while (gBrowser.tabContainer.hasAttribute("overflow")) {
|
||||
lastTab = gBrowser.tabContainer.lastElementChild;
|
||||
lastTab = gBrowser.tabs[gBrowser.tabs.length - 1];
|
||||
if (gBrowser.selectedTab !== lastTab) {
|
||||
await BrowserTestUtils.switchTab(gBrowser, lastTab);
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ async function test_playing_icon_on_hidden_tab(tab) {
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE, true, true),
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE, true, true),
|
||||
];
|
||||
let tabContainer = tab.parentNode;
|
||||
let tabContainer = tab.container;
|
||||
let alltabsButton = document.getElementById("alltabs-button");
|
||||
let alltabsBadge = alltabsButton.badgeLabel;
|
||||
|
||||
|
@ -59,9 +59,11 @@ add_task(async function test() {
|
||||
EventUtils.synthesizeMouseAtCenter(newTabButton, metaKeyEvent);
|
||||
openEvent = await promiseTabOpened;
|
||||
newTab = openEvent.target;
|
||||
is(newTab.previousElementSibling, tab3,
|
||||
"New tab should be opened after tab3 when tab1 and tab3 are selected");
|
||||
is(newTab.nextElementSibling, null,
|
||||
let previous = gBrowser.tabContainer.findNextTab(newTab, {direction: -1});
|
||||
is(previous, tab3,
|
||||
"New tab should be opened after tab3 when tab1 and tab3 are selected");
|
||||
let next = gBrowser.tabContainer.findNextTab(newTab, {direction: 1});
|
||||
is(next, null,
|
||||
"New tab should be opened at the end of the tabstrip when tab1 and tab3 are selected");
|
||||
BrowserTestUtils.removeTab(newTab);
|
||||
|
||||
@ -74,9 +76,11 @@ add_task(async function test() {
|
||||
EventUtils.synthesizeMouseAtCenter(newTabButton, {});
|
||||
openEvent = await promiseTabOpened;
|
||||
newTab = openEvent.target;
|
||||
is(newTab.previousElementSibling, tab3,
|
||||
previous = gBrowser.tabContainer.findNextTab(newTab, {direction: -1});
|
||||
is(previous, tab3,
|
||||
"New tab should be opened after tab3 when ctrlKey is not used without multiselection");
|
||||
is(newTab.nextElementSibling, null,
|
||||
next = gBrowser.tabContainer.findNextTab(newTab, {direction: 1});
|
||||
is(next, null,
|
||||
"New tab should be opened at the end of the tabstrip when ctrlKey is not used without multiselection");
|
||||
BrowserTestUtils.removeTab(newTab);
|
||||
|
||||
@ -90,9 +94,11 @@ add_task(async function test() {
|
||||
EventUtils.synthesizeMouseAtCenter(newTabButton, {});
|
||||
openEvent = await promiseTabOpened;
|
||||
newTab = openEvent.target;
|
||||
is(newTab.previousElementSibling, tab3,
|
||||
previous = gBrowser.tabContainer.findNextTab(newTab, {direction: -1});
|
||||
is(previous, tab3,
|
||||
"New tab should be opened after tab3 when ctrlKey is not used with multiselection");
|
||||
is(newTab.nextElementSibling, null,
|
||||
next = gBrowser.tabContainer.findNextTab(newTab, {direction: 1});
|
||||
is(next, null,
|
||||
"New tab should be opened at the end of the tabstrip when ctrlKey is not used with multiselection");
|
||||
BrowserTestUtils.removeTab(newTab);
|
||||
|
||||
|
@ -10,7 +10,6 @@ add_task(async function() {
|
||||
let arrowScrollbox = gBrowser.tabContainer.arrowScrollbox;
|
||||
let scrollbox = arrowScrollbox.scrollbox;
|
||||
let originalSmoothScroll = arrowScrollbox.smoothScroll;
|
||||
let tabs = gBrowser.tabs;
|
||||
let tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth);
|
||||
|
||||
let rect = ele => ele.getBoundingClientRect();
|
||||
@ -25,7 +24,7 @@ add_task(async function() {
|
||||
let elementFromPoint = x => arrowScrollbox._elementFromPoint(x);
|
||||
let nextLeftElement = () => elementFromPoint(left(scrollbox) - 1);
|
||||
let nextRightElement = () => elementFromPoint(right(scrollbox) + 1);
|
||||
let firstScrollable = () => tabs[gBrowser._numPinnedTabs];
|
||||
let firstScrollable = () => gBrowser.tabs[gBrowser._numPinnedTabs];
|
||||
let waitForNextFrame = async function() {
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
|
||||
@ -36,11 +35,11 @@ add_task(async function() {
|
||||
arrowScrollbox.smoothScroll = originalSmoothScroll;
|
||||
});
|
||||
|
||||
while (tabs.length < tabCountForOverflow) {
|
||||
while (gBrowser.tabs.length < tabCountForOverflow) {
|
||||
BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true });
|
||||
}
|
||||
|
||||
gBrowser.pinTab(tabs[0]);
|
||||
gBrowser.pinTab(gBrowser.tabs[0]);
|
||||
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
return Array.from(gBrowser.tabs).every(tab => tab._fullyOpen);
|
||||
@ -62,7 +61,7 @@ add_task(async function() {
|
||||
await waitForNextFrame();
|
||||
isRight(element, "Scrolled one tab to the right with a single click");
|
||||
|
||||
gBrowser.selectedTab = tabs[tabs.length - 1];
|
||||
gBrowser.selectedTab = gBrowser.tabs[gBrowser.tabs.length - 1];
|
||||
await waitForNextFrame();
|
||||
ok(right(gBrowser.selectedTab) <= right(scrollbox), "Selecting the last tab scrolls it into view " +
|
||||
"(" + right(gBrowser.selectedTab) + " <= " + right(scrollbox) + ")");
|
||||
@ -88,7 +87,7 @@ add_task(async function() {
|
||||
ok(left(scrollbox) <= firstScrollableLeft, "Scrolled to the start with a triple click " +
|
||||
"(" + left(scrollbox) + " <= " + firstScrollableLeft + ")");
|
||||
|
||||
while (tabs.length > 1) {
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
BrowserTestUtils.removeTab(gBrowser.tabs[0]);
|
||||
}
|
||||
});
|
||||
|
@ -9,7 +9,6 @@ add_task(async function() {
|
||||
let initialTabsLength = gBrowser.tabs.length;
|
||||
|
||||
let arrowScrollbox = gBrowser.tabContainer.arrowScrollbox;
|
||||
let tabs = gBrowser.tabs;
|
||||
let tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth);
|
||||
|
||||
let width = ele => ele.getBoundingClientRect().width;
|
||||
@ -20,24 +19,26 @@ add_task(async function() {
|
||||
let newTab2 = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:about", {skipAnimation: true});
|
||||
let newTab3 = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:config", {skipAnimation: true});
|
||||
|
||||
while (tabs.length < tabCountForOverflow) {
|
||||
while (gBrowser.tabs.length < tabCountForOverflow) {
|
||||
BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true });
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
while (tabs.length > initialTabsLength) {
|
||||
gBrowser.removeTab(gBrowser.tabs[initialTabsLength]);
|
||||
while (gBrowser.tabs.length > initialTabsLength) {
|
||||
gBrowser.removeTab(gBrowser.tabContainer.getItemAtIndex(initialTabsLength));
|
||||
}
|
||||
});
|
||||
|
||||
is(gBrowser.tabs.length, tabCountForOverflow, "new tabs are opened");
|
||||
is(gBrowser.tabs[initialTabsLength], newTab1, "newTab1 position is correct");
|
||||
is(gBrowser.tabs[initialTabsLength + 1], newTab2, "newTab2 position is correct");
|
||||
is(gBrowser.tabs[initialTabsLength + 2], newTab3, "newTab3 position is correct");
|
||||
let tabs = gBrowser.tabs;
|
||||
is(tabs.length, tabCountForOverflow, "new tabs are opened");
|
||||
is(tabs[initialTabsLength], newTab1, "newTab1 position is correct");
|
||||
is(tabs[initialTabsLength + 1], newTab2, "newTab2 position is correct");
|
||||
is(tabs[initialTabsLength + 2], newTab3, "newTab3 position is correct");
|
||||
|
||||
await dragAndDrop(newTab1, newTab2, false);
|
||||
is(gBrowser.tabs.length, tabCountForOverflow, "tabs are still there");
|
||||
is(gBrowser.tabs[initialTabsLength], newTab2, "newTab2 and newTab1 are swapped");
|
||||
is(gBrowser.tabs[initialTabsLength + 1], newTab1, "newTab1 and newTab2 are swapped");
|
||||
is(gBrowser.tabs[initialTabsLength + 2], newTab3, "newTab3 stays same place");
|
||||
tabs = gBrowser.tabs;
|
||||
is(tabs.length, tabCountForOverflow, "tabs are still there");
|
||||
is(tabs[initialTabsLength], newTab2, "newTab2 and newTab1 are swapped");
|
||||
is(tabs[initialTabsLength + 1], newTab1, "newTab1 and newTab2 are swapped");
|
||||
is(tabs[initialTabsLength + 2], newTab3, "newTab3 stays same place");
|
||||
});
|
||||
|
@ -52,8 +52,8 @@ add_task(async function test() {
|
||||
is(browser2.contentPrincipal.userContextId, 2, "Tab2 UCI must be 2");
|
||||
|
||||
let found = false;
|
||||
for (let i = 0; i < gBrowser.tabContainer.children.length; ++i) {
|
||||
let tab = gBrowser.tabContainer.children[i];
|
||||
for (let i = 0; i < gBrowser.tabs.length; ++i) {
|
||||
let tab = gBrowser.tabs[i];
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
if (browser.contentTitle == "?new") {
|
||||
is(browser.contentPrincipal.userContextId, 1, "Tab3 UCI must be 1");
|
||||
|
@ -90,7 +90,7 @@ add_task(async function test_sidebarpanels_click() {
|
||||
|
||||
// Remove tabs created by sub-tests.
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeTab(gBrowser.tabContainer.lastElementChild);
|
||||
gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -150,7 +150,7 @@ function ensure_opentabs_match_db() {
|
||||
if (browserWin.closed)
|
||||
continue;
|
||||
|
||||
for (let i = 0; i < browserWin.gBrowser.tabContainer.childElementCount; i++) {
|
||||
for (let i = 0; i < browserWin.gBrowser.tabs.length; i++) {
|
||||
let browser = browserWin.gBrowser.getBrowserAtIndex(i);
|
||||
let url = browser.currentURI.spec;
|
||||
if (browserWin.isBlankPageURL(url))
|
||||
|
@ -58,11 +58,11 @@ function test() {
|
||||
checkSelectedTab();
|
||||
|
||||
// Remove #3 (non active)
|
||||
gBrowser.removeTab(gBrowser.tabContainer.lastElementChild);
|
||||
gBrowser.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
checkPreviews(2, "Expected number of previews after closing unselected via browser");
|
||||
|
||||
// Remove #1 (active)
|
||||
gBrowser.removeTab(gBrowser.tabContainer.firstElementChild);
|
||||
gBrowser.removeTab(gBrowser.tabs[0]);
|
||||
checkPreviews(1, "Expected number of previews after closing selected tab via browser");
|
||||
|
||||
// Add a new tab
|
||||
|
@ -186,7 +186,7 @@ PushRecord.prototype = {
|
||||
continue;
|
||||
}
|
||||
// `gBrowser` on Desktop; `BrowserApp` on Fennec.
|
||||
let tabs = window.gBrowser ? window.gBrowser.tabContainer.children :
|
||||
let tabs = window.gBrowser ? window.gBrowser.tabs :
|
||||
window.BrowserApp.tabs;
|
||||
for (let tab of tabs) {
|
||||
// `linkedBrowser` on Desktop; `browser` on Fennec.
|
||||
|
@ -564,7 +564,7 @@ Tester.prototype = {
|
||||
// Remove stale tabs
|
||||
if (this.currentTest && window.gBrowser && gBrowser.tabs.length > 1) {
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
let lastTab = gBrowser.tabContainer.lastElementChild;
|
||||
let lastTab = gBrowser.tabs[gBrowser.tabs.length - 1];
|
||||
if (!lastTab.closing) {
|
||||
// Report the stale tab as an error only when they're not closing.
|
||||
// Tests can finish without waiting for the closing tabs.
|
||||
|
@ -487,8 +487,8 @@ _ContextualIdentityService.prototype = {
|
||||
}
|
||||
|
||||
let tabbrowser = win.gBrowser;
|
||||
for (let i = tabbrowser.tabContainer.children.length - 1; i >= 0; --i) {
|
||||
let tab = tabbrowser.tabContainer.children[i];
|
||||
for (let i = tabbrowser.tabs.length - 1; i >= 0; --i) {
|
||||
let tab = tabbrowser.tabs[i];
|
||||
if (tab.hasAttribute("usercontextid") &&
|
||||
(!userContextId ||
|
||||
parseInt(tab.getAttribute("usercontextid"), 10) == userContextId)) {
|
||||
|
@ -82,34 +82,34 @@ function test_tabbox()
|
||||
var tabs = document.getElementById("tabs");
|
||||
var tabpanels = document.getElementById("tabpanels");
|
||||
|
||||
test_tabbox_State(tabbox, "tabbox initial", 0, tabs.firstChild, tabpanels.firstChild);
|
||||
test_tabbox_State(tabbox, "tabbox initial", 0, tabs.allTabs[0], tabpanels.firstChild);
|
||||
|
||||
// check the selectedIndex property
|
||||
tabbox.selectedIndex = 1;
|
||||
test_tabbox_State(tabbox, "tabbox selectedIndex 1", 1, tabs.lastChild, tabpanels.lastChild);
|
||||
test_tabbox_State(tabbox, "tabbox selectedIndex 1", 1, tabs.allTabs[tabs.allTabs.length - 1], tabpanels.lastChild);
|
||||
|
||||
tabbox.selectedIndex = 2;
|
||||
test_tabbox_State(tabbox, "tabbox selectedIndex 2", 1, tabs.lastChild, tabpanels.lastChild);
|
||||
test_tabbox_State(tabbox, "tabbox selectedIndex 2", 1, tabs.allTabs[tabs.allTabs.length - 1], tabpanels.lastChild);
|
||||
|
||||
// tabbox must have a selection, so setting to -1 should do nothing
|
||||
tabbox.selectedIndex = -1;
|
||||
test_tabbox_State(tabbox, "tabbox selectedIndex -1", 1, tabs.lastChild, tabpanels.lastChild);
|
||||
test_tabbox_State(tabbox, "tabbox selectedIndex -1", 1, tabs.allTabs[tabs.allTabs.length - 1], tabpanels.lastChild);
|
||||
|
||||
// check the selectedTab property
|
||||
tabbox.selectedTab = tabs.firstChild;
|
||||
test_tabbox_State(tabbox, "tabbox selected", 0, tabs.firstChild, tabpanels.firstChild);
|
||||
tabbox.selectedTab = tabs.allTabs[0];
|
||||
test_tabbox_State(tabbox, "tabbox selected", 0, tabs.allTabs[0], tabpanels.firstChild);
|
||||
|
||||
// setting selectedTab to null should not do anything
|
||||
tabbox.selectedTab = null;
|
||||
test_tabbox_State(tabbox, "tabbox selectedTab null", 0, tabs.firstChild, tabpanels.firstChild);
|
||||
test_tabbox_State(tabbox, "tabbox selectedTab null", 0, tabs.allTabs[0], tabpanels.firstChild);
|
||||
|
||||
// check the selectedPanel property
|
||||
tabbox.selectedPanel = tabpanels.lastChild;
|
||||
test_tabbox_State(tabbox, "tabbox selectedPanel", 0, tabs.firstChild, tabpanels.lastChild);
|
||||
test_tabbox_State(tabbox, "tabbox selectedPanel", 0, tabs.allTabs[0], tabpanels.lastChild);
|
||||
|
||||
// setting selectedPanel to null should not do anything
|
||||
tabbox.selectedPanel = null;
|
||||
test_tabbox_State(tabbox, "tabbox selectedPanel null", 0, tabs.firstChild, tabpanels.lastChild);
|
||||
test_tabbox_State(tabbox, "tabbox selectedPanel null", 0, tabs.allTabs[0], tabpanels.lastChild);
|
||||
|
||||
tabbox.selectedIndex = 0;
|
||||
test_tabpanels(tabpanels, tabbox);
|
||||
|
@ -233,7 +233,7 @@ class MozTabpanels extends MozXULElement {
|
||||
if (tabpanelIdx == -1)
|
||||
return null;
|
||||
|
||||
let tabElms = tabsElm.children;
|
||||
let tabElms = tabsElm.allTabs;
|
||||
let tabElmFromIndex = tabElms[tabpanelIdx];
|
||||
|
||||
let tabpanelId = aTabPanelElm.id;
|
||||
@ -324,7 +324,7 @@ MozElements.MozTab = class MozTab extends MozElements.BaseText {
|
||||
|
||||
// Call this before setting the 'ignorefocus' attribute because this
|
||||
// will pass on focus if the formerly selected tab was focused as well.
|
||||
this.parentNode._selectNewTab(this);
|
||||
this.closest("tabs")._selectNewTab(this);
|
||||
|
||||
var isTabFocused = false;
|
||||
try {
|
||||
@ -352,7 +352,7 @@ MozElements.MozTab = class MozTab extends MozElements.BaseText {
|
||||
switch (event.keyCode) {
|
||||
case KeyEvent.DOM_VK_LEFT: {
|
||||
let direction = window.getComputedStyle(this.parentNode).direction;
|
||||
this.parentNode.advanceSelectedTab(direction == "ltr" ? -1 : 1,
|
||||
this.container.advanceSelectedTab(direction == "ltr" ? -1 : 1,
|
||||
this.arrowKeysShouldWrap);
|
||||
event.preventDefault();
|
||||
break;
|
||||
@ -360,30 +360,30 @@ MozElements.MozTab = class MozTab extends MozElements.BaseText {
|
||||
|
||||
case KeyEvent.DOM_VK_RIGHT: {
|
||||
let direction = window.getComputedStyle(this.parentNode).direction;
|
||||
this.parentNode.advanceSelectedTab(direction == "ltr" ? 1 : -1,
|
||||
this.container.advanceSelectedTab(direction == "ltr" ? 1 : -1,
|
||||
this.arrowKeysShouldWrap);
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
case KeyEvent.DOM_VK_UP:
|
||||
this.parentNode.advanceSelectedTab(-1, this.arrowKeysShouldWrap);
|
||||
this.container.advanceSelectedTab(-1, this.arrowKeysShouldWrap);
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case KeyEvent.DOM_VK_DOWN:
|
||||
this.parentNode.advanceSelectedTab(1, this.arrowKeysShouldWrap);
|
||||
this.container.advanceSelectedTab(1, this.arrowKeysShouldWrap);
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case KeyEvent.DOM_VK_HOME:
|
||||
this.parentNode._selectNewTab(this.parentNode.children[0]);
|
||||
this.container._selectNewTab(this.container.allTabs[0]);
|
||||
event.preventDefault();
|
||||
break;
|
||||
|
||||
case KeyEvent.DOM_VK_END: {
|
||||
let tabs = this.parentNode.children;
|
||||
this.parentNode._selectNewTab(tabs[tabs.length - 1], -1);
|
||||
let {allTabs} = this.container;
|
||||
this.container._selectNewTab(allTabs[allTabs.length - 1], -1);
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
|
@ -46,6 +46,12 @@
|
||||
]]>
|
||||
</constructor>
|
||||
|
||||
<property name="allTabs">
|
||||
<getter>
|
||||
return this.children;
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<!-- nsIDOMXULRelatedElement -->
|
||||
<method name="getRelatedElement">
|
||||
<parameter name="aTabElm"/>
|
||||
@ -269,6 +275,69 @@
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="findNextTab">
|
||||
<parameter name="startTab"/>
|
||||
<parameter name="opts"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
/**
|
||||
* Find an adjacent tab.
|
||||
*
|
||||
* startTab A <tab> element to start searching from.
|
||||
* opts.direction 1 to search forward, -1 to search backward.
|
||||
* opts.wrap If true, wrap around if the search reaches
|
||||
* the end (or beginning) of the tab strip.
|
||||
* opts.startWithAdjacent If true (which is the default), start
|
||||
* searching from the next tab after
|
||||
* (or before) startTab. If false,
|
||||
* startTab may be returned if it passes
|
||||
* the filter.
|
||||
* opts.filter A function to select which tabs to return.
|
||||
*
|
||||
* returns the next <tab> element or, if none exists, null.
|
||||
*/
|
||||
let {
|
||||
direction = 1,
|
||||
wrap = false,
|
||||
startWithAdjacent = true,
|
||||
filter = tab => true,
|
||||
} = opts || {};
|
||||
|
||||
let tab = startTab;
|
||||
if (!startWithAdjacent && filter(tab)) {
|
||||
return tab;
|
||||
}
|
||||
|
||||
let tabs = this.allTabs;
|
||||
let i = tabs.indexOf(tab);
|
||||
if (i < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
i += direction;
|
||||
if (wrap) {
|
||||
if (i < 0) {
|
||||
i = tabs.length - 1;
|
||||
} else if (i >= tabs.length) {
|
||||
i = 0;
|
||||
}
|
||||
} else if (i < 0 || i >= tabs.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
tab = tabs[i];
|
||||
if (tab == startTab) {
|
||||
return null;
|
||||
}
|
||||
if (filter(tab)) {
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="_selectNewTab">
|
||||
<parameter name="aNewTab"/>
|
||||
<parameter name="aFallbackDir"/>
|
||||
@ -277,15 +346,12 @@
|
||||
<![CDATA[
|
||||
this.ariaFocusedItem = null;
|
||||
|
||||
var requestedTab = aNewTab;
|
||||
while (aNewTab.hidden || aNewTab.disabled || !this._canAdvanceToTab(aNewTab)) {
|
||||
aNewTab = aFallbackDir == -1 ? aNewTab.previousElementSibling : aNewTab.nextElementSibling;
|
||||
if (!aNewTab && aWrap)
|
||||
aNewTab = aFallbackDir == -1 ? this.children[this.children.length - 1] :
|
||||
this.children[0];
|
||||
if (!aNewTab || aNewTab == requestedTab)
|
||||
return;
|
||||
}
|
||||
aNewTab = this.findNextTab(aNewTab, {
|
||||
direction: aFallbackDir,
|
||||
wrap: aWrap,
|
||||
startWithAdjacent: false,
|
||||
filter: tab => !tab.hidden && !tab.disabled && this._canAdvanceToTab(tab),
|
||||
});
|
||||
|
||||
var isTabFocused = false;
|
||||
try {
|
||||
@ -332,14 +398,11 @@
|
||||
<body>
|
||||
<![CDATA[
|
||||
var startTab = this.ariaFocusedItem || this.selectedItem;
|
||||
var next = startTab[(aDir == -1 ? "previous" : "next") + "ElementSibling"];
|
||||
if (!next && aWrap) {
|
||||
next = aDir == -1 ? this.children[this.children.length - 1] :
|
||||
this.children[0];
|
||||
}
|
||||
if (next && next != startTab) {
|
||||
this._selectNewTab(next, aDir, aWrap);
|
||||
}
|
||||
let newTab = this.findNextTab(startTab, {
|
||||
direction: aDir,
|
||||
wrap: aWrap,
|
||||
});
|
||||
this._selectNewTab(newTab, aDir, aWrap);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
Loading…
Reference in New Issue
Block a user