summaryrefslogtreecommitdiffstats
path: root/toolkit/content/widgets/tabbox.xml
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/widgets/tabbox.xml')
-rw-r--r--toolkit/content/widgets/tabbox.xml892
1 files changed, 892 insertions, 0 deletions
diff --git a/toolkit/content/widgets/tabbox.xml b/toolkit/content/widgets/tabbox.xml
new file mode 100644
index 000000000..02adb70b3
--- /dev/null
+++ b/toolkit/content/widgets/tabbox.xml
@@ -0,0 +1,892 @@
+<?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>
+
+ <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>
+
+ <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 -->
+ <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>
+
+ <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>
+