bug 453417 - Implement new aria-label property, r=surkov
authorMarco Zehe <marco.zehe@googlemail.com>
Thu, 04 Sep 2008 16:11:40 +0200
changeset 18793 8ebb9800a80de79fd169d6e8d367309a1c59e773
parent 18792 eb481b7dfdb1593d162f06f522753d49bc92594c
child 18794 3997eb4f3f959ebfd2224b504d519161b8432ab8
push idunknown
push userunknown
push dateunknown
reviewerssurkov
bugs453417
milestone1.9.1b1pre
bug 453417 - Implement new aria-label property, r=surkov
accessible/src/base/nsAccessibilityAtomList.h
accessible/src/base/nsAccessibilityService.cpp
accessible/src/base/nsAccessible.cpp
accessible/src/html/nsHTMLFormControlAccessible.cpp
accessible/src/html/nsHTMLImageAccessible.cpp
accessible/src/xforms/nsXFormsAccessible.cpp
accessible/src/xforms/nsXFormsFormControlsAccessible.cpp
accessible/tests/mochitest/Makefile.in
accessible/tests/mochitest/nsIAccessible_name.js
accessible/tests/mochitest/test_nsIAccessible_name.html
accessible/tests/mochitest/test_nsIAccessible_name.xul
--- a/accessible/src/base/nsAccessibilityAtomList.h
+++ b/accessible/src/base/nsAccessibilityAtomList.h
@@ -208,16 +208,17 @@ ACCESSIBILITY_ATOM(aria_describedby, "ar
 ACCESSIBILITY_ATOM(aria_droppable, "aria-droppable")
 ACCESSIBILITY_ATOM(aria_disabled, "aria-disabled")
 ACCESSIBILITY_ATOM(aria_dropeffect, "aria-dropeffect")
 ACCESSIBILITY_ATOM(aria_expanded, "aria-expanded")
 ACCESSIBILITY_ATOM(aria_flowto, "aria-flowto")
 ACCESSIBILITY_ATOM(aria_grab, "aria-grab")
 ACCESSIBILITY_ATOM(aria_haspopup, "aria-haspopup")
 ACCESSIBILITY_ATOM(aria_invalid, "aria-invalid")
+ACCESSIBILITY_ATOM(aria_label, "aria-label")
 ACCESSIBILITY_ATOM(aria_labelledby, "aria-labelledby")
 ACCESSIBILITY_ATOM(aria_level, "aria-level")
 ACCESSIBILITY_ATOM(aria_live, "aria-live")
 ACCESSIBILITY_ATOM(aria_multiline, "aria-multiline")
 ACCESSIBILITY_ATOM(aria_multiselectable, "aria-multiselectable")
 ACCESSIBILITY_ATOM(aria_owns, "aria-owns")
 ACCESSIBILITY_ATOM(aria_posinset, "aria-posinset")
 ACCESSIBILITY_ATOM(aria_pressed, "aria-pressed")
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -1623,16 +1623,17 @@ nsAccessibilityService::HasUniversalAria
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_controls) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_datatype) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_describedby) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_dropeffect) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_flowto) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_grab) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_haspopup) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_invalid) ||
+         aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_live) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_owns) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_relevant) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_required) ||
          aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_sort);
 }
 
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -1838,18 +1838,24 @@ nsresult nsAccessible::GetTextFromRelati
   */
 nsresult nsAccessible::GetHTMLName(nsAString& aLabel, PRBool aCanAggregateSubtree)
 {
   nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
   if (!content) {
     return NS_ERROR_FAILURE;   // Node shut down
   }
 
-  // Check for DHTML accessibility labelledby relationship property
+  // Check for aria-label property
   nsAutoString label;
+  if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label, label)) {
+    aLabel = label;
+    return NS_OK;
+  }
+
+  // Check for aria-labelledby relationship property
   nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, label);
   if (NS_SUCCEEDED(rv)) {
     aLabel = label;
     return rv;
   }
 
   nsIContent *labelContent = GetHTMLLabelContent(content);
   if (labelContent) {
@@ -1889,18 +1895,24 @@ nsresult nsAccessible::GetHTMLName(nsASt
   *  the control that uses the control="controlID" syntax will use
   *  the child label for its Name.
   */
 nsresult nsAccessible::GetXULName(nsAString& aLabel, PRBool aCanAggregateSubtree)
 {
   nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
   NS_ASSERTION(content, "No nsIContent for DOM node");
 
-  // First check for label override via accessibility labelledby relationship
+  // First check for label override via aria-label property
   nsAutoString label;
+  if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label, label)) {
+    aLabel = label;
+    return NS_OK;
+  }
+
+  // Second check for label override via aria-labelledby relationship
   nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, label);
   if (NS_SUCCEEDED(rv)) {
     aLabel = label;
     return rv;
   }
 
   // CASE #1 (via label attribute) -- great majority of the cases
   nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl(do_QueryInterface(mDOMNode));
