Bug 391490. Every single <option> shows up in JAWS virtual cursor mode. Terrible for screen reader usability. r=ginn.chen, a=dsicore

This commit is contained in:
aaronleventhal@moonset.net 2007-11-15 11:46:29 -08:00
Родитель 30909d3979
Коммит 9a82827216
10 изменённых файлов: 100 добавлений и 15 удалений

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

@ -741,17 +741,32 @@ interface nsIAccessibleRole : nsISupports
/**
* A item of list that is shown by combobox;
*/
const unsigned long ROLE_COMBOBOX_LISTITEM = 115;
const unsigned long ROLE_COMBOBOX_OPTION = 115;
/**
* An image map -- has child links representing the areas
*/
const unsigned long ROLE_IMAGE_MAP = 116;
/**
* An option in a listbox
*/
const unsigned long ROLE_OPTION = 117;
/**
* A rich option in a listbox, it can have other widgets as children
*/
const unsigned long ROLE_RICH_OPTION = 118;
/**
* A list of options
*/
const unsigned long ROLE_LISTBOX = 119;
/**
* It's not role actually. This contanst is important to help ensure
* nsRoleMap's are synchronized.
*/
const unsigned long ROLE_LAST_ENTRY = 117;
const unsigned long ROLE_LAST_ENTRY = 120;
};

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

@ -161,8 +161,11 @@ static const PRUint32 atkRoleMap[] = {
ATK_ROLE_MENU, // nsIAccessibleRole::ROLE_PARENT_MENUITEM 112
ATK_ROLE_CALENDAR, // nsIAccessibleRole::ROLE_CALENDAR 113
ATK_ROLE_MENU, // nsIAccessibleRole::ROLE_COMBOBOX_LIST 114
ATK_ROLE_MENU_ITEM, // nsIAccessibleRole::ROLE_COMBOBOX_LISTITEM 115
ATK_ROLE_MENU_ITEM, // nsIAccessibleRole::ROLE_COMBOBOX_OPTION 115
ATK_ROLE_IMAGE, // nsIAccessibleRole::ROLE_IMAGE_MAP 116
ATK_ROLE_LIST_ITEM, // nsIAccessibleRole::ROLE_OPTION 117
ATK_ROLE_LIST_ITEM, // nsIAccessibleRole::ROLE_RICH_OPTION 118
ATK_ROLE_LIST, // nsIAccessibleRole::ROLE_LISTBOX 119
kROLE_ATK_LAST_ENTRY // nsIAccessibleRole::ROLE_LAST_ENTRY
};

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

