Bug 391490. Every single <option> shows up in JAWS virtual cursor mode. Terrible for screen reader usability. r=ginn.chen, a=dsicore
authoraaronleventhal@moonset.net
Thu, 15 Nov 2007 11:46:29 -0800
changeset 8051 f2a0f2dc264274a53e68471b5b17ed65c7b80877
parent 8050 bd939f6eff1b71030df8177f6bfc03826db8ca74
child 8052 85e8ae4514b6a9530f97bda0e5ed5faff89f4bbc
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersginn.chen, dsicore
bugs391490
milestone1.9b2pre
Bug 391490. Every single <option> shows up in JAWS virtual cursor mode. Terrible for screen reader usability. r=ginn.chen, a=dsicore
accessible/public/nsIAccessibleRole.idl
accessible/src/atk/nsRoleMap.h
accessible/src/base/nsARIAMap.cpp
accessible/src/base/nsAccessibilityService.h
accessible/src/base/nsAccessible.cpp
accessible/src/html/nsHTMLSelectAccessible.cpp
accessible/src/mac/nsRoleMap.h
accessible/src/msaa/nsRoleMap.h
accessible/src/xul/nsXULMenuAccessible.cpp
accessible/src/xul/nsXULSelectAccessible.cpp
--- a/accessible/public/nsIAccessibleRole.idl
+++ b/accessible/public/nsIAccessibleRole.idl
@@ -736,22 +736,37 @@ interface nsIAccessibleRole : nsISupport
   /**
    * A list of items that is shown by combobox.
    */
   const unsigned long ROLE_COMBOBOX_LIST = 114;
 
   /**
    * 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;
 };
 
--- a/accessible/src/atk/nsRoleMap.h
+++ b/accessible/src/atk/nsRoleMap.h
@@ -156,13 +156,16 @@ static const PRUint32 atkRoleMap[] = {
     ATK_ROLE_SECTION,             // nsIAccessibleRole::ROLE_SECTION              107
     ATK_ROLE_REDUNDANT_OBJECT,    // nsIAccessibleRole::ROLE_REDUNDANT_OBJECT     108
     ATK_ROLE_FORM,                // nsIAccessibleRole::ROLE_FORM                 109
     ATK_ROLE_INPUT_METHOD_WINDOW, // nsIAccessibleRole::ROLE_IME                  110
     ATK_ROLE_APPLICATION,         // nsIAccessibleRole::ROLE_APP_ROOT             111
     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
 };
 
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -113,17 +113,17 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] 
   {"heading", nsIAccessibleRole::ROLE_HEADING, eNameLabelOrTitle, eNoValue, kNoReqStates, kEndEntry},
   {"img", nsIAccessibleRole::ROLE_GRAPHIC, eNameLabelOrTitle, eNoValue, kNoReqStates, kEndEntry},
   {"label", nsIAccessibleRole::ROLE_LABEL, eNameOkFromChildren, eNoValue, kNoReqStates, kEndEntry},
   {"link", nsIAccessibleRole::ROLE_LINK, eNameOkFromChildren, eNoValue, nsIAccessibleStates::STATE_LINKED,
             {eAria_disabled, kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE}, kEndEntry},
   {"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},
   {"listitem", nsIAccessibleRole::ROLE_LISTITEM, eNameOkFromChildren, eNoValue, kNoReqStates,
             {eAria_selected, kBoolState, nsIAccessibleStates::STATE_SELECTED | nsIAccessibleStates::STATE_SELECTABLE},
             {eAria_selected, "false", nsIAccessibleStates::STATE_SELECTABLE},
             {eAria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE},
             {eAria_checked, "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
@@ -139,17 +139,17 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] 
             {eAria_checked, "false", nsIAccessibleStates::STATE_CHECKABLE}, kEndEntry},
   {"menuitemcheckbox", nsIAccessibleRole::ROLE_CHECK_MENU_ITEM, eNameOkFromChildren, eNoValue, nsIAccessibleStates::STATE_CHECKABLE,
             {eAria_disabled, kBoolState, nsIAccessibleStates::STATE_UNAVAILABLE},
             {eAria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED },
             {eAria_checked, "mixed", nsIAccessibleStates::STATE_MIXED}, kEndEntry},
   {"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},
             {eAria_checked, kBoolState, nsIAccessibleStates::STATE_CHECKED | nsIAccessibleStates::STATE_CHECKABLE},
             {eAria_checked, "mixed", nsIAccessibleStates::STATE_MIXED | nsIAccessibleStates::STATE_CHECKABLE},
             {eAria_checked, "false", nsIAccessibleStates::STATE_CHECKABLE}, kEndEntry},
   {"presentation", nsIAccessibleRole::ROLE_NOTHING, eNameLabelOrTitle, eNoValue, kNoReqStates, kEndEntry},
   {"progressbar", nsIAccessibleRole::ROLE_PROGRESSBAR, eNameLabelOrTitle, eHasValueMinMax, nsIAccessibleStates::STATE_READONLY,
--- a/accessible/src/base/nsAccessibilityService.h
+++ b/accessible/src/base/nsAccessibilityService.h
@@ -252,17 +252,17 @@ static const char kRoleNames[][20] = {
   "section",             //ROLE_SECTION
   "redundant object",    //ROLE_REDUNDANT_OBJECT
   "form",                //ROLE_FORM
   "ime",                 //ROLE_IME
   "app root",            //ROLE_APP_ROOT
   "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
 };
 
 /**
  * Map nsIAccessibleEvents constants to strings. Used by
  * nsIAccessibleRetrieval::getStringEventType() method.
  */
 static const char kEventTypeNames[][40] = {
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -1986,17 +1986,32 @@ NS_IMETHODIMP nsAccessible::GetFinalRole
           if (nsAccUtils::GetAriaProperty(content, mWeakShell, eAria_haspopup, haspopup) &&
               haspopup.EqualsLiteral("true")) {
             // For button with aaa:haspopup="true"
             *aRole = nsIAccessibleRole::ROLE_BUTTONMENU;
           }
         }
       }
     }
