зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1555060 Convert <tabs> to a custom element
The conversion for "regular" <tabs> elements is straightforward, most of the work here is to support the tab strip (the <tabs> element with the id "tabbrowser-tabs"). The markup needed for the tab strip moves directly into browser.xhtml and the construction/teardown logic is now explicitly driven from gBrowser. There are many more little tweaks too numerous to enumerate. Differential Revision: https://phabricator.services.mozilla.com/D32855 --HG-- rename : browser/base/content/tabbrowser.xml => browser/base/content/tabbrowser-tabs.js extra : source : c163881a80c9a032b40f7d965fad6a6850fcf8a4 extra : intermediate-source : a5c6deeda8a9475ac0268a4351417c8ff659c962 extra : histedit_source : 5eb8b3d14027f2aeca5c52534096837cd0343104%2Ca23a07ddd5e1fb8bd084644dd6db0ee028b7c4b4
This commit is contained in:
Родитель
f1d137eff5
Коммит
7696519cff
|
@ -87,7 +87,9 @@ Relation XULTabAccessible::RelationByType(RelationType aType) const {
|
|||
if (aType != RelationType::LABEL_FOR) return rel;
|
||||
|
||||
// Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible.
|
||||
nsIContent* parent = mContent->GetParent();
|
||||
ErrorResult rv;
|
||||
nsIContent* parent =
|
||||
mContent->AsElement()->Closest(NS_LITERAL_STRING("tabs"), rv);
|
||||
if (!parent) return rel;
|
||||
|
||||
nsCOMPtr<nsIDOMXULRelatedElement> tabsElm =
|
||||
|
|
|
@ -126,10 +126,6 @@ panelview[mainview] > .panel-header {
|
|||
transition: height var(--panelui-subview-transition-duration);
|
||||
}
|
||||
|
||||
#tabbrowser-tabs {
|
||||
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
|
||||
}
|
||||
|
||||
@supports -moz-bool-pref("layout.css.emulate-moz-box-with-flex") {
|
||||
#tabbrowser-tabs {
|
||||
/* Without this, the tabs container width extends beyond the window width */
|
||||
|
@ -203,13 +199,13 @@ panelview[mainview] > .panel-header {
|
|||
}
|
||||
%endif
|
||||
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] {
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned] {
|
||||
position: fixed !important;
|
||||
display: block; /* position:fixed already does this (bug 579776), but let's be explicit */
|
||||
}
|
||||
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[selected],
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[multiselected] {
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[selected],
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[multiselected] {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
pointer-events: none; /* avoid blocking dragover events on scroll buttons */
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
Services.scriptloader.loadSubScript("chrome://browser/content/browser-tabsintitlebar.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser-tab.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser-tabs.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/search/autocomplete-popup.js", this);
|
||||
Services.scriptloader.loadSubScript("chrome://browser/content/search/searchbar.js", this);
|
||||
|
||||
|
@ -723,11 +724,19 @@
|
|||
<hbox flex="1" align="end" class="toolbar-items">
|
||||
<hbox id="TabsToolbar-customization-target" flex="1">
|
||||
<tabs id="tabbrowser-tabs"
|
||||
is="tabbrowser-tabs"
|
||||
flex="1"
|
||||
setfocus="false"
|
||||
tooltip="tabbrowser-tab-tooltip"
|
||||
stopwatchid="FX_TAB_CLICK_MS">
|
||||
<tab is="tabbrowser-tab" class="tabbrowser-tab" selected="true" visuallyselected="true" fadein="true"/>
|
||||
<hbox class="tab-drop-indicator-box">
|
||||
<image class="tab-drop-indicator" hidden="true"/>
|
||||
</hbox>
|
||||
<arrowscrollbox orient="horizontal" flex="1" style="min-width: 1px;" clicktoscroll="true" class="tabbrowser-arrowscrollbox" scrolledtostart="true" scrolledtoend="true">
|
||||
<tab is="tabbrowser-tab" class="tabbrowser-tab" selected="true" visuallyselected="true" fadein="true"/>
|
||||
<toolbarbutton class="tabs-newtab-button toolbarbutton-1" command="cmd_newNavigatorTab" onclick="checkForMiddleClick(this, event);"/>
|
||||
<spacer class="closing-tabs-spacer" style="width: 0;"/>
|
||||
</arrowscrollbox>
|
||||
</tabs>
|
||||
|
||||
<toolbarbutton id="new-tab-button"
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
.tab-close-button[pinned],
|
||||
#tabbrowser-tabs[closebuttons="activetab"] > .tabbrowser-tab > .tab-stack > .tab-content > .tab-close-button:not([selected="true"]),
|
||||
#tabbrowser-tabs[closebuttons="activetab"] > .tabbrowser-arrowscrollbox > .tabbrowser-tab > .tab-stack > .tab-content > .tab-close-button:not([selected="true"]),
|
||||
.tab-icon-pending:not([pendingicon]),
|
||||
.tab-icon-pending[busy],
|
||||
.tab-icon-pending[pinned],
|
||||
|
|
|
@ -32,6 +32,7 @@ window._gBrowser = {
|
|||
window.addEventListener("occlusionstatechange", this);
|
||||
window.addEventListener("framefocusrequested", this);
|
||||
|
||||
this.tabContainer.init();
|
||||
this._setupInitialBrowserAndTab();
|
||||
|
||||
if (Services.prefs.getBoolPref("browser.display.use_system_colors")) {
|
||||
|
@ -67,6 +68,7 @@ window._gBrowser = {
|
|||
"toolkit.cosmeticAnimations.enabled");
|
||||
|
||||
this._setupEventListeners();
|
||||
this._initialized = true;
|
||||
},
|
||||
|
||||
ownerGlobal: window,
|
||||
|
@ -77,6 +79,8 @@ window._gBrowser = {
|
|||
|
||||
_visibleTabs: null,
|
||||
|
||||
_tabs: null,
|
||||
|
||||
_lastRelatedTabMap: new WeakMap(),
|
||||
|
||||
mProgressListeners: [],
|
||||
|
@ -205,7 +209,10 @@ window._gBrowser = {
|
|||
},
|
||||
|
||||
get tabs() {
|
||||
return this.tabContainer.allTabs;
|
||||
if (!this._tabs) {
|
||||
this._tabs = this.tabContainer.allTabs;
|
||||
}
|
||||
return this._tabs;
|
||||
},
|
||||
|
||||
get tabbox() {
|
||||
|
@ -218,19 +225,16 @@ window._gBrowser = {
|
|||
return this.tabpanels = document.getElementById("tabbrowser-tabpanels");
|
||||
},
|
||||
|
||||
get addEventListener() {
|
||||
delete this.addEventListener;
|
||||
return this.addEventListener = this.tabpanels.addEventListener.bind(this.tabpanels);
|
||||
addEventListener(...args) {
|
||||
this.tabpanels.addEventListener(...args);
|
||||
},
|
||||
|
||||
get removeEventListener() {
|
||||
delete this.removeEventListener;
|
||||
return this.removeEventListener = this.tabpanels.removeEventListener.bind(this.tabpanels);
|
||||
removeEventListener(...args) {
|
||||
this.tabpanels.removeEventListener(...args);
|
||||
},
|
||||
|
||||
get dispatchEvent() {
|
||||
delete this.dispatchEvent;
|
||||
return this.dispatchEvent = this.tabpanels.dispatchEvent.bind(this.tabpanels);
|
||||
dispatchEvent(...args) {
|
||||
return this.tabpanels.dispatchEvent(...args);
|
||||
},
|
||||
|
||||
get visibleTabs() {
|
||||
|
@ -494,6 +498,11 @@ window._gBrowser = {
|
|||
return this.selectedBrowser.userTypedValue;
|
||||
},
|
||||
|
||||
_invalidateCachedTabs() {
|
||||
this._tabs = null;
|
||||
this._visibleTabs = null;
|
||||
},
|
||||
|
||||
_setFindbarData() {
|
||||
// Ensure we know what the find bar key is in the content process:
|
||||
let {sharedData} = Services.ppmm;
|
||||
|
@ -2366,9 +2375,6 @@ window._gBrowser = {
|
|||
}, 0, this.tabContainer);
|
||||
}
|
||||
|
||||
// invalidate cache
|
||||
this._visibleTabs = null;
|
||||
|
||||
let usingPreloadedContent = false;
|
||||
let b;
|
||||
|
||||
|
@ -2416,6 +2422,7 @@ window._gBrowser = {
|
|||
}
|
||||
|
||||
let tabAfter = this.tabs[index] || null;
|
||||
this._invalidateCachedTabs();
|
||||
this.tabContainer.insertBefore(t, tabAfter);
|
||||
if (tabAfter) {
|
||||
this._updateTabsAfterInsert();
|
||||
|
@ -2959,7 +2966,7 @@ window._gBrowser = {
|
|||
|
||||
aTab.closing = true;
|
||||
this._removingTabs.push(aTab);
|
||||
this._visibleTabs = null; // invalidate cache
|
||||
this._invalidateCachedTabs();
|
||||
|
||||
// Invalidate hovered tab state tracking for this closing tab.
|
||||
if (this.tabContainer._hoveredTab == aTab)
|
||||
|
@ -3085,6 +3092,7 @@ window._gBrowser = {
|
|||
|
||||
// Remove the tab ...
|
||||
aTab.remove();
|
||||
this._invalidateCachedTabs();
|
||||
|
||||
// Update hashiddentabs if this tab was hidden.
|
||||
if (aTab.hidden)
|
||||
|
@ -3538,7 +3546,7 @@ window._gBrowser = {
|
|||
showTab(aTab) {
|
||||
if (aTab.hidden) {
|
||||
aTab.removeAttribute("hidden");
|
||||
this._visibleTabs = null; // invalidate cache
|
||||
this._invalidateCachedTabs();
|
||||
|
||||
this.tabContainer._updateCloseButtons();
|
||||
this.tabContainer._updateHiddenTabsStatus();
|
||||
|
@ -3556,7 +3564,7 @@ window._gBrowser = {
|
|||
if (!aTab.hidden && !aTab.pinned && !aTab.selected &&
|
||||
!aTab.closing && !aTab._sharingState) {
|
||||
aTab.setAttribute("hidden", "true");
|
||||
this._visibleTabs = null; // invalidate cache
|
||||
this._invalidateCachedTabs();
|
||||
|
||||
this.tabContainer._updateCloseButtons();
|
||||
this.tabContainer._updateHiddenTabsStatus();
|
||||
|
@ -3723,10 +3731,9 @@ window._gBrowser = {
|
|||
|
||||
aIndex = aIndex < aTab._tPos ? aIndex : aIndex + 1;
|
||||
|
||||
// invalidate cache
|
||||
this._visibleTabs = null;
|
||||
|
||||
this.tabContainer.insertBefore(aTab, this.tabs[aIndex] || null);
|
||||
let neighbor = this.tabs[aIndex] || null;
|
||||
this._invalidateCachedTabs();
|
||||
this.tabContainer.insertBefore(aTab, neighbor);
|
||||
this._updateTabsAfterInsert();
|
||||
|
||||
if (wasFocused)
|
||||
|
@ -3889,7 +3896,7 @@ window._gBrowser = {
|
|||
return;
|
||||
}
|
||||
|
||||
const tabs = this._visibleTabs;
|
||||
const tabs = this.visibleTabs;
|
||||
const indexOfTab1 = tabs.indexOf(aTab1);
|
||||
const indexOfTab2 = tabs.indexOf(aTab2);
|
||||
|
||||
|
@ -4450,6 +4457,7 @@ window._gBrowser = {
|
|||
},
|
||||
|
||||
destroy() {
|
||||
this.tabContainer.destroy();
|
||||
Services.obs.removeObserver(this, "contextual-identity-updated");
|
||||
|
||||
for (let tab of this.tabs) {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -31,18 +31,6 @@ const whitelist = [
|
|||
platforms: ["linux", "win", "macosx"],
|
||||
},
|
||||
|
||||
{
|
||||
file: "chrome://browser/skin/tabbrowser/tabDragIndicator.png",
|
||||
hidpi: "chrome://browser/skin/tabbrowser/tabDragIndicator@2x.png",
|
||||
platforms: ["macosx"],
|
||||
},
|
||||
|
||||
{
|
||||
file: "chrome://browser/skin/tabbrowser/tabDragIndicator.png",
|
||||
hidpi: "<not loaded>",
|
||||
platforms: ["linux", "win"],
|
||||
},
|
||||
|
||||
{
|
||||
file: "resource://gre-resources/loading-image.png",
|
||||
platforms: ["win", "macosx"],
|
||||
|
|
|
@ -26,10 +26,8 @@ add_task(async function() {
|
|||
await BrowserTestUtils.waitForCondition(() => tab._fullyOpen);
|
||||
|
||||
let tabStripRect = gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
|
||||
let newTabButtonRect =
|
||||
document.getAnonymousElementByAttribute(gBrowser.tabContainer,
|
||||
"anonid", "tabs-newtab-button")
|
||||
.getBoundingClientRect();
|
||||
let newTabButtonRect = gBrowser.tabContainer.newTabButton
|
||||
.getBoundingClientRect();
|
||||
let inRange = (val, min, max) => min <= val && val <= max;
|
||||
|
||||
// Add a reflow observer and open a new tab.
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
const EXPECTED_REFLOWS = [
|
||||
{
|
||||
stack: [
|
||||
"clientX@chrome://browser/content/tabbrowser.xml",
|
||||
"onxbldragstart@chrome://browser/content/tabbrowser.xml",
|
||||
"clientX@chrome://browser/content/tabbrowser-tabs.js",
|
||||
"on_dragstart@chrome://browser/content/tabbrowser-tabs.js",
|
||||
"handleEvent@chrome://browser/content/tabbrowser-tabs.js",
|
||||
"synthesizeMouseAtPoint@chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
|
||||
"synthesizeMouse@chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
|
||||
"synthesizePlainDragAndDrop@chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
|
||||
|
@ -23,7 +24,8 @@ const EXPECTED_REFLOWS = [
|
|||
|
||||
{
|
||||
stack: [
|
||||
"onxbldragstart@chrome://browser/content/tabbrowser.xml",
|
||||
"on_dragstart@chrome://browser/content/tabbrowser-tabs.js",
|
||||
"handleEvent@chrome://browser/content/tabbrowser-tabs.js",
|
||||
"synthesizeMouseAtPoint@chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
|
||||
"synthesizeMouse@chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
|
||||
"synthesizePlainDragAndDrop@chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
|
||||
|
|
|
@ -278,9 +278,7 @@ async function ensureFocusedUrlbar() {
|
|||
*/
|
||||
function computeMaxTabCount() {
|
||||
let currentTabCount = gBrowser.tabs.length;
|
||||
let newTabButton =
|
||||
document.getAnonymousElementByAttribute(gBrowser.tabContainer,
|
||||
"anonid", "tabs-newtab-button");
|
||||
let newTabButton = gBrowser.tabContainer.newTabButton;
|
||||
let newTabRect = newTabButton.getBoundingClientRect();
|
||||
let tabStripRect = gBrowser.tabContainer.arrowScrollbox.getBoundingClientRect();
|
||||
let availableTabStripWidth = tabStripRect.width - newTabRect.width;
|
||||
|
|
|
@ -21,8 +21,7 @@ add_task(async function test() {
|
|||
|
||||
let metaKeyEvent = AppConstants.platform == "macosx" ? {metaKey: true} : {ctrlKey: true};
|
||||
|
||||
let tabs = document.getElementById("tabbrowser-tabs");
|
||||
let newTabButton = document.getAnonymousElementByAttribute(tabs, "anonid", "tabs-newtab-button");
|
||||
let newTabButton = gBrowser.tabContainer.newTabButton;
|
||||
let promiseTabOpened = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen");
|
||||
EventUtils.synthesizeMouseAtCenter(newTabButton, metaKeyEvent);
|
||||
let openEvent = await promiseTabOpened;
|
||||
|
|
|
@ -92,7 +92,7 @@ browser.jar:
|
|||
content/browser/tabbrowser.css (content/tabbrowser.css)
|
||||
content/browser/tabbrowser.js (content/tabbrowser.js)
|
||||
content/browser/tabbrowser-tab.js (content/tabbrowser-tab.js)
|
||||
content/browser/tabbrowser.xml (content/tabbrowser.xml)
|
||||
content/browser/tabbrowser-tabs.js (content/tabbrowser-tabs.js)
|
||||
* content/browser/urlbarBindings.xml (content/urlbarBindings.xml)
|
||||
content/browser/utilityOverlay.js (content/utilityOverlay.js)
|
||||
content/browser/webext-panels.js (content/webext-panels.js)
|
||||
|
|
|
@ -3,18 +3,23 @@
|
|||
// Testing that when the user opens the add tab menu and clicks menu items
|
||||
// the correct context id is opened
|
||||
|
||||
function findPopup(browser = gBrowser) {
|
||||
return browser.tabContainer.querySelector(".new-tab-popup");
|
||||
}
|
||||
|
||||
add_task(async function test_menu_with_timeout() {
|
||||
await SpecialPowers.pushPrefEnv({"set": [
|
||||
["privacy.userContext.enabled", true],
|
||||
["privacy.userContext.longPressBehavior", 2],
|
||||
]});
|
||||
|
||||
let newTab = document.getElementById("tabbrowser-tabs");
|
||||
let newTabButton = document.getAnonymousElementByAttribute(newTab, "anonid", "tabs-newtab-button");
|
||||
let newTabButton = gBrowser.tabContainer.newTabButton;
|
||||
ok(newTabButton, "New tab button exists");
|
||||
ok(!newTabButton.hidden, "New tab button is visible");
|
||||
await BrowserTestUtils.waitForCondition(() => !!document.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup"), "Wait for popup to exist");
|
||||
let popup = document.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup");
|
||||
|
||||
await BrowserTestUtils.waitForCondition(() => !!findPopup(), "Wait for popup to exist");
|
||||
|
||||
let popup = findPopup();
|
||||
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
||||
|
@ -41,12 +46,12 @@ add_task(async function test_menu_without_timeout() {
|
|||
["privacy.userContext.longPressBehavior", 1],
|
||||
]});
|
||||
|
||||
let newTab = document.getElementById("tabbrowser-tabs");
|
||||
let newTabButton = document.getAnonymousElementByAttribute(newTab, "anonid", "tabs-newtab-button");
|
||||
let newTabButton = gBrowser.tabContainer.newTabButton;
|
||||
ok(newTabButton, "New tab button exists");
|
||||
ok(!newTabButton.hidden, "New tab button is visible");
|
||||
await BrowserTestUtils.waitForCondition(() => !!document.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup"), "Wait for popup to exist");
|
||||
let popup = document.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup");
|
||||
|
||||
await BrowserTestUtils.waitForCondition(() => !!findPopup(), "Wait for popup to exist");
|
||||
let popup = findPopup();
|
||||
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(popup, "popuphidden");
|
||||
|
@ -88,11 +93,10 @@ add_task(async function test_no_menu() {
|
|||
["privacy.userContext.longPressBehavior", 0],
|
||||
]});
|
||||
|
||||
let newTab = document.getElementById("tabbrowser-tabs");
|
||||
let newTabButton = document.getAnonymousElementByAttribute(newTab, "anonid", "tabs-newtab-button");
|
||||
let newTabButton = gBrowser.tabContainer.newTabButton;
|
||||
ok(newTabButton, "New tab button exists");
|
||||
ok(!newTabButton.hidden, "New tab button is visible");
|
||||
let popup = document.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup");
|
||||
let popup = findPopup();
|
||||
ok(!popup, "new tab should not have a popup");
|
||||
});
|
||||
|
||||
|
@ -100,12 +104,13 @@ add_task(async function test_private_mode() {
|
|||
let privateWindow = await BrowserTestUtils.openNewBrowserWindow({private: true});
|
||||
let privateDocument = privateWindow.document;
|
||||
let {tabContainer} = privateWindow.gBrowser;
|
||||
let newTab = privateDocument.getAnonymousElementByAttribute(tabContainer, "anonid", "tabs-newtab-button");
|
||||
let newTab = tabContainer.newTabButton;
|
||||
let newTab2 = privateDocument.getElementById("new-tab-button");
|
||||
// Check to ensure we are talking about the right button
|
||||
ok(!!newTab.clientWidth, "new tab button should not be hidden");
|
||||
ok(!newTab2.clientWidth, "overflow new tab button should be hidden");
|
||||
let popup = privateDocument.getAnonymousElementByAttribute(newTab, "anonid", "newtab-popup");
|
||||
let popup = findPopup(privateWindow.gBrowser);
|
||||
ok(!popup, "new tab should not have a popup");
|
||||
await BrowserTestUtils.closeWindow(privateWindow);
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
const kGlobalNewTabButton = document.getElementById("new-tab-button");
|
||||
const kInnerNewTabButton = document.getAnonymousElementByAttribute(gBrowser.tabContainer, "anonid", "tabs-newtab-button");
|
||||
const kInnerNewTabButton = gBrowser.tabContainer.newTabButton;
|
||||
|
||||
function assertNewTabButton(which) {
|
||||
if (which == "global") {
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
#tabbrowser-tabs,
|
||||
#tabbrowser-tabs > .tabbrowser-arrowscrollbox,
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] {
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned] {
|
||||
min-height: var(--tab-min-height);
|
||||
}
|
||||
|
||||
|
@ -537,8 +537,8 @@
|
|||
*/
|
||||
|
||||
/* Lightweight theme on tabs */
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background[multiselected=true]:-moz-lwtheme,
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab > .tab-stack > .tab-background[selected=true]:-moz-lwtheme {
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-arrowscrollbox > .tabbrowser-tab > .tab-stack > .tab-background[multiselected=true]:-moz-lwtheme,
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-arrowscrollbox > .tabbrowser-tab > .tab-stack > .tab-background[selected=true]:-moz-lwtheme {
|
||||
background-attachment: scroll, scroll, fixed;
|
||||
background-color: transparent;
|
||||
background-image: linear-gradient(var(--lwt-selected-tab-background-color, transparent), var(--lwt-selected-tab-background-color, transparent)),
|
||||
|
@ -582,19 +582,19 @@
|
|||
/* Pinned tabs */
|
||||
|
||||
/* Pinned tab separators need position: absolute when positioned (during overflow). */
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::after {
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned]::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]:-moz-locale-dir(rtl)::after {
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned]:-moz-locale-dir(rtl)::after {
|
||||
right: unset;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] > .tab-stack {
|
||||
#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[pinned] > .tab-stack {
|
||||
border-inline-end: 1px solid transparent;
|
||||
}
|
||||
|
||||
|
@ -664,16 +664,16 @@
|
|||
%endif
|
||||
/* Show full height tab separators on hover and multiselection. */
|
||||
.tabbrowser-tab:hover::after,
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab[beforehovered]::after,
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-arrowscrollbox > .tabbrowser-tab[beforehovered]::after,
|
||||
.tabbrowser-tab[multiselected]::after,
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab[before-multiselected]::after {
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-arrowscrollbox > .tabbrowser-tab[before-multiselected]::after {
|
||||
margin-top: var(--tabs-top-border-width);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Show full height tab separators on selected tabs. */
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-tab[beforeselected-visible]::after,
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[visuallyselected]::before,
|
||||
#tabbrowser-tabs:not([movingtab]) > .tabbrowser-arrowscrollbox > .tabbrowser-tab[beforeselected-visible]::after,
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[visuallyselected]::before,
|
||||
.tabbrowser-tab[visuallyselected]::after {
|
||||
border-color: var(--tabs-border-color);
|
||||
margin-top: 0;
|
||||
|
@ -683,7 +683,7 @@
|
|||
|
||||
.tabbrowser-tab::after,
|
||||
/* Also show separators beside the selected tab when dragging it. */
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-tab[visuallyselected]::before {
|
||||
#tabbrowser-tabs[movingtab] > .tabbrowser-arrowscrollbox > .tabbrowser-tab[visuallyselected]::before {
|
||||
content: "";
|
||||
display: -moz-box;
|
||||
}
|
||||
|
|
|
@ -59,8 +59,7 @@ var Tabs = {
|
|||
browserWindow.gBrowser.selectTabAtIndex(5);
|
||||
hoverTab(browserWindow.gBrowser.tabs[2]);
|
||||
// also hover the new tab button
|
||||
let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.
|
||||
gBrowser.tabContainer, "anonid", "tabs-newtab-button");
|
||||
let newTabButton = browserWindow.gBrowser.tabContainer.newTabButton;
|
||||
hoverTab(newTabButton);
|
||||
browserWindow.gBrowser.tabs[browserWindow.gBrowser.tabs.length - 1].
|
||||
setAttribute("beforehovered", true);
|
||||
|
@ -197,7 +196,7 @@ function closeAllButOneTab(url = "about:blank") {
|
|||
});
|
||||
if (gBrowser.selectedTab.pinned)
|
||||
gBrowser.unpinTab(gBrowser.selectedTab);
|
||||
let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.gBrowser.tabContainer, "class", "tabs-newtab-button toolbarbutton-1");
|
||||
let newTabButton = gBrowser.tabContainer.newTabButton;
|
||||
hoverTab(newTabButton, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class TabBar(UIBaseLib):
|
|||
|
||||
:returns: Reference to the new tab button.
|
||||
"""
|
||||
return self.toolbar.find_element(By.ANON_ATTRIBUTE, {'anonid': 'tabs-newtab-button'})
|
||||
return self.toolbar.find_element(By.CLASS_NAME, 'tabs-newtab-button')
|
||||
|
||||
@property
|
||||
def tabs(self):
|
||||
|
|
|
@ -71,7 +71,6 @@ toolkit.jar:
|
|||
content/global/bindings/richlistbox.xml (widgets/richlistbox.xml)
|
||||
content/global/bindings/scrollbox.xml (widgets/scrollbox.xml)
|
||||
content/global/bindings/spinner.js (widgets/spinner.js)
|
||||
content/global/bindings/tabbox.xml (widgets/tabbox.xml)
|
||||
* content/global/bindings/textbox.xml (widgets/textbox.xml)
|
||||
content/global/bindings/timekeeper.js (widgets/timekeeper.js)
|
||||
content/global/bindings/timepicker.js (widgets/timepicker.js)
|
||||
|
|
|
@ -431,4 +431,354 @@ MozElements.MozTab = class MozTab extends MozElements.BaseText {
|
|||
|
||||
MozXULElement.implementCustomInterface(MozElements.MozTab, [Ci.nsIDOMXULSelectControlItemElement]);
|
||||
customElements.define("tab", MozElements.MozTab);
|
||||
|
||||
class TabsBase extends MozElements.BaseControl {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.addEventListener("DOMMouseScroll", (event) => {
|
||||
if (Services.prefs.getBoolPref("toolkit.tabbox.switchByScrolling")) {
|
||||
if (event.detail > 0) {
|
||||
this.advanceSelectedTab(1, false);
|
||||
} else {
|
||||
this.advanceSelectedTab(-1, false);
|
||||
}
|
||||
event.stopPropagation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// to be called from derived class connectedCallback
|
||||
baseConnect() {
|
||||
this._tabbox = null;
|
||||
this.ACTIVE_DESCENDANT_ID = "keyboard-focused-tab-" + Math.trunc(Math.random() * 1000000);
|
||||
|
||||
if (!this.hasAttribute("orient"))
|
||||
this.setAttribute("orient", "horizontal");
|
||||
|
||||
if (this.tabbox && this.tabbox.hasAttribute("selectedIndex")) {
|
||||
let selectedIndex = parseInt(this.tabbox.getAttribute("selectedIndex"));
|
||||
this.selectedIndex = selectedIndex > 0 ? selectedIndex : 0;
|
||||
return;
|
||||
}
|
||||
|
||||
let children = this.allTabs;
|
||||
let length = children.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (children[i].getAttribute("selected") == "true") {
|
||||
this.selectedIndex = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var value = this.value;
|
||||
if (value)
|
||||
this.value = value;
|
||||
else
|
||||
this.selectedIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nsIDOMXULSelectControlElement
|
||||
*/
|
||||
get itemCount() {
|
||||
return this.allTabs.length;
|
||||
}
|
||||
|
||||
set value(val) {
|
||||
this.setAttribute("value", val);
|
||||
var children = this.allTabs;
|
||||
for (var c = children.length - 1; c >= 0; c--) {
|
||||
if (children[c].value == val) {
|
||||
this.selectedIndex = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this.getAttribute("value");
|
||||
}
|
||||
|
||||
get tabbox() {
|
||||
if (!this._tabbox) {
|
||||
// Memoize the result in a field rather than replacing this property,
|
||||
// so that it can be reset along with the binding.
|
||||
this._tabbox = this.closest("tabbox");
|
||||
}
|
||||
|
||||
return this._tabbox;
|
||||
}
|
||||
|
||||
set selectedIndex(val) {
|
||||
var tab = this.getItemAtIndex(val);
|
||||
if (tab) {
|
||||
for (let otherTab of this.allTabs) {
|
||||
if (otherTab != tab && otherTab.selected) {
|
||||
otherTab._selected = false;
|
||||
}
|
||||
}
|
||||
tab._selected = true;
|
||||
|
||||
this.setAttribute("value", tab.value);
|
||||
|
||||
let linkedPanel = this.getRelatedElement(tab);
|
||||
if (linkedPanel) {
|
||||
this.tabbox.setAttribute("selectedIndex", val);
|
||||
|
||||
// This will cause an onselect event to fire for the tabpanel
|
||||
// element.
|
||||
this.tabbox.tabpanels.selectedPanel = linkedPanel;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
get selectedIndex() {
|
||||
const tabs = this.allTabs;
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (tabs[i].selected)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
set selectedItem(val) {
|
||||
if (val && !val.selected)
|
||||
// The selectedIndex setter ignores invalid values
|
||||
// such as -1 if |val| isn't one of our child nodes.
|
||||
this.selectedIndex = this.getIndexOfItem(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
get selectedItem() {
|
||||
const tabs = this.allTabs;
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (tabs[i].selected)
|
||||
return tabs[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
get ariaFocusedIndex() {
|
||||
const tabs = this.allTabs;
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (tabs[i].id == this.ACTIVE_DESCENDANT_ID)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
set ariaFocusedItem(val) {
|
||||
let setNewItem = val && this.getIndexOfItem(val) != -1;
|
||||
let clearExistingItem = this.ariaFocusedItem && (!val || setNewItem);
|
||||
if (clearExistingItem) {
|
||||
let ariaFocusedItem = this.ariaFocusedItem;
|
||||
ariaFocusedItem.classList.remove("keyboard-focused-tab");
|
||||
ariaFocusedItem.id = "";
|
||||
this.selectedItem.removeAttribute("aria-activedescendant");
|
||||
}
|
||||
|
||||
if (setNewItem) {
|
||||
this.ariaFocusedItem = null;
|
||||
val.id = this.ACTIVE_DESCENDANT_ID;
|
||||
val.classList.add("keyboard-focused-tab");
|
||||
this.selectedItem.setAttribute("aria-activedescendant", this.ACTIVE_DESCENDANT_ID);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
get ariaFocusedItem() {
|
||||
return document.getElementById(this.ACTIVE_DESCENDANT_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* nsIDOMXULRelatedElement
|
||||
*/
|
||||
getRelatedElement(aTabElm) {
|
||||
if (!aTabElm)
|
||||
return null;
|
||||
|
||||
let tabboxElm = this.tabbox;
|
||||
if (!tabboxElm)
|
||||
return null;
|
||||
|
||||
let tabpanelsElm = tabboxElm.tabpanels;
|
||||
if (!tabpanelsElm)
|
||||
return null;
|
||||
|
||||
// Get linked tab panel by 'linkedpanel' attribute on the given tab
|
||||
// element.
|
||||
let linkedPanelId = aTabElm.linkedPanel;
|
||||
if (linkedPanelId) {
|
||||
return this.ownerDocument.getElementById(linkedPanelId);
|
||||
}
|
||||
|
||||
// otherwise linked tabpanel element has the same index as the given
|
||||
// tab element.
|
||||
let tabElmIdx = this.getIndexOfItem(aTabElm);
|
||||
return tabpanelsElm.children[tabElmIdx];
|
||||
}
|
||||
|
||||
getIndexOfItem(item) {
|
||||
return Array.prototype.indexOf.call(this.allTabs, item);
|
||||
}
|
||||
|
||||
getItemAtIndex(index) {
|
||||
return this.allTabs[index] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an adjacent tab.
|
||||
*
|
||||
* @param {Node} startTab A <tab> element to start searching from.
|
||||
* @param {Number} opts.direction 1 to search forward, -1 to search backward.
|
||||
* @param {Boolean} opts.wrap If true, wrap around if the search reaches
|
||||
* the end (or beginning) of the tab strip.
|
||||
* @param {Boolean} 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.
|
||||
* @param {Boolean} opts.advance If false, start searching with startTab. If
|
||||
* true, start searching with an adjacent tab.
|
||||
* @param {Function} opts.filter A function to select which tabs to return.
|
||||
*
|
||||
* @return {Node | null} The next <tab> element or, if none exists, null.
|
||||
*/
|
||||
findNextTab(startTab, opts = {}) {
|
||||
let {
|
||||
direction = 1,
|
||||
wrap = false,
|
||||
startWithAdjacent = true,
|
||||
filter = tab => true,
|
||||
} = opts;
|
||||
|
||||
let tab = startTab;
|
||||
if (!startWithAdjacent && filter(tab)) {
|
||||
return tab;
|
||||
}
|
||||
|
||||
let children = this.allTabs;
|
||||
let i = children.indexOf(tab);
|
||||
if (i < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
i += direction;
|
||||
if (wrap) {
|
||||
if (i < 0) {
|
||||
i = children.length - 1;
|
||||
} else if (i >= children.length) {
|
||||
i = 0;
|
||||
}
|
||||
} else if (i < 0 || i >= children.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
tab = children[i];
|
||||
if (tab == startTab) {
|
||||
return null;
|
||||
}
|
||||
if (filter(tab)) {
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_selectNewTab(aNewTab, aFallbackDir, aWrap) {
|
||||
this.ariaFocusedItem = null;
|
||||
|
||||
aNewTab = this.findNextTab(aNewTab, {
|
||||
direction: aFallbackDir,
|
||||
wrap: aWrap,
|
||||
startWithAdjacent: false,
|
||||
filter: tab => !tab.hidden && !tab.disabled && this._canAdvanceToTab(tab),
|
||||
});
|
||||
|
||||
var isTabFocused = false;
|
||||
try {
|
||||
isTabFocused =
|
||||
(document.commandDispatcher.focusedElement == this.selectedItem);
|
||||
} catch (e) {}
|
||||
this.selectedItem = aNewTab;
|
||||
if (isTabFocused) {
|
||||
aNewTab.focus();
|
||||
} else if (this.getAttribute("setfocus") != "false") {
|
||||
let selectedPanel = this.tabbox.selectedPanel;
|
||||
document.commandDispatcher.advanceFocusIntoSubtree(selectedPanel);
|
||||
|
||||
// Make sure that the focus doesn't move outside the tabbox
|
||||
if (this.tabbox) {
|
||||
try {
|
||||
let el = document.commandDispatcher.focusedElement;
|
||||
while (el && el != this.tabbox.tabpanels) {
|
||||
if (el == this.tabbox || el == selectedPanel)
|
||||
return;
|
||||
el = el.parentNode;
|
||||
}
|
||||
aNewTab.focus();
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_canAdvanceToTab(aTab) {
|
||||
return true;
|
||||
}
|
||||
|
||||
advanceSelectedTab(aDir, aWrap) {
|
||||
let startTab = this.ariaFocusedItem || this.selectedItem;
|
||||
let newTab = this.findNextTab(startTab, {
|
||||
direction: aDir,
|
||||
wrap: aWrap,
|
||||
});
|
||||
this._selectNewTab(newTab, aDir, aWrap);
|
||||
}
|
||||
|
||||
appendItem(label, value) {
|
||||
var tab = document.createXULElement("tab");
|
||||
tab.setAttribute("label", label);
|
||||
tab.setAttribute("value", value);
|
||||
this.appendChild(tab);
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
|
||||
MozXULElement.implementCustomInterface(TabsBase, [Ci.nsIDOMXULSelectControlElement, Ci.nsIDOMXULRelatedElement]);
|
||||
|
||||
MozElements.TabsBase = TabsBase;
|
||||
|
||||
class MozTabs extends TabsBase {
|
||||
connectedCallback() {
|
||||
if (this.delayConnectedCallback()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let start = MozXULElement.parseXULToFragment(`<spacer class="tabs-left"/>`);
|
||||
this.insertBefore(start, this.firstChild);
|
||||
|
||||
let end = MozXULElement.parseXULToFragment(`<spacer class="tabs-right" flex="1"/>`);
|
||||
this.insertBefore(end, null);
|
||||
|
||||
this.baseConnect();
|
||||
}
|
||||
|
||||
// Accessor for tabs. This element has spacers as the first and
|
||||
// last elements and <tab>s are everything in between.
|
||||
get allTabs() {
|
||||
let children = Array.from(this.children);
|
||||
return children.splice(1, children.length - 2);
|
||||
}
|
||||
|
||||
appendChild(tab) {
|
||||
// insert before the end spacer.
|
||||
this.insertBefore(tab, this.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("tabs", MozTabs);
|
||||
}
|
||||
|
|
|
@ -1,440 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
|
||||
<bindings id="tabBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
<binding id="tabs"
|
||||
extends="chrome://global/content/bindings/general.xml#basecontrol">
|
||||
<content>
|
||||
<xul:spacer class="tabs-left"/>
|
||||
<children/>
|
||||
<xul:spacer class="tabs-right" flex="1"/>
|
||||
</content>
|
||||
|
||||
<implementation implements="nsIDOMXULSelectControlElement, nsIDOMXULRelatedElement">
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
|
||||
if (!this.hasAttribute("orient"))
|
||||
this.setAttribute("orient", "horizontal");
|
||||
|
||||
if (this.tabbox && this.tabbox.hasAttribute("selectedIndex")) {
|
||||
let selectedIndex = parseInt(this.tabbox.getAttribute("selectedIndex"));
|
||||
this.selectedIndex = selectedIndex > 0 ? selectedIndex : 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var children = this.children;
|
||||
var length = children.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (children[i].getAttribute("selected") == "true") {
|
||||
this.selectedIndex = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var value = this.value;
|
||||
if (value)
|
||||
this.value = value;
|
||||
else
|
||||
this.selectedIndex = 0;
|
||||
]]>
|
||||
</constructor>
|
||||
|
||||
<property name="allTabs">
|
||||
<getter>
|
||||
return this.children;
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<!-- nsIDOMXULRelatedElement -->
|
||||
<method name="getRelatedElement">
|
||||
<parameter name="aTabElm"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (!aTabElm)
|
||||
return null;
|
||||
|
||||
let tabboxElm = this.tabbox;
|
||||
if (!tabboxElm)
|
||||
return null;
|
||||
|
||||
let tabpanelsElm = tabboxElm.tabpanels;
|
||||
if (!tabpanelsElm)
|
||||
return null;
|
||||
|
||||
// Get linked tab panel by 'linkedpanel' attribute on the given tab
|
||||
// element.
|
||||
let linkedPanelId = aTabElm.linkedPanel;
|
||||
if (linkedPanelId) {
|
||||
let ownerDoc = this.ownerDocument;
|
||||
|
||||
// XXX bug 565858: if XUL tab element is anonymous element then
|
||||
// suppose linked tab panel is hosted within the same XBL binding
|
||||
// and search it by ID attribute inside an anonymous content of
|
||||
// the binding. This is not robust assumption since tab elements may
|
||||
// live outside a tabbox element so that for example tab elements
|
||||
// can be explicit content but tab panels can be anonymous.
|
||||
|
||||
let bindingParent = ownerDoc.getBindingParent(aTabElm);
|
||||
if (bindingParent)
|
||||
return ownerDoc.getAnonymousElementByAttribute(bindingParent,
|
||||
"id",
|
||||
linkedPanelId);
|
||||
|
||||
return ownerDoc.getElementById(linkedPanelId);
|
||||
}
|
||||
|
||||
// otherwise linked tabpanel element has the same index as the given
|
||||
// tab element.
|
||||
let tabElmIdx = this.getIndexOfItem(aTabElm);
|
||||
return tabpanelsElm.children[tabElmIdx];
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<!-- nsIDOMXULSelectControlElement -->
|
||||
<property name="itemCount" readonly="true"
|
||||
onget="return this.children.length"/>
|
||||
|
||||
<property name="value" onget="return this.getAttribute('value');">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this.setAttribute("value", val);
|
||||
var children = this.children;
|
||||
for (var c = children.length - 1; c >= 0; c--) {
|
||||
if (children[c].value == val) {
|
||||
this.selectedIndex = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<field name="_tabbox">null</field>
|
||||
<property name="tabbox" readonly="true">
|
||||
<getter><![CDATA[
|
||||
// Memoize the result in a field rather than replacing this property,
|
||||
// so that it can be reset along with the binding.
|
||||
if (this._tabbox) {
|
||||
return this._tabbox;
|
||||
}
|
||||
|
||||
let parent = this.parentNode;
|
||||
while (parent) {
|
||||
if (parent.localName == "tabbox") {
|
||||
break;
|
||||
}
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
return this._tabbox = parent;
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<!-- _tabbox is deprecated, it exists only for backwards compatibility. -->
|
||||
<field name="_tabbox" readonly="true"><![CDATA[
|
||||
this.tabbox;
|
||||
]]></field>
|
||||
|
||||
<field name="_prefService" readonly="true"><![CDATA[
|
||||
Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
|
||||
]]></field>
|
||||
|
||||
<property name="selectedIndex">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
const tabs = this.children;
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (tabs[i].selected)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
]]>
|
||||
</getter>
|
||||
|
||||
<setter>
|
||||
<![CDATA[
|
||||
var tab = this.getItemAtIndex(val);
|
||||
if (tab) {
|
||||
for (let otherTab of this.children) {
|
||||
if (otherTab != tab && otherTab.selected) {
|
||||
otherTab._selected = false;
|
||||
}
|
||||
}
|
||||
tab._selected = true;
|
||||
|
||||
this.setAttribute("value", tab.value);
|
||||
|
||||
let linkedPanel = this.getRelatedElement(tab);
|
||||
if (linkedPanel) {
|
||||
this.tabbox.setAttribute("selectedIndex", val);
|
||||
|
||||
// This will cause an onselect event to fire for the tabpanel
|
||||
// element.
|
||||
this.tabbox.tabpanels.selectedPanel = linkedPanel;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<property name="selectedItem">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
const tabs = this.children;
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (tabs[i].selected)
|
||||
return tabs[i];
|
||||
}
|
||||
return null;
|
||||
]]>
|
||||
</getter>
|
||||
|
||||
<setter>
|
||||
<![CDATA[
|
||||
if (val && !val.selected)
|
||||
// The selectedIndex setter ignores invalid values
|
||||
// such as -1 if |val| isn't one of our child nodes.
|
||||
this.selectedIndex = this.getIndexOfItem(val);
|
||||
return val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<field name="ACTIVE_DESCENDANT_ID" readonly="true"><![CDATA[
|
||||
"keyboard-focused-tab-" + Math.trunc(Math.random() * 1000000);
|
||||
]]></field>
|
||||
|
||||
<property name="ariaFocusedIndex" readonly="true">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
const tabs = this.children;
|
||||
for (var i = 0; i < tabs.length; i++) {
|
||||
if (tabs[i].id == this.ACTIVE_DESCENDANT_ID)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="ariaFocusedItem">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
return document.getElementById(this.ACTIVE_DESCENDANT_ID);
|
||||
]]>
|
||||
</getter>
|
||||
|
||||
<setter>
|
||||
<![CDATA[
|
||||
let setNewItem = val && this.getIndexOfItem(val) != -1;
|
||||
let clearExistingItem = this.ariaFocusedItem && (!val || setNewItem);
|
||||
if (clearExistingItem) {
|
||||
let ariaFocusedItem = this.ariaFocusedItem;
|
||||
ariaFocusedItem.classList.remove("keyboard-focused-tab");
|
||||
ariaFocusedItem.id = "";
|
||||
this.selectedItem.removeAttribute("aria-activedescendant");
|
||||
}
|
||||
|
||||
if (setNewItem) {
|
||||
this.ariaFocusedItem = null;
|
||||
val.id = this.ACTIVE_DESCENDANT_ID;
|
||||
val.classList.add("keyboard-focused-tab");
|
||||
this.selectedItem.setAttribute("aria-activedescendant", this.ACTIVE_DESCENDANT_ID);
|
||||
}
|
||||
|
||||
return val;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<method name="getIndexOfItem">
|
||||
<parameter name="item"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
return Array.prototype.indexOf.call(this.children, item);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="getItemAtIndex">
|
||||
<parameter name="index"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
return this.children.item(index);
|
||||
]]>
|
||||
</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"/>
|
||||
<parameter name="aWrap"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.ariaFocusedItem = null;
|
||||
|
||||
aNewTab = this.findNextTab(aNewTab, {
|
||||
direction: aFallbackDir,
|
||||
wrap: aWrap,
|
||||
startWithAdjacent: false,
|
||||
filter: tab => !tab.hidden && !tab.disabled && this._canAdvanceToTab(tab),
|
||||
});
|
||||
|
||||
var isTabFocused = false;
|
||||
try {
|
||||
isTabFocused =
|
||||
(document.commandDispatcher.focusedElement == this.selectedItem);
|
||||
} catch (e) {}
|
||||
this.selectedItem = aNewTab;
|
||||
if (isTabFocused) {
|
||||
aNewTab.focus();
|
||||
} else if (this.getAttribute("setfocus") != "false") {
|
||||
let selectedPanel = this.tabbox.selectedPanel;
|
||||
document.commandDispatcher.advanceFocusIntoSubtree(selectedPanel);
|
||||
|
||||
// Make sure that the focus doesn't move outside the tabbox
|
||||
if (this.tabbox) {
|
||||
try {
|
||||
let el = document.commandDispatcher.focusedElement;
|
||||
while (el && el != this.tabbox.tabpanels) {
|
||||
if (el == this.tabbox || el == selectedPanel)
|
||||
return;
|
||||
el = el.parentNode;
|
||||
}
|
||||
aNewTab.focus();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="_canAdvanceToTab">
|
||||
<parameter name="aTab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
return true;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="advanceSelectedTab">
|
||||
<parameter name="aDir"/>
|
||||
<parameter name="aWrap"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var startTab = this.ariaFocusedItem || this.selectedItem;
|
||||
let newTab = this.findNextTab(startTab, {
|
||||
direction: aDir,
|
||||
wrap: aWrap,
|
||||
});
|
||||
this._selectNewTab(newTab, aDir, aWrap);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="appendItem">
|
||||
<parameter name="label"/>
|
||||
<parameter name="value"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var tab = document.createXULElement("tab");
|
||||
tab.setAttribute("label", label);
|
||||
tab.setAttribute("value", value);
|
||||
this.appendChild(tab);
|
||||
return tab;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="DOMMouseScroll">
|
||||
<![CDATA[
|
||||
if (this._prefService.getBoolPref("toolkit.tabbox.switchByScrolling")) {
|
||||
if (event.detail > 0) {
|
||||
this.advanceSelectedTab(1, false);
|
||||
} else {
|
||||
this.advanceSelectedTab(-1, false);
|
||||
}
|
||||
event.stopPropagation();
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
</bindings>
|
|
@ -408,7 +408,6 @@ tabbox {
|
|||
}
|
||||
|
||||
tabs {
|
||||
-moz-binding: url("chrome://global/content/bindings/tabbox.xml#tabs");
|
||||
-moz-box-orient: horizontal;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче