Bug 585558 - Implement properties for easier visible tab access via JS and CSS. r=dao, r=fyan

This commit is contained in:
Adam Dane [:hobophobe] 2013-03-19 18:41:01 -05:00
parent 8a53315a2f
commit b7eee0869d
3 changed files with 242 additions and 3 deletions

View File

@ -1029,6 +1029,8 @@
} while (false);
}
this.tabContainer._setPositionalAttributes();
if (!aForceUpdate)
TelemetryStopwatch.finish("FX_TAB_SWITCH_UPDATE_MS");
]]>
@ -1340,8 +1342,7 @@
t.linkedPanel = uniqueId;
t.linkedBrowser = b;
t._tPos = position;
if (t.previousSibling.selected)
t.setAttribute("afterselected", true);
this.tabContainer._setPositionalAttributes();
// NB: this appendChild call causes us to run constructors for the
// browser element, which fires off a bunch of notifications. Some
@ -1781,8 +1782,9 @@
}, 0, this.tabContainer);
}
// update first-tab/last-tab/beforeselected/afterselected attributes
// update tab positional properties and attributes
this.selectedTab._selected = true;
this.tabContainer._setPositionalAttributes();
// Removing the panel requires fixing up selectedPanel immediately
// (see below), which would be hindered by the potentially expensive
@ -2110,6 +2112,8 @@
this.tabContainer.adjustTabstrip();
this.tabContainer._setPositionalAttributes();
let event = document.createEvent("Events");
event.initEvent("TabShow", true, false);
aTab.dispatchEvent(event);
@ -2129,6 +2133,8 @@
this.tabContainer.adjustTabstrip();
this.tabContainer._setPositionalAttributes();
let event = document.createEvent("Events");
event.initEvent("TabHide", true, false);
aTab.dispatchEvent(event);
@ -2255,6 +2261,8 @@
if (aTab.pinned)
this.tabContainer._positionPinnedTabs();
this.tabContainer._setPositionalAttributes();
var evt = document.createEvent("UIEvents");
evt.initUIEvent("TabMove", true, false, window, oldPosition);
aTab.dispatchEvent(evt);
@ -2897,6 +2905,8 @@
<xul:toolbarbutton class="tabs-newtab-button"
command="cmd_newNavigatorTab"
onclick="checkForMiddleClick(this, event);"
onmouseenter="document.getBindingParent(this)._enterNewTab();"
onmouseleave="document.getBindingParent(this)._leaveNewTab();"
tooltiptext="&newTabButton.tooltip;"/>
<xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
style="width: 0;"/>
@ -2954,6 +2964,53 @@
document.getAnonymousElementByAttribute(this, "anonid", "arrowscrollbox");
</field>
<field name="_firstTab">null</field>
<field name="_lastTab">null</field>
<field name="_afterSelectedTab">null</field>
<field name="_beforeHoveredTab">null</field>
<field name="_afterHoveredTab">null</field>
<method name="_setPositionalAttributes">
<body><![CDATA[
let visibleTabs = this.tabbrowser.visibleTabs;
if (!visibleTabs.length)
return;
let selectedIndex = visibleTabs.indexOf(this.selectedItem);
let lastVisible = visibleTabs.length - 1;
// selectedItem will not be in visibleTabs briefly when
// browser.tabs.closeWindowWithLastTab is disabled and the user closes
// the last tab
if (!this.selectedItem.closing && selectedIndex != 0) {
let beforeSelectedTab = visibleTabs[selectedIndex - 1];
beforeSelectedTab.removeAttribute("beforehovered");
}
if (this._afterSelectedTab)
this._afterSelectedTab.removeAttribute("afterselected-visible");
if (this.selectedItem.closing || selectedIndex == lastVisible) {
this._afterSelectedTab = null;
} else {
this._afterSelectedTab = visibleTabs[selectedIndex + 1];
this._afterSelectedTab.setAttribute("afterselected-visible",
"true");
this._afterSelectedTab.removeAttribute("afterhovered");
}
if (this._firstTab)
this._firstTab.removeAttribute("first-visible-tab");
this._firstTab = visibleTabs[0];
this._firstTab.setAttribute("first-visible-tab", "true");
if (this._lastTab)
this._lastTab.removeAttribute("last-visible-tab");
this._lastTab = visibleTabs[lastVisible];
this._lastTab.setAttribute("last-visible-tab", "true");
]]></body>
</method>
<field name="_prefObserver"><![CDATA[({
tabContainer: this,
@ -3004,6 +3061,26 @@
]]></setter>
</property>
<method name="_enterNewTab">
<body><![CDATA[
let visibleTabs = this.tabbrowser.visibleTabs;
let candidate = visibleTabs[visibleTabs.length - 1];
if (!candidate.selected) {
this._beforeHoveredTab = candidate;
candidate.setAttribute("beforehovered", "true");
}
]]></body>
</method>
<method name="_leaveNewTab">
<body><![CDATA[
if (this._beforeHoveredTab) {
this._beforeHoveredTab.removeAttribute("beforehovered");
this._beforeHoveredTab = null;
}
]]></body>
</method>
<method name="_propagateVisibility">
<body><![CDATA[
let visible = this.visible;
@ -4181,6 +4258,45 @@
if (anonid == "close-button")
this.mOverCloseButton = false;
</handler>
<handler event="mouseenter" phase="target"><![CDATA[
let tab = event.target;
if (tab.selected)
return;
let tabContainer = this.parentNode;
let visibleTabs = tabContainer.tabbrowser.visibleTabs;
let tabIndex = visibleTabs.indexOf(tab);
if (tabIndex == 0) {
tabContainer._beforeHoveredTab = null;
} else {
let candidate = visibleTabs[tabIndex - 1];
if (!candidate.selected) {
tabContainer._beforeHoveredTab = candidate;
candidate.setAttribute("beforehovered", "true");
}
}
if (tabIndex == visibleTabs.length - 1) {
tabContainer._afterHoveredTab = null;
} else {
let candidate = visibleTabs[tabIndex + 1];
if (!candidate.selected) {
tabContainer._afterHoveredTab = candidate;
candidate.setAttribute("afterhovered", "true");
}
}
]]></handler>
<handler event="mouseleave" phase="target"><![CDATA[
let tabContainer = this.parentNode;
if (tabContainer._beforeHoveredTab) {
tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
tabContainer._beforeHoveredTab = null;
}
if (tabContainer._afterHoveredTab) {
tabContainer._afterHoveredTab.removeAttribute("afterhovered");
tabContainer._afterHoveredTab = null;
}
]]></handler>
<handler event="dragstart" phase="capturing">
this.style.MozUserFocus = '';
</handler>

View File

@ -126,6 +126,7 @@ _BROWSER_FILES = \
browser_bug581242.js \
browser_bug581253.js \
browser_bug581947.js \
browser_bug585558.js \
browser_bug585785.js \
browser_bug585830.js \
browser_bug590206.js \

View File

@ -0,0 +1,122 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
let tabs = [];
function addTab(aURL) {
tabs.push(gBrowser.addTab(aURL, {skipAnimation: true}));
}
function testAttrib(elem, attrib, attribValue, msg) {
is(elem.hasAttribute(attrib), attribValue, msg);
}
function test() {
waitForExplicitFinish();
is(gBrowser.tabs.length, 1, "one tab is open initially");
// Add several new tabs in sequence, hiding some, to ensure that the
// correct attributes get set
addTab("http://mochi.test:8888/#0");
addTab("http://mochi.test:8888/#1");
addTab("http://mochi.test:8888/#2");
addTab("http://mochi.test:8888/#3");
gBrowser.selectedTab = gBrowser.tabs[0];
testAttrib(gBrowser.tabs[0], "first-visible-tab", true,
"First tab marked first-visible-tab!");
testAttrib(gBrowser.tabs[4], "last-visible-tab", true,
"Fifth tab marked last-visible-tab!");
testAttrib(gBrowser.tabs[0], "selected", true, "First tab marked selected!");
testAttrib(gBrowser.tabs[0], "afterselected-visible", false,
"First tab not marked afterselected-visible!");
testAttrib(gBrowser.tabs[1], "afterselected-visible", true,
"Second tab marked afterselected-visible!");
gBrowser.hideTab(gBrowser.tabs[1]);
executeSoon(test_hideSecond);
}
function test_hideSecond() {
testAttrib(gBrowser.tabs[2], "afterselected-visible", true,
"Third tab marked afterselected-visible!");
gBrowser.showTab(gBrowser.tabs[1])
executeSoon(test_showSecond);
}
function test_showSecond() {
testAttrib(gBrowser.tabs[1], "afterselected-visible", true,
"Second tab marked afterselected-visible!");
testAttrib(gBrowser.tabs[2], "afterselected-visible", false,
"Third tab not marked as afterselected-visible!");
gBrowser.selectedTab = gBrowser.tabs[1];
gBrowser.hideTab(gBrowser.tabs[0]);
executeSoon(test_hideFirst);
}
function test_hideFirst() {
testAttrib(gBrowser.tabs[0], "first-visible-tab", false,
"Hidden first tab not marked first-visible-tab!");
testAttrib(gBrowser.tabs[1], "first-visible-tab", true,
"Second tab marked first-visible-tab!");
gBrowser.showTab(gBrowser.tabs[0]);
executeSoon(test_showFirst);
}
function test_showFirst() {
testAttrib(gBrowser.tabs[0], "first-visible-tab", true,
"First tab marked first-visible-tab!");
gBrowser.selectedTab = gBrowser.tabs[2];
testAttrib(gBrowser.tabs[3], "afterselected-visible", true,
"Fourth tab marked afterselected-visible!");
gBrowser.moveTabTo(gBrowser.selectedTab, 1);
executeSoon(test_movedLower);
}
function test_movedLower() {
testAttrib(gBrowser.tabs[2], "afterselected-visible", true,
"Third tab marked afterselected-visible!");
test_hoverOne();
}
function test_hoverOne() {
EventUtils.synthesizeMouseAtCenter(gBrowser.tabs[4], { type: "mousemove" });
testAttrib(gBrowser.tabs[3], "beforehovered", true,
"Fourth tab marked beforehovered");
EventUtils.synthesizeMouseAtCenter(gBrowser.tabs[3], { type: "mousemove" });
testAttrib(gBrowser.tabs[2], "beforehovered", true,
"Third tab marked beforehovered!");
testAttrib(gBrowser.tabs[4], "afterhovered", true,
"Fifth tab marked afterhovered!");
gBrowser.removeTab(tabs.pop());
test_pinning();
}
function test_pinning() {
gBrowser.selectedTab = gBrowser.tabs[3];
testAttrib(gBrowser.tabs[3], "last-visible-tab", true,
"Fourth tab marked last-visible-tab!");
testAttrib(gBrowser.tabs[3], "selected", true, "Fourth tab marked selected!");
testAttrib(gBrowser.tabs[3], "afterselected-visible", false,
"Fourth tab not marked afterselected-visible!");
// Causes gBrowser.tabs to change indices
gBrowser.pinTab(gBrowser.tabs[3]);
testAttrib(gBrowser.tabs[3], "last-visible-tab", true,
"Fourth tab marked last-visible-tab!");
testAttrib(gBrowser.tabs[1], "afterselected-visible", true,
"Second tab marked afterselected-visible!");
testAttrib(gBrowser.tabs[0], "first-visible-tab", true,
"First tab marked first-visible-tab!");
testAttrib(gBrowser.tabs[0], "selected", true, "First tab marked selected!");
gBrowser.selectedTab = gBrowser.tabs[1];
testAttrib(gBrowser.tabs[2], "afterselected-visible", true,
"Third tab marked afterselected-visible!");
test_cleanUp();
}
function test_cleanUp() {
tabs.forEach(gBrowser.removeTab, gBrowser);
finish();
}