-  
+    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;
     }
   }
   return mDOMNode ? GetRole(aRole) : NS_ERROR_FAILURE;  // Node already shut down
 }
 
 NS_IMETHODIMP
@@ -3306,16 +3321,18 @@ nsAccessible::GetAttrValue(EAriaProperty
 
   return result;
 }
 
 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 ||
          role == nsIAccessibleRole::ROLE_TOGGLE_BUTTON ||
          role == nsIAccessibleRole::ROLE_GRAPHIC ||
          role == nsIAccessibleRole::ROLE_SLIDER ||
          role == nsIAccessibleRole::ROLE_PROGRESSBAR ||
          role == nsIAccessibleRole::ROLE_SEPARATOR;
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -328,16 +328,26 @@ nsHTMLSelectListAccessible::nsHTMLSelect
 NS_IMETHODIMP
 nsHTMLSelectListAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
 {
   nsresult rv = nsHTMLSelectableAccessible::GetState(aState, aExtraState);
   NS_ENSURE_SUCCESS(rv, rv);
 
   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 )
       *aState |= nsIAccessibleStates::STATE_MULTISELECTABLE |
                  nsIAccessibleStates::STATE_EXTSELECTABLE;
   }
 
   return NS_OK;
@@ -480,20 +490,20 @@ nsHyperTextAccessibleWrap(aDOMNode, aShe
   }
   SetParent(parentAccessible);
 }
 
 /** We are a ListItem */
 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;
 }
 
 /**
   * Get our Name from our Content's subtree
   */
 NS_IMETHODIMP nsHTMLSelectOptionAccessible::GetName(nsAString& aName)
