Bug 370163. Fix a11y hierarchy for selects with optgroup. r=evan.yan
authoraaronleventhal@moonset.net
Wed, 25 Apr 2007 08:11:02 -0700
changeset 789 b2ccbd1126d2c945eacfe2948808cf3f669366b6
parent 788 9a4c4999240298e5c13846c877e0e2472ba22edb
child 790 e50c1bfe2b18729a609450169cf74d7305ad5f5b
push idunknown
push userunknown
push dateunknown
reviewersevan.yan
bugs370163
milestone1.9a4pre
Bug 370163. Fix a11y hierarchy for selects with optgroup. r=evan.yan
accessible/src/html/nsHTMLSelectAccessible.cpp
accessible/src/html/nsHTMLSelectAccessible.h
--- a/accessible/src/html/nsHTMLSelectAccessible.cpp
+++ b/accessible/src/html/nsHTMLSelectAccessible.cpp
@@ -474,17 +474,17 @@ nsHyperTextAccessible(aDOMNode, aShell)
   }
   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_LISTITEM;
   }
   else {
     *aRole = nsIAccessibleRole::ROLE_COMBOBOX_LISTITEM;
   }
   return NS_OK;
 }
 
 /**
@@ -523,34 +523,52 @@ NS_IMETHODIMP nsHTMLSelectOptionAccessib
   
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 nsHTMLSelectOptionAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
 {
   NS_ENSURE_ARG_POINTER(aAttributes);
-  NS_ENSURE_TRUE(mDOMNode, NS_ERROR_FAILURE);
+  if (!mDOMNode) {
+    return NS_ERROR_FAILURE;  // Accessible shut down
+  }
 
   nsresult rv = nsHyperTextAccessible::GetAttributesInternal(aAttributes);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
-  NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
+  nsCOMPtr<nsIDOMNode> parentNode;
+  mDOMNode->GetParentNode(getter_AddRefs(parentNode));
+  nsCOMPtr<nsIDOMElement> parentElement(do_QueryInterface(parentNode));
+  NS_ENSURE_TRUE(parentElement, NS_ERROR_FAILURE);
+  nsAutoString parentTagName;
+  parentNode->GetLocalName(parentTagName);
+
+  PRInt32 level = parentTagName.LowerCaseEqualsLiteral("optgroup") ? 2 : 1;
+  if (level == 1 && Role(this) != nsIAccessibleRole::ROLE_HEADING) {
+    level = 0; // In a single level list, the level is irrelevant
+  }
 
-  nsCOMPtr<nsIContent> parentContent = content->GetParent();
-  NS_ENSURE_TRUE(parentContent, NS_ERROR_FAILURE);
-
-  PRUint32 level =
-    parentContent->NodeInfo()->Equals(nsAccessibilityAtoms::optgroup) ? 2 : 1;
-  PRUint32 childCount = parentContent->GetChildCount();
-  PRUint32 indexOf = parentContent->IndexOf(content);
+  nsAutoString tagName;
+  mDOMNode->GetLocalName(tagName);  // Will be looking for similar DOM siblings
+  nsCOMPtr<nsIDOMNodeList> siblings;
+  parentElement->GetElementsByTagName(tagName, getter_AddRefs(siblings));
+  PRInt32 posInSet = 0;
+  PRUint32 setSize = 0;
+  if (siblings) {
+    siblings->GetLength(&setSize);
+    nsCOMPtr<nsIDOMNode> itemNode;
+    while (NS_SUCCEEDED(siblings->Item(posInSet ++, getter_AddRefs(itemNode))) &&
+           itemNode != mDOMNode) {
+      // Keep looping, to increment posInSet
+    }
+  }
 
   nsAccessibilityUtils::
-    SetAccGroupAttrs(aAttributes, level, indexOf + 1, childCount);
+    SetAccGroupAttrs(aAttributes, level, posInSet, NS_STATIC_CAST(PRInt32, setSize));
   return  NS_OK;
 }
 
 nsIFrame* nsHTMLSelectOptionAccessible::GetBoundsFrame()
 {
   PRUint32 state;
   nsCOMPtr<nsIContent> content = GetSelectState(&state);
   if (state & nsIAccessibleStates::STATE_COLLAPSED) {
@@ -807,21 +825,23 @@ nsIContent* nsHTMLSelectOptionAccessible
 /** ----- nsHTMLSelectOptGroupAccessible ----- */
 
 /** Default Constructor */
 nsHTMLSelectOptGroupAccessible::nsHTMLSelectOptGroupAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
 nsHTMLSelectOptionAccessible(aDOMNode, aShell)
 {
 }
 
+NS_IMETHODIMP
+nsHTMLSelectOptGroupAccessible::GetRole(PRUint32 *aRole)
+{
+  *aRole = nsIAccessibleRole::ROLE_HEADING;
+  return NS_OK;
+}
 
-/**
-  * As a nsHTMLSelectOptGroupAccessible we can have the following states:
-  *     nsIAccessibleStates::STATE_SELECTABLE
-  */
 NS_IMETHODIMP
 nsHTMLSelectOptGroupAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
 {
   nsresult rv = nsHTMLSelectOptionAccessible::GetState(aState, aExtraState);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aState &= ~(nsIAccessibleStates::STATE_FOCUSABLE |
                nsIAccessibleStates::STATE_SELECTABLE);
@@ -839,16 +859,34 @@ NS_IMETHODIMP nsHTMLSelectOptGroupAccess
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsHTMLSelectOptGroupAccessible::GetNumActions(PRUint8 *_retval)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+void nsHTMLSelectOptGroupAccessible::CacheChildren()
+{
+  if (!mWeakShell) {
+    // This node has been shut down
+    mAccChildCount = eChildCountUninitialized;
+    return;
+  }
+
+  if (mAccChildCount == eChildCountUninitialized) {
+    // XXX To do (bug 378612) - create text child for the anonymous attribute content, so that
+    // nsIAccessibleText is supported for the <optgroup> as it is for an <option>
+    // Attribute content is what layout creates for the label="foo" on the <optgroup>
+    // See eStyleContentType_Attr and CreateAttributeContent() in nsCSSFrameConstructor
+    mAccChildCount = 0;
+    SetFirstChild(nsnull);
+  }
+}
+
 /** ------------------------------------------------------ */
 /**  Finally, the Combobox widgets                         */
 /** ------------------------------------------------------ */
 
 /** ----- nsHTMLComboboxAccessible ----- */
 
 nsHTMLComboboxAccessible::nsHTMLComboboxAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell):
 nsAccessibleWrap(aDOMNode, aShell)
--- a/accessible/src/html/nsHTMLSelectAccessible.h
+++ b/accessible/src/html/nsHTMLSelectAccessible.h
@@ -185,20 +185,22 @@ private:
 class nsHTMLSelectOptGroupAccessible : public nsHTMLSelectOptionAccessible
 {
 public:
 
   nsHTMLSelectOptGroupAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell);
   virtual ~nsHTMLSelectOptGroupAccessible() {}
 
   /* ----- nsIAccessible ----- */
+  NS_IMETHOD GetRole(PRUint32 *aRole);
   NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
   NS_IMETHOD DoAction(PRUint8 index);  
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD GetNumActions(PRUint8 *_retval);
+  void CacheChildren();
 };
 
 /** ------------------------------------------------------ */
 /**  Finally, the Combobox widgets                         */
 /** ------------------------------------------------------ */
 
 class nsHTMLComboboxListAccessible;