@ -118,7 +118,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
{"list", nsIAccessibleRole::ROLE_LIST, eNameLabelOrTitle, eNoValue, kNoReqStates,
{eAria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
{eAria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE}, kEndEntry},
{"listbox", nsIAccessibleRole::ROLE_LIST, eNameLabelOrTitle, eNoValue, kNoReqStates,
{"listbox", nsIAccessibleRole::ROLE_LISTBOX, eNameLabelOrTitle, eNoValue, kNoReqStates,
{eAria_disabled, kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
{eAria_readonly, kBoolState, nsIAccessibleStates::STATE_READONLY},
{eAria_multiselectable, kBoolState, nsIAccessibleStates::STATE_MULTISELECTABLE | nsIAccessibleStates::STATE_EXTSELECTABLE}, kEndEntry},
@ -144,7 +144,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] =
{"menuitemradio", nsIAccessibleRole::ROLE_RADIO_MENU_ITEM, eNameOkFromChildren, eNoValue, nsIAccessibleStates::STATE_CHECKABLE,
{eAria_disabled, kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
{eAria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED }, kEndEntry},
{"option", nsIAccessibleRole::ROLE_LISTITEM, eNameOkFromChildren, eNoValue, kNoReqStates,
{"option", nsIAccessibleRole::ROLE_OPTION, eNameOkFromChildren, eNoValue, kNoReqStates,
{eAria_disabled, kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
{eAria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
{eAria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},

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

@ -257,7 +257,7 @@ static const char kRoleNames[][20] = {
"parent menuitem", //ROLE_PARENT_MENUITEM
"calendar", //ROLE_CALENDAR
"combobox list", //ROLE_COMBOBOX_LIST
"combobox listitem", //ROLE_COMBOBOX_LISTITEM
"combobox option", //ROLE_COMBOBOX_OPTION
"image map" //ROLE_IMAGE_MAP
};

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

@ -1991,7 +1991,22 @@ NS_IMETHODIMP nsAccessible::GetFinalRole(PRUint32 *aRole)
}
}
}
else if (*aRole == nsIAccessibleRole::ROLE_LISTBOX) {
// A listbox inside of a combo box needs a special role because of ATK mapping to menu
nsCOMPtr<nsIAccessible> parent;
GetParent(getter_AddRefs(parent));
if (parent && Role(parent) == nsIAccessibleRole::ROLE_COMBOBOX) {
*aRole = nsIAccessibleRole::ROLE_COMBOBOX_LIST;
}
}
else if (*aRole == nsIAccessibleRole::ROLE_OPTION) {
nsCOMPtr<nsIAccessible> parent;
GetParent(getter_AddRefs(parent));
if (parent && Role(parent) == nsIAccessibleRole::ROLE_COMBOBOX_LIST) {
*aRole = nsIAccessibleRole::ROLE_COMBOBOX_OPTION;
}
}
if (*aRole != nsIAccessibleRole::ROLE_NOTHING) {
return NS_OK;
}
@ -3311,6 +3326,8 @@ PRBool nsAccessible::MustPrune(nsIAccessible *aAccessible)
{
PRUint32 role = Role(aAccessible);
return role == nsIAccessibleRole::ROLE_MENUITEM ||
role == nsIAccessibleRole::ROLE_COMBOBOX_OPTION ||
role == nsIAccessibleRole::ROLE_OPTION ||
role == nsIAccessibleRole::ROLE_ENTRY ||
role == nsIAccessibleRole::ROLE_PASSWORD_TEXT ||
role == nsIAccessibleRole::ROLE_PUSHBUTTON ||

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

@ -333,6 +333,16 @@ nsHTMLSelectListAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
nsCOMPtr<nsIDOMHTMLSelectElement> select (do_QueryInterface(mDOMNode));
if (select) {
if (*aState | nsIAccessibleStates::STATE_FOCUSED) {
// Treat first focusable option node as actual focus, in order
// to avoid confusing JAWS, which needs focus on the option
nsCOMPtr<nsIDOMNode> focusedOption;
nsHTMLSelectOptionAccessible::GetFocusedOptionNode(mDOMNode,
getter_AddRefs(focusedOption));
if (focusedOption) { // Clear focused state since it is on option
*aState &= ~nsIAccessibleStates::STATE_FOCUSED;
}
}
PRBool multiple;
select->GetMultiple(&multiple);
if ( multiple )
@ -485,10 +495,10 @@ nsHyperTextAccessibleWrap(aDOMNode, aShell)
NS_IMETHODIMP nsHTMLSelectOptionAccessible::GetRole(PRUint32 *aRole)
{
if (mParent && Role(mParent) == nsIAccessibleRole::ROLE_COMBOBOX_LIST) {
*aRole = nsIAccessibleRole::ROLE_COMBOBOX_LISTITEM;
*aRole = nsIAccessibleRole::ROLE_COMBOBOX_OPTION;
}
else {
*aRole = nsIAccessibleRole::ROLE_LISTITEM;
*aRole = nsIAccessibleRole::ROLE_OPTION;
}
return NS_OK;
}
@ -622,6 +632,18 @@ nsHTMLSelectOptionAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
if (0 == (*aState & nsIAccessibleStates::STATE_UNAVAILABLE)) {
*aState |= (nsIAccessibleStates::STATE_FOCUSABLE |
nsIAccessibleStates::STATE_SELECTABLE);
// When the list is focused but no option is actually focused,
// Firefox draws a focus ring around the first non-disabled option.
// We need to indicated STATE_FOCUSED in that case, because it
// prevents JAWS from ignoring the list
// GetFocusedOptionNode() ensures that an option node is
// returned in this case, as long as some focusable option exists
// in the listbox
nsCOMPtr<nsIDOMNode> focusedOptionNode;
GetFocusedOptionNode(selectNode, getter_AddRefs(focusedOptionNode));
if (focusedOptionNode == mDOMNode) {
*aState |= nsIAccessibleStates::STATE_FOCUSED;
}
}
// Are we selected?
@ -788,6 +810,22 @@ nsresult nsHTMLSelectOptionAccessible::GetFocusedOptionNode(nsIDOMNode *aListNod
// when there is more than 1 item selected. We need the focused item, not
// the first selected item.
focusedOptionIndex = listFrame->GetSelectedIndex();
if (focusedOptionIndex == -1) {
nsCOMPtr<nsIDOMNode> nextOption;
while (PR_TRUE) {
++ focusedOptionIndex;
options->Item(focusedOptionIndex, getter_AddRefs(nextOption));
nsCOMPtr<nsIDOMHTMLOptionElement> optionElement = do_QueryInterface(nextOption);
if (!optionElement) {
break;
}
PRBool disabled;
optionElement->GetDisabled(&disabled);
if (!disabled) {
break;
}
}
}
}
else // Combo boxes can only have 1 selected option, so they can use the dom interface for this
rv = selectElement->GetSelectedIndex(&focusedOptionIndex);

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

@ -157,7 +157,10 @@ static const NSString* AXRoles [] = {
NSAccessibilityMenuItemRole, // ROLE_PARENT_MENUITEM
NSAccessibilityGroupRole, // ROLE_CALENDAR
NSAccessibilityMenuRole, // ROLE_COMBOBOX_LIST
NSAccessibilityMenuItemRole, // ROLE_COMBOBOX_LISTITEM
NSAccessibilityMenuItemRole, // ROLE_COMBOBOX_OPTION
NSAccessibilityImageRole, // ROLE_IMAGE_MAP
NSAccessibilityRowRole, // ROLE_OPTION
NSAccessibilityRowRole, // ROLE_RICH_OPTION
NSAccessibilityListRole, // ROLE_LISTBOX
@"ROLE_LAST_ENTRY" // ROLE_LAST_ENTRY. bogus role that will never be shown (just marks the end of this array)!
};

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

@ -419,12 +419,21 @@ static const WindowsRoleMapItem gWindowsRoleMap[] = {
// nsIAccessibleRole::ROLE_COMBOBOX_LIST
{ ROLE_SYSTEM_LIST, ROLE_SYSTEM_LIST },
// nsIAccessibleRole::ROLE_COMBOBOX_LISTITEM
// nsIAccessibleRole::ROLE_COMBOBOX_OPTION
{ ROLE_SYSTEM_LISTITEM, ROLE_SYSTEM_LISTITEM },
// nsIAccessibleRole::ROLE_IMAGE_MAP
{ ROLE_SYSTEM_GRAPHIC, ROLE_SYSTEM_GRAPHIC },
// nsIAccessibleRole::ROLE_OPTION
{ ROLE_SYSTEM_LISTITEM, ROLE_SYSTEM_LISTITEM },
// nsIAccessibleRole::ROLE_RICH_OPTION
{ ROLE_SYSTEM_LIST, ROLE_SYSTEM_LIST },
// nsIAccessibleRole::ROLE_LISTBOX
{ ROLE_SYSTEM_LIST, ROLE_SYSTEM_LIST },
// nsIAccessibleRole::ROLE_LAST_ENTRY
{ ROLE_WINDOWS_LAST_ENTRY, ROLE_WINDOWS_LAST_ENTRY }
};

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

@ -318,7 +318,7 @@ nsXULMenuitemAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
}
// Combo box listitem
if (Role(this) == nsIAccessibleRole::ROLE_COMBOBOX_LISTITEM) {
if (Role(this) == nsIAccessibleRole::ROLE_COMBOBOX_OPTION) {
// Is selected?
PRBool isSelected = PR_FALSE;
nsCOMPtr<nsIDOMXULSelectControlItemElement>
@ -363,7 +363,7 @@ nsXULMenuitemAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
}
} // isCollapsed
} // isSelected
} // ROLE_COMBOBOX_LISTITEM
} // ROLE_COMBOBOX_OPTION
return NS_OK;
}
@ -461,7 +461,7 @@ NS_IMETHODIMP nsXULMenuitemAccessible::GetRole(PRUint32 *aRole)
{
*aRole = nsIAccessibleRole::ROLE_MENUITEM;
if (mParent && Role(mParent) == nsIAccessibleRole::ROLE_COMBOBOX_LIST) {
*aRole = nsIAccessibleRole::ROLE_COMBOBOX_LISTITEM;
*aRole = nsIAccessibleRole::ROLE_COMBOBOX_OPTION;
return NS_OK;
}
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));

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

@ -250,7 +250,7 @@ NS_IMETHODIMP nsXULListitemAccessible::GetRole(PRUint32 *aRole)
if (mIsCheckbox)
*aRole = nsIAccessibleRole::ROLE_CHECKBUTTON;
else
*aRole = nsIAccessibleRole::ROLE_LISTITEM;
*aRole = nsIAccessibleRole::ROLE_RICH_OPTION;
return NS_OK;
}