@@ -617,16 +627,28 @@ nsHTMLSelectOptionAccessible::GetState(P
 
   nsCOMPtr<nsIDOMNode> selectNode = do_QueryInterface(selectContent); 
   NS_ENSURE_TRUE(selectNode, NS_ERROR_FAILURE);
 
   // Is disabled?
   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?
   PRBool isSelected = PR_FALSE;
   nsCOMPtr<nsIDOMHTMLOptionElement> option (do_QueryInterface(mDOMNode));
   if (option) {
     option->GetSelected(&isSelected);
     if ( isSelected ) 
@@ -783,16 +805,32 @@ nsresult nsHTMLSelectOptionAccessible::G
     nsIListControlFrame *listFrame = nsnull;
     frame->QueryInterface(NS_GET_IID(nsIListControlFrame), (void**)&listFrame);
     if (listFrame) {
       // Get what's focused in listbox by asking frame for "selected item". 
       // Can't use dom interface for this, because it will always return the first selected item
       // 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);
   }
 
   // Either use options and focused index, or default return null
   if (NS_SUCCEEDED(rv) && options && focusedOptionIndex >= 0) {  // Something is focused
     rv = options->Item(focusedOptionIndex, aFocusedOptionNode);
--- a/accessible/src/mac/nsRoleMap.h
+++ b/accessible/src/mac/nsRoleMap.h
@@ -152,12 +152,15 @@ static const NSString* AXRoles [] = {
   NSAccessibilityGroupRole,                     // ROLE_SECTION
   NSAccessibilityUnknownRole,                   // ROLE_REDUNDANT_OBJECT
   NSAccessibilityGroupRole,                     // ROLE_FORM
   NSAccessibilityUnknownRole,                   // ROLE_IME
   NSAccessibilityUnknownRole,                   // ROLE_APP_ROOT. unused on OS X
   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)!
 };
--- a/accessible/src/msaa/nsRoleMap.h
+++ b/accessible/src/msaa/nsRoleMap.h
@@ -414,18 +414,27 @@ static const WindowsRoleMapItem gWindows
   { ROLE_SYSTEM_MENUITEM, ROLE_SYSTEM_MENUITEM },
 
   // nsIAccessibleRole::ROLE_CALENDAR
   { ROLE_SYSTEM_CLIENT, ROLE_SYSTEM_CLIENT },
 
   // 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 }
 };
 
--- a/accessible/src/xul/nsXULMenuAccessible.cpp
+++ b/accessible/src/xul/nsXULMenuAccessible.cpp
@@ -313,17 +313,17 @@ nsXULMenuitemAccessible::GetState(PRUint
     nsAutoString checkValue;
     element->GetAttribute(NS_LITERAL_STRING("checked"), checkValue);
     if (checkValue.EqualsLiteral("true")) {
       *aState |= nsIAccessibleStates::STATE_CHECKED;
     }
   }
 
   // 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>
       item(do_QueryInterface(mDOMNode));
     NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
     item->GetSelected(&isSelected);
 
     // Is collapsed?
@@ -358,17 +358,17 @@ nsXULMenuitemAccessible::GetState(PRUint
         *aState |= grandParentState & nsIAccessibleStates::STATE_OFFSCREEN |
                    grandParentState & nsIAccessibleStates::STATE_INVISIBLE;
         if (aExtraState) {
           *aExtraState |=
             grandParentExtState & nsIAccessibleStates::EXT_STATE_OPAQUE;
         }
       } // isCollapsed
     } // isSelected
-  } // ROLE_COMBOBOX_LISTITEM
+  } // ROLE_COMBOBOX_OPTION
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsXULMenuitemAccessible::GetName(nsAString& _retval)
 {
   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(mDOMNode));
   if (!element) {
@@ -456,17 +456,17 @@ nsXULMenuitemAccessible::GetDefaultKeyBi
 
   return NS_OK;
 }
 
 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));
   if (!element)
     return NS_ERROR_FAILURE;
   nsAutoString menuItemType;
   element->GetAttribute(NS_LITERAL_STRING("type"), menuItemType);
   if (menuItemType.EqualsIgnoreCase("radio"))
--- a/accessible/src/xul/nsXULSelectAccessible.cpp
+++ b/accessible/src/xul/nsXULSelectAccessible.cpp
@@ -245,17 +245,17 @@ NS_IMETHODIMP nsXULListitemAccessible::G
 /**
   *
   */
 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;
 }
 
 /**
   *
   */
 NS_IMETHODIMP
 nsXULListitemAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)