Bug 250006. Clean up GetNextTabbableContent(). Remove tabbable property from DOM interfaces. Clean up spurious focus outline effects. r=bryner, sr=jst

This commit is contained in:
aaronleventhal%moonset.net 2004-07-24 21:12:43 +00:00
Родитель 1f0c9d8a54
Коммит 0319b80e0e
24 изменённых файлов: 551 добавлений и 465 удалений

Просмотреть файл

@ -435,6 +435,35 @@ public:
{
}
/**
* Check if this content is focusable and in the current tab order.
* Note: most callers should use nsIFrame::IsFocusable() instead as it
* checks visibility and other layout factors as well.
* Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.
* For example, only the selected radio button in a group is in the
* tab order, unless the radio group has no selection in which case
* all of the visible, non-disabled radio buttons in the group are
* in the tab order. On the other hand, all of the visible, non-disabled
* radio buttons are always focusable via clicking or script.
* Also, depending on the pref accessibility.tabfocus some widgets may be
* focusable but removed from the tab order. This is the default on
* Mac OS X, where fewer items are focusable.
* @param [inout, optional] aTabIndex the computed tab index
* In: default tabindex for element (-1 nonfocusable, == 0 focusable)
* Out: computed tabindex
* @param [optional] aTabIndex the computed tab index
* < 0 if not tabbable
* == 0 if in normal tab order
* > 0 can be tabbed to in the order specified by this value
* @return whether the content is focusable via mouse, kbd or script.
*/
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull)
{
if (aTabIndex)
*aTabIndex = -1; // Default, not tabbable
return PR_FALSE;
}
/**
* Sets content node with the binding responsible for our construction (and
* existence). Used by anonymous content (XBL-generated). null for all
@ -577,6 +606,17 @@ public:
PRBool aDumpAll = PR_TRUE) const = 0;
#endif
enum ETabFocusType {
//eTabFocus_textControlsMask = (1<<0), // unused - textboxes 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 PRInt32 sTabFocusModel;
protected:
typedef PRWord PtrBits;

Просмотреть файл

@ -117,7 +117,7 @@ DebugListContentTree(nsIContent* aElement)
PLDHashTable nsGenericElement::sRangeListsHash;
PLDHashTable nsGenericElement::sEventListenerManagersHash;
PRInt32 nsIContent::sTabFocusModel = eTabFocus_any;
//----------------------------------------------------------------------
nsChildContentList::nsChildContentList(nsIContent *aContent)

Просмотреть файл

@ -108,7 +108,6 @@ public:
// If aCanFocusDoc == PR_TRUE, the current document will be focused if caret is not on a focusable element
NS_IMETHOD MoveFocusToCaret(PRBool aCanFocusDoc, PRBool *aIsSelectionWithFocus) = 0;
NS_IMETHOD MoveCaretToFocus() = 0;
NS_IMETHOD GetTabbable(PRInt32 aTabFocusType, PRBool *aIsTabbable) = 0;
// This is an experiment and may be temporary
NS_IMETHOD ConsumeFocusEvents(PRBool aDoConsume) = 0;
@ -144,12 +143,4 @@ enum EFocusedWithType {
eEventFocusedByApplication // focus gained via Application (like script)
};
// Tab focus model bit field:
enum ETabFocusType {
//eTabFocus_textControlsMask = (1<<0), // unused - textboxes 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
};
#endif // nsIEventStateManager_h__

Просмотреть файл

@ -155,7 +155,6 @@ static PRBool sLeftClickOnly = PR_TRUE;
static PRBool sKeyCausesActivation = PR_TRUE;
static PRUint32 sESMInstanceCount = 0;
static PRInt32 sGeneralAccesskeyModifier = -1; // magic value of -1 means uninitialized
static PRInt32 sTabFocusModel = eTabFocus_any;
enum {
MOUSE_SCROLL_N_LINES,
@ -260,8 +259,9 @@ nsEventStateManager::Init()
nsContentUtils::GetIntPref("ui.key.generalAccessKey",
sGeneralAccesskeyModifier);
sTabFocusModel = nsContentUtils::GetIntPref("accessibility.tabfocus",
sTabFocusModel);
nsIContent::sTabFocusModel =
nsContentUtils::GetIntPref("accessibility.tabfocus",
nsIContent::sTabFocusModel);
}
prefBranch->AddObserver("accessibility.accesskeycausesactivation", this, PR_TRUE);
prefBranch->AddObserver("accessibility.browsewithcaret", this, PR_TRUE);
@ -382,8 +382,9 @@ nsEventStateManager::Observe(nsISupports *aSubject,
} else if (data.EqualsLiteral("accessibility.browsewithcaret")) {
ResetBrowseWithCaret();
} else if (data.EqualsLiteral("accessibility.tabfocus")) {
sTabFocusModel = nsContentUtils::GetIntPref("accessibility.tabfocus",
sTabFocusModel);
nsIContent::sTabFocusModel =
nsContentUtils::GetIntPref("accessibility.tabfocus",
nsIContent::sTabFocusModel);
} else if (data.EqualsLiteral("nglayout.events.dispatchLeftClickOnly")) {
sLeftClickOnly =
nsContentUtils::GetBoolPref("nglayout.events.dispatchLeftClickOnly",
@ -1916,9 +1917,7 @@ nsEventStateManager::PostHandleEvent(nsIPresContext* aPresContext,
break;
}
const nsStyleUserInterface* ui = currFrame->GetStyleUserInterface();
if ((ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE) &&
(ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE)) {
if (currFrame->IsFocusable()) {
newFocus = currFrame->GetContent();
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocus));
if (domElement)
@ -3393,35 +3392,6 @@ nsEventStateManager::TabIndexFrom(nsIContent *aFrom, PRInt32 *aOutIndex)
}
}
PRBool
nsEventStateManager::HasFocusableAncestor(nsIFrame *aFrame)
{
// This method helps prevent a situation where a link or other element
// with -moz-user-focus is focused twice, because the parent link
// would get focused, and all of the children also get focus.
nsIFrame *ancestorFrame = aFrame;
while ((ancestorFrame = ancestorFrame->GetParent()) != nsnull) {
nsIContent *ancestorContent = ancestorFrame->GetContent();
if (!ancestorContent) {
break;
}
nsIAtom *ancestorTag = ancestorContent->Tag();
if (ancestorTag == nsHTMLAtoms::frame || ancestorTag == nsHTMLAtoms::iframe) {
break; // The only focusable containers that can also have focusable children
}
// Any other parent that's focusable can't have focusable children
const nsStyleUserInterface *ui = ancestorFrame->GetStyleUserInterface();
if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
// Inside a focusable parent -- let parent get focus
// instead of child (to avoid links within links etc.)
return PR_TRUE;
}
}
return PR_FALSE;
}
nsresult
nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent,
nsIContent* aStartContent,
@ -3433,286 +3403,128 @@ nsEventStateManager::GetNextTabbableContent(nsIContent* aRootContent,
{
*aResultNode = nsnull;
*aResultFrame = nsnull;
PRBool keepFirstFrame = PR_FALSE;
PRBool findLastFrame = PR_FALSE;
nsresult rv;
nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
// --- Get frame to start with ---
if (!aStartFrame) {
//No frame means we need to start with the root content again.
if (mPresContext) {
nsIFrame* result = nsnull;
nsIPresShell *presShell = mPresContext->GetPresShell();
if (presShell) {
presShell->GetPrimaryFrameFor(aRootContent, &result);
}
aStartFrame = result;
if (!forward)
findLastFrame = PR_TRUE;
// No frame means we need to start with the root content again.
NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE);
nsIPresShell *presShell = mPresContext->GetPresShell();
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
presShell->GetPrimaryFrameFor(aRootContent, &aStartFrame);
NS_ENSURE_TRUE(aStartFrame, NS_ERROR_FAILURE);
rv = trav->NewFrameTraversal(getter_AddRefs(frameTraversal), FOCUS,
mPresContext, aStartFrame);
NS_ENSURE_SUCCESS(rv, rv);
if (!forward) {
rv = frameTraversal->Last();
}
if (!aStartFrame) {
return NS_ERROR_FAILURE;
}
keepFirstFrame = PR_TRUE;
}
// Need to do special check in case we're in an imagemap which has multiple content per frame
if (aStartContent) {
if (aStartContent->Tag() == nsHTMLAtoms::area &&
aStartContent->IsContentOfType(nsIContent::eHTML)) {
// We're starting from an imagemap area, so don't skip over the starting frame.
keepFirstFrame = PR_TRUE;
else {
rv = trav->NewFrameTraversal(getter_AddRefs(frameTraversal), FOCUS,
mPresContext, aStartFrame);
NS_ENSURE_SUCCESS(rv, rv);
if (!aStartContent || aStartContent->Tag() != nsHTMLAtoms::area ||
!aStartContent->IsContentOfType(nsIContent::eHTML)) {
// Need to do special check in case we're in an imagemap which has multiple
// content per frame, so don't skip over the starting frame.
rv = forward ? frameTraversal->Next() : frameTraversal->Prev();
}
}
nsresult result;
nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
if (NS_FAILED(result))
return result;
result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal), FOCUS,
mPresContext, aStartFrame);
if (NS_FAILED(result))
return NS_OK;
if (!keepFirstFrame) {
if (forward)
frameTraversal->Next();
else frameTraversal->Prev();
} else if (findLastFrame)
frameTraversal->Last();
nsISupports* currentItem;
frameTraversal->CurrentItem(&currentItem);
nsIFrame* currentFrame = (nsIFrame*)currentItem;
while (currentFrame) {
const nsStyleVisibility* vis = currentFrame->GetStyleVisibility();
const nsStyleUserInterface* ui = currentFrame->GetStyleUserInterface();
PRBool viewShown = currentFrame->AreAncestorViewsVisible();
nsIContent* child = currentFrame->GetContent();
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(child));
PRInt32 tabIndex = -1;
if (element && viewShown) {
if (vis->mVisible != NS_STYLE_VISIBILITY_COLLAPSE &&
vis->mVisible != NS_STYLE_VISIBILITY_HIDDEN &&
ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
tabIndex = 0; // Default value when focusable via -moz-user-focus
}
TabIndexFrom(child, &tabIndex);
// -- Walk frames to find something tabbable matching mCurrentTabIndex --
while (NS_SUCCEEDED(rv)) {
nsISupports* currentItem;
frameTraversal->CurrentItem(&currentItem);
*aResultFrame = (nsIFrame*)currentItem;
if (!*aResultFrame) {
break;
}
// if collapsed or hidden, we don't get tabbed into.
// TabIndex not set defaults to 0 for form elements, anchors and other
// elements that are normally focusable. Tabindex defaults to -1
// for elements that are not normally focusable.
// The returned computed tabindex from IsFocusable() is as follows:
// < 0 not tabbable at all
// == 0 in normal tab order (last after positive tabindex'd items)
// > 0 can be tabbed to in the order specified by this value
PRInt32 tabIndex;
nsIContent* currentContent = (*aResultFrame)->GetContent();
(*aResultFrame)->IsFocusable(&tabIndex);
if (tabIndex >= 0) {
PRBool disabled = PR_TRUE;
nsIAtom *tag = child->Tag();
if (child->IsContentOfType(nsIContent::eHTML)) {
if (tag == nsHTMLAtoms::input) {
nsCOMPtr<nsIDOMNSHTMLInputElement> nextInputNS(do_QueryInterface(child));
NS_ASSERTION(nextInputNS, "input element must QI to nsIDOMNSHTMLInputElement!");
PRBool isTabbable;
nextInputNS->GetTabbable(&isTabbable);
disabled = !isTabbable;
}
else if (tag == nsHTMLAtoms::select) {
// Select counts as form but not as text
disabled = !(sTabFocusModel & eTabFocus_formElementsMask);
if (!disabled) {
nsCOMPtr<nsIDOMHTMLSelectElement> nextSelect(do_QueryInterface(child));
if (nextSelect) {
nextSelect->GetDisabled(&disabled);
}
}
}
else if (tag == nsHTMLAtoms::textarea) {
// it's a textarea
disabled = PR_FALSE;
if (!disabled) {
nsCOMPtr<nsIDOMHTMLTextAreaElement> nextTextArea(do_QueryInterface(child));
if (nextTextArea) {
nextTextArea->GetDisabled(&disabled);
}
}
}
else if (tag == nsHTMLAtoms::a) {
// it's a link
disabled = !(sTabFocusModel & eTabFocus_linksMask);
nsCOMPtr<nsIDOMHTMLAnchorElement> nextAnchor(do_QueryInterface(child));
if (!disabled) {
nsAutoString href;
nextAnchor->GetAttribute(NS_LITERAL_STRING("href"), href);
if (href.IsEmpty()) {
disabled = PR_TRUE; // Don't tab unless href, bug 17605
} else {
disabled = PR_FALSE;
}
}
}
else if (tag == nsHTMLAtoms::button) {
// Button counts as a form element but not as text
disabled = !(sTabFocusModel & eTabFocus_formElementsMask);
if (!disabled) {
nsCOMPtr<nsIDOMHTMLButtonElement> nextButton(do_QueryInterface(child));
if (nextButton) {
nextButton->GetDisabled(&disabled);
}
}
}
else if (tag == nsHTMLAtoms::img) {
PRBool hasImageMap = PR_FALSE;
// Don't need to set disabled here, because if we
// match an imagemap, we'll return from there.
nsCOMPtr<nsIDOMHTMLImageElement> nextImage(do_QueryInterface(child));
nsAutoString usemap;
if (nextImage) {
nsCOMPtr<nsIDocument> doc = child->GetDocument();
if (doc) {
nextImage->GetAttribute(NS_LITERAL_STRING("usemap"), usemap);
nsCOMPtr<nsIDOMHTMLMapElement> imageMap = nsImageMapUtils::FindImageMap(doc,usemap);
if (imageMap) {
hasImageMap = PR_TRUE;
if (sTabFocusModel & eTabFocus_linksMask) {
nsCOMPtr<nsIContent> map(do_QueryInterface(imageMap));
if (map) {
nsIContent *childArea;
PRUint32 index, count = map->GetChildCount();
// First see if mCurrentFocus is in this map
for (index = 0; index < count; index++) {
childArea = map->GetChildAt(index);
if (childArea == mCurrentFocus) {
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.
break;
}
}
}
PRInt32 increment = forward ? 1 : -1;
// In the following two lines we might substract 1 from zero,
// the |index < count| loop condition will be false in that case too.
index = index < count ? index + increment : (forward ? 0 : count - 1);
for (; index < count; index += increment) {
//Iterate over the children.
childArea = map->GetChildAt(index);
//Got the map area, check its tabindex.
PRInt32 val = 0;
TabIndexFrom(childArea, &val);
if (mCurrentTabIndex == val) {
//tabindex == the current one, use it.
*aResultNode = childArea;
NS_IF_ADDREF(*aResultNode);
*aResultFrame = currentFrame;
return NS_OK;
}
}
}
}
}
}
}
// Might be using -moz-user-focus and imitating a control.
// If already inside link, -moz-user-focus'd element, or already has
// image map with tab stops, then don't use the
// image frame itself as a tab stop.
disabled = HasFocusableAncestor(currentFrame) || hasImageMap ||
!(sTabFocusModel & eTabFocus_formElementsMask);
}
else if (tag == nsHTMLAtoms::object) {
// OBJECT is treated as a form element.
disabled = !(sTabFocusModel & eTabFocus_formElementsMask);
}
else if (tag == nsHTMLAtoms::iframe || tag == nsHTMLAtoms::frame) {
disabled = PR_TRUE;
if (child) {
nsCOMPtr<nsIDocument> doc = child->GetDocument();
if (doc) {
nsIDocument *subDoc = doc->GetSubDocumentFor(child);
if (subDoc) {
nsCOMPtr<nsISupports> container = subDoc->GetContainer();
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
if (docShell) {
nsCOMPtr<nsIContentViewer> contentViewer;
docShell->GetContentViewer(getter_AddRefs(contentViewer));
if (contentViewer) {
nsCOMPtr<nsIContentViewer> zombieViewer;
contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
if (!zombieViewer) {
// If there are 2 viewers for the current docshell, that
// means the current document is a zombie document.
// Only navigate into the frame/iframe if it's not a zombie
disabled = PR_FALSE;
}
}
}
}
}
}
}
else {
// Let any HTML element with -moz-user-focus: normal be tabbable
// Without this rule, DHTML form controls made from div or span
// cannot be put in the tab order.
disabled = !(sTabFocusModel & eTabFocus_formElementsMask) ||
HasFocusableAncestor(currentFrame);
if (currentContent->Tag() == nsHTMLAtoms::img &&
currentContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::usemap)) {
// Must be an image w/ a map -- it's tabbable but no tabindex is specified
// Special case for image maps: they don't get walked by nsIFrameTraversal
nsIContent *areaContent = GetNextTabbableMapArea(forward, currentContent);
if (areaContent) {
NS_ADDREF(*aResultNode = areaContent);
return NS_OK;
}
}
else {
// Is it disabled?
nsAutoString value;
child->GetAttr(kNameSpaceID_None, nsHTMLAtoms::disabled, value);
if (!value.EqualsLiteral("true")) {
nsCOMPtr<nsIDOMXULControlElement> control(do_QueryInterface(child));
if (control)
control->GetDisabled(&disabled);
else
disabled = PR_FALSE;
}
}
// TabIndex not set treated at same level as set to 0
if (!disabled && (aIgnoreTabIndex || mCurrentTabIndex == tabIndex) &&
child != aStartContent) {
*aResultNode = child;
NS_IF_ADDREF(*aResultNode);
*aResultFrame = currentFrame;
else if ((aIgnoreTabIndex || mCurrentTabIndex == tabIndex) &&
currentContent != aStartContent) {
NS_ADDREF(*aResultNode = currentContent);
return NS_OK;
}
}
if (forward)
frameTraversal->Next();
else frameTraversal->Prev();
frameTraversal->CurrentItem(&currentItem);
currentFrame = (nsIFrame*)currentItem;
rv = forward ? frameTraversal->Next() : frameTraversal->Prev();
}
// Reached end or beginning of document
//If already at lowest priority tab (0), end
if (((forward) && (0 == mCurrentTabIndex)) ||
((!forward) && (1 == mCurrentTabIndex))) {
// -- Reached end or beginning of document --
// If already at lowest priority tab (0), end search completely.
// A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
if (mCurrentTabIndex == (forward? 0: 1)) {
return NS_OK;
}
//else continue looking for next highest priority tab
// else continue looking for next highest priority tabindex
mCurrentTabIndex = GetNextTabIndex(aRootContent, forward);
return GetNextTabbableContent(aRootContent, aStartContent, nsnull, forward,
aIgnoreTabIndex, aResultNode, aResultFrame);
}
nsIContent*
nsEventStateManager::GetNextTabbableMapArea(PRBool aForward,
nsIContent *aImageContent)
{
nsAutoString useMap;
aImageContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::usemap, useMap);
nsCOMPtr<nsIDocument> doc = aImageContent->GetDocument();
if (doc) {
nsCOMPtr<nsIDOMHTMLMapElement> imageMap = nsImageMapUtils::FindImageMap(doc, useMap);
nsCOMPtr<nsIContent> mapContent = do_QueryInterface(imageMap);
PRUint32 count = mapContent->GetChildCount();
// First see if mCurrentFocus is in this map
PRInt32 index = mapContent->IndexOf(mCurrentFocus);
PRInt32 tabIndex;
if (index < 0 || (mCurrentFocus->IsFocusable(&tabIndex) &&
tabIndex != mCurrentTabIndex)) {
// If mCurrentFocus is in this map we must start iterating past it.
// We skip the case where mCurrentFocus has tabindex == mCurrentTabIndex
// since the next tab ordered element might be before it
// (or after for backwards) in the child list.
index = aForward? -1 : count;
}
// GetChildAt will return nsnull if our index < 0 or index >= count
nsCOMPtr<nsIContent> areaContent;
while ((areaContent = mapContent->GetChildAt(aForward? ++index : --index)) != nsnull) {
if (areaContent->IsFocusable(&tabIndex) && tabIndex == mCurrentTabIndex) {
return areaContent;
}
}
}
return nsnull;
}
PRInt32
nsEventStateManager::GetNextTabIndex(nsIContent* aParent, PRBool forward)
{
@ -5134,12 +4946,6 @@ nsEventStateManager::ResetBrowseWithCaret()
}
}
NS_IMETHODIMP nsEventStateManager::GetTabbable(PRInt32 aTabFocusType, PRBool *aIsTabbable)
{
*aIsTabbable = (sTabFocusModel & aTabFocusType) != 0;
return NS_OK;
}
//--------------------------------------------------------------------------------
//-- DocShell Focus Traversal Methods
//--------------------------------------------------------------------------------

Просмотреть файл

@ -145,7 +145,6 @@ public:
NS_IMETHOD MoveFocusToCaret(PRBool aCanFocusDoc, PRBool *aIsSelectionWithFocus);
NS_IMETHOD MoveCaretToFocus();
NS_IMETHOD GetTabbable(PRInt32 aTabFocusType, PRBool *aIsTabbable);
protected:
friend class CurrentEventShepherd;
@ -167,13 +166,13 @@ protected:
nsresult SetClickCount(nsIPresContext* aPresContext, nsMouseEvent *aEvent, nsEventStatus* aStatus);
nsresult CheckForAndDispatchClick(nsIPresContext* aPresContext, nsMouseEvent *aEvent, nsEventStatus* aStatus);
PRBool ChangeFocus(nsIContent* aFocus, PRInt32 aFocusedWith);
static PRBool HasFocusableAncestor(nsIFrame *aFrame);
nsresult GetNextTabbableContent(nsIContent* aRootContent,
nsIContent* aStartContent,
nsIFrame* aStartFrame,
PRBool forward, PRBool ignoreTabIndex,
nsIContent** aResultNode,
nsIFrame** aResultFrame);
nsIContent *GetNextTabbableMapArea(PRBool aForward, nsIContent *imageContent);
void TabIndexFrom(nsIContent *aFrom, PRInt32 *aOutIndex);
PRInt32 GetNextTabIndex(nsIContent* aParent, PRBool foward);

Просмотреть файл

@ -40,6 +40,7 @@
#include "nsCOMPtr.h"
#include "nsIAtom.h"
#include "nsINodeInfo.h"
#include "nsIContentViewer.h"
#include "nsICSSParser.h"
#include "nsICSSLoader.h"
#include "nsICSSStyleRule.h"
@ -1374,14 +1375,11 @@ nsGenericHTMLElement::HandleDOMEvent(nsIPresContext* aPresContext,
!(aFlags & (NS_EVENT_FLAG_CAPTURE | NS_EVENT_FLAG_SYSTEM_EVENT)) &&
aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN &&
!IsContentOfType(eHTML_FORM_CONTROL) &&
HasAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex)) {
HasAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex) &&
IsFocusable()) {
// Any visible element is focusable if tabindex >= 0
PRInt32 tabIndex;
GetTabIndex(&tabIndex);
if (tabIndex >= 0) {
Focus();
*aEventStatus = nsEventStatus_eConsumeNoDefault;
}
ret = Focus();
*aEventStatus = nsEventStatus_eConsumeNoDefault;
}
return ret;
@ -3300,6 +3298,45 @@ nsGenericHTMLFormElement::GetForm(nsIDOMHTMLFormElement** aForm)
return NS_OK;
}
PRBool
nsGenericHTMLFrameElement::IsFocusable(PRInt32 *aTabIndex)
{
if (!nsGenericHTMLElement::IsFocusable(aTabIndex)) {
return PR_FALSE;
}
// If there is no subdocument, docshell or content viewer, it's not tabbable
PRBool isFocusable = PR_FALSE;
if (mDocument) {
nsIDocument *subDoc = mDocument->GetSubDocumentFor(this);
if (subDoc) {
nsCOMPtr<nsISupports> container = subDoc->GetContainer();
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
if (docShell) {
nsCOMPtr<nsIContentViewer> contentViewer;
docShell->GetContentViewer(getter_AddRefs(contentViewer));
if (contentViewer) {
isFocusable = PR_TRUE;
nsCOMPtr<nsIContentViewer> zombieViewer;
contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
if (zombieViewer) {
// If there are 2 viewers for the current docshell, that
// means the current document is a zombie document.
// Only navigate into the frame/iframe if it's not a zombie.
isFocusable = PR_FALSE;
}
}
}
}
}
if (!isFocusable && aTabIndex) {
*aTabIndex = -1;
}
return isFocusable;
}
void
nsGenericHTMLFormElement::SetParent(nsIContent* aParent)
{
@ -3623,17 +3660,47 @@ nsGenericHTMLElement::Focus()
{
// Generic HTML elements are focusable only if
// tabindex explicitly set and tabindex >= 0
nsAutoString tabIndexStr;
GetAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex, tabIndexStr);
if (!tabIndexStr.IsEmpty()) {
PRInt32 rv, tabIndexVal = tabIndexStr.ToInteger(&rv);
if (NS_SUCCEEDED(rv) && tabIndexVal >= 0) {
SetElementFocus(PR_TRUE);
return NS_OK;
}
if (IsFocusable()) {
SetElementFocus(PR_TRUE);
}
return NS_ERROR_FAILURE;
return NS_OK;
}
void
nsGenericHTMLElement::RemoveFocus(nsIPresContext *aPresContext)
{
if (!aPresContext)
return;
if (IsContentOfType(eHTML_FORM_CONTROL)) {
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
if (formControlFrame) {
formControlFrame->SetFocus(PR_FALSE, PR_FALSE);
}
}
if (mDocument) {
aPresContext->EventStateManager()->SetContentState(nsnull,
NS_EVENT_STATE_FOCUS);
}
}
PRBool
nsGenericHTMLElement::IsFocusable(PRInt32 *aTabIndex)
{
PRInt32 tabIndex = 0; // Default value for non HTML elements with -moz-user-focus
GetTabIndex(&tabIndex);
// Just check for disabled attribute on all HTML elements
if (HasAttr(kNameSpaceID_None, nsHTMLAtoms::disabled)) {
tabIndex = -1;
}
if (aTabIndex) {
*aTabIndex = tabIndex;
}
return tabIndex >= 0;
}
void

Просмотреть файл

@ -189,6 +189,8 @@ public:
virtual void DumpContent(FILE* out, PRInt32 aIndent,PRBool aDumpAll) const;
#endif
virtual PRBool IsContentOfType(PRUint32 aFlags) const;
virtual void RemoveFocus(nsIPresContext *aPresContext);
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
nsresult HandleDOMEvent(nsIPresContext* aPresContext,
nsEvent* aEvent,
@ -945,6 +947,7 @@ public:
NS_IMETHOD SetFrameLoader(nsIFrameLoader *aFrameLoader);
// nsIContent
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
virtual void SetParent(nsIContent *aParent);
virtual void SetDocument(nsIDocument *aDocument, PRBool aDeep,
PRBool aCompileEventHandlers);

Просмотреть файл

@ -102,7 +102,8 @@ public:
virtual void SetDocument(nsIDocument* aDocument, PRBool aDeep,
PRBool aCompileEventHandlers);
virtual void SetFocus(nsIPresContext* aPresContext);
virtual void RemoveFocus(nsIPresContext* aPresContext);
virtual PRBool IsFocusable(PRBool *aTabIndex = nsnull);
virtual nsresult HandleDOMEvent(nsIPresContext* aPresContext,
nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
PRUint32 aFlags,
@ -234,20 +235,30 @@ nsHTMLAnchorElement::SetFocus(nsIPresContext* aPresContext)
}
}
void
nsHTMLAnchorElement::RemoveFocus(nsIPresContext* aPresContext)
PRBool
nsHTMLAnchorElement::IsFocusable(PRInt32 *aTabIndex)
{
if (!aPresContext) {
return;
if (!nsGenericHTMLElement::IsFocusable(aTabIndex)) {
return PR_FALSE;
}
// If we are disabled, we probably shouldn't have focus in the
// first place, so allow it to be removed.
if (mDocument) {
aPresContext->EventStateManager()->SetContentState(nsnull,
NS_EVENT_STATE_FOCUS);
PRBool isFocusable = PR_FALSE;
nsAutoString href;
GetAttribute(NS_LITERAL_STRING("href"), href);
if (href.IsEmpty() && !HasAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex)) {
// Not tabbable or focusable without href (bug 17605), unless
// forced to be via presence of nonnegative tabindex attribute
if (aTabIndex) {
*aTabIndex = -1;
}
return PR_FALSE;
}
if (aTabIndex && (sTabFocusModel & eTabFocus_linksMask) == 0) {
*aTabIndex = -1;
}
return PR_TRUE;
}
nsresult

Просмотреть файл

@ -88,7 +88,6 @@ public:
PRUint32 aFlags,
nsEventStatus* aEventStatus);
virtual void SetFocus(nsIPresContext* aPresContext);
virtual void RemoveFocus(nsIPresContext* aPresContext);
virtual void SetDocument(nsIDocument* aDocument, PRBool aDeep,
PRBool aCompileEventHandlers);
@ -202,16 +201,6 @@ nsHTMLAreaElement::SetFocus(nsIPresContext* aPresContext)
}
}
void
nsHTMLAreaElement::RemoveFocus(nsIPresContext* aPresContext)
{
if (!aPresContext)
return;
aPresContext->EventStateManager()->SetContentState(nsnull,
NS_EVENT_STATE_FOCUS);
}
void
nsHTMLAreaElement::SetDocument(nsIDocument* aDocument, PRBool aDeep,
PRBool aCompileEventHandlers)

Просмотреть файл

@ -98,7 +98,7 @@ public:
// nsIContent overrides...
virtual void SetFocus(nsIPresContext* aPresContext);
virtual void RemoveFocus(nsIPresContext* aPresContext);
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
virtual PRBool ParseAttribute(nsIAtom* aAttribute,
const nsAString& aValue,
nsAttrValue& aResult);
@ -219,6 +219,18 @@ nsHTMLButtonElement::Click()
return NS_OK;
}
PRBool
nsHTMLButtonElement::IsFocusable(PRInt32 *aTabIndex)
{
if (!nsGenericHTMLElement::IsFocusable(aTabIndex)) {
return PR_FALSE;
}
if (aTabIndex && (sTabFocusModel & eTabFocus_formElementsMask) == 0) {
*aTabIndex = -1;
}
return PR_TRUE;
}
void
nsHTMLButtonElement::SetFocus(nsIPresContext* aPresContext)
{
@ -241,26 +253,6 @@ nsHTMLButtonElement::SetFocus(nsIPresContext* aPresContext)
}
}
void
nsHTMLButtonElement::RemoveFocus(nsIPresContext* aPresContext)
{
if (!aPresContext)
return;
// If we are disabled, we probably shouldn't have focus in the
// first place, so allow it to be removed.
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
if (formControlFrame) {
formControlFrame->SetFocus(PR_FALSE, PR_FALSE);
}
if (mDocument) {
aPresContext->EventStateManager()->SetContentState(nsnull,
NS_EVENT_STATE_FOCUS);
}
}
static const nsHTMLValue::EnumTable kButtonTypeTable[] = {
{ "button", NS_FORM_BUTTON_BUTTON },
{ "reset", NS_FORM_BUTTON_RESET },

Просмотреть файл

@ -77,6 +77,8 @@
#include "nsIJSContextStack.h"
#include "nsIView.h"
#include "nsImageMapUtils.h"
#include "nsIDOMHTMLMapElement.h"
// XXX nav attrs: suppress
@ -128,6 +130,7 @@ public:
nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
PRUint32 aFlags,
nsEventStatus* aEventStatus);
PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
// SetAttr override. C++ is stupid, so have to override both
// overloaded methods.
@ -527,6 +530,34 @@ nsHTMLImageElement::HandleDOMEvent(nsIPresContext* aPresContext,
aFlags, aEventStatus);
}
PRBool
nsHTMLImageElement::IsFocusable(PRInt32 *aTabIndex)
{
PRInt32 tabIndex;
GetTabIndex(&tabIndex);
if (mDocument) {
nsAutoString usemap;
GetUseMap(usemap);
nsCOMPtr<nsIDOMHTMLMapElement> imageMap =
nsImageMapUtils::FindImageMap(mDocument, usemap);
if (imageMap) {
if (aTabIndex) {
// Use tab index on individual map areas
*aTabIndex = (sTabFocusModel & eTabFocus_linksMask)? 0 : -1;
}
return PR_TRUE;
}
}
if (aTabIndex) {
// Can be in tab order if tabindex >=0 and form controls are tabbable.
*aTabIndex = (sTabFocusModel & eTabFocus_formElementsMask)? tabIndex : -1;
}
return tabIndex >= 0;
}
nsresult
nsHTMLImageElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsIAtom* aPrefix, const nsAString& aValue,

Просмотреть файл

@ -175,7 +175,7 @@ public:
// nsIContent
virtual void SetFocus(nsIPresContext* aPresContext);
virtual void RemoveFocus(nsIPresContext* aPresContext);
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
virtual PRBool ParseAttribute(nsIAtom* aAttribute,
const nsAString& aValue,
@ -1081,27 +1081,6 @@ nsHTMLInputElement::SetFocus(nsIPresContext* aPresContext)
}
}
void
nsHTMLInputElement::RemoveFocus(nsIPresContext* aPresContext)
{
if (!aPresContext)
return;
// If we are disabled, we probably shouldn't have focus in the
// first place, so allow it to be removed.
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
formControlFrame->SetFocus(PR_FALSE, PR_FALSE);
}
if (mDocument) {
aPresContext->EventStateManager()->SetContentState(nsnull,
NS_EVENT_STATE_FOCUS);
}
}
NS_IMETHODIMP
nsHTMLInputElement::Select()
{
@ -2633,43 +2612,45 @@ nsHTMLInputElement::WillRemoveFromRadioGroup()
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::GetTabbable(PRBool *isTabbable)
PRBool
nsHTMLInputElement::IsFocusable(PRInt32 *aTabIndex)
{
*isTabbable = PR_TRUE;
PRBool disabled;
GetDisabled(&disabled);
// XXX aaronlev: when fix for bug 171366 goes in we'll need to check
// for negative tabIndex, which indicates something is not tabbable.
// PRInt32 tabIndex;
// GetTabIndex(&tabIndex);
if (disabled || /* tabIndex < 0 || */ mType == NS_FORM_INPUT_HIDDEN ||
mType == NS_FORM_INPUT_FILE) {
// Sub controls of file input are tabbable, not the file input itself.
*isTabbable = PR_FALSE;
return NS_OK;
if (!nsGenericHTMLElement::IsFocusable(aTabIndex)) {
return PR_FALSE;
}
if (mType != NS_FORM_INPUT_TEXT && mType != NS_FORM_INPUT_PASSWORD) {
nsCOMPtr<nsIPresContext> presContext;
GetPresContext(this, getter_AddRefs(presContext));
if (presContext) {
nsIEventStateManager *esm = presContext->EventStateManager();
esm->GetTabbable(eTabFocus_formElementsMask, isTabbable);
if (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD) {
return PR_TRUE;
}
if (mType == NS_FORM_INPUT_HIDDEN || mType == NS_FORM_INPUT_FILE) {
// Sub controls of file input are tabbable, not the file input itself.
if (aTabIndex) {
*aTabIndex = -1;
}
return PR_FALSE;
}
if (!aTabIndex) {
// The other controls are all focusable
return PR_TRUE;
}
// We need to set tabindex to -1 if we're not tabbable
if (mType != NS_FORM_INPUT_TEXT && mType != NS_FORM_INPUT_PASSWORD &&
!(sTabFocusModel & eTabFocus_formElementsMask)) {
*aTabIndex = -1;
}
if (mType != NS_FORM_INPUT_RADIO) {
return NS_OK;
return PR_TRUE;
}
PRBool checked;
GetChecked(&checked);
if (checked) {
// Selected radio buttons are tabbable
return NS_OK;
return PR_TRUE;
}
// Current radio button is not selected.
@ -2677,16 +2658,15 @@ nsHTMLInputElement::GetTabbable(PRBool *isTabbable)
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
nsAutoString name;
if (!container || !GetNameIfExists(name)) {
return NS_OK;
return PR_TRUE;
}
nsCOMPtr<nsIDOMHTMLInputElement> currentRadio;
container->GetCurrentRadioButton(name, getter_AddRefs(currentRadio));
if (currentRadio) {
*isTabbable = PR_FALSE;
*aTabIndex = -1;
}
return NS_OK;
return PR_TRUE;
}
nsresult

Просмотреть файл

@ -70,6 +70,9 @@ public:
// nsIDOMHTMLObjectElement
NS_DECL_NSIDOMHTMLOBJECTELEMENT
// nsIContent
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
// Overriden nsIFormControl methods
NS_IMETHOD_(PRInt32) GetType() { return NS_FORM_OBJECT; }
NS_IMETHOD Reset();
@ -145,6 +148,18 @@ nsHTMLObjectElement::GetForm(nsIDOMHTMLFormElement** aForm)
return nsGenericHTMLFormElement::GetForm(aForm);
}
PRBool
nsHTMLObjectElement::IsFocusable(PRInt32 *aTabIndex)
{
if (!nsGenericHTMLElement::IsFocusable(aTabIndex)) {
return PR_FALSE;
}
if (aTabIndex && (sTabFocusModel & eTabFocus_formElementsMask) == 0) {
*aTabIndex = -1;
}
return PR_TRUE;
}
// nsIFormControl
NS_IMETHODIMP

Просмотреть файл

@ -223,7 +223,7 @@ public:
nsEventStatus* aEventStatus);
virtual void SetFocus(nsIPresContext* aPresContext);
virtual void RemoveFocus(nsIPresContext* aPresContext);
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
// Overriden nsIFormControl methods
NS_IMETHOD_(PRInt32) GetType() { return NS_FORM_SELECT; }
@ -1620,25 +1620,16 @@ nsHTMLSelectElement::SetFocus(nsIPresContext* aPresContext)
}
}
void
nsHTMLSelectElement::RemoveFocus(nsIPresContext* aPresContext)
PRBool
nsHTMLSelectElement::IsFocusable(PRInt32 *aTabIndex)
{
if (!aPresContext)
return;
// If we are disabled, we probably shouldn't have focus in the
// first place, so allow it to be removed.
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
if (formControlFrame) {
formControlFrame->SetFocus(PR_FALSE, PR_FALSE);
if (!nsGenericHTMLElement::IsFocusable(aTabIndex)) {
return PR_FALSE;
}
if (mDocument) {
aPresContext->EventStateManager()->SetContentState(nsnull,
NS_EVENT_STATE_FOCUS);
if (aTabIndex && (sTabFocusModel & eTabFocus_formElementsMask) == 0) {
*aTabIndex = -1;
}
return PR_TRUE;
}
NS_IMETHODIMP

Просмотреть файл

@ -130,7 +130,6 @@ public:
PRUint32 aFlags,
nsEventStatus* aEventStatus);
virtual void SetFocus(nsIPresContext* aPresContext);
virtual void RemoveFocus(nsIPresContext* aPresContext);
virtual nsresult GetInnerHTML(nsAString& aInnerHTML);
virtual nsresult SetInnerHTML(const nsAString& aInnerHTML);
@ -260,27 +259,6 @@ nsHTMLTextAreaElement::SetFocus(nsIPresContext* aPresContext)
}
}
void
nsHTMLTextAreaElement::RemoveFocus(nsIPresContext* aPresContext)
{
if (!aPresContext)
return;
// If we are disabled, we probably shouldn't have focus in the
// first place, so allow it to be removed.
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
if (formControlFrame) {
formControlFrame->SetFocus(PR_FALSE, PR_FALSE);
}
if (mDocument) {
aPresContext->EventStateManager()->SetContentState(nsnull,
NS_EVENT_STATE_FOCUS);
}
}
NS_IMETHODIMP
nsHTMLTextAreaElement::Select()
{

Просмотреть файл

@ -147,6 +147,7 @@
#include "nsDOMAttributeMap.h"
#include "nsDOMCSSDeclaration.h"
#include "nsGenericHTMLElement.h"
#include "nsHTMLAtoms.h"
#include "prlog.h"
#include "rdf.h"
@ -1424,6 +1425,32 @@ nsXULElement::GetListenerManager(nsIEventListenerManager** aResult)
return NS_OK;
}
PRBool
nsXULElement::IsFocusable(PRInt32 *aTabIndex)
{
// Use incoming tabindex as default value
PRInt32 tabIndex = aTabIndex? *aTabIndex : -1;
PRBool disabled = tabIndex < 0;
nsCOMPtr<nsIDOMXULControlElement> xulControl =
do_QueryInterface(NS_STATIC_CAST(nsIContent*, this));
if (xulControl) {
xulControl->GetDisabled(&disabled);
if (disabled) {
tabIndex = -1; // Can't tab to disabled elements
}
else if (HasAttr(kNameSpaceID_None, nsHTMLAtoms::tabindex)) {
// If attribute not set, will use default value passed in
xulControl->GetTabIndex(&tabIndex);
}
}
if (aTabIndex) {
*aTabIndex = tabIndex;
}
return !disabled;
}
//----------------------------------------------------------------------
// nsIScriptEventHandlerOwner interface