--- a/accessible/src/html/nsHTMLFormControlAccessible.cpp
+++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp
@@ -283,16 +283,18 @@ nsHTMLButtonAccessible::GetName(nsAStrin
   nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
   if (!content) {
     return NS_ERROR_FAILURE; // Node shut down
   }
 
   nsAutoString name;
   // Prefer aria-labelledby attribute for name
   if (content->HasAttr(kNameSpaceID_None,
+                       nsAccessibilityAtoms::aria_label) ||
+      content->HasAttr(kNameSpaceID_None,
                        nsAccessibilityAtoms::aria_labelledby)) {
     GetHTMLName(name, PR_FALSE);
   }
 
   if (name.IsEmpty()) {
     // no label from HTML or ARIA
     if (!content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::value,
                           name) &&
--- a/accessible/src/html/nsHTMLImageAccessible.cpp
+++ b/accessible/src/html/nsHTMLImageAccessible.cpp
@@ -138,18 +138,19 @@ NS_IMETHODIMP nsHTMLImageAccessible::Get
   NS_ASSERTION(content, "Image node always supports nsIContent");
     
   // No alt attribute means AT can repair if there is no accessible name
   // alt="" with no title or aria-labelledby means image is presentational and 
   // AT should leave accessible name empty
   PRBool hasAltAttrib =
     content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::alt, aName);
   if (aName.IsEmpty()) {
-    if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby)) {
-      // Use HTML label or DHTML accessibility's labelledby attribute for name
+    if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label) ||
+        content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby)) {
+      // Use HTML label or DHTML accessibility's label or labelledby attribute for name
       // GetHTMLName will also try title attribute as a last resort
       GetHTMLName(aName, PR_FALSE);
     }
     if (aName.IsEmpty()) { // No name from alt or aria-labelledby
       content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::title, aName);
       if (!hasAltAttrib && aName.IsEmpty()) { 
         // Still no accessible name and no alt attribute is present.
         // SetIsVoid() is different from empty string -- this means a name was not 
--- a/accessible/src/xforms/nsXFormsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsAccessible.cpp
@@ -219,17 +219,29 @@ nsXFormsAccessible::GetState(PRUint32 *a
     *aState |= nsIAccessibleStates::STATE_INVALID;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXFormsAccessible::GetName(nsAString& aName)
 {
+  nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
+  if (!content) {
+    return NS_ERROR_FAILURE;   // Node shut down
+  }
+
+  // Check for ARIA label property
   nsAutoString name;
+  if (content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_label, name)) {
+    aName = name;
+    return NS_OK;
+  }
+
+  // Check for ARIA labelledby relationship property
   nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, name);
   if (NS_SUCCEEDED(rv) && !name.IsEmpty()) {
     aName = name;
     return NS_OK;
   }
 
   // search the xforms:label element
   return GetBoundChildElementValue(NS_LITERAL_STRING("label"), aName);
--- a/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsFormControlsAccessible.cpp
@@ -53,16 +53,17 @@ nsXFormsLabelAccessible::GetRole(PRUint3
 
   *aRole = nsIAccessibleRole::ROLE_LABEL;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXFormsLabelAccessible::GetName(nsAString& aName)
 {
+  // XXX Correct name calculation for this, see bug 453594.
   nsAutoString name;
   nsresult rv = GetTextFromRelationID(nsAccessibilityAtoms::aria_labelledby, name);
   aName = name;
   return rv;
 }
 
 NS_IMETHODIMP
 nsXFormsLabelAccessible::GetDescription(nsAString& aDescription)
--- a/accessible/tests/mochitest/Makefile.in
+++ b/accessible/tests/mochitest/Makefile.in
@@ -46,16 +46,17 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =\
 		moz.png \
 		longdesc_src.html \
 		common.js \
 		nsIAccessible_actions.js \
 		nsIAccessible_name.css \
+		nsIAccessible_name.js \
 		nsIAccessible_name.xbl \
 		nsIAccessibleEditableText.js \
 		test_aria_activedescendant.html \
 		test_aria_role_article.html \
 		test_bug368835.xul \
 		test_bug420863.html \
 		test_cssattrs.html \
 		test_groupattrs.xul \
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/nsIAccessible_name.js
@@ -0,0 +1,10 @@
+function testName(aID, aName)
+{
+  var acc = getAccessible(aID);
+  if (!acc) {
+    ok(false, "No accessible for " + aID + "!");
+  }
+
+  is(acc.name, aName, "Wrong name of the accessible for " + aID);
+  return acc;
+}
--- a/accessible/tests/mochitest/test_nsIAccessible_name.html
+++ b/accessible/tests/mochitest/test_nsIAccessible_name.html
@@ -1,50 +1,33 @@
 <html>
 
 <head>
   <title>nsIAccessible::name calculation</title>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
-  <script type="application/javascript" src="chrome://mochikit/content/MochiKit/packed.js"></script>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/common.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/nsIAccessible_name.js"></script>
 
   <script type="application/javascript">
-    const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
-    const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
-
-    var gAccRetrieval = null;
-
-    function testName(aID, aName)
-    {
-      var elm = document.getElementById(aID);
-      if (!elm) {
-        ok(false, "There is no element with ID " + aID);
-        return;
-      }
-
-      var acc = null;
-      try {
-        acc = gAccRetrieval.getAccessibleFor(elm);
-      } catch(e) {
-      }
-
-      if (!acc) {
-        ok(false, "There is no accessible for " + aID);
-        return;
-      }
-
-      is(acc.name, aName, "Wrong name of the accessible for " + aID);
-    }
-
     function doTest()
     {
-      gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
-                      getService(nsIAccessibleRetrieval);
+      // aria-label
 
+      // Simple label provided via ARIA
+      testName("btn_simple_aria_label", "I am a button");
+
+      // aria-label and aria-labelledby, expect aria-label
+      testName("btn_both_aria_labels", "I am a button, two");
 
       //////////////////////////////////////////////////////////////////////////
       // aria-labelledby
       
       // Single relation. The value of 'aria-labelledby' contains the ID of
       // an element. Gets the name from text node of that element.
       testName("btn_labelledby_text", "text");
 
@@ -148,16 +131,24 @@
      title="mochitest for accessible name calculating">
     Mozilla Bug 444279
   </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
+  <!-- aria-label, simple label -->
+  <span id="btn_simple_aria_label" role="button" aria-label="I am a button"/>
+  <br/>
+  <!-- aria-label plus aria-labelledby -->
+  <span id="btn_both_aria_labels" role="button" aria-label="I am a button, two"
+        aria-labelledby="labelledby_text"/>
+  <br/>
+
   <!-- aria-labelledby, single relation -->
   <span id="labelledby_text">text</span>
   <button id="btn_labelledby_text"
           aria-labelledby="labelledby_text">1</button>
   <br/>
 
   <!-- aria-labelledby, multiple relations -->
   <span id="labelledby_text1">text1</span>
--- a/accessible/tests/mochitest/test_nsIAccessible_name.xul
+++ b/accessible/tests/mochitest/test_nsIAccessible_name.xul
@@ -8,51 +8,31 @@
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="Accessibility Name Calculating Test.">
 
   <script type="application/javascript" 
           src="chrome://mochikit/content/MochiKit/packed.js"></script>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/common.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/a11y/accessible/nsIAccessible_name.js"></script>
   <script type="application/javascript">
   <![CDATA[
-    const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval;
-    const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
-
-    var gAccRetrieval = null;
-
-    function testName(aID, aName)
-    {
-      var elm = document.getElementById(aID);
-      if (!elm) {
-        ok(false, "There is no element with ID " + aID);
-        return null;
-      }
-
-      var acc = null;
-      try {
-        acc = gAccRetrieval.getAccessibleFor(elm);
-      } catch(e) {
-      }
-
-      if (!acc) {
-        ok(false, "There is no accessible for " + aID);
-        return null;
-      }
-
-      is(acc.name, aName, "Wrong name of the accessible for " + aID);
-      return acc;
-    }
-
     function doTest()
     {
-      gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"].
-                      getService(nsIAccessibleRetrieval);
+      // aria-label
 
+      // Simple label provided via ARIA
+      testName("btn_simple_aria_label", "I am a button");
+
+      // aria-label and aria-labelledby, expect aria-label
+      testName("btn_both_aria_labels", "I am a button, two");
 
       //////////////////////////////////////////////////////////////////////////
       // aria-labelledby
       
       // Single relation. The value of 'aria-labelledby' contains the ID of
       // an element. Gets the name from text node of that element.
       testName("btn_labelledby_text", "text");
 
@@ -193,16 +173,22 @@
     </a>
     <p id="display"></p>
     <div id="content" style="display: none">
     </div>
     <pre id="test">
     </pre>
   </body>
 
+  <!-- aria-label, simple label -->
+  <button id="btn_simple_aria_label" aria-label="I am a button"/>
+  <!-- aria-label plus aria-labelledby -->
+  <button id="btn_both_aria_labels" aria-label="I am a button, two"
+        aria-labelledby="labelledby_text"/>
+
   <!-- aria-labelledby, single relation -->
   <description id="labelledby_text">text</description>
   <button id="btn_labelledby_text"
           aria-labelledby="labelledby_text"/>
 
   <!-- aria-labelledby, multiple relations -->
   <description id="labelledby_text1">text1</description>
   <description id="labelledby_text2">text2</description>