Bug 366527 - build 1-1 relation between tab and tabpanel for accessible object, r=ginn.chen, a=dsicore
authorsurkov.alexander@gmail.com
Sun, 11 Nov 2007 22:02:42 -0800
changeset 7840 f1d72fa0784f3fc9cf40c407d8c0aeb3e55f1ed6
parent 7839 c5e8be3da478f6d991d20b3b6843f29d8810df21
child 7841 389374fe91d079ff6453206a71cd196689b05fac
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersginn.chen, dsicore
bugs366527
milestone1.9b2pre
Bug 366527 - build 1-1 relation between tab and tabpanel for accessible object, r=ginn.chen, a=dsicore
accessible/src/base/nsAccessibilityAtomList.h
accessible/src/base/nsAccessibilityService.cpp
accessible/src/xul/nsXULTabAccessible.cpp
accessible/src/xul/nsXULTabAccessible.h
--- a/accessible/src/base/nsAccessibilityAtomList.h
+++ b/accessible/src/base/nsAccessibilityAtomList.h
@@ -121,16 +121,17 @@ ACCESSIBILITY_ATOM(object, "object")
 ACCESSIBILITY_ATOM(ol, "ol")
 ACCESSIBILITY_ATOM(optgroup, "optgroup")
 ACCESSIBILITY_ATOM(option, "option")
 ACCESSIBILITY_ATOM(q, "q")
 ACCESSIBILITY_ATOM(select, "select")
 ACCESSIBILITY_ATOM(select1, "select1") // XForms
 ACCESSIBILITY_ATOM(svg, "svg")
 ACCESSIBILITY_ATOM(table, "table")
+ACCESSIBILITY_ATOM(tabpanels, "tabpanels") // XUL
 ACCESSIBILITY_ATOM(tbody, "tbody")
 ACCESSIBILITY_ATOM(td, "td")
 ACCESSIBILITY_ATOM(th, "th")
 ACCESSIBILITY_ATOM(tfoot, "tfoot")
 ACCESSIBILITY_ATOM(thead, "thead")
 ACCESSIBILITY_ATOM(textarea, "textarea") // XForms
 ACCESSIBILITY_ATOM(textbox, "textbox")   // XUL
 ACCESSIBILITY_ATOM(toolbaritem, "toolbaritem")   // XUL
@@ -153,16 +154,17 @@ ACCESSIBILITY_ATOM(curpos, "curpos") // 
 ACCESSIBILITY_ATOM(data, "data")
 ACCESSIBILITY_ATOM(droppable, "droppable")   // XUL combo box
 ACCESSIBILITY_ATOM(editable, "editable")
 ACCESSIBILITY_ATOM(_for, "for")
 ACCESSIBILITY_ATOM(hidden, "hidden")   // XUL tree columns
 ACCESSIBILITY_ATOM(href, "href")
 ACCESSIBILITY_ATOM(increment, "increment") // XUL
 ACCESSIBILITY_ATOM(lang, "lang")
+ACCESSIBILITY_ATOM(linkedPanel, "linkedpanel") // XUL
 ACCESSIBILITY_ATOM(maxpos, "maxpos") // XUL
 ACCESSIBILITY_ATOM(minpos, "minpos") // XUL
 ACCESSIBILITY_ATOM(name, "name")
 ACCESSIBILITY_ATOM(onclick, "onclick")
 ACCESSIBILITY_ATOM(src, "src")
 ACCESSIBILITY_ATOM(summary, "summary")
 ACCESSIBILITY_ATOM(tabindex, "tabindex")
 ACCESSIBILITY_ATOM(title, "title")
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -1920,15 +1920,28 @@ nsAccessibilityService::GetAccessibleFor
   if (content) {
     frame = shell->GetPrimaryFrameFor(content);
   }
 
   if (frame && (frame->GetType() == nsAccessibilityAtoms::boxFrame ||
                 frame->GetType() == nsAccessibilityAtoms::scrollFrame)) { 
     parentFrame = frame->GetParent();
     if (parentFrame && parentFrame->GetType() == nsAccessibilityAtoms::deckFrame) {
-      *aAccessible = new nsEnumRoleAccessible(aNode, weakShell, nsIAccessibleRole::ROLE_PROPERTYPAGE);
+      // If deck frame is for xul:tabpanels element then the given node has
+      // tabpanel accessible.
+      nsCOMPtr<nsIContent> parentContent = parentFrame->GetContent();
+      if (parentContent->NodeInfo()->Equals(nsAccessibilityAtoms::tabpanels,
+                                            kNameSpaceID_XUL)) {
+        *aAccessible = new nsXULTabpanelAccessible(aNode, weakShell);
+      } else {
+        *aAccessible =
+          new nsEnumRoleAccessible(aNode, weakShell,
+                                   nsIAccessibleRole::ROLE_PROPERTYPAGE);
+      }
+
+      NS_ENSURE_TRUE(*aAccessible, NS_ERROR_OUT_OF_MEMORY);
+
       NS_ADDREF(*aAccessible);
     }
   }
 
   return NS_OK;
 }
