diff --git a/content/events/src/nsEventStateManager.cpp b/content/events/src/nsEventStateManager.cpp index dae90234b98b..b8197721e4a6 100644 --- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -140,6 +140,8 @@ nsIContent * gLastFocusedContent = 0; // Strong reference nsIDocument * gLastFocusedDocument = 0; // Strong reference nsIPresContext* gLastFocusedPresContext = 0; // Weak reference +PRInt32 nsEventStateManager::sTabFocusModel = eTabFocus_unset; + PRUint32 nsEventStateManager::mInstanceCount = 0; PRInt32 nsEventStateManager::gGeneralAccesskeyModifier = -1; // magic value of -1 means uninitialized @@ -3286,7 +3288,7 @@ nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent, nsIFrame* if (mCurrentFocus) { nsCOMPtr tag; mCurrentFocus->GetTag(*getter_AddRefs(tag)); - if(nsHTMLAtoms::area==tag.get()) { + if(nsHTMLAtoms::area==tag) { //Focus is in an imagemap area nsCOMPtr presShell; if (mPresContext) { @@ -3366,10 +3368,22 @@ nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent, nsIFrame* PRBool disabled = PR_TRUE; PRBool hidden = PR_FALSE; + // Tab focus mode is constant across all windows. + // It would be nicer if ESM had a prefs callback, + // so we could store this and change behavior when it changes. + // But until the pref is exposed, that doesn't matter. + if (sTabFocusModel == eTabFocus_unset) { + sTabFocusModel = (eTabFocus_textControlsMask | eTabFocus_formElementsMask + | eTabFocus_linksMask); + nsresult rv = getPrefService(); + if (NS_SUCCEEDED(rv)) + mPrefService->GetIntPref("accessibility.tabfocus", &sTabFocusModel); + } + child->GetTag(*getter_AddRefs(tag)); nsCOMPtr htmlElement(do_QueryInterface(child)); if (htmlElement) { - if (nsHTMLAtoms::input==tag.get()) { + if (nsHTMLAtoms::input==tag) { nsCOMPtr nextInput(do_QueryInterface(child)); if (nextInput) { nextInput->GetDisabled(&disabled); @@ -3377,30 +3391,49 @@ nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent, nsIFrame* nsAutoString type; nextInput->GetType(type); - if (type.EqualsIgnoreCase("hidden")) { + if (type.EqualsIgnoreCase("text")) { + // It's a text field + disabled = !(sTabFocusModel & eTabFocus_textControlsMask); + } + else if (type.EqualsIgnoreCase("hidden")) { hidden = PR_TRUE; } else if (type.EqualsIgnoreCase("file")) { disabled = PR_TRUE; } + else { + // it's some other type of form element + disabled = !(sTabFocusModel & eTabFocus_formElementsMask); + } } } - else if (nsHTMLAtoms::select==tag.get()) { + else if (nsHTMLAtoms::select==tag) { + // Select counts as form but not as text + disabled = !(sTabFocusModel & eTabFocus_formElementsMask); + if (!disabled) { nsCOMPtr nextSelect(do_QueryInterface(child)); if (nextSelect) { nextSelect->GetDisabled(&disabled); nextSelect->GetTabIndex(&tabIndex); } } - else if (nsHTMLAtoms::textarea==tag.get()) { + } + else if (nsHTMLAtoms::textarea==tag) { + // it's a textarea + disabled = !(sTabFocusModel & eTabFocus_textControlsMask); + if (!disabled) { nsCOMPtr nextTextArea(do_QueryInterface(child)); if (nextTextArea) { nextTextArea->GetDisabled(&disabled); nextTextArea->GetTabIndex(&tabIndex); } } - else if(nsHTMLAtoms::a==tag.get()) { + } + else if (nsHTMLAtoms::a==tag) { + // it's a link + disabled = !(sTabFocusModel & eTabFocus_linksMask); nsCOMPtr nextAnchor(do_QueryInterface(child)); + if (!disabled) { if (nextAnchor) nextAnchor->GetTabIndex(&tabIndex); nsAutoString href; @@ -3411,14 +3444,22 @@ nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent, nsIFrame* disabled = PR_FALSE; } } - else if(nsHTMLAtoms::button==tag.get()) { + } + else if (nsHTMLAtoms::button==tag) { + // Button counts as a form element but not as text + disabled = !(sTabFocusModel & eTabFocus_formElementsMask); + if (!disabled) { nsCOMPtr nextButton(do_QueryInterface(child)); if (nextButton) { nextButton->GetTabIndex(&tabIndex); nextButton->GetDisabled(&disabled); } } - else if(nsHTMLAtoms::img==tag.get()) { + } + else if (nsHTMLAtoms::img==tag) { + // Images are treated like links for tab focus purposes. + disabled = !(sTabFocusModel & eTabFocus_linksMask); + if (!disabled) { nsCOMPtr nextImage(do_QueryInterface(child)); nsAutoString usemap; if (nextImage) { @@ -3439,10 +3480,12 @@ nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent, nsIFrame* PRInt32 val = 0; TabIndexFrom(childArea, &val); if (mCurrentTabIndex == val) { - //mCurrentFocus is in this map so we must start iterating past it. - //We skip the case where mCurrentFocus has the same tab index - //as mCurrentTabIndex since the next tab ordered element might - //be before it (or after for backwards) in the child list. + // mCurrentFocus is in this map so we must start + // iterating past it. + // We skip the case where mCurrentFocus has the + // same tab index as mCurrentTabIndex since the + // next tab ordered element might be before it + // (or after for backwards) in the child list. break; } } @@ -3468,20 +3511,27 @@ nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent, nsIFrame* } } } - else if(nsHTMLAtoms::object==tag.get()) { + } + else if (nsHTMLAtoms::object==tag) { + // OBJECT is treated as a form element. + disabled = !(sTabFocusModel & eTabFocus_formElementsMask); + if (!disabled) { nsCOMPtr nextObject(do_QueryInterface(child)); if (nextObject) nextObject->GetTabIndex(&tabIndex); disabled = PR_FALSE; } - else if (nsHTMLAtoms::iframe==tag.get()) { + } + else if (nsHTMLAtoms::iframe==tag) { disabled = PR_FALSE; } - else if (nsHTMLAtoms::frame==tag.get()) { + else if (nsHTMLAtoms::frame==tag) { disabled = PR_FALSE; } } - else { + // Check the tabindex attribute, unless the model specifies text only. + // If the model is unset then we'll depend on tabindex. + else if (sTabFocusModel != eTabFocus_textControlsMask) { nsAutoString value; child->GetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, value); nsAutoString tabStr; diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h index bbaba30dc32c..5e5b9b43da9e 100644 --- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -71,6 +71,13 @@ class nsEventStateManager : public nsSupportsWeakReference, public nsIEventStateManager, public nsIObserver { + // Tab focus model bit field: + enum nsTabFocusModel { + eTabFocus_unset = 0, // unset, check preferences + eTabFocus_textControlsMask = (1<<0), // text elements + eTabFocus_formElementsMask = (1<<1), // non-text form elements + eTabFocus_linksMask = (1<<2) // links + }; public: nsEventStateManager(); @@ -273,6 +280,10 @@ protected: // So we don't have to keep checking accessibility.browsewithcaret pref PRBool mBrowseWithCaret; + // Tab focus policy (static, constant across the app): + // Which types of elements are in the tab order? + static PRInt32 sTabFocusModel; + // Recursion guard for tabbing PRBool mTabbedThroughDocument; diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 5aff278d2c95..1f15b5dd4fb9 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -125,6 +125,10 @@ pref("browser.helperApps.neverAsk.openFile", ""); pref("accessibility.browsewithcaret", false); pref("accessibility.warn_on_browsewithcaret", true); +// 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. +pref("accessibility.tabfocus", 7); pref("accessibility.usetexttospeech", ""); pref("accessibility.usebrailledisplay", ""); pref("accessibility.accesskeycausesactivation", true); diff --git a/modules/libpref/src/mac/macprefs.js b/modules/libpref/src/mac/macprefs.js index 3a82876f93a6..e8defd8468bc 100644 --- a/modules/libpref/src/mac/macprefs.js +++ b/modules/libpref/src/mac/macprefs.js @@ -180,6 +180,10 @@ pref("font.size.fixed.zh-CN", 16); pref("font.size.variable.zh-TW", 15); pref("font.size.fixed.zh-TW", 16); +// Tab focus model bit field: +// 1 focuses text controls, 2 focuses other form elements, 4 adds links. +pref("accessibility.tabfocus", 1); + // Override the Windows settings: no menu key, meta accelerator key. ctrl for general access key in HTML/XUL // Use 17 for Ctrl, 18 for Option, 224 for Cmd, 0 for none pref("ui.key.menuAccessKey", 0); diff --git a/modules/libpref/src/unix/unix.js b/modules/libpref/src/unix/unix.js index f7b8a9f9d36b..02e386fbf1ba 100644 --- a/modules/libpref/src/unix/unix.js +++ b/modules/libpref/src/unix/unix.js @@ -67,6 +67,10 @@ pref("clipboard.autocopy", true); pref("browser.urlbar.clickSelectsAll", false); +// Tab focus model bit field: +// 1 focuses text controls, 2 focuses other form elements, 4 adds links. +pref("accessibility.tabfocus", 1); + // override double-click word selection behavior. pref("layout.word_select.stop_at_punctuation", false);