зеркало из https://github.com/mozilla/gecko-dev.git
893 строки
28 KiB
XML
893 строки
28 KiB
XML
<?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="tab-base">
|
|
<resources>
|
|
<stylesheet src="chrome://global/skin/tabbox.css"/>
|
|
</resources>
|
|
</binding>
|
|
|
|
<binding id="tabbox"
|
|
extends="chrome://global/content/bindings/tabbox.xml#tab-base">
|
|
<implementation implements="nsIDOMEventListener">
|
|
<property name="handleCtrlTab">
|
|
<setter>
|
|
<![CDATA[
|
|
this.setAttribute("handleCtrlTab", val);
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
<getter>
|
|
<![CDATA[
|
|
return (this.getAttribute("handleCtrlTab") != "false");
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<property name="handleCtrlPageUpDown">
|
|
<setter>
|
|
<![CDATA[
|
|
this.setAttribute("handleCtrlPageUpDown", val);
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
<getter>
|
|
<![CDATA[
|
|
return (this.getAttribute("handleCtrlPageUpDown") != "false");
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<field name="_handleMetaAltArrows" readonly="true">
|
|
/Mac/.test(navigator.platform)
|
|
</field>
|
|
|
|
<!-- _tabs and _tabpanels are deprecated, they exist only for
|
|
backwards compatibility. -->
|
|
<property name="_tabs" readonly="true" onget="return this.tabs;"/>
|
|
<property name="_tabpanels" readonly="true" onget="return this.tabpanels;"/>
|
|
|
|
<property name="tabs" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
return this.getElementsByTagNameNS(
|
|
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
|
"tabs").item(0);
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<property name="tabpanels" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
return this.getElementsByTagNameNS(
|
|
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
|
"tabpanels").item(0);
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<property name="selectedIndex">
|
|
<getter>
|
|
<![CDATA[
|
|
var tabs = this.tabs;
|
|
return tabs ? tabs.selectedIndex : -1;
|
|
]]>
|
|
</getter>
|
|
|
|
<setter>
|
|
<![CDATA[
|
|
var tabs = this.tabs;
|
|
if (tabs)
|
|
tabs.selectedIndex = val;
|
|
this.setAttribute("selectedIndex", val);
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<property name="selectedTab">
|
|
<getter>
|
|
<![CDATA[
|
|
var tabs = this.tabs;
|
|
return tabs && tabs.selectedItem;
|
|
]]>
|
|
</getter>
|
|
|
|
<setter>
|
|
<![CDATA[
|
|
if (val) {
|
|
var tabs = this.tabs;
|
|
if (tabs)
|
|
tabs.selectedItem = val;
|
|
}
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<property name="selectedPanel">
|
|
<getter>
|
|
<![CDATA[
|
|
var tabpanels = this.tabpanels;
|
|
return tabpanels && tabpanels.selectedPanel;
|
|
]]>
|
|
</getter>
|
|
|
|
<setter>
|
|
<![CDATA[
|
|
if (val) {
|
|
var tabpanels = this.tabpanels;
|
|
if (tabpanels)
|
|
tabpanels.selectedPanel = val;
|
|
}
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<method name="handleEvent">
|
|
<parameter name="event"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!event.isTrusted) {
|
|
// Don't let untrusted events mess with tabs.
|
|
return;
|
|
}
|
|
|
|
// Don't check if the event was already consumed because tab
|
|
// navigation should always work for better user experience.
|
|
|
|
switch (event.keyCode) {
|
|
case event.DOM_VK_TAB:
|
|
if (event.ctrlKey && !event.altKey && !event.metaKey)
|
|
if (this.tabs && this.handleCtrlTab) {
|
|
this.tabs.advanceSelectedTab(event.shiftKey ? -1 : 1, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
case event.DOM_VK_PAGE_UP:
|
|
if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey)
|
|
if (this.tabs && this.handleCtrlPageUpDown) {
|
|
this.tabs.advanceSelectedTab(-1, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
case event.DOM_VK_PAGE_DOWN:
|
|
if (event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey)
|
|
if (this.tabs && this.handleCtrlPageUpDown) {
|
|
this.tabs.advanceSelectedTab(1, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
case event.DOM_VK_LEFT:
|
|
if (event.metaKey && event.altKey && !event.shiftKey && !event.ctrlKey)
|
|
if (this.tabs && this._handleMetaAltArrows) {
|
|
var offset = window.getComputedStyle(this, "")
|
|
.direction == "ltr" ? -1 : 1;
|
|
this.tabs.advanceSelectedTab(offset, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
case event.DOM_VK_RIGHT:
|
|
if (event.metaKey && event.altKey && !event.shiftKey && !event.ctrlKey)
|
|
if (this.tabs && this._handleMetaAltArrows) {
|
|
offset = window.getComputedStyle(this, "")
|
|
.direction == "ltr" ? 1 : -1;
|
|
this.tabs.advanceSelectedTab(offset, true);
|
|
event.preventDefault();
|
|
}
|
|
break;
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<field name="_eventNode">this</field>
|
|
|
|
<property name="eventNode" onget="return this._eventNode;">
|
|
<setter>
|
|
<![CDATA[
|
|
if (val != this._eventNode) {
|
|
const nsIEventListenerService =
|
|
Components.interfaces.nsIEventListenerService;
|
|
let els = Components.classes["@mozilla.org/eventlistenerservice;1"]
|
|
.getService(nsIEventListenerService);
|
|
els.addSystemEventListener(val, "keydown", this, false);
|
|
els.removeSystemEventListener(this._eventNode, "keydown", this, false);
|
|
this._eventNode = val;
|
|
}
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<constructor>
|
|
switch (this.getAttribute("eventnode")) {
|
|
case "parent": this._eventNode = this.parentNode; break;
|
|
case "window": this._eventNode = window; break;
|
|
case "document": this._eventNode = document; break;
|
|
}
|
|
const nsIEventListenerService =
|
|
Components.interfaces.nsIEventListenerService;
|
|
let els = Components.classes["@mozilla.org/eventlistenerservice;1"]
|
|
.getService(nsIEventListenerService);
|
|
els.addSystemEventListener(this._eventNode, "keydown", this, false);
|
|
</constructor>
|
|
|
|
<destructor>
|
|
const nsIEventListenerService =
|
|
Components.interfaces.nsIEventListenerService;
|
|
let els = Components.classes["@mozilla.org/eventlistenerservice;1"]
|
|
.getService(nsIEventListenerService);
|
|
els.removeSystemEventListener(this._eventNode, "keydown", this, false);
|
|
</destructor>
|
|
</implementation>
|
|
</binding>
|
|
|
|
<binding id="tabs" role="xul:tabs"
|
|
extends="chrome://global/content/bindings/general.xml#basecontrol">
|
|
<resources>
|
|
<stylesheet src="chrome://global/skin/tabbox.css"/>
|
|
</resources>
|
|
|
|
<content>
|
|
<xul:spacer class="tabs-left"/>
|
|
<children/>
|
|
<xul:spacer class="tabs-right" flex="1"/>
|
|
</content>
|
|
|
|
<implementation implements="nsIDOMXULSelectControlElement, nsIDOMXULRelatedElement">
|
|
<constructor>
|
|
<![CDATA[
|
|
// first and last tabs need to be able to have unique styles
|
|
// and also need to select first tab on startup.
|
|
if (this.firstChild)
|
|
this.firstChild.setAttribute("first-tab", "true");
|
|
if (this.lastChild)
|
|
this.lastChild.setAttribute("last-tab", "true");
|
|
|
|
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.childNodes;
|
|
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>
|
|
|
|
<!-- 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 linkedPanelElm = null;
|
|
|
|
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.childNodes[tabElmIdx];
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<!-- nsIDOMXULSelectControlElement -->
|
|
<property name="itemCount" readonly="true"
|
|
onget="return this.childNodes.length"/>
|
|
|
|
<property name="value" onget="return this.getAttribute('value');">
|
|
<setter>
|
|
<![CDATA[
|
|
this.setAttribute("value", val);
|
|
var children = this.childNodes;
|
|
for (var c = children.length - 1; c >= 0; c--) {
|
|
if (children[c].value == val) {
|
|
this.selectedIndex = c;
|
|
break;
|
|
}
|
|
}
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<property name="tabbox" readonly="true">
|
|
<getter><![CDATA[
|
|
let parent = this.parentNode;
|
|
while (parent) {
|
|
if (parent.localName == "tabbox") {
|
|
break;
|
|
}
|
|
parent = parent.parentNode;
|
|
}
|
|
|
|
// Avoid having to recalculate the tabbox property each time
|
|
Object.defineProperty(this, "tabbox", {
|
|
configurable: true,
|
|
enumerable: true,
|
|
writable: true,
|
|
value: parent
|
|
});
|
|
return parent;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<!-- _tabbox is deprecated, it exists only for backwards compatibility. -->
|
|
<field name="_tabbox" readonly="true"><![CDATA[
|
|
this.tabbox;
|
|
]]></field>
|
|
|
|
<property name="selectedIndex">
|
|
<getter>
|
|
<![CDATA[
|
|
const tabs = this.childNodes;
|
|
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) {
|
|
var alreadySelected = tab.selected;
|
|
|
|
Array.forEach(this.childNodes, function (aTab) {
|
|
if (aTab.selected && aTab != tab)
|
|
aTab._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;
|
|
}
|
|
|
|
if (!alreadySelected) {
|
|
// Fire an onselect event for the tabs element.
|
|
var event = document.createEvent('Events');
|
|
event.initEvent('select', true, true);
|
|
this.dispatchEvent(event);
|
|
}
|
|
}
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<property name="selectedItem">
|
|
<getter>
|
|
<![CDATA[
|
|
const tabs = this.childNodes;
|
|
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>
|
|
|
|
<method name="getIndexOfItem">
|
|
<parameter name="item"/>
|
|
<body>
|
|
<![CDATA[
|
|
return Array.indexOf(this.childNodes, item);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="getItemAtIndex">
|
|
<parameter name="index"/>
|
|
<body>
|
|
<![CDATA[
|
|
return this.childNodes.item(index);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_selectNewTab">
|
|
<parameter name="aNewTab"/>
|
|
<parameter name="aFallbackDir"/>
|
|
<parameter name="aWrap"/>
|
|
<body>
|
|
<![CDATA[
|
|
var requestedTab = aNewTab;
|
|
while (aNewTab.hidden || aNewTab.disabled || !this._canAdvanceToTab(aNewTab)) {
|
|
aNewTab = aFallbackDir == -1 ? aNewTab.previousSibling : aNewTab.nextSibling;
|
|
if (!aNewTab && aWrap)
|
|
aNewTab = aFallbackDir == -1 ? this.childNodes[this.childNodes.length - 1] :
|
|
this.childNodes[0];
|
|
if (!aNewTab || aNewTab == requestedTab)
|
|
return;
|
|
}
|
|
|
|
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.selectedItem;
|
|
var next = startTab[aDir == -1 ? "previousSibling" : "nextSibling"];
|
|
if (!next && aWrap) {
|
|
next = aDir == -1 ? this.childNodes[this.childNodes.length - 1] :
|
|
this.childNodes[0];
|
|
}
|
|
if (next && next != startTab) {
|
|
this._selectNewTab(next, aDir, aWrap);
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="appendItem">
|
|
<parameter name="label"/>
|
|
<parameter name="value"/>
|
|
<body>
|
|
<![CDATA[
|
|
var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
var tab = document.createElementNS(XULNS, "tab");
|
|
tab.setAttribute("label", label);
|
|
tab.setAttribute("value", value);
|
|
this.appendChild(tab);
|
|
return tab;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="insertItemAt">
|
|
<parameter name="index"/>
|
|
<parameter name="label"/>
|
|
<parameter name="value"/>
|
|
<body>
|
|
<![CDATA[
|
|
var XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
var tab = document.createElementNS(XULNS, "tab");
|
|
tab.setAttribute("label", label);
|
|
tab.setAttribute("value", value);
|
|
var before = this.getItemAtIndex(index);
|
|
if (before)
|
|
this.insertBefore(tab, before);
|
|
else
|
|
this.appendChild(tab);
|
|
return tab;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="removeItemAt">
|
|
<parameter name="index"/>
|
|
<body>
|
|
<![CDATA[
|
|
var remove = this.getItemAtIndex(index);
|
|
if (remove)
|
|
this.removeChild(remove);
|
|
return remove;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
</implementation>
|
|
|
|
#ifdef MOZ_WIDGET_GTK
|
|
<handlers>
|
|
<handler event="DOMMouseScroll">
|
|
<![CDATA[
|
|
if (event.detail > 0)
|
|
this.advanceSelectedTab(1, false);
|
|
else
|
|
this.advanceSelectedTab(-1, false);
|
|
|
|
event.stopPropagation();
|
|
]]>
|
|
</handler>
|
|
</handlers>
|
|
#endif
|
|
</binding>
|
|
|
|
<binding id="tabpanels" role="xul:tabpanels"
|
|
extends="chrome://global/content/bindings/tabbox.xml#tab-base">
|
|
<implementation implements="nsIDOMXULRelatedElement">
|
|
<!-- nsIDOMXULRelatedElement -->
|
|
<method name="getRelatedElement">
|
|
<parameter name="aTabPanelElm"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!aTabPanelElm)
|
|
return null;
|
|
|
|
let tabboxElm = this.tabbox;
|
|
if (!tabboxElm)
|
|
return null;
|
|
|
|
let tabsElm = tabboxElm.tabs;
|
|
if (!tabsElm)
|
|
return null;
|
|
|
|
// Return tab element having 'linkedpanel' attribute equal to the id
|
|
// of the tab panel or the same index as the tab panel element.
|
|
let tabpanelIdx = Array.indexOf(this.childNodes, aTabPanelElm);
|
|
if (tabpanelIdx == -1)
|
|
return null;
|
|
|
|
let tabElms = tabsElm.childNodes;
|
|
let tabElmFromIndex = tabElms[tabpanelIdx];
|
|
|
|
let tabpanelId = aTabPanelElm.id;
|
|
if (tabpanelId) {
|
|
for (let idx = 0; idx < tabElms.length; idx++) {
|
|
var tabElm = tabElms[idx];
|
|
if (tabElm.linkedPanel == tabpanelId)
|
|
return tabElm;
|
|
}
|
|
}
|
|
|
|
return tabElmFromIndex;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<!-- public -->
|
|
<property name="tabbox" readonly="true">
|
|
<getter><![CDATA[
|
|
let parent = this.parentNode;
|
|
while (parent) {
|
|
if (parent.localName == "tabbox") {
|
|
break;
|
|
}
|
|
parent = parent.parentNode;
|
|
}
|
|
|
|
// Avoid having to recalculate the tabbox property each time
|
|
Object.defineProperty(this, "tabbox", {
|
|
configurable: true,
|
|
enumerable: true,
|
|
writable: true,
|
|
value: parent
|
|
});
|
|
return parent;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<field name="_selectedPanel">this.childNodes.item(this.selectedIndex)</field>
|
|
|
|
<property name="selectedIndex">
|
|
<getter>
|
|
<![CDATA[
|
|
var indexStr = this.getAttribute("selectedIndex");
|
|
return indexStr ? parseInt(indexStr) : -1;
|
|
]]>
|
|
</getter>
|
|
|
|
<setter>
|
|
<![CDATA[
|
|
if (val < 0 || val >= this.childNodes.length)
|
|
return val;
|
|
var panel = this._selectedPanel;
|
|
this._selectedPanel = this.childNodes[val];
|
|
this.setAttribute("selectedIndex", val);
|
|
if (this._selectedPanel != panel) {
|
|
var event = document.createEvent("Events");
|
|
event.initEvent("select", true, true);
|
|
this.dispatchEvent(event);
|
|
}
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<property name="selectedPanel">
|
|
<getter>
|
|
<![CDATA[
|
|
return this._selectedPanel;
|
|
]]>
|
|
</getter>
|
|
|
|
<setter>
|
|
<![CDATA[
|
|
var selectedIndex = -1;
|
|
for (var panel = val; panel != null; panel = panel.previousSibling)
|
|
++selectedIndex;
|
|
this.selectedIndex = selectedIndex;
|
|
return val;
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
</implementation>
|
|
</binding>
|
|
|
|
<binding id="tab" display="xul:button" role="xul:tab"
|
|
extends="chrome://global/content/bindings/general.xml#control-item">
|
|
<resources>
|
|
<stylesheet src="chrome://global/skin/tabbox.css"/>
|
|
</resources>
|
|
|
|
<content>
|
|
<xul:hbox class="tab-middle box-inherit" xbl:inherits="align,dir,pack,orient,selected,visuallyselected" flex="1">
|
|
<xul:image class="tab-icon"
|
|
xbl:inherits="validate,src=image"
|
|
role="presentation"/>
|
|
<xul:label class="tab-text"
|
|
xbl:inherits="value=label,accesskey,crop,disabled"
|
|
flex="1"
|
|
role="presentation"/>
|
|
</xul:hbox>
|
|
</content>
|
|
|
|
<implementation implements="nsIDOMXULSelectControlItemElement">
|
|
<property name="control" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
var parent = this.parentNode;
|
|
if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
|
|
return parent;
|
|
return null;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<property name="selected" readonly="true"
|
|
onget="return this.getAttribute('selected') == 'true';"/>
|
|
|
|
<property name="_selected">
|
|
<setter><![CDATA[
|
|
if (val) {
|
|
this.setAttribute("selected", "true");
|
|
this.setAttribute("visuallyselected", "true");
|
|
} else {
|
|
this.removeAttribute("selected");
|
|
this.removeAttribute("visuallyselected");
|
|
}
|
|
|
|
this._setPositionAttributes(val);
|
|
|
|
return val;
|
|
]]></setter>
|
|
</property>
|
|
|
|
<method name="_setPositionAttributes">
|
|
<parameter name="aSelected"/>
|
|
<body><![CDATA[
|
|
if (this.previousSibling && this.previousSibling.localName == "tab") {
|
|
if (aSelected)
|
|
this.previousSibling.setAttribute("beforeselected", "true");
|
|
else
|
|
this.previousSibling.removeAttribute("beforeselected");
|
|
this.removeAttribute("first-tab");
|
|
} else {
|
|
this.setAttribute("first-tab", "true");
|
|
}
|
|
|
|
if (this.nextSibling && this.nextSibling.localName == "tab") {
|
|
if (aSelected)
|
|
this.nextSibling.setAttribute("afterselected", "true");
|
|
else
|
|
this.nextSibling.removeAttribute("afterselected");
|
|
this.removeAttribute("last-tab");
|
|
} else {
|
|
this.setAttribute("last-tab", "true");
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<property name="linkedPanel" onget="return this.getAttribute('linkedpanel')"
|
|
onset="this.setAttribute('linkedpanel', val); return val;"/>
|
|
|
|
<field name="arrowKeysShouldWrap" readonly="true">
|
|
/Mac/.test(navigator.platform)
|
|
</field>
|
|
<property name="TelemetryStopwatch" readonly="true">
|
|
<getter><![CDATA[
|
|
let module = {};
|
|
Cu.import("resource://gre/modules/TelemetryStopwatch.jsm", module);
|
|
Object.defineProperty(this, "TelemetryStopwatch", {
|
|
configurable: true,
|
|
enumerable: true,
|
|
writable: true,
|
|
value: module.TelemetryStopwatch
|
|
});
|
|
return module.TelemetryStopwatch;
|
|
]]></getter>
|
|
</property>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="mousedown" button="0">
|
|
<![CDATA[
|
|
if (this.disabled)
|
|
return;
|
|
|
|
if (this != this.parentNode.selectedItem) { // Not selected yet
|
|
let stopwatchid = this.parentNode.getAttribute("stopwatchid");
|
|
if (stopwatchid) {
|
|
this.TelemetryStopwatch.start(stopwatchid);
|
|
}
|
|
|
|
// Call this before setting the 'ignorefocus' attribute because this
|
|
// will pass on focus if the formerly selected tab was focused as well.
|
|
this.parentNode._selectNewTab(this);
|
|
|
|
var isTabFocused = false;
|
|
try {
|
|
isTabFocused = (document.commandDispatcher.focusedElement == this);
|
|
} catch (e) {}
|
|
|
|
// Set '-moz-user-focus' to 'ignore' so that PostHandleEvent() can't
|
|
// focus the tab; we only want tabs to be focusable by the mouse if
|
|
// they are already focused. After a short timeout we'll reset
|
|
// '-moz-user-focus' so that tabs can be focused by keyboard again.
|
|
if (!isTabFocused) {
|
|
this.setAttribute("ignorefocus", "true");
|
|
setTimeout(tab => tab.removeAttribute("ignorefocus"), 0, this);
|
|
}
|
|
|
|
if (stopwatchid) {
|
|
this.TelemetryStopwatch.finish(stopwatchid);
|
|
}
|
|
}
|
|
// Otherwise this tab is already selected and we will fall
|
|
// through to mousedown behavior which sets focus on the current tab,
|
|
// Only a click on an already selected tab should focus the tab itself.
|
|
]]>
|
|
</handler>
|
|
|
|
<handler event="keydown" keycode="VK_LEFT" group="system" preventdefault="true">
|
|
<![CDATA[
|
|
var direction = window.getComputedStyle(this.parentNode, null).direction;
|
|
this.parentNode.advanceSelectedTab(direction == 'ltr' ? -1 : 1, this.arrowKeysShouldWrap);
|
|
]]>
|
|
</handler>
|
|
|
|
<handler event="keydown" keycode="VK_RIGHT" group="system" preventdefault="true">
|
|
<![CDATA[
|
|
var direction = window.getComputedStyle(this.parentNode, null).direction;
|
|
this.parentNode.advanceSelectedTab(direction == 'ltr' ? 1 : -1, this.arrowKeysShouldWrap);
|
|
]]>
|
|
</handler>
|
|
|
|
<handler event="keydown" keycode="VK_UP" group="system" preventdefault="true">
|
|
<![CDATA[
|
|
this.parentNode.advanceSelectedTab(-1, this.arrowKeysShouldWrap);
|
|
]]>
|
|
</handler>
|
|
|
|
<handler event="keydown" keycode="VK_DOWN" group="system" preventdefault="true">
|
|
<![CDATA[
|
|
this.parentNode.advanceSelectedTab(1, this.arrowKeysShouldWrap);
|
|
]]>
|
|
</handler>
|
|
|
|
<handler event="keydown" keycode="VK_HOME" group="system" preventdefault="true">
|
|
<![CDATA[
|
|
this.parentNode._selectNewTab(this.parentNode.childNodes[0]);
|
|
]]>
|
|
</handler>
|
|
|
|
<handler event="keydown" keycode="VK_END" group="system" preventdefault="true">
|
|
<![CDATA[
|
|
var tabs = this.parentNode.childNodes;
|
|
this.parentNode._selectNewTab(tabs[tabs.length - 1], -1);
|
|
]]>
|
|
</handler>
|
|
</handlers>
|
|
</binding>
|
|
|
|
</bindings>
|
|
|