Просмотреть файл

@ -493,6 +493,7 @@ public:
virtual PRBool IsContentOfType(PRUint32 aFlags) const;
virtual already_AddRefed<nsIURI> GetBaseURI() const;
virtual nsresult GetListenerManager(nsIEventListenerManager** aResult);
virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
// nsIXMLContent
NS_IMETHOD MaybeTriggerAutoLink(nsIDocShell *aShell);

Просмотреть файл

@ -58,9 +58,4 @@ interface nsIDOMNSHTMLInputElement : nsISupports
/* convenience */
void setSelectionRange(in long selectionStart,
in long selectionEnd);
/**
* Is this input control in the tab order?
*/
readonly attribute boolean tabbable;
};

Просмотреть файл

@ -1240,6 +1240,26 @@ NS_PTR_TO_INT32(frame->GetProperty(nsLayoutAtoms::embeddingLevel))
*/
virtual PRBool SupportsVisibilityHidden() { return PR_TRUE; }
/**
* Check if this frame is focusable and in the current tab order.
* Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.
* For example, only the selected radio button in a group is in the
* tab order, unless the radio group has no selection in which case
* all of the visible, non-disabled radio buttons in the group are
* in the tab order. On the other hand, all of the visible, non-disabled
* radio buttons are always focusable via clicking or script.
* Also, depending on the pref accessibility.tabfocus some widgets may be
* focusable but removed from the tab order. This is the default on
* Mac OS X, where fewer items are focusable.
* @param [in, optional] aTabIndex the computed tab index
* < 0 if not tabbable
* == 0 if in normal tab order
* > 0 can be tabbed to in the order specified by this value
* @return whether the frame is focusable via mouse, kbd or script.
*/
PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
static nsIFrame *FocusableAncestor(nsIFrame *aFrame);
protected:
// Members
nsRect mRect;

Просмотреть файл

@ -4618,6 +4618,59 @@ nsFrame::GetStyleDataExternal(nsStyleStructID aSID) const
return mStyleContext->GetStyleData(aSID);
}
nsIFrame*
nsIFrame::FocusableAncestor(nsIFrame *aFrame)
{
// This method helps prevent a situation where a link or other element
// with -moz-user-focus is focused twice, because the parent link
// would get focused, and all of the children also get focus.
// Scroll frames are the only focusable containers that
// can have focusable children.
nsIFrame *ancestorFrame = aFrame;
while ((ancestorFrame = ancestorFrame->GetParent()) != nsnull &&
ancestorFrame->GetType() != nsLayoutAtoms::scrollFrame) {
// Any other parent that's focusable can't have focusable children
const nsStyleUserInterface *ui = ancestorFrame->GetStyleUserInterface();
if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
// Inside a focusable parent -- let parent get focus
// instead of child (to avoid links within links etc.)
return ancestorFrame;
}
}
return nsnull;
}
PRBool
nsIFrame::IsFocusable(PRInt32 *aTabIndex)
{
PRInt32 tabIndex = -1;
PRBool isFocusable = PR_FALSE;
if (AreAncestorViewsVisible() && mContent &&
mContent->IsContentOfType(nsIContent::eELEMENT) &&
!FocusableAncestor(this)) {
const nsStyleVisibility* vis = GetStyleVisibility();
if (vis->mVisible != NS_STYLE_VISIBILITY_COLLAPSE &&
vis->mVisible != NS_STYLE_VISIBILITY_HIDDEN) {
const nsStyleUserInterface* ui = GetStyleUserInterface();
if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
// Pass in default tabindex of -1 for nonfocusable and 0 for focusable
tabIndex = 0;
}
isFocusable = mContent->IsFocusable(&tabIndex);
}
}
if (aTabIndex) {
*aTabIndex = tabIndex;
}
return isFocusable;
}
#ifdef NS_DEBUG
static void
GetTagName(nsFrame* aFrame, nsIContent* aContent, PRIntn aResultSize,

Просмотреть файл

@ -1240,6 +1240,26 @@ NS_PTR_TO_INT32(frame->GetProperty(nsLayoutAtoms::embeddingLevel))
*/
virtual PRBool SupportsVisibilityHidden() { return PR_TRUE; }
/**
* Check if this frame is focusable and in the current tab order.
* Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.
* For example, only the selected radio button in a group is in the
* tab order, unless the radio group has no selection in which case
* all of the visible, non-disabled radio buttons in the group are
* in the tab order. On the other hand, all of the visible, non-disabled
* radio buttons are always focusable via clicking or script.
* Also, depending on the pref accessibility.tabfocus some widgets may be
* focusable but removed from the tab order. This is the default on
* Mac OS X, where fewer items are focusable.
* @param [in, optional] aTabIndex the computed tab index
* < 0 if not tabbable
* == 0 if in normal tab order
* > 0 can be tabbed to in the order specified by this value
* @return whether the frame is focusable via mouse, kbd or script.
*/
PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
static nsIFrame *FocusableAncestor(nsIFrame *aFrame);
protected:
// Members
nsRect mRect;

Просмотреть файл

@ -4618,6 +4618,59 @@ nsFrame::GetStyleDataExternal(nsStyleStructID aSID) const
return mStyleContext->GetStyleData(aSID);
}
nsIFrame*
nsIFrame::FocusableAncestor(nsIFrame *aFrame)
{
// This method helps prevent a situation where a link or other element
// with -moz-user-focus is focused twice, because the parent link
// would get focused, and all of the children also get focus.
// Scroll frames are the only focusable containers that
// can have focusable children.
nsIFrame *ancestorFrame = aFrame;
while ((ancestorFrame = ancestorFrame->GetParent()) != nsnull &&
ancestorFrame->GetType() != nsLayoutAtoms::scrollFrame) {
// Any other parent that's focusable can't have focusable children
const nsStyleUserInterface *ui = ancestorFrame->GetStyleUserInterface();
if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
// Inside a focusable parent -- let parent get focus
// instead of child (to avoid links within links etc.)
return ancestorFrame;
}
}
return nsnull;
}
PRBool
nsIFrame::IsFocusable(PRInt32 *aTabIndex)
{
PRInt32 tabIndex = -1;
PRBool isFocusable = PR_FALSE;
if (AreAncestorViewsVisible() && mContent &&
mContent->IsContentOfType(nsIContent::eELEMENT) &&
!FocusableAncestor(this)) {
const nsStyleVisibility* vis = GetStyleVisibility();
if (vis->mVisible != NS_STYLE_VISIBILITY_COLLAPSE &&
vis->mVisible != NS_STYLE_VISIBILITY_HIDDEN) {
const nsStyleUserInterface* ui = GetStyleUserInterface();
if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
// Pass in default tabindex of -1 for nonfocusable and 0 for focusable
tabIndex = 0;
}
isFocusable = mContent->IsFocusable(&tabIndex);
}
}
if (aTabIndex) {
*aTabIndex = tabIndex;
}
return isFocusable;
}
#ifdef NS_DEBUG
static void
GetTagName(nsFrame* aFrame, nsIContent* aContent, PRIntn aResultSize,

Просмотреть файл

@ -408,8 +408,20 @@ noframes {
display: none;
}
*|*:-moz-any-link:focus img {
-moz-outline: 1px dotted invert;
}
*|*:-moz-any-link:focus object {
-moz-outline: 1px dotted invert;
}
*|*:-moz-any-link:focus embed {
-moz-outline: 1px dotted invert;
}
/* focusable content: anything w/ tabindex >=0 is focusable */
a:focus, abbr:focus, acronym:focus, address:focus, applet:focus, b:focus,
abbr:focus, acronym:focus, address:focus, applet:focus, b:focus,
base:focus, big:focus, blockquote:focus, br:focus, caption:focus, center:focus,
cite:focus, code:focus, col:focus, colgroup:focus, dd:focus, del:focus,
dfn:focus, dir:focus, div:focus, dl:focus, dt:focus, em:focus, fieldset:focus,

Просмотреть файл

@ -408,8 +408,20 @@ noframes {
display: none;
}
*|*:-moz-any-link:focus img {
-moz-outline: 1px dotted invert;
}
*|*:-moz-any-link:focus object {
-moz-outline: 1px dotted invert;
}
*|*:-moz-any-link:focus embed {
-moz-outline: 1px dotted invert;
}
/* focusable content: anything w/ tabindex >=0 is focusable */
a:focus, abbr:focus, acronym:focus, address:focus, applet:focus, b:focus,
abbr:focus, acronym:focus, address:focus, applet:focus, b:focus,
base:focus, big:focus, blockquote:focus, br:focus, caption:focus, center:focus,
cite:focus, code:focus, col:focus, colgroup:focus, dd:focus, del:focus,
dfn:focus, dir:focus, div:focus, dl:focus, dt:focus, em:focus, fieldset:focus,