Bug 1476854 - Expose whether tabs are multiselected for accessibility. r?dao,jamie
MozReview-Commit-ID: 4LAwQxqrKiO
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -3719,16 +3719,17 @@ window._gBrowser = {
},
addToMultiSelectedTabs(aTab, skipPositionalAttributes) {
if (aTab.multiselected) {
return;
}
aTab.setAttribute("multiselected", "true");
+ aTab.setAttribute("aria-selected", "true");
this._multiSelectedTabsSet.add(aTab);
this._startMultiSelectChange();
if (this._multiSelectChangeRemovals.has(aTab)) {
this._multiSelectChangeRemovals.delete(aTab);
} else {
this._multiSelectChangeAdditions.add(aTab);
}
@@ -3758,16 +3759,17 @@ window._gBrowser = {
this.tabContainer._setPositionalAttributes();
},
removeFromMultiSelectedTabs(aTab, updatePositionalAttributes) {
if (!aTab.multiselected) {
return;
}
aTab.removeAttribute("multiselected");
+ aTab.removeAttribute("aria-selected");
this._multiSelectedTabsSet.delete(aTab);
this._startMultiSelectChange();
if (this._multiSelectChangeAdditions.has(aTab)) {
this._multiSelectChangeAdditions.delete(aTab);
} else {
this._multiSelectChangeRemovals.add(aTab);
}
if (updatePositionalAttributes) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -123,16 +123,21 @@
newValue => {
const LIMIT = 50;
return Math.max(newValue, LIMIT);
},
);
this._tabMinWidth = this._tabMinWidthPref;
+ XPCOMUtils.defineLazyPreferenceGetter(this, "_multiselectEnabledPref",
+ "browser.tabs.multiselect", null,
+ (pref, prevValue, newValue) => this._multiselectEnabled = newValue);
+ this._multiselectEnabled = this._multiselectEnabledPref;
+
this._setPositionalAttributes();
CustomizableUI.addListener(this);
this._updateNewTabVisibility();
XPCOMUtils.defineLazyPreferenceGetter(this, "_closeTabByDblclick",
"browser.tabs.closeTabByDblclick", false);
]]>
@@ -167,16 +172,27 @@
<property name="_tabMinWidth">
<setter>
this.style.setProperty("--tab-min-width", val + "px");
return val;
</setter>
</property>
+ <property name="_multiselectEnabled">
+ <setter>
+ // Unlike boolean HTML attributes, the value of boolean ARIA attributes actually matters.
+ this.setAttribute("aria-multiselectable", !!val);
+ return val;
+ </setter>
+ <getter>
+ return this.getAttribute("aria-multiselectable") == "true";
+ </getter>
+ </property>
+
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body><![CDATA[
switch (aTopic) {
case "nsPref:changed":
// This is has to deal with changes in
@@ -2064,17 +2080,17 @@
this._selectedOnFirstMouseDown = this.selected;
}
if (this.selected) {
this.style.MozUserFocus = "ignore";
} else {
// When browser.tabs.multiselect config is set to false,
// then we ignore the state of multi-selection keys (Ctrl/Cmd).
- const tabSelectionToggled = Services.prefs.getBoolPref("browser.tabs.multiselect") &&
+ const tabSelectionToggled = tabContainer._multiselectEnabled &&
(event.getModifierState("Accel") || event.shiftKey);
if (this.mOverCloseButton || this._overPlayingIcon || tabSelectionToggled) {
// Prevent tabbox.xml from selecting the tab.
event.stopPropagation();
}
}
@@ -2087,17 +2103,18 @@
// Make sure that clear-selection is released.
// Otherwise selection using Shift key may be broken.
gBrowser.unlockClearMultiSelection();
this.style.MozUserFocus = "";
</handler>
<handler event="click" button="0"><![CDATA[
- if (Services.prefs.getBoolPref("browser.tabs.multiselect")) {
+ let tabContainer = this.parentNode;
+ if (tabContainer._multiselectEnabled) {
let shiftKey = event.shiftKey;
let accelKey = event.getModifierState("Accel");
if (shiftKey) {
const lastSelectedTab = gBrowser.lastMultiSelectedTab;
if (!accelKey) {
gBrowser.selectedTab = lastSelectedTab;
// Make sure selection is cleared when tab-switch doesn't happen.
--- a/browser/base/content/test/tabs/browser_multiselect_tabs_using_selectedTabs.js
+++ b/browser/base/content/test/tabs/browser_multiselect_tabs_using_selectedTabs.js
@@ -7,30 +7,35 @@ const PREF_MULTISELECT_TABS = "browser.t
add_task(async function() {
await SpecialPowers.pushPrefEnv({
set: [
[PREF_MULTISELECT_TABS, true]
]
});
function testSelectedTabs(tabs) {
+ is(gBrowser.tabContainer.getAttribute("aria-multiselectable"), "true",
+ "tabbrowser should be marked as aria-multiselectable");
gBrowser.selectedTabs = tabs;
let {selectedTab, selectedTabs, _multiSelectedTabsSet} = gBrowser;
is(selectedTab, tabs[0], "The selected tab should be the expected one");
if (tabs.length == 1) {
ok(!selectedTab.multiselected, "Selected tab shouldn't be multi-selected because we are not in multi-select context yet");
ok(!_multiSelectedTabsSet.has(selectedTab), "Selected tab shouldn't be in _multiSelectedTabsSet");
is(selectedTabs.length, 1, "selectedTabs should contain a single tab");
is(selectedTabs[0], selectedTab, "selectedTabs should contain the selected tab");
+ ok(!selectedTab.hasAttribute("aria-selected"),
+ "Selected tab shouldn't be marked as aria-selected when only one tab is selected");
} else {
ok(selectedTabs.length, tabs.length, "Check number of selected tabs");
for (let tab of tabs) {
ok(tab.multiselected, "Tab should be multi-selected");
ok(_multiSelectedTabsSet.has(tab), "Tab should be in _multiSelectedTabsSet");
ok(selectedTabs.includes(tab), "Tab should be in selectedTabs");
+ is(tab.getAttribute("aria-selected"), "true", "Selected tab should be marked as aria-selected");
}
}
}
const tab1 = await addTab();
const tab2 = await addTab();
const tab3 = await addTab();