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:
Andrew Swan 2019-06-20 14:09:37 -07:00
Родитель f1d137eff5
Коммит 7696519cff
22 изменённых файлов: 2267 добавлений и 2442 удалений

Просмотреть файл

@ -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;
}