diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js index ce81ad3da207..53220a714ffc 100644 --- a/browser/components/preferences/main.js +++ b/browser/components/preferences/main.js @@ -2247,9 +2247,8 @@ var gMainPane = { // 1/2/4 values set via about:config should persist return this._storedFullKeyboardNavigation; } - // When the checkbox is unchecked, this pref shouldn't exist - // at all. - return undefined; + // When the checkbox is unchecked, default to just text controls. + return 1; }, /** diff --git a/browser/components/preferences/tests/browser_keyboardfocus.js b/browser/components/preferences/tests/browser_keyboardfocus.js index bed452b679b8..89576b926a36 100644 --- a/browser/components/preferences/tests/browser_keyboardfocus.js +++ b/browser/components/preferences/tests/browser_keyboardfocus.js @@ -13,40 +13,42 @@ add_task(async function () { let checkbox = gBrowser.contentDocument.querySelector( "#useFullKeyboardNavigation" ); - Assert.ok( - !Services.prefs.getIntPref("accessibility.tabfocus", undefined), - "no pref value should exist" + Assert.equal( + Services.prefs.getIntPref("accessibility.tabfocus"), + 7, + "default should be full keyboard access" ); Assert.ok( - !checkbox.checked, - "checkbox should be unchecked before clicking on checkbox" + checkbox.checked, + "checkbox should be checked before clicking on checkbox" ); checkbox.click(); Assert.equal( Services.prefs.getIntPref("accessibility.tabfocus"), - 7, + 1, "Prefstore should reflect checkbox's associated numeric value" ); Assert.ok( - checkbox.checked, - "checkbox should be checked after clicking on checkbox" + !checkbox.checked, + "checkbox should be unchecked after clicking on checkbox" ); checkbox.click(); Assert.ok( - !checkbox.checked, - "checkbox should be unchecked after clicking on checkbox" + checkbox.checked, + "checkbox should be checked after clicking on checkbox" ); - Assert.ok( - !Services.prefs.getIntPref("accessibility.tabfocus", undefined), - "No pref value should exist" + Assert.equal( + Services.prefs.getIntPref("accessibility.tabfocus"), + 7, + "Should restore default value" ); BrowserTestUtils.removeTab(gBrowser.selectedTab); - SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 4]] }); + await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 4]] }); await launchPreferences(); checkbox = gBrowser.contentDocument.querySelector( "#useFullKeyboardNavigation" @@ -57,20 +59,20 @@ add_task(async function () { "checkbox should stay unchecked after setting non-7 pref value" ); Assert.equal( - Services.prefs.getIntPref("accessibility.tabfocus", 0), + Services.prefs.getIntPref("accessibility.tabfocus"), 4, "pref should have value in store" ); BrowserTestUtils.removeTab(gBrowser.selectedTab); - SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] }); + await SpecialPowers.pushPrefEnv({ set: [["accessibility.tabfocus", 7]] }); await launchPreferences(); checkbox = gBrowser.contentDocument.querySelector( "#useFullKeyboardNavigation" ); Assert.equal( - Services.prefs.getIntPref("accessibility.tabfocus", 0), + Services.prefs.getIntPref("accessibility.tabfocus"), 7, "Pref value should update after modification" ); diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 87fd81bfa325..164017574bbf 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -105,8 +105,6 @@ using namespace mozilla; using namespace mozilla::dom; -int32_t nsIContent::sTabFocusModel = eTabFocus_any; -bool nsIContent::sTabFocusModelAppliesToXUL = false; uint64_t nsMutationGuard::sGeneration = 0; NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent) diff --git a/dom/base/TabFocusModel.h b/dom/base/TabFocusModel.h new file mode 100644 index 000000000000..11b3377fd935 --- /dev/null +++ b/dom/base/TabFocusModel.h @@ -0,0 +1,32 @@ +/* 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/. */ + +#ifndef mozilla_TabFocusModel_h +#define mozilla_TabFocusModel_h + +#include "mozilla/StaticPrefs_accessibility.h" + +namespace mozilla { + +enum class TabFocusableType : uint8_t { + TextControls = 1, // Textboxes and lists always tabbable + FormElements = 1 << 1, // Non-text form elements + Links = 1 << 2, // Links + Any = TextControls | FormElements | Links, +}; + +class TabFocusModel final { + public: + static bool AppliesToXUL() { + return StaticPrefs::accessibility_tabfocus_applies_to_xul(); + } + + static bool IsTabFocusable(TabFocusableType aType) { + return StaticPrefs::accessibility_tabfocus() & int32_t(aType); + } +}; + +} // namespace mozilla + +#endif diff --git a/dom/base/moz.build b/dom/base/moz.build index 634ae904d25d..d309cd1c4693 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -135,6 +135,7 @@ EXPORTS.mozilla += [ "ScriptableContentIterator.h", "ScrollingMetrics.h", "SelectionChangeEventDispatcher.h", + "TabFocusModel.h", "TextInputProcessor.h", "UseCounter.h", ] diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index a189a93563a9..474d6fe02133 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -173,7 +173,6 @@ bool nsFocusManager::sTestMode = false; uint64_t nsFocusManager::sFocusActionCounter = 0; static const char* kObservedPrefs[] = {"accessibility.browsewithcaret", - "accessibility.tabfocus_applies_to_xul", "focusmanager.testmode", nullptr}; nsFocusManager::nsFocusManager() @@ -198,10 +197,6 @@ nsFocusManager::~nsFocusManager() { nsresult nsFocusManager::Init() { sInstance = new nsFocusManager(); - nsIContent::sTabFocusModelAppliesToXUL = - Preferences::GetBool("accessibility.tabfocus_applies_to_xul", - nsIContent::sTabFocusModelAppliesToXUL); - sTestMode = Preferences::GetBool("focusmanager.testmode", false); Preferences::RegisterCallbacks(nsFocusManager::PrefChanged, kObservedPrefs, @@ -229,10 +224,6 @@ void nsFocusManager::PrefChanged(const char* aPref) { nsDependentCString pref(aPref); if (pref.EqualsLiteral("accessibility.browsewithcaret")) { UpdateCaretForCaretBrowsingMode(); - } else if (pref.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) { - nsIContent::sTabFocusModelAppliesToXUL = - Preferences::GetBool("accessibility.tabfocus_applies_to_xul", - nsIContent::sTabFocusModelAppliesToXUL); } else if (pref.EqualsLiteral("focusmanager.testmode")) { sTestMode = Preferences::GetBool("focusmanager.testmode", false); } @@ -3362,9 +3353,6 @@ nsresult nsFocusManager::DetermineElementToMoveFocus( doc = aWindow->GetExtantDoc(); if (!doc) return NS_OK; - LookAndFeel::GetInt(LookAndFeel::IntID::TabFocusModel, - &nsIContent::sTabFocusModel); - // True if we are navigating by document (F6/Shift+F6) or false if we are // navigating by element (Tab/Shift+Tab). const bool forDocumentNavigation = diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h index 700855370fcc..ec04f3eda585 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -753,21 +753,6 @@ class nsIContent : public nsINode { virtual void DumpContent(FILE* out = stdout, int32_t aIndent = 0, bool aDumpAll = true) const = 0; #endif - - enum ETabFocusType { - eTabFocus_textControlsMask = - (1 << 0), // textboxes and lists always tabbable - eTabFocus_formElementsMask = (1 << 1), // non-text form elements - eTabFocus_linksMask = (1 << 2), // links - eTabFocus_any = 1 + (1 << 1) + (1 << 2) // everything that can be focused - }; - - // Tab focus model bit field: - static int32_t sTabFocusModel; - - // accessibility.tabfocus_applies_to_xul pref - if it is set to true, - // the tabfocus bit field applies to xul elements. - static bool sTabFocusModelAppliesToXUL; }; NON_VIRTUAL_ADDREF_RELEASE(nsIContent) diff --git a/dom/html/HTMLAnchorElement.cpp b/dom/html/HTMLAnchorElement.cpp index 54fadd31486e..d3cedcececde 100644 --- a/dom/html/HTMLAnchorElement.cpp +++ b/dom/html/HTMLAnchorElement.cpp @@ -14,7 +14,7 @@ #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsGkAtoms.h" -#include "nsAttrValueOrString.h" +#include "mozilla/TabFocusModel.h" #include "mozilla/dom/Document.h" #include "nsPresContext.h" #include "nsIURI.h" @@ -119,7 +119,7 @@ bool HTMLAnchorElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, } } - if ((sTabFocusModel & eTabFocus_linksMask) == 0) { + if (!TabFocusModel::IsTabFocusable(TabFocusableType::Links)) { *aTabIndex = -1; } *aIsFocusable = true; diff --git a/dom/html/HTMLImageElement.cpp b/dom/html/HTMLImageElement.cpp index 691216d15274..a2538baaf4f3 100644 --- a/dom/html/HTMLImageElement.cpp +++ b/dom/html/HTMLImageElement.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/PresShell.h" +#include "mozilla/TabFocusModel.h" #include "mozilla/dom/BindContext.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/HTMLImageElementBinding.h" @@ -494,7 +495,8 @@ bool HTMLImageElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, if (IsInComposedDoc() && FindImageMap()) { // Use tab index on individual map areas. - *aTabIndex = (sTabFocusModel & eTabFocus_linksMask) ? 0 : -1; + *aTabIndex = + TabFocusModel::IsTabFocusable(TabFocusableType::Links) ? 0 : -1; // Image map is not focusable itself, but flag as tabbable // so that image map areas get walked into. *aIsFocusable = false; @@ -502,7 +504,9 @@ bool HTMLImageElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable, } // Can be in tab order if tabindex >=0 and form controls are tabbable. - *aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask) ? tabIndex : -1; + *aTabIndex = TabFocusModel::IsTabFocusable(TabFocusableType::FormElements) + ? tabIndex + : -1; *aIsFocusable = IsFormControlDefaultFocusable(aWithMouse) && (tabIndex >= 0 || GetTabIndexAttrValue().isSome()); diff --git a/dom/mathml/MathMLElement.cpp b/dom/mathml/MathMLElement.cpp index af5e9bc22bef..6da9fb6ce33b 100644 --- a/dom/mathml/MathMLElement.cpp +++ b/dom/mathml/MathMLElement.cpp @@ -7,10 +7,10 @@ #include "mozilla/dom/MathMLElement.h" #include "base/compiler_specific.h" +#include "mozilla/TabFocusModel.h" #include "mozilla/dom/BindContext.h" #include "mozilla/ArrayUtils.h" #include "mozilla/EventListenerManager.h" -#include "mozilla/FontPropertyTypes.h" #include "mozilla/StaticPrefs_mathml.h" #include "mozilla/TextUtils.h" #include "nsGkAtoms.h" @@ -20,7 +20,6 @@ #include "nsStyleConsts.h" #include "mozilla/dom/Document.h" #include "nsPresContext.h" -#include "mozAutoDocUpdate.h" #include "nsIScriptError.h" #include "nsContentUtils.h" #include "nsIURI.h" @@ -637,7 +636,7 @@ Focusable MathMLElement::IsFocusableWithoutStyle(bool aWithMouse) { return {}; } - if ((sTabFocusModel & eTabFocus_linksMask) == 0) { + if (!TabFocusModel::IsTabFocusable(TabFocusableType::Links)) { tabIndex = -1; } diff --git a/dom/svg/SVGAElement.cpp b/dom/svg/SVGAElement.cpp index 1533cceb34b8..ef183512b1d6 100644 --- a/dom/svg/SVGAElement.cpp +++ b/dom/svg/SVGAElement.cpp @@ -6,11 +6,10 @@ #include "mozilla/dom/SVGAElement.h" -#include "mozilla/Attributes.h" #include "mozilla/EventDispatcher.h" -#include "mozilla/dom/BindContext.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/SVGAElementBinding.h" +#include "mozilla/TabFocusModel.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsGkAtoms.h" @@ -182,7 +181,7 @@ Focusable SVGAElement::IsFocusableWithoutStyle(bool aWithMouse) { return {}; } } - if ((sTabFocusModel & eTabFocus_linksMask) == 0) { + if (!TabFocusModel::IsTabFocusable(TabFocusableType::Links)) { result.mTabIndex = -1; } return result; diff --git a/dom/svg/test/test_tabindex.html b/dom/svg/test/test_tabindex.html index 65315420bc8e..3d29d1070bd9 100644 --- a/dom/svg/test/test_tabindex.html +++ b/dom/svg/test/test_tabindex.html @@ -37,7 +37,6 @@ function main() { var t = document.getElementById("t"); var l1 = document.getElementById("l1"); var l2 = document.getElementById("l2"); - const isMac = ("nsILocalFileMac" in SpecialPowers.Ci); try { // Step 1: Checking by assigning tabIndex @@ -74,20 +73,10 @@ function main() { is(document.activeElement.tabIndex, 3, "The active element tabindex is 3"); synthesizeKey("KEY_Tab"); - // On Mac, SVG link elements should not be focused. - if (isMac) { - is(document.activeElement.tabIndex, 6, "The active element tabindex is 6"); - } else { - is(document.activeElement.tabIndex, 4, "The active element tabindex is 4"); - } + is(document.activeElement.tabIndex, 4, "The active element tabindex is 4"); synthesizeKey("KEY_Tab"); - // On Mac, SVG link elements should not be focused. - if (isMac) { - is(document.activeElement.tabIndex, 7, "The active element tabindex is 7"); - } else { - is(document.activeElement.tabIndex, 5, "The active element tabindex is 5"); - } + is(document.activeElement.tabIndex, 5, "The active element tabindex is 5"); } catch (e) { ok(false, "Got unexpected exception" + e); } diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 5bccc15f6985..7e2aba1de619 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -50,6 +50,7 @@ #include "mozilla/StaticAnalysisFunctions.h" #include "mozilla/StaticPrefs_javascript.h" #include "mozilla/StaticPtr.h" +#include "mozilla/TabFocusModel.h" #include "mozilla/TaskController.h" #include "mozilla/UniquePtr.h" #include "mozilla/URLExtraData.h" @@ -402,15 +403,12 @@ nsXULElement::XULFocusability nsXULElement::GetXULFocusability( result.mForcedFocusable.emplace(true); result.mForcedTabIndexIfFocusable.emplace(attrVal.value()); } - if (xulControl && sTabFocusModelAppliesToXUL && - !(sTabFocusModel & eTabFocus_formElementsMask) && IsNonList(mNodeInfo)) { + if (xulControl && TabFocusModel::AppliesToXUL() && + !TabFocusModel::IsTabFocusable(TabFocusableType::FormElements) && + IsNonList(mNodeInfo)) { // By default, the tab focus model doesn't apply to xul element on any - // system but OS X. on OS X we're following it for UI elements (XUL) as - // sTabFocusModel is based on "Full Keyboard Access" system setting (see - // mac/nsILookAndFeel). both textboxes and list elements (i.e. trees and - // list) should always be focusable (textboxes are handled as html:input) - // For compatibility, we only do this for controls, otherwise elements - // like cannot take this focus. + // system but OS X. For compatibility, we only do this for controls, + // otherwise elements like cannot take this focus. result.mForcedTabIndexIfFocusable = Some(-1); } return result; diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index fab417543339..5222b09f8e1a 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -200,6 +200,22 @@ # Prefs starting with "accessibility." #--------------------------------------------------------------------------- +# Tab focus model bit field: +# 1 focuses text controls, 2 focuses other form elements, 4 adds links. +# Most users will want 1, 3, or 7. On macOS we expose a checkbox to alter +# between 7 and 3. +- name: accessibility.tabfocus + type: int32_t + value: 7 + mirror: always + +# Only on mac tabfocus is expected to handle UI widgets as well as web content. +# FIXME(emilio): This is weird now that we have a lot of HTML in our pages. +- name: accessibility.tabfocus_applies_to_xul + type: bool + value: @IS_XP_MACOSX@ + mirror: always + - name: accessibility.accesskeycausesactivation type: bool value: true diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 4161a504146f..a6769a159346 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -472,20 +472,6 @@ pref("accessibility.warn_on_browsewithcaret", true); pref("accessibility.browsewithcaret_shortcut.enabled", true); -#ifndef XP_MACOSX - // Tab focus model bit field: - // 1 focuses text controls, 2 focuses other form elements, 4 adds links. - // Most users will want 1, 3, or 7. - // On OS X, we use Full Keyboard Access system preference, - // unless accessibility.tabfocus is set by the user. - pref("accessibility.tabfocus", 7); - pref("accessibility.tabfocus_applies_to_xul", false); -#else - // Only on mac tabfocus is expected to handle UI widgets as well as web - // content. - pref("accessibility.tabfocus_applies_to_xul", true); -#endif - // We follow the "Click in the scrollbar to:" system preference on OS X and // "gtk-primary-button-warps-slider" property with GTK (since 2.24 / 3.6), // unless this preference is explicitly set. diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h index 13219ff54b1d..2300f5138831 100644 --- a/widget/LookAndFeel.h +++ b/widget/LookAndFeel.h @@ -92,8 +92,6 @@ class LookAndFeel { TreeScrollDelay, // the maximum number of lines to be scrolled at ones TreeScrollLinesMax, - // What type of tab-order to use - TabFocusModel, // Should menu items blink when they're chosen? ChosenMenuItemsShouldBlink, diff --git a/widget/cocoa/nsLookAndFeel.mm b/widget/cocoa/nsLookAndFeel.mm index 5e37a3cf3abe..223619f312c5 100644 --- a/widget/cocoa/nsLookAndFeel.mm +++ b/widget/cocoa/nsLookAndFeel.mm @@ -459,11 +459,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::AlertNotificationOrigin: aResult = NS_ALERT_TOP; break; - case IntID::TabFocusModel: - aResult = [NSApp isFullKeyboardAccessEnabled] - ? nsIContent::eTabFocus_any - : nsIContent::eTabFocus_textControlsMask; - break; case IntID::ScrollToClick: { aResult = [[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollerPagingBehavior"]; diff --git a/widget/headless/HeadlessLookAndFeelGTK.cpp b/widget/headless/HeadlessLookAndFeelGTK.cpp index aa55bcc347c3..09938e1983b3 100644 --- a/widget/headless/HeadlessLookAndFeelGTK.cpp +++ b/widget/headless/HeadlessLookAndFeelGTK.cpp @@ -84,9 +84,6 @@ nsresult HeadlessLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::TreeScrollLinesMax: aResult = 3; break; - case IntID::TabFocusModel: - aResult = nsIContent::eTabFocus_textControlsMask; - break; case IntID::ChosenMenuItemsShouldBlink: aResult = 1; break; diff --git a/widget/nsXPLookAndFeel.cpp b/widget/nsXPLookAndFeel.cpp index 0c5b5207f27e..3c2330794224 100644 --- a/widget/nsXPLookAndFeel.cpp +++ b/widget/nsXPLookAndFeel.cpp @@ -146,7 +146,6 @@ static const char sIntPrefs[][45] = { "ui.treeLazyScrollDelay", "ui.treeScrollDelay", "ui.treeScrollLinesMax", - "accessibility.tabfocus", // Weird one... "ui.chosenMenuItemsShouldBlink", "ui.windowsAccentColorInTitlebar", "ui.macBigSurTheme", @@ -527,9 +526,6 @@ void nsXPLookAndFeel::Init() { // for each types. Then, we could reduce the unnecessary loop from // nsXPLookAndFeel::OnPrefChanged(). Preferences::RegisterPrefixCallback(OnPrefChanged, "ui."); - // We really do just want the accessibility.tabfocus pref, not other prefs - // that start with that string. - Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus"); for (const auto& pref : kMediaQueryPrefs) { Preferences::RegisterCallback( diff --git a/widget/uikit/nsLookAndFeel.mm b/widget/uikit/nsLookAndFeel.mm index 51a9a95b52c5..f720a8681e31 100644 --- a/widget/uikit/nsLookAndFeel.mm +++ b/widget/uikit/nsLookAndFeel.mm @@ -282,9 +282,6 @@ nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::TreeScrollLinesMax: aResult = 3; break; - case IntID::TabFocusModel: - aResult = 1; // default to just textboxes - break; case IntID::ScrollToClick: aResult = 0; break;