--- a/accessible/src/xul/nsXULTabAccessible.cpp
+++ b/accessible/src/xul/nsXULTabAccessible.cpp
@@ -124,16 +124,108 @@ nsXULTabAccessible::GetState(PRUint32 *a
   if (tab) {
     PRBool selected = PR_FALSE;
     if (NS_SUCCEEDED(tab->GetSelected(&selected)) && selected)
       *aState |= nsIAccessibleStates::STATE_SELECTED;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXULTabAccessible::GetAccessibleRelated(PRUint32 aRelationType,
+                                         nsIAccessible **aRelatedAccessible)
+{
+  NS_ENSURE_ARG_POINTER(aRelatedAccessible);
+  *aRelatedAccessible = nsnull;
+
+  if (!mDOMNode)
+    return NS_ERROR_FAILURE;
+
+  nsresult rv = nsLeafAccessible::GetAccessibleRelated(aRelationType,
+                                                       aRelatedAccessible);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (*aRelatedAccessible ||
+      aRelationType != nsIAccessibleRelation::RELATION_LABEL_FOR)
+    return NS_OK;
+
+  // Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible.
+  // XXX: It makes sense to require the interface from xul:tab to get linked
+  // tabpanel element.
+  nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
+
+  // Check whether tab and tabpanel are related by 'linkedPanel' attribute on
+  // xul:tab element.
+  nsAutoString linkedPanelID;
+  content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::linkedPanel,
+                   linkedPanelID);
+
+  if (!linkedPanelID.IsEmpty()) {
+    nsCOMPtr<nsIDOMDocument> document;
+    mDOMNode->GetOwnerDocument(getter_AddRefs(document));
+    NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+    nsCOMPtr<nsIDOMElement> linkedPanel;
+    document->GetElementById(linkedPanelID, getter_AddRefs(linkedPanel));
+    if (linkedPanel) {
+      nsCOMPtr<nsIDOMNode> linkedPanelNode(do_QueryInterface(linkedPanel));
+      GetAccService()->GetAccessibleInWeakShell(linkedPanelNode, mWeakShell,
+                                                aRelatedAccessible);
+      return NS_OK;
+    }
+  }
+
+  // If there is no 'linkedPanel' attribute on xul:tab element then we
+  // assume tab and tabpanels are related 1 to 1. We follow algorithm from
+  // the setter 'selectedIndex' of tabbox.xml#tabs binding.
+
+  nsCOMPtr<nsIAccessible> tabsAcc = GetParent();
+  NS_ENSURE_TRUE(tabsAcc && Role(tabsAcc) == nsIAccessibleRole::ROLE_PAGETABLIST,
+                 NS_ERROR_FAILURE);
+
+  PRInt32 tabIndex = -1;
+
+  nsCOMPtr<nsIAccessible> childAcc;
+  tabsAcc->GetFirstChild(getter_AddRefs(childAcc));
+  while (childAcc) {
+    if (Role(childAcc) == nsIAccessibleRole::ROLE_PAGETAB)
+      tabIndex++;
+
+    if (childAcc == this)
+      break;
+
+    nsCOMPtr<nsIAccessible> acc;
+    childAcc->GetNextSibling(getter_AddRefs(acc));
+    childAcc.swap(acc);
+  }
+
+  nsCOMPtr<nsIAccessible> tabBoxAcc;
+  tabsAcc->GetParent(getter_AddRefs(tabBoxAcc));
+  NS_ENSURE_TRUE(tabBoxAcc && Role(tabBoxAcc) == nsIAccessibleRole::ROLE_PANE,
+                 NS_ERROR_FAILURE);
+
+  tabBoxAcc->GetFirstChild(getter_AddRefs(childAcc));
+  while (childAcc) {
+    if (Role(childAcc) == nsIAccessibleRole::ROLE_PROPERTYPAGE) {
+      if (tabIndex == 0) {
+        NS_ADDREF(*aRelatedAccessible = childAcc);
+        return NS_OK;
+      }
+
+      tabIndex--;
+    }
+
+    nsCOMPtr<nsIAccessible> acc;
+    childAcc->GetNextSibling(getter_AddRefs(acc));
+    childAcc.swap(acc);
+  }
+
+  return NS_OK;
+}
+
 nsresult
 nsXULTabAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
 {
   NS_ENSURE_ARG_POINTER(aAttributes);
   NS_ENSURE_TRUE(mDOMNode, NS_ERROR_FAILURE);
 
   nsresult rv = nsLeafAccessible::GetAttributesInternal(aAttributes);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -220,8 +312,116 @@ NS_IMETHODIMP nsXULTabsAccessible::GetVa
 }
 
 /** no name*/
 NS_IMETHODIMP nsXULTabsAccessible::GetName(nsAString& _retval)
 {
   _retval.Truncate();
   return NS_OK;
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// nsXULTabpanelAccessible
+
+nsXULTabpanelAccessible::
+  nsXULTabpanelAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell):
+  nsAccessibleWrap(aNode, aShell)
+{
+}
+
+NS_IMETHODIMP
+nsXULTabpanelAccessible::GetRole(PRUint32 *aRole)
+{
+  NS_ENSURE_ARG_POINTER(aRole);
+
+  *aRole = nsIAccessibleRole::ROLE_PROPERTYPAGE;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULTabpanelAccessible::GetAccessibleRelated(PRUint32 aRelationType,
+                                              nsIAccessible **aRelatedAccessible)
+{
+  NS_ENSURE_ARG_POINTER(aRelatedAccessible);
+  *aRelatedAccessible = nsnull;
+
+  if (!mDOMNode)
+    return NS_ERROR_FAILURE;
+
+  nsresult rv = nsAccessibleWrap::GetAccessibleRelated(aRelationType,
+                                                       aRelatedAccessible);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (*aRelatedAccessible ||
+      aRelationType != nsIAccessibleRelation::RELATION_LABELLED_BY)
+    return NS_OK;
+
+  // Expose 'LABELLED_BY' relation on tabpanel accessible for tab accessible.
+  nsCOMPtr<nsIAccessible> tabBoxAcc;
+  GetParent(getter_AddRefs(tabBoxAcc));
+  NS_ENSURE_TRUE(tabBoxAcc && Role(tabBoxAcc) == nsIAccessibleRole::ROLE_PANE,
+                 NS_ERROR_FAILURE);
+
+  PRInt32 tabpanelIndex = -1;
+  nsCOMPtr<nsIAccessible> tabsAcc;
+
+  PRBool isTabpanelFound = PR_FALSE;
+  nsCOMPtr<nsIAccessible> childAcc;
+  tabBoxAcc->GetFirstChild(getter_AddRefs(childAcc));
+  while (childAcc && (!tabsAcc || !isTabpanelFound)) {
+    if (Role(childAcc) == nsIAccessibleRole::ROLE_PAGETABLIST)
+      tabsAcc = childAcc;
+
+    if (!isTabpanelFound &&
+        Role(childAcc) == nsIAccessibleRole::ROLE_PROPERTYPAGE)
+      tabpanelIndex++;
+
+    if (childAcc == this)
+      isTabpanelFound = PR_TRUE;
+
+    nsCOMPtr<nsIAccessible> acc;
+    childAcc->GetNextSibling(getter_AddRefs(acc));
+    childAcc.swap(acc);
+  }
+
+  if (!tabsAcc || tabpanelIndex == -1)
+    return NS_OK;
+
+  nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
+  nsIAtom *atomID = content->GetID();
+
+  nsCOMPtr<nsIAccessible> foundTabAcc;
+  tabsAcc->GetFirstChild(getter_AddRefs(childAcc));
+  while (childAcc) {
+    if (Role(childAcc) == nsIAccessibleRole::ROLE_PAGETAB) {
+      if (atomID) {
+        nsCOMPtr<nsIAccessNode> tabAccNode(do_QueryInterface(childAcc));
+        nsCOMPtr<nsIDOMNode> tabNode;
+        tabAccNode->GetDOMNode(getter_AddRefs(tabNode));
+        nsCOMPtr<nsIContent> tabContent(do_QueryInterface(tabNode));
+        NS_ENSURE_TRUE(tabContent, NS_ERROR_FAILURE);
+
+        if (tabContent->AttrValueIs(kNameSpaceID_None,
+                                    nsAccessibilityAtoms::linkedPanel, atomID,
+                                    eCaseMatters)) {
+          NS_ADDREF(*aRelatedAccessible = childAcc);
+          return NS_OK;
+        }
+      }
+
+      if (tabpanelIndex == 0) {
+        foundTabAcc = childAcc;
+        if (!atomID)
+          break;
+      }
+
+      tabpanelIndex--;
+    }
+
+    nsCOMPtr<nsIAccessible> acc;
+    childAcc->GetNextSibling(getter_AddRefs(acc));
+    childAcc.swap(acc);
+  }
+
+  NS_IF_ADDREF(*aRelatedAccessible = foundTabAcc);
+  return NS_OK;
+}
+
--- a/accessible/src/xul/nsXULTabAccessible.h
+++ b/accessible/src/xul/nsXULTabAccessible.h
@@ -38,28 +38,36 @@
 
 #ifndef _nsXULTabAccessible_H_
 #define _nsXULTabAccessible_H_
 
 // NOTE: alphabetically ordered
 #include "nsBaseWidgetAccessible.h"
 #include "nsXULSelectAccessible.h"
 
-/** An individual tab */
+/**
+ * An individual tab, xul:tab element
+ */
 class nsXULTabAccessible : public nsLeafAccessible
 {
 public:
   enum { eAction_Switch = 0 };
 
   nsXULTabAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
+
+  // nsIAccessible
   NS_IMETHOD GetRole(PRUint32 *_retval); 
   NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
   NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
+  NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType,
+                                  nsIAccessible **aRelatedAccessible);
+
+  // nsAccessible
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 };
 
 /** 
   * Contains a tabs object and a tabPanels object. A complete
   *    entity with relationships between tabs and content to
   *    be displayed in the tabpanels object
   */
@@ -67,21 +75,41 @@ class nsXULTabBoxAccessible : public nsA
 {
 public:
   nsXULTabBoxAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
   NS_IMETHOD GetRole(PRUint32 *_retval); 
   NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
   //NS_IMETHOD GetChildCount(PRInt32 *_retval); // aaronl remove this?
 };
 
-/** merely a container of tab obejcts */
+/**
+ * A container of tab obejcts, xul:tabs element.
+ */
 class nsXULTabsAccessible : public nsXULSelectableAccessible
 {
 public:
   nsXULTabsAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
   NS_IMETHOD GetRole(PRUint32 *_retval);
   NS_IMETHOD GetNumActions(PRUint8 *_retval);
   NS_IMETHOD GetState(PRUint32 *aState, PRUint32 *aExtraState);
   NS_IMETHOD GetValue(nsAString& _retval);
   NS_IMETHOD GetName(nsAString& _retval);
 };
 
+/**
+ * A tabpanel object, child elements of xul:tabpanels element. Note,the object
+ * is created from nsAccessibilityService::GetAccessibleForDeckChildren()
+ * method and we do not use nsIAccessibleProvider interface here because
+ * all children of xul:tabpanels element acts as xul:tabpanel element.
+ */
+class nsXULTabpanelAccessible : public nsAccessibleWrap
+{
+public:
+  nsXULTabpanelAccessible(nsIDOMNode *aNode, nsIWeakReference *aShell);
+
+  // nsIAccessible
+  NS_IMETHOD GetRole(PRUint32 *aRole);
+  NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType,
+                                  nsIAccessible **aRelatedAccessible);
+};
+
 